Шановні друзі!
26 та 31 грудня магазин працюватиме до 16 години. 1 та 2 січня магазин працювати не буде.
Вітаємо всіх з Різдвом та Новим Роком!
Мобільна версія Форум Arduino Документація Гарантійні умови 0 0
UA RU
Графік роботи магазину:
Пн-Пт: 8.00 - 19.00
Сб: 10.00 - 17.00
Нд: вихідний
Каталог
Напиши статтю і отримай знижку!

Дисплей SSD1306 128x64 версія з SPI та модуль розширення портів

2023-09-05

Всі статті →

Придбав дисплей ssd1306 128x64 версію з SPI інтерфейсом щоб підключити до модуля розширення портів (код: AOC824) напряму, без Ардуіно, але виявилося, що в версії з SPI є нюанси, тому розкажу про підключення детальніше.

У дисплея є звичайні входи SPI: CS, SCK, MOSI - тут все добре, і є також входи RES і D/C - от з ними розберемося.

Одразу приведу робочу схему підключення і поясню призначення частин.

Нижні три деталі виконують "підтяжку з затримкою" для RES, щоб входи мікросхеми, які відповідають за вибір інтерфейсу обміну (I2C, SPI та ще кілька), встигли встановитись. При підтяжці одним лише резистором робота буде дуже нестабільною, в чому особисто переконався. Конденсатор робить наростання напруги на RES при включенні живлення повільним, а діод потрібен, щоб при вимкненні живлення швидко розрядити конденсатор. Номінали вказані приблизні, можна взяти резистор 4.7k або 20k, конденсатор - 1uF або 10uF. До речі, ця схема є в версії плати з I2C інтерфейсом.

Якщо в протоколі I2C для цього дислея першим байтом вказувався тип даних, які записуються - дані (0x40) чи команди (0x00), то в SPI цей байт не передаємо, а за вибір типу відповідає вхід D/C (data - high / command - low). Спочатку я використовував схему без верхнього діода, P0 (на схемі CS0) через підсистему gpio керував станом D/C, а весь обмін був через spi.1 (CS1 керує сигналом CS). Схема робоча, але виявилась незручною, тоді я придумав красивіше рішення: spi.1 зробити для надсилання даних, а spi.0 - для надсилання команд. Для цього CS0 і CS1 повинні мати змогу незалежно один від одного понижувати рівень одного й того ж CS, це і реалізується за допомогою верхнього діода, таблиця станів виглядає так:

          CS0 CS1 => CS D/C

idle    1       1           1       1

spi.0  0       1           0       0

spi.1  1       0           0       1

Розділення на різні інтерфейси дозволяє не так плутатися, в якому режимі D/C зараз знаходимось, крім того можна для інтерфейсу даних окремо встановити, наприклад, порядок передачі бітів у байті (LSB замість MSB). Для створення двох spidev виконуєм команди:

# echo 0 spidev >/sys/devices/w1_bus_master1/20-594e480f4241/spi_bind

# echo 1 spidev >/sys/devices/w1_bus_master1/20-594e480f4241/spi_bind

З'являться два пристрої:

# ls -ld /dev/spi*

crw-rw-rw- 1 root root 153, 0 Jul 31 14:21 /dev/spidev0.0

crw-rw-rw- 1 root root 153, 1 Jul 31 14:21 /dev/spidev0.1

lrwxrwxrwx 1 root root 9 Jul 31 14:21 /dev/spidev_20-594e480f4241.0 -> spidev0.0

lrwxrwxrwx 1 root root 9 Jul 31 14:21 /dev/spidev_20-594e480f4241.1 -> spidev0.1

Як ми знаємо, SPI - повнодуплексний інтерфейс, тому в Linux для роботи з файловим дескриптором spidev використовується не read/write а ioctl для одночасного запису і читання. Як виявляється, і про це сказано в документації, можна використовувати і read і/або write, але обмін буде напівдуплексним. А оскільки у екранчика і так передача лише в один бік, тому сміливо можна просто перенаправляти вивід в файл spidev (підсистема ядра зробить все, що потрібно - понизить відповідний CS, передасть дані і поверне високий рівень CS).

Ось мінімальна послідовність команд, щоб просто увімкнути дисплей (при цьому на екранчику відобразиться випадковий стан його пам'яті):

# echo -ne "\x8d\x14\xaf" >/dev/spidev_20-594e480f4241.0

Можна встановити, наприклад, вертикальний режим адресації і записати 1024 нульові байти в пам'ять:

# echo -ne "\xc8\x20\x01" >/dev/spidev_20-594e480f4241.0

# head -c 1024 /dev/zero >/dev/spidev_20-594e480f4241.1

Дисплей очиститься, це відбуватиметься зверху вниз, якщо він повернутий як на малюнку на схемі (у портретному режимі, контактами зліва). Якщо бути ще точнішим, то кожен рядок пікселів заповнювався зліва направо, цей напрям був заданий додатковою командою xc8. Я так зробив навмисно, щоб порядок байтів пам'яті екранчика був таким же, як порядок байтів у растрових зображень.

Є такий підходящий формат для монохромних зображень без компресії .pbm (Portable Bitmap), який можна записувати в пам'ять дисплея як є. В цьому форматі нульові біти відповідають білому кольору, а одиничні - чорному, тому потрібно ввімкнути інверсію (дисплей стане білим, бо в пам'яті нулі):

