Ранее было рассмотрено несколько простых способов как работать с GPIO на плате Zybo с помощью Linux и было сказано несколько слов про работу с памятью ОС в целом.
Для работы с периферией нужно использовать специальный драйвер предназначенный конкретно для неё, либо обладать правами администратора, знать где в памяти ОС находится необходимые регистры и работать с ними напрямую через /dev/mem.
Но в целях безопасности, обычные пользователи не обладают достаточными правами для работы с памятью напрямую, и необходимого драйвера может не быть в принципе. К тому же многие устройства генерируют прерывания. Для их обработки без драйвера не обойтись.
Создание своего драйвера требует довольно больших затрат и возможно оно нее оправдано, к примеру для простого доступа к светодиоду. Для его написания нужно взаимодействовать с исходниками ядра, что в конечном итоге может повредить всю ОС.
Хорошая книга по Linux драйверам: Linux Device Drivers,
Для упрощения работы, была создана UIO система. Для работы требуется минимальное взаимодействие с ядром и периферия настраивается через device tree.
Сперва нужно добавить новое устройство в device tree. Как его изменить было описано здесь.
Мы будем работать со светодиодом через AXI, по адресу 0x41200000. Он был подключен здесь.
В самый конец файла я добавил следующие строчки
&axi_led { compatible = "generic-uio"; reg = < 0x41200000 0x1000 >; };axi_led - название устройства
compatible - строчка по которой драйвер будет искать данный модуль
reg - адрес в памяти откуда начинается необходимый нам блок, 0x1000 - размер блока который нам необходим.
Подробнее про device tree: xillybus.com.
В ядре Linux, произошли некоторые изменения и теперь драйвер по умолчанию не ищет устройства в device tree, подробнее об этом в коммите на гитхаб и на форуме. Рекомендуется добавить в device tree следующую строчку (или изменить существующую):
bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";Она должна быть в первом объявленном устройстве, там происходит описание системы.
Либо в ручную изменить исходники ядра. Убрав изменения. Для этого в файле drivers/uio/uio_pdrv_genirq.c. Нужно чтобы часть с поиском устройства была следующей:
#ifdef CONFIG_OF static struct of_device_id uio_of_genirq_match[] = { { .compatible = "generic-uio", }, { /* This is filled with module_parm */ }, { /* Sentinel */ }, };Я выбрал последний способ.
Перед компиляцией ядра нужно подключить UIO драйвер.
При настройке ядра и выполнении команды
make ARCH=arm menuconfigследует перейти в
Device Drivers ---> <*> Userspace I/O drivers ---> <*> Userspace I/O platform driver with generic IRQ handling <*> Userspace platform driver with generic irq and dynamic memoryИ подключить драйвер, поставив "*".
Рис.1 Настройка ядра Linux |
Рис.2 Проверка подключения нового устройства |
Для проверки можно воспользоваться немного изменённым кодом из предыдущей статьи.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <signal.h> #include <fcntl.h> #include <ctype.h> #include <termios.h> #include <sys/types.h> #include <sys/mman.h> #define MAP_SIZE 0x1000 #define LED_BIT 1 int main(int argc, char **argv) { int fd; void *map_base; unsigned char *led_val; off_t offset; if ((fd = open ("/dev/uio0", O_RDWR | O_SYNC)) == -1) { printf ("/dev/uio0 not opened.\n"); return -1; } map_base = mmap (0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (map_base == (void *) -1) { close(fd); printf ("/dev/uio0 mapping failed.\n"); } led_val = map_base; while(1) { printf ("LED is %s \n", (*led_val & 1) ? "On": "Off"); fflush (stdout); sleep(1); printf ("Changing LED Status\n"); fflush (stdout); *led_val ^= LED_BIT; sleep(1); } if (munmap(map_base, MAP_SIZE) == -1) { printf("unmap failed\n"); } close (fd); return 0; }
Основные источники:
www.kernel.org
fpga.org
svenand.blogdrives.com
r4nd0m6uy.ch
yurovsky.github.io
xillybus.com
forums.xilinx.com
github.com
stackoverflow.com
www.equestionanswers.com
Комментариев нет :
Отправить комментарий