Ви не увійшли.
Сторінки 1
Є гарна новина для користувачів такого модуля розширення портів:
Написав для нього драйвер під Linux.
Нагадаю, що вміє модуль розширення портів:
5шт GPIO з режимами: input, input_pullup, low, high
Активація тривоги при появі заданого логічного рівня на GPIO
Додаткова функція АЦП на 4-ох портах
Активація тривоги при виході результату АЦП за діапазон
Додаткова функція ШІМ на 1-ому порту
Конвертер 1-wire в SPI-master
В Linux є підходящі підсистеми: gpio, industrialio, pwm та шина SPI, реалізував можливість їх окремого підключення до кожного пристрою лише за необхідності під час роботи. Підтримку кожної підсистеми в драйвері пристрою також можна повністю відключити на етапі конфігурування перед компіляцією:
Device Drivers --->
<M> Dallas's 1-wire support --->
1-wire Slaves --->
<M> DS2450 compatible Port Expander 0x20 family support
[*] Include 1-wire to SPI-master bridge support
[*] Include GPIO framework support
[*] Include PWM framework support
[*] Include Industrial IO framework support
Як бачимо, без підтримки залишились "тривоги". Хоч в підсистемі w1 і є підтримка пошуку на шині пристроїв з тривогою, але вона ніяк не задіяна, лише доступна через інтерфейс netlink для простору користувача.
Крім того за будь-яких обставин залишається доступний інтерфейс керування пристроєм через його память (опис буде нижче).
Підсистеми ядра дають уніфікований (такий же як і для інших пристроїв, ховаючи за інтерфейсом особливості пристроїв) інтерфейс до пристрою умовно трьох видів: внутрішній між драйверами, через символьний пристрій (в /dev) для програм на Сі, через файлову систему sysfs (в /sys) для доступу з командного рядка.
Розглянемо, що пропонує драйвер.
Без драйвера в sysfs модуль розширення портів виглядає так:
# ls -l /sys/devices/w1_bus_master1/20-594e480f4241
lrwxrwxrwx 1 root root 0 чер 25 15:49 driver -> ../../../bus/w1/drivers/w1_slave_driver
-r--r--r-- 1 root root 4096 чер 25 15:49 id
-r--r--r-- 1 root root 4096 чер 25 15:49 name
drwxr-xr-x 2 root root 0 чер 25 15:49 power
-rw-r--r-- 1 root root 4096 чер 25 15:49 rw
lrwxrwxrwx 1 root root 0 чер 25 15:49 subsystem -> ../../../bus/w1
-rw-r--r-- 1 root root 4096 чер 25 15:45 uevent
А з драйвером так:
# ls -l /sys/devices/w1_bus_master1/20-594e480f4241
--w------- 1 root root 0 чер 25 16:17 convert
lrwxrwxrwx 1 root root 0 чер 25 16:17 driver -> ../../../bus/w1/drivers/w1_slave_driver
--w------- 1 root root 0 чер 25 16:17 gpio_enable
-r--r--r-- 1 root root 4096 чер 25 16:17 id
--w------- 1 root root 0 чер 25 16:17 iio_enable
-rw-r--r-- 1 root root 32 чер 25 16:17 memory
-r--r--r-- 1 root root 4096 чер 25 16:17 name
drwxr-xr-x 2 root root 0 чер 25 16:17 power
--w------- 1 root root 0 чер 25 16:17 pwm_enable
--w------- 1 root root 0 чер 25 16:17 spi_bind
lrwxrwxrwx 1 root root 0 чер 25 16:17 subsystem -> ../../../bus/w1
-rw-r--r-- 1 root root 4096 чер 25 16:16 uevent
Коротко розгляну кожен новий файл.
convert
В нього потрібно записати рівно два байти для запуску АЦП, перший байт - маска каналів, другий - як заповнити комірки памяті перед запуском АЦП (детальніше в даташиті на DS2450). Наприклад так:
# echo -ne '\x0f\x00' >/sys/devices/w1_bus_master1/20-594e480f4241/convert
Результати АЦП будуть збережені в пам'яті (див. наступний абзац).
memory
Відображає 32 байти пам'яті пристрою, файл можна читати і писати, причому з довільної позиції (підтримується lseek). Ось так виглядають його нутрощі:
# hexdump -C /sys/devices/w1_bus_master1/20-594e480f4241/memory
00000000 c0 ff cc 3e bd 43 68 29 20 8c 00 8c 00 8c 00 8c |...>.Ch) .......|
00000010 00 ff 00 ff 00 ff 00 ff 00 00 00 00 40 00 ff 00 |............@...|
Опис комірок пам'яті є тут.
Для керування пристроєм через memory і convert є бібліотека на Сі з кількома прикладами.
gpio_enable
Дозволяє запис 0 або 1 і відповідно підключає або відключає підсистему ядра gpio до відповідного модуля розширення портів.
# echo 1 >/sys/devices/w1_bus_master1/20-594e480f4241/gpio_enable
# ls -l /sys/devices/w1_bus_master1/20-594e480f4241
--w------- 1 root root 0 чер 25 18:00 convert
lrwxrwxrwx 1 root root 0 чер 25 18:10 driver -> ../../../bus/w1/drivers/w1_slave_driver
drwxr-xr-x 3 root root 0 чер 25 18:10 gpio
--w------- 1 root root 0 чер 25 18:10 gpio_enable
drwxr-xr-x 3 root root 0 чер 25 18:10 gpiochip6
-r--r--r-- 1 root root 4096 чер 25 18:10 id
--w------- 1 root root 0 чер 25 18:10 iio_enable
-rw-r--r-- 1 root root 32 чер 25 17:59 memory
-r--r--r-- 1 root root 4096 чер 25 18:10 name
drwxr-xr-x 2 root root 0 чер 25 18:10 power
--w------- 1 root root 0 чер 25 18:10 pwm_enable
--w------- 1 root root 0 чер 25 18:10 spi_bind
lrwxrwxrwx 1 root root 0 чер 25 18:10 subsystem -> ../../../bus/w1
-rw-r--r-- 1 root root 4096 чер 25 18:10 uevent
# cat /sys/devices/w1_bus_master1/20-594e480f4241/gpio/gpiochip478/base
478
# cat /sys/devices/w1_bus_master1/20-594e480f4241/gpio/gpiochip478/ngpio
5
Бачимо, що з'явився gpiochip6 з 5шт GPIO, номери яких будуть 478..482.
GPIO вже доступні через символьний пристрій:
# ls -l /dev/gpio*
crw-rw-rw- 1 root root 254, 6 чер 25 18:10 /dev/gpiochip6
lrwxrwxrwx 1 root root 9 чер 25 18:10 /dev/gpiochip_20-594e480f4241 -> gpiochip6
Можемо також експортувати один з GPIO в sysfs і поблимати на ньому світлодіодом з командного рядка:
# echo 479 >/sys/class/gpio/export
# echo high >/sys/devices/w1_bus_master1/20-594e480f4241/gpiochip6/gpio/gpio479/direction
# echo low >/sys/devices/w1_bus_master1/20-594e480f4241/gpiochip6/gpio/gpio479/direction
Робота з GPIO модуля розширення портів нічим не відрізняється від роботи з GPIO тієї ж 40-пінової гребінки в Raspberry Pi.
Ремарка:
Символьне посилання gpiochip_20-594e480f4241 створює udevd, для цого потрібно прописати правила, ось приклад таких правил. Правила також можуть автоматично записати 1 в gpio_enable при появі пристрою в системі, а інше правило зробить символьне посилання і змінить права доступу.
pwm_enable
Дозволяє запис 0 або 1 і відповідно підключає або відключає підсистему ядра pwm до відповідного модуля розширення портів.
# echo 1 >/sys/devices/w1_bus_master1/20-594e480f4241/pwm_enable
# ls -l /sys/devices/w1_bus_master1/20-594e480f4241
--w------- 1 root root 0 чер 25 18:00 convert
lrwxrwxrwx 1 root root 0 чер 25 18:58 driver -> ../../../bus/w1/drivers/w1_slave_driver
--w------- 1 root root 0 чер 25 18:58 gpio_enable
-r--r--r-- 1 root root 4096 чер 25 18:59 id
--w------- 1 root root 0 чер 25 19:22 iio_enable
-rw-r--r-- 1 root root 32 чер 25 17:59 memory
-r--r--r-- 1 root root 4096 чер 25 18:59 name
drwxr-xr-x 2 root root 0 чер 25 18:59 power
drwxr-xr-x 3 root root 0 чер 25 19:22 pwm
--w------- 1 root root 0 чер 25 19:22 pwm_enable
--w------- 1 root root 0 чер 25 18:59 spi_bind
lrwxrwxrwx 1 root root 0 чер 25 18:58 subsystem -> ../../../bus/w1
-rw-r--r-- 1 root root 4096 чер 25 18:58 uevent
Процедура експорту в sysfs трохи нагадує таку ж для gpio:
# echo 0 >/sys/devices/w1_bus_master1/20-594e480f4241/pwm/pwmchip0/export
# ls -l /sys/devices/w1_bus_master1/20-594e480f4241/pwm/pwmchip0/pwm0
-r--r--r-- 1 root root 4096 чер 25 19:29 capture
-rw-r--r-- 1 root root 4096 чер 25 19:29 duty_cycle
-rw-r--r-- 1 root root 4096 чер 25 19:29 enable
-rw-r--r-- 1 root root 4096 чер 25 19:29 period
-rw-r--r-- 1 root root 4096 чер 25 19:29 polarity
drwxr-xr-x 2 root root 0 чер 25 19:29 power
-rw-r--r-- 1 root root 4096 чер 25 19:29 uevent
# cat /sys/devices/w1_bus_master1/20-594e480f4241/pwm/pwmchip0/pwm0/period
256
# echo 1 >/sys/devices/w1_bus_master1/20-594e480f4241/pwm/pwmchip0/pwm0/enable
# echo 123 >/sys/devices/w1_bus_master1/20-594e480f4241/pwm/pwmchip0/pwm0/duty_cycle
Період 256 змінити не можна, duty_cycle можна встановити від 0 до 256 включно, якщо в enable записати 1, то буде виводитись ШІМ сигнал, якщо 0 - то пін переводиться в режим input.
В Linux відсутній символьний пристрій для pwm в /dev, тому, якщо необхідно керувати з програми, то єдиний спосіб - через memory.
iio_enable
Дозволяє запис 0 або 1 і відповідно підключає або відключає підсистему ядра industrialio до відповідного модуля розширення портів.
# echo 1 >/sys/devices/w1_bus_master1/20-594e480f4241/iio_enable
# ls -l /sys/devices/w1_bus_master1/20-594e480f4241
--w------- 1 root root 0 чер 25 18:00 convert
lrwxrwxrwx 1 root root 0 чер 25 18:58 driver -> ../../../bus/w1/drivers/w1_slave_driver
--w------- 1 root root 0 чер 25 18:58 gpio_enable
-r--r--r-- 1 root root 4096 чер 25 18:59 id
drwxr-xr-x 3 root root 0 чер 25 18:59 iio:device0
--w------- 1 root root 0 чер 25 18:59 iio_enable
-rw-r--r-- 1 root root 32 чер 25 17:59 memory
-r--r--r-- 1 root root 4096 чер 25 18:59 name
drwxr-xr-x 2 root root 0 чер 25 18:59 power
--w------- 1 root root 0 чер 25 18:59 pwm_enable
--w------- 1 root root 0 чер 25 18:59 spi_bind
lrwxrwxrwx 1 root root 0 чер 25 18:58 subsystem -> ../../../bus/w1
-rw-r--r-- 1 root root 4096 чер 25 18:58 uevent
# ls -l /sys/devices/w1_bus_master1/20-594e480f4241/iio\:device0
-r--r--r-- 1 root root 4096 чер 25 19:00 dev
-r--r--r-- 1 root root 4096 чер 25 19:00 in_voltage0_pin
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage0_raw
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage0_reference
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage0_resolution
-r--r--r-- 1 root root 4096 чер 25 19:00 in_voltage1_pin
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage1_raw
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage1_reference
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage1_resolution
-r--r--r-- 1 root root 4096 чер 25 19:00 in_voltage2_pin
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage2_raw
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage2_reference
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage2_resolution
-r--r--r-- 1 root root 4096 чер 25 19:00 in_voltage3_pin
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage3_raw
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage3_reference
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage3_resolution
-r--r--r-- 1 root root 4096 чер 25 19:00 in_voltage_reference_available
-r--r--r-- 1 root root 4096 чер 25 19:00 in_voltage_resolution_available
-rw-r--r-- 1 root root 4096 чер 25 19:00 in_voltage_scale
-r--r--r-- 1 root root 4096 чер 25 19:00 name
drwxr-xr-x 2 root root 0 чер 25 19:00 power
lrwxrwxrwx 1 root root 0 чер 25 19:00 subsystem -> ../../../../bus/iio
-rw-r--r-- 1 root root 4096 чер 25 18:59 uevent
# cat /sys/devices/w1_bus_master1/20-594e480f4241/iio\:device0/in_voltage_reference_available
vcc 1v1
# cat /sys/devices/w1_bus_master1/20-594e480f4241/iio\:device0/in_voltage_resolution_available
[1 1 16]
# cat /sys/devices/w1_bus_master1/20-594e480f4241/iio\:device0/in_voltage*_raw
65472
16164
2217
24883
Файли *_reference і *_resolution доступні для запису. З'являється також символьний пристрій в /dev.
spi_bind
Дозволяє запис у форматі "cs modalias" або "cs", перший підключає протокол-драйвер до відповідного cs (chip select), другий - звільняє cs. cs може приймати значення 0 або 1. modalias може бути протокол-драйвером шини SPI з наявних в Linux (bmp280, bme680, max31856, ...). Після запису в spi_bind протокол-драйвер через внутрішній інтерфейс ядра починає обмін даними по SPI з своїм пристроєм (якщо пристрій і драйвер сумісні, вони зрозуміють одне одного), також протокол-драйвер може підключати інші підсистеми ядра, наприклад сенсори/АЦП/ЦАП підключають industrialio.
Є один спеціальний протокол-драйвер spidev, який створює символьний пристрій в /dev, через який з'являється доступ до SPI з простору користувача:
# echo 0 spidev >/sys/devices/w1_bus_master1/20-594e480f4241/spi_bind
# ls -l /dev/spi*
crw-rw-rw- 1 root root 153, 0 чер 26 17:09 /dev/spidev0.0
lrwxrwxrwx 1 root root 9 чер 26 17:09 /dev/spidev_20-594e480f4241.0 -> spidev0.0
# echo 1 >/sys/devices/w1_bus_master1/20-594e480f4241/gpio_enable
# avrdude -c linuxspi -E noreset -P /dev/spidev_20-594e480f4241.0:/dev/gpiochip_20-594e480f4241:0 -p m328p -U sig:r:-:h
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.05s
avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading signature memory:
Reading | ################################################## | 100% 0.05s
avrdude: writing output file "<stdout>"
0x1e,0x95,0xf
avrdude done. Thank you.
Остання редакція Honey (2023-06-26 18:31:15)
Неактивний
Придбав екранчик ssd1306 128x64 версію з SPI інтерфейсом щоб підключити до модуля розширення портів напряму, без Ардуіно, але виявилося, що в версії з 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
Остання редакція Honey (2023-07-31 23:31:44)
Неактивний
Сторінки 1