# echo -ne "\xa7" >/dev/spidev_20-594e480f4241.0

Є ще один нюанс: самий перший, верхній лівий піксель в форматі .pbm відповідає старшому (7-ому) біту першого байту, а в пам'яті екранчика цей самий піксель відповідає молодшому (0-му) біту першого байту, тому потрібно якимось чином змінити порядок бітів у всіх байтах зображення. Це можна зробити переконвертувавши файл, а можна зробити хитріше: SPI-приймач (екранчик) очікує байти в режимі MSB (від старшого до молодшого біта), а передавач (spidev) можна налаштувати в режим LSB (від молодшого до старшого біта). Змінимо режим spidev, який відповідає за передачу даних, за допомогою невеликої програми:

#include <fcntl.h>

#include <sys/ioctl.h>

#include <linux/spi/spidev.h>

int main(int argc, char **argv) {

     char s = 1;

     int fd = open(argv[1], O_RDWR);

     ioctl(fd, SPI_IOC_WR_LSB_FIRST, &s);

}

Компілюєм і запускаєм:

# gcc spi_lsb.c -o spi_lsb

# ./spi_lsb /dev/spidev_20-594e480f4241.1

Тепер дисплей готовий до прийому зображень.

Підготував ось таке зображення mona.pbm розміром 64x128:

Відправляєм його на дисплей, але не весь файл, а частину після заголовків, це рівно 1024 останні байти файлу:

# tail -c 1024 mona.pbm >/dev/spidev_20-594e480f4241.1

Ось, що відобразиться на дисплеї:

Тепер коротко мінімальний список команд:

# echo 0 spidev >/sys/devices/w1_bus_master1/20-594e480f4241/spi_bind

# echo 1 spidev >/sys/devices/w1_bus_master1/20-594e480f4241/spi_bind

# echo -ne "\x8d\x14\xa7\xc8\xaf\x20\x01" >/dev/spidev_20-594e480f4241.0

# ./spi_lsb /dev/spidev_20-594e480f4241.1

# tail -c 1024 mona.pbm >/dev/spidev_20-594e480f4241.1

Перші чотири команди можна доручити udevd виконувати автоматично щоразу при появі на шині 1-wire модуля розширення портів з підключеним екранчиком:

ACTION=="add", SUBSYSTEM=="w1", KERNEL=="20-594e480f4241",\

ATTR{spi_bind}+="0 spidev", ATTR{spi_bind}+="1 spidev"

ACTION=="add", SUBSYSTEM=="spidev", KERNELS=="20-594e480f4241",\

DEVPATH=="*/spi0.0/*", SYMLINK+="ssd1306_cmd", MODE="0666",\

RUN+="/bin/sh -c '/bin/cat /usr/local/share/ssd1306_init.bin >/dev/%k'"

ACTION=="add", SUBSYSTEM=="spidev", KERNELS=="20-594e480f4241",\

DEVPATH=="*/spi0.1/*", SYMLINK+="ssd1306_data", MODE="0666",\

RUN+="/usr/local/bin/spi_lsb /dev/%k"

Необхідні файли створюєм один раз так:

# gcc spi_lsb.c -o /usr/local/bin/spi_lsb

# echo -ne "\x8d\x14\xa7\xc8\xaf\x20\x01" >/usr/local/share/ssd1306_init.bin

Правила створять також символьні посилання: /dev/ssd1306_cmd та /dev/ssd1306_data для відправки команд і даних відповідно.

Команди для конвертації будь-яких зображень в формат .pbm розміром 64x128:

# convert -resize 64x128^ -gravity center -crop 64x128+0+0 +repage -monochrome -dither FloydSteinberg mona_big.jpg mona1.pbm

# convert -resize 64x128^ -gravity center -crop 64x128+0+0 +repage -remap pattern:gray50 -dither FloydSteinberg mona_big.jpg mona.pbm

Два варіанти на вибір. Якщо зображення в ландшафтному форматі, то спочатку треба його повернути:

# convert -rotate 90 -resize 64x128^ -gravity center -crop 64x128+0+0 +repage -monochrome -dither FloydSteinberg landscape.jpg img.pbm

Ваша оцінка статті:

Відмінно
Добре
Задовільно
Погано
Дуже погано

Загальна оцінка:

Оцінка "Дисплей SSD1306 128x64 версія з SPI та модуль розширення портів"
5 з 5
зроблена на основі 1 оцінки 1 клієнтських відгуку.

Дякуємо Вам за звернення! Ваш відгук з'явиться після модерації адміністратором.
Igor
19.11.2023 21:14:48
Чудовий матеріал. Корисний для розробників МП систем. Щира подяка автору цінну інформацію.
оплата картами Visa і MasterCard