Ранее было рассмотрено несколько простых способов как работать с 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
Комментариев нет :
Отправить комментарий