24.06.2014 Настройка и ресурсы Xilinx ZYNQ ZC702
Ustinov (обсуждение | вклад) |
Ustinov (обсуждение | вклад) |
||
(не показаны 53 промежуточные версии 4 участников) | |||
Строка 1: | Строка 1: | ||
<summary [ hidden ]> | <summary [ hidden ]> | ||
+ | <center>[[Файл:ZYNQ ZC702.jpg|300px]]</center> | ||
Страница, посвященная Xilinx ZYNQ ZC702. | Страница, посвященная Xilinx ZYNQ ZC702. | ||
</summary> | </summary> | ||
− | |||
− | |||
− | + | [[Файл:ZYNQ ZC702.jpg|right|500px]] | |
− | + | ||
− | |||
− | |||
− | + | == Boot Modes == | |
+ | [[Файл:Таблица режимов переключателя sw16.jpg|660px|sw16 modes]] | ||
− | Из скачанного архива '' | + | |
+ | == Запуск Linux с карты памяти == | ||
+ | Готовые релизы Linux можно найти по данной [http://www.wiki.xilinx.com/Zynq+Releases ссылке]. Выберем релиз [http://www.wiki.xilinx.com/Zynq+2014.2+Release 2014.2], т.к. он основа на ядре 3.14, к которому применим preempt_rt [https://www.kernel.org/pub/linux/kernel/projects/rt/ патч]. | ||
+ | Распаковываем архив: | ||
+ | <source lang="bash"> | ||
+ | tar xvJf 2014.2-release.tar.xz | ||
+ | </source> | ||
+ | Подготавливаем карту памяти как в этой [[Blog:Ustinov/07.10.2013_Прошивка_AM3517 | статье]]. | ||
+ | Из скачанного архива ''2014.2-release.tar.xz'' на карту памяти копируем следующие файлы: | ||
* boot.bin | * boot.bin | ||
* uImage | * uImage | ||
Строка 20: | Строка 25: | ||
Карточку вставляем в ZYNQ, джамперы (SW16) выставляем в комбинацию '''00110''', что выглядит как: | Карточку вставляем в ZYNQ, джамперы (SW16) выставляем в комбинацию '''00110''', что выглядит как: | ||
− | [[Файл:Wiki 7.1 boot mode switch.jpg]] | + | [[Файл:Wiki 7.1 boot mode switch.jpg|250px|SD boot mode]] |
На компе запускаем скрипт: | На компе запускаем скрипт: | ||
Строка 34: | Строка 39: | ||
Включаем плату и видим загрузку Linux. Для логина используем Username & Password: '''root'''. | Включаем плату и видим загрузку Linux. Для логина используем Username & Password: '''root'''. | ||
+ | |||
== Прошиваем Linux в QSPI Flash == | == Прошиваем Linux в QSPI Flash == | ||
Строка 59: | Строка 65: | ||
Чтобы грузиться с QSPI Flash необходимо переключить джамперы (SW16) в комбинацию '''00010'''. | Чтобы грузиться с QSPI Flash необходимо переключить джамперы (SW16) в комбинацию '''00010'''. | ||
− | [[Категория:HOWTO]] | + | |
+ | == Модификация FS == | ||
+ | * '''Изменения FS скачанного выше релиза.''' | ||
+ | Необходимо развернуть образ FS из "обертки" u-boot. Для этого следует пропустить первые 64 байта: | ||
+ | <source lang="bash"> | ||
+ | dd if=uramdisk.image.gz bs=64 skip=1 of=ramdisk.image.gz | ||
+ | </source> | ||
+ | Теперь разархивируем полученный архив в папку: | ||
+ | <source lang="bash"> | ||
+ | mkdir ./tmp_mnt/ | ||
+ | gunzip -c ramdisk.image.gz | sudo sh -c 'cd ./tmp_mnt/ && cpio -i' | ||
+ | cd ./tmp_mnt | ||
+ | </source> | ||
+ | Далее выполняем нужные изменения и снова запечатываем в архив: | ||
+ | <source lang="bash"> | ||
+ | sh -c 'cd ./tmp_mnt/ && sudo find . | sudo cpio -H newc -o' | gzip -9 > new_initramfs.cpio.gz | ||
+ | </source> | ||
+ | Теперь необходимо образ "завернуть" в u-boot: | ||
+ | <source lang="bash">mkimage -A arm -T ramdisk -C gzip -d new_initramfs.cpio.gz uramdisk.image.gz</source> | ||
+ | Образ готов для загрузки как с SD, так и с QSPI Flash. | ||
+ | |||
+ | * '''Модификация стандартной FS''' | ||
+ | Качаем образ FS по-умолчанию: [http://www.wiki.xilinx.com/file/view/arm_ramdisk.image.gz/419243558/arm_ramdisk.image.gz RamDisk]. | ||
+ | Распаковываем его: | ||
+ | <source lang="bash">gunzip ramdisk.image.gz</source> | ||
+ | Монтируем распакованное содержимое: | ||
+ | <source lang="bash"> | ||
+ | chmod u+rwx ramdisk.image | ||
+ | mkdir tmp_mnt/ | ||
+ | sudo mount -o loop ramdisk.image tmp_mnt/ | ||
+ | cd tmp_mnt/ | ||
+ | </source> | ||
+ | Делаем нужные изменения. | ||
+ | Размонтируем и запаковываем: | ||
+ | <source lang="bash"> | ||
+ | sudo umount tmp_mnt/ | ||
+ | gzip ramdisk.image | ||
+ | </source> | ||
+ | Соответственно, для загрузки в железку данный образ должен быть "обернут" в U-Boot: | ||
+ | <source lang="bash">mkimage -A arm -T ramdisk -C gzip -d ramdisk.image.gz uramdisk.image.gz</source> | ||
+ | |||
+ | Кроме того, можно создать изначально пустой образ и затем уже заполнять его файлами, как описано выше: | ||
+ | <source lang="bash"> | ||
+ | dd if=/dev/zero of=ramdisk.image bs=1024 count=8192 | ||
+ | mke2fs -F ramdisk.image -L "ramdisk" -b 1024 -m 0 | ||
+ | tune2fs ramdisk.image -i 0 | ||
+ | chmod a+rwx ramdisk.image | ||
+ | </source> | ||
+ | |||
+ | == Установка PetaLinux SDK == | ||
+ | Заходим [http://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/petalinux.html тык]. Качаем '''PetaLinux 2014.2 Installation archive for Zynq and MicroBlaze''' для релиза 2014.2. | ||
+ | |||
+ | Переходим в папку, куда закачали, там выполняем: | ||
+ | <source lang="bash"> | ||
+ | ./petalinux-v2014.2-final-installer.run <путь установки> | ||
+ | </source> | ||
+ | После установки запускаем скрипт: | ||
+ | <source lang="bash"> | ||
+ | source <путь, куда устанавливали>/settings.sh | ||
+ | </source> | ||
+ | Проверяем, сработал ли скрипт: | ||
+ | <source lang="bash"> | ||
+ | echo $PETALINUX | ||
+ | </source> | ||
+ | Выключаем отправку статистики на сервера [http://www.xilinx.com Xilinx]: | ||
+ | <source lang="bash"> | ||
+ | petalinux-util --webtalk off | ||
+ | </source> | ||
+ | |||
+ | |||
+ | == Беспарольный доступ == | ||
+ | Настройки, аналогичны этому [http://193.233.71.244/wiki/Blog:Boldenkov/03.09.2013_Автозапуск_SSH_в_Импале#.D0.9D.D0.B0.D1.81.D1.82.D1.80.D0.BE.D0.B9.D0.BA.D0.B0_SSH примеру], за исключением некоторых но. | ||
+ | Dropbear при старте платы всегда генерирует новые ключи, потому как грузится Linux из RAM. Соответвенно, забираем их из ''/etc/dropbear/'' себе на комп. По вышеописанной инструкции модификации FS вносим эти файлы в ''/etc/dropbear/'' и создаем в ''/home/root/.ssh'' файл authorized_keys. В него вносим содержимое нашего открытого ключа. | ||
+ | |||
+ | Устанавливаем все права как: | ||
+ | |||
+ | ''Обратите внимание, что права устанавливаются именно для подмонтированной файловой системы, а не для файлов, используемых нашим компом!'' | ||
+ | <source lang="bash"> | ||
+ | chmod 700 ./home/root | ||
+ | chmod 700 ./home/root/.ssh | ||
+ | chmod 600 ./home/root/.ssh/authorized_keys | ||
+ | </source> | ||
+ | |||
+ | Собираем FS и грузимся со всеми нашими модификациями. | ||
+ | |||
+ | |||
+ | == Компиляция исходников == | ||
+ | Необходимо установить '''ISE''', есть на сервере. | ||
+ | Далее по окончании установки и каждый раз при открытии новой консоли необходимо устанавливать параметры среды $PATH, запуская скрипт: | ||
+ | <source lang="bash"> | ||
+ | source <путь установки ISE>/14.7/ISE_DC/settings64.sh | ||
+ | </source> | ||
+ | |||
+ | После этого уже можно стандартно использовать компилятор. Для ZYNQ zc702 он называется ''arm-xilinx-linux-gnueabi-gcc'', ну или ''arm-xilinx-linux-gnueabi-g++''. | ||
+ | |||
+ | |||
+ | == Функции чтения и записи в регистры == | ||
+ | Литература: [[Медиа:Ug585-Zynq-7000-TRM.pdf|Техническое справочное руководство]], [[Медиа:Xtp185-zc702-schematic-rev1-1.pdf|схема]]. | ||
+ | |||
+ | Адреса регистров описаны в техническом руководстве, B.28 (с. 1554). | ||
+ | |||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <unistd.h> | ||
+ | #include <string.h> | ||
+ | #include <errno.h> | ||
+ | #include <fcntl.h> | ||
+ | #include <signal.h> | ||
+ | #include <sys/mman.h> | ||
+ | |||
+ | #define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \ | ||
+ | __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0) | ||
+ | |||
+ | #define MAP_SIZE 4096UL | ||
+ | #define MAP_MASK (MAP_SIZE - 1) | ||
+ | #define BASE_ADDRESS 0xF8000000 | ||
+ | |||
+ | #define SET_AS_GPIO 0x1200 | ||
+ | #define SET_UNLOCK 0xDF0D | ||
+ | #define SLCR_UNLOCK 0x8 | ||
+ | #define MIO_PIN_8 0x720 | ||
+ | #define MIO_PIN_10 0x728 | ||
+ | |||
+ | /*Чтение регистров по адресу*/ | ||
+ | void register_read(unsigned long addr) | ||
+ | { | ||
+ | int fd; | ||
+ | void *map_base, *virt_addr; | ||
+ | unsigned long read_result; | ||
+ | if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL; | ||
+ | fflush(stdout); | ||
+ | /* Map one page */ | ||
+ | map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BASE_ADDRESS & ~MAP_MASK); | ||
+ | if(map_base == (void *) -1) FATAL; | ||
+ | fflush(stdout); | ||
+ | virt_addr = map_base + ((BASE_ADDRESS + addr) & MAP_MASK); | ||
+ | read_result = *((unsigned long *) virt_addr); | ||
+ | printf("0x%08X r: 0x%X\n", addr, read_result); | ||
+ | fflush(stdout); | ||
+ | } | ||
+ | |||
+ | /*Запись в регистр по адресу*/ | ||
+ | void register_write(unsigned long addr, unsigned long writeval) | ||
+ | { | ||
+ | int fd; | ||
+ | void *map_base, *virt_addr; | ||
+ | unsigned long read_result; | ||
+ | if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL; | ||
+ | // printf("/dev/mem opened.\n"); | ||
+ | fflush(stdout); | ||
+ | /* Map one page */ | ||
+ | map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BASE_ADDRESS & ~MAP_MASK); | ||
+ | if(map_base == (void *) -1) FATAL; | ||
+ | // printf("Memory mapped at address %p.\n", map_base); | ||
+ | fflush(stdout); | ||
+ | virt_addr = map_base + ((BASE_ADDRESS + addr) & MAP_MASK); | ||
+ | *((unsigned long *) virt_addr) = writeval; | ||
+ | read_result = *((unsigned long *) virt_addr); | ||
+ | printf("0x%08X w: 0x%X\n", addr, read_result); | ||
+ | } | ||
+ | |||
+ | |||
+ | int main (void) | ||
+ | { | ||
+ | /*Изначально, при загрузке, MIO_PIN_8 (на нём висит диод DS12) не сконфигурирован для GPIO, исправим это.*/ | ||
+ | register_write(SLCR_UNLOCK, SET_UNLOCK); // Необходимый анлок регистров | ||
+ | register_read(MIO_PIN_8); // Считываем начальное значение регистра | ||
+ | register_write(MIO_PIN_8, SET_AS_GPIO); // Записываем новое значение | ||
+ | register_read(MIO_PIN_8); // Считываем новое значение регистра | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Управление GPIO == | ||
+ | Функции управления GPIO такие же как и для Impal'ы в CPU.cpp (на всякий случай копирну): | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <unistd.h> | ||
+ | #include <string.h> | ||
+ | #include <errno.h> | ||
+ | #include <fcntl.h> | ||
+ | #include <signal.h> | ||
+ | #include <sys/mman.h> | ||
+ | |||
+ | #define SYSFS_GPIO_DIR "/sys/class/gpio" | ||
+ | |||
+ | /* Включение режима GPIO по номеру */ | ||
+ | int gpio_export(unsigned int gpio) | ||
+ | { | ||
+ | |||
+ | FILE *fid; | ||
+ | fid = fopen(SYSFS_GPIO_DIR "/export", "w"); | ||
+ | if (fid == NULL) printf("fopen: %s\n", strerror(errno)); | ||
+ | if (fprintf(fid, "%d", gpio) <= 0) | ||
+ | printf("fprintf: %s\n", strerror(errno)); | ||
+ | fclose(fid); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | /* Настройка направления GPIO по номеру */ | ||
+ | int gpio_set_dir(unsigned int gpio, unsigned int out_flag) | ||
+ | { | ||
+ | |||
+ | FILE *fid; | ||
+ | char filename[40]; | ||
+ | sprintf(filename, SYSFS_GPIO_DIR "/gpio%d/direction", gpio); | ||
+ | |||
+ | fid = fopen(filename, "w"); | ||
+ | if (fid == NULL) printf("fopen %s: %s\n", filename, strerror(errno)); | ||
+ | if (out_flag) { | ||
+ | if (fprintf(fid, "out") <= 0) | ||
+ | printf("fprintf: %s\n", strerror(errno)); | ||
+ | } else { | ||
+ | if (fprintf(fid, "in") <= 0) | ||
+ | printf("fprintf: %s\n", strerror(errno)); | ||
+ | } | ||
+ | fclose(fid); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | /* Установка значение GPIO по номеру */ | ||
+ | int gpio_set_value(unsigned int gpio, unsigned int value) | ||
+ | { | ||
+ | |||
+ | FILE *fid; | ||
+ | char filename[40]; | ||
+ | sprintf(filename, SYSFS_GPIO_DIR "/gpio%d/value", gpio); | ||
+ | |||
+ | fid = fopen(filename, "w"); | ||
+ | if (fid == NULL) printf("fopen: %s\n", strerror(errno)); | ||
+ | |||
+ | if (value == 1) { | ||
+ | if (fprintf(fid, "1") <= 0) | ||
+ | printf("fprintf: %s\n", strerror(errno)); | ||
+ | } else { | ||
+ | if (fprintf(fid, "0") <= 0) | ||
+ | printf("fprintf: %s\n", strerror(errno)); | ||
+ | } | ||
+ | fclose(fid); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | /* Чтение значения из GPIO по номеру */ | ||
+ | int gpio_get_value(unsigned int gpio) | ||
+ | { | ||
+ | |||
+ | FILE *fid; | ||
+ | char filename[40]; | ||
+ | int value; | ||
+ | sprintf(filename, SYSFS_GPIO_DIR "/gpio%d/value", gpio); | ||
+ | |||
+ | fid = fopen(filename, "r"); | ||
+ | if (fid == NULL) printf("fopen: %s\n", strerror(errno)); | ||
+ | |||
+ | if (fscanf(fid, "%1d", &value) <= 0) | ||
+ | printf("gpio_get_value failed: %s\n", strerror(errno)); | ||
+ | |||
+ | fclose(fid); | ||
+ | |||
+ | if (value == 0) | ||
+ | return 0; | ||
+ | else | ||
+ | return 1; | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | /* Настройка фронта срабатывания GPIO по номеру */ | ||
+ | int gpio_set_edge(unsigned int gpio, char *edge) | ||
+ | { | ||
+ | FILE *fid; | ||
+ | char filename[40]; | ||
+ | sprintf(filename, SYSFS_GPIO_DIR "/gpio%d/edge", gpio); | ||
+ | |||
+ | fid = fopen(filename, "w"); | ||
+ | if (fid == NULL) printf("fopen %s: %s\n", filename, strerror(errno)); | ||
+ | |||
+ | if (fprintf(fid, "%s", edge) <= 0) | ||
+ | printf("fprintf: %s\n", strerror(errno)); | ||
+ | fclose(fid); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | /* Открывает файл, соответствующий GPIO, по номеру */ | ||
+ | int gpio_fd_open(unsigned int gpio) | ||
+ | { | ||
+ | int fd; | ||
+ | char buf[MAX_BUF]; | ||
+ | |||
+ | snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio); | ||
+ | |||
+ | fd = open(buf, O_RDONLY | O_NONBLOCK ); | ||
+ | if (fd < 0) { | ||
+ | perror("gpio/fd_open"); | ||
+ | } | ||
+ | return fd; | ||
+ | |||
+ | } | ||
+ | |||
+ | int main (void) | ||
+ | { | ||
+ | /* Программа поочередного мигания диодами DS23 и DS12*/ | ||
+ | register_write(SLCR_UNLOCK, SET_UNLOCK); | ||
+ | register_read(MIO_PIN_8); | ||
+ | register_write(MIO_PIN_8, SET_AS_GPIO); | ||
+ | register_read(MIO_PIN_8); | ||
+ | |||
+ | gpio_export(10); | ||
+ | gpio_set_dir(10, 1); | ||
+ | gpio_export(8); | ||
+ | gpio_set_dir(8, 1); | ||
+ | for (;;){ | ||
+ | gpio_set_value(10, 0); | ||
+ | printf("10: %d\n",gpio_get_value(10)); | ||
+ | gpio_set_value(8, 1); | ||
+ | printf("8: %d\n",gpio_get_value(8)); | ||
+ | sleep(1); | ||
+ | gpio_set_value(10, 1); | ||
+ | printf("10: %d\n",gpio_get_value(10)); | ||
+ | gpio_set_value(8, 0); | ||
+ | printf("8: %d\n",gpio_get_value(8)); | ||
+ | sleep(1); | ||
+ | } | ||
+ | |||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == GPIO Interrupts == | ||
+ | Пример кода обработки прерывания по ноге GPIO 14, на ней сидит кнопка SW13. | ||
+ | <syntaxhighlight lang=c> | ||
+ | // Добавлена куча ашников, взятых из кода Impal´ы, наверняка нужны не все... | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <unistd.h> | ||
+ | #include <string.h> | ||
+ | #include <errno.h> | ||
+ | #include <fcntl.h> | ||
+ | #include <signal.h> | ||
+ | #include <sys/mman.h> | ||
+ | #include <poll.h> | ||
+ | #include <sys/time.h> | ||
+ | #include <sys/resource.h> | ||
+ | #include <semaphore.h> | ||
+ | #include <pthread.h> | ||
+ | #include <sched.h> | ||
+ | #include <time.h> | ||
+ | #include <sys/prctl.h> | ||
+ | #include <math.h> | ||
+ | #include <sys/types.h> | ||
+ | |||
+ | //Необходимо добавить нужные функции из предыдущих глав. | ||
+ | |||
+ | int main (void) | ||
+ | { | ||
+ | int gpio = 14; | ||
+ | |||
+ | gpio_export(gpio); | ||
+ | gpio_set_dir(gpio,0); | ||
+ | gpio_set_edge(gpio, (char*)"rising"); | ||
+ | |||
+ | // Технический обработчик прерваний. | ||
+ | int POLL_TIMEOUT = 1000; /* 1 second */ | ||
+ | size_t POLL_FD_MAX_BUF = 64; /* fdset read buffer */ | ||
+ | |||
+ | printf("IRQ handler started\n"); | ||
+ | |||
+ | struct pollfd fdset[1]; // Структура параметров функции poll() | ||
+ | |||
+ | int rc; | ||
+ | char *buf[POLL_FD_MAX_BUF]; | ||
+ | |||
+ | // Настройка параметров функции poll() | ||
+ | memset((void*)fdset, 0, sizeof(fdset)); | ||
+ | fdset[0].fd = gpio_fd_open(gpio); | ||
+ | fdset[0].events = POLLPRI; // Ожидаем одно событие - прерывание | ||
+ | |||
+ | prctl(PR_SET_NAME,"recv:IRQ"); | ||
+ | |||
+ | for (;;) { | ||
+ | |||
+ | lseek(fdset[0].fd, 0, SEEK_SET); // Устанавливаем смещение на начало файла | ||
+ | rc = poll(fdset, 1, POLL_TIMEOUT); // Здесь происходит ожидание прерывания | ||
+ | |||
+ | |||
+ | if (rc < 0) { | ||
+ | printf("IRQ handler: poll() failed!\n"); | ||
+ | return NULL; | ||
+ | } | ||
+ | |||
+ | if (rc == 0) { // IRQ timeout | ||
+ | printf("IRQ timeout!\n"); | ||
+ | } | ||
+ | |||
+ | if (fdset[0].revents & POLLPRI) { // Прерывание действительно произошло | ||
+ | printf("Interrupt!\n"); | ||
+ | |||
+ | if (read(fdset[0].fd, buf, POLL_FD_MAX_BUF) < 0) printf("Interrupt handler problem: %s\n", strerror(errno)); | ||
+ | } | ||
+ | printf("GPIO_%d: %d\n", gpio, gpio_get_value(gpio)); | ||
+ | } | ||
+ | }</syntaxhighlight> | ||
+ | |||
+ | |||
+ | |||
+ | ---- | ||
+ | Ссылка на [http://xgoogle.xilinx.com/search?output=xml_no_dtd&ie=UTF-8&oe=UTF-8&client=support&proxystylesheet=support&site=Answers_Docs&filter=0&resultsView=category&tab=dt&num=1000&sortBy=date&show_dynamic_navigation=1&sort=date%3AD%3AR%3Ad1&documentClass=Document&requiredfields=-Archived%3Atrue&getfields=*&q=+inmeta:Design%2520Tools%3DPetaLinux%2520SDK+inmeta:Document%2520Class%3DDocument+inmeta:Product%2520Type%3DDesign%2520Tools&dnavs=inmeta:Design%2520Tools%3DPetaLinux%2520SDK+inmeta:Document%2520Class%3DDocument+inmeta:Product%2520Type%3DDesign%2520Tools гайды] и [http://www.wiki.xilinx.com вики] | ||
+ | |||
+ | |||
+ | == Использование связки GDB/GDBSERVER == | ||
+ | |||
+ | Описано [https://gist.github.com/plesiv/4bb88a175c86b119f8c7 тут]. | ||
+ | |||
+ | [[Категория:HOWTO]] [[Категория:Zynq]] [[Категория:EmbeddedLinux]] | ||
{{wl-publish: 2014-06-24 16:13:30 +0400 | Ustinov }} | {{wl-publish: 2014-06-24 16:13:30 +0400 | Ustinov }} |
Текущая версия на 15:08, 9 июня 2018
Содержание |
[править] Boot Modes
convert: no images defined `/tmp/transform_128eeda8319f-1.jpg' @ error/convert.c/ConvertImageCommand/3044.
[править] Запуск Linux с карты памяти
Готовые релизы Linux можно найти по данной ссылке. Выберем релиз 2014.2, т.к. он основа на ядре 3.14, к которому применим preempt_rt патч. Распаковываем архив:
Подготавливаем карту памяти как в этой статье. Из скачанного архива 2014.2-release.tar.xz на карту памяти копируем следующие файлы:
- boot.bin
- uImage
- devicetree.dtb
- uramdisk.image.gz
Карточку вставляем в ZYNQ, джамперы (SW16) выставляем в комбинацию 00110, что выглядит как:
На компе запускаем скрипт:
set line /dev/ttyUSB0
set FLOW none
set speed 115200
set serial 8n1
SET CARRIER-WATCH Off
connect
Включаем плату и видим загрузку Linux. Для логина используем Username & Password: root.
[править] Прошиваем Linux в QSPI Flash
После загрузки Linux с карты памяти, примонтируем её:
Далее вводим команду:
Видим следующее:
mtd0 отведен для boot.bin
mtd1 отведен для uImage
mtd2 отведен для devicetree.dtb
mtd3 отведен для uramdisk.image.gz
Соответственно, переходим в подмантированную папку /mnt и вводим следующие команды для записи:
flashcp -v uImage /dev/mtd1
flashcp -v devicetree.dtb /dev/mtd2
flashcp -v uramdisk.image.gz /dev/mtd3
Чтобы грузиться с QSPI Flash необходимо переключить джамперы (SW16) в комбинацию 00010.
[править] Модификация FS
- Изменения FS скачанного выше релиза.
Необходимо развернуть образ FS из "обертки" u-boot. Для этого следует пропустить первые 64 байта:
Теперь разархивируем полученный архив в папку:
gunzip -c ramdisk.image.gz | sudo sh -c 'cd ./tmp_mnt/ && cpio -i'
cd ./tmp_mnt
Далее выполняем нужные изменения и снова запечатываем в архив:
Теперь необходимо образ "завернуть" в u-boot:
Образ готов для загрузки как с SD, так и с QSPI Flash.
- Модификация стандартной FS
Качаем образ FS по-умолчанию: RamDisk. Распаковываем его:
Монтируем распакованное содержимое:
mkdir tmp_mnt/
sudo mount -o loop ramdisk.image tmp_mnt/
cd tmp_mnt/
Делаем нужные изменения. Размонтируем и запаковываем:
gzip ramdisk.image
Соответственно, для загрузки в железку данный образ должен быть "обернут" в U-Boot:
Кроме того, можно создать изначально пустой образ и затем уже заполнять его файлами, как описано выше:
mke2fs -F ramdisk.image -L "ramdisk" -b 1024 -m 0
tune2fs ramdisk.image -i 0
chmod a+rwx ramdisk.image
[править] Установка PetaLinux SDK
Заходим тык. Качаем PetaLinux 2014.2 Installation archive for Zynq and MicroBlaze для релиза 2014.2.
Переходим в папку, куда закачали, там выполняем:
После установки запускаем скрипт:
Проверяем, сработал ли скрипт:
Выключаем отправку статистики на сервера Xilinx:
[править] Беспарольный доступ
Настройки, аналогичны этому примеру, за исключением некоторых но. Dropbear при старте платы всегда генерирует новые ключи, потому как грузится Linux из RAM. Соответвенно, забираем их из /etc/dropbear/ себе на комп. По вышеописанной инструкции модификации FS вносим эти файлы в /etc/dropbear/ и создаем в /home/root/.ssh файл authorized_keys. В него вносим содержимое нашего открытого ключа.
Устанавливаем все права как:
Обратите внимание, что права устанавливаются именно для подмонтированной файловой системы, а не для файлов, используемых нашим компом!
chmod 700 ./home/root/.ssh
chmod 600 ./home/root/.ssh/authorized_keys
Собираем FS и грузимся со всеми нашими модификациями.
[править] Компиляция исходников
Необходимо установить ISE, есть на сервере. Далее по окончании установки и каждый раз при открытии новой консоли необходимо устанавливать параметры среды $PATH, запуская скрипт:
После этого уже можно стандартно использовать компилятор. Для ZYNQ zc702 он называется arm-xilinx-linux-gnueabi-gcc, ну или arm-xilinx-linux-gnueabi-g++.
[править] Функции чтения и записи в регистры
Литература: Техническое справочное руководство, схема.
Адреса регистров описаны в техническом руководстве, B.28 (с. 1554).
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/mman.h>
#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
__LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
#define BASE_ADDRESS 0xF8000000
#define SET_AS_GPIO 0x1200
#define SET_UNLOCK 0xDF0D
#define SLCR_UNLOCK 0x8
#define MIO_PIN_8 0x720
#define MIO_PIN_10 0x728
/*Чтение регистров по адресу*/
void register_read(unsigned long addr)
{
int fd;
void *map_base, *virt_addr;
unsigned long read_result;
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL;
fflush(stdout);
/* Map one page */
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BASE_ADDRESS & ~MAP_MASK);
if(map_base == (void *) -1) FATAL;
fflush(stdout);
virt_addr = map_base + ((BASE_ADDRESS + addr) & MAP_MASK);
read_result = *((unsigned long *) virt_addr);
printf("0x%08X r: 0x%X\n", addr, read_result);
fflush(stdout);
}
/*Запись в регистр по адресу*/
void register_write(unsigned long addr, unsigned long writeval)
{
int fd;
void *map_base, *virt_addr;
unsigned long read_result;
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL;
// printf("/dev/mem opened.\n");
fflush(stdout);
/* Map one page */
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BASE_ADDRESS & ~MAP_MASK);
if(map_base == (void *) -1) FATAL;
// printf("Memory mapped at address %p.\n", map_base);
fflush(stdout);
virt_addr = map_base + ((BASE_ADDRESS + addr) & MAP_MASK);
*((unsigned long *) virt_addr) = writeval;
read_result = *((unsigned long *) virt_addr);
printf("0x%08X w: 0x%X\n", addr, read_result);
}
int main (void)
{
/*Изначально, при загрузке, MIO_PIN_8 (на нём висит диод DS12) не сконфигурирован для GPIO, исправим это.*/
register_write(SLCR_UNLOCK, SET_UNLOCK); // Необходимый анлок регистров
register_read(MIO_PIN_8); // Считываем начальное значение регистра
register_write(MIO_PIN_8, SET_AS_GPIO); // Записываем новое значение
register_read(MIO_PIN_8); // Считываем новое значение регистра
}
[править] Управление GPIO
Функции управления GPIO такие же как и для Impal'ы в CPU.cpp (на всякий случай копирну):
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/mman.h>
#define SYSFS_GPIO_DIR "/sys/class/gpio"
/* Включение режима GPIO по номеру */
int gpio_export(unsigned int gpio)
{
FILE *fid;
fid = fopen(SYSFS_GPIO_DIR "/export", "w");
if (fid == NULL) printf("fopen: %s\n", strerror(errno));
if (fprintf(fid, "%d", gpio) <= 0)
printf("fprintf: %s\n", strerror(errno));
fclose(fid);
return 0;
}
/* Настройка направления GPIO по номеру */
int gpio_set_dir(unsigned int gpio, unsigned int out_flag)
{
FILE *fid;
char filename[40];
sprintf(filename, SYSFS_GPIO_DIR "/gpio%d/direction", gpio);
fid = fopen(filename, "w");
if (fid == NULL) printf("fopen %s: %s\n", filename, strerror(errno));
if (out_flag) {
if (fprintf(fid, "out") <= 0)
printf("fprintf: %s\n", strerror(errno));
} else {
if (fprintf(fid, "in") <= 0)
printf("fprintf: %s\n", strerror(errno));
}
fclose(fid);
return 0;
}
/* Установка значение GPIO по номеру */
int gpio_set_value(unsigned int gpio, unsigned int value)
{
FILE *fid;
char filename[40];
sprintf(filename, SYSFS_GPIO_DIR "/gpio%d/value", gpio);
fid = fopen(filename, "w");
if (fid == NULL) printf("fopen: %s\n", strerror(errno));
if (value == 1) {
if (fprintf(fid, "1") <= 0)
printf("fprintf: %s\n", strerror(errno));
} else {
if (fprintf(fid, "0") <= 0)
printf("fprintf: %s\n", strerror(errno));
}
fclose(fid);
return 0;
}
/* Чтение значения из GPIO по номеру */
int gpio_get_value(unsigned int gpio)
{
FILE *fid;
char filename[40];
int value;
sprintf(filename, SYSFS_GPIO_DIR "/gpio%d/value", gpio);
fid = fopen(filename, "r");
if (fid == NULL) printf("fopen: %s\n", strerror(errno));
if (fscanf(fid, "%1d", &value) <= 0)
printf("gpio_get_value failed: %s\n", strerror(errno));
fclose(fid);
if (value == 0)
return 0;
else
return 1;
return 0;
}
/* Настройка фронта срабатывания GPIO по номеру */
int gpio_set_edge(unsigned int gpio, char *edge)
{
FILE *fid;
char filename[40];
sprintf(filename, SYSFS_GPIO_DIR "/gpio%d/edge", gpio);
fid = fopen(filename, "w");
if (fid == NULL) printf("fopen %s: %s\n", filename, strerror(errno));
if (fprintf(fid, "%s", edge) <= 0)
printf("fprintf: %s\n", strerror(errno));
fclose(fid);
return 0;
}
/* Открывает файл, соответствующий GPIO, по номеру */
int gpio_fd_open(unsigned int gpio)
{
int fd;
char buf[MAX_BUF];
snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
fd = open(buf, O_RDONLY | O_NONBLOCK );
if (fd < 0) {
perror("gpio/fd_open");
}
return fd;
}
int main (void)
{
/* Программа поочередного мигания диодами DS23 и DS12*/
register_write(SLCR_UNLOCK, SET_UNLOCK);
register_read(MIO_PIN_8);
register_write(MIO_PIN_8, SET_AS_GPIO);
register_read(MIO_PIN_8);
gpio_export(10);
gpio_set_dir(10, 1);
gpio_export(8);
gpio_set_dir(8, 1);
for (;;){
gpio_set_value(10, 0);
printf("10: %d\n",gpio_get_value(10));
gpio_set_value(8, 1);
printf("8: %d\n",gpio_get_value(8));
sleep(1);
gpio_set_value(10, 1);
printf("10: %d\n",gpio_get_value(10));
gpio_set_value(8, 0);
printf("8: %d\n",gpio_get_value(8));
sleep(1);
}
}
[править] GPIO Interrupts
Пример кода обработки прерывания по ноге GPIO 14, на ней сидит кнопка SW13.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/mman.h>
#include <poll.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <semaphore.h>
#include <pthread.h>
#include <sched.h>
#include <time.h>
#include <sys/prctl.h>
#include <math.h>
#include <sys/types.h>
//Необходимо добавить нужные функции из предыдущих глав.
int main (void)
{
int gpio = 14;
gpio_export(gpio);
gpio_set_dir(gpio,0);
gpio_set_edge(gpio, (char*)"rising");
// Технический обработчик прерваний.
int POLL_TIMEOUT = 1000; /* 1 second */
size_t POLL_FD_MAX_BUF = 64; /* fdset read buffer */
printf("IRQ handler started\n");
struct pollfd fdset[1]; // Структура параметров функции poll()
int rc;
char *buf[POLL_FD_MAX_BUF];
// Настройка параметров функции poll()
memset((void*)fdset, 0, sizeof(fdset));
fdset[0].fd = gpio_fd_open(gpio);
fdset[0].events = POLLPRI; // Ожидаем одно событие - прерывание
prctl(PR_SET_NAME,"recv:IRQ");
for (;;) {
lseek(fdset[0].fd, 0, SEEK_SET); // Устанавливаем смещение на начало файла
rc = poll(fdset, 1, POLL_TIMEOUT); // Здесь происходит ожидание прерывания
if (rc < 0) {
printf("IRQ handler: poll() failed!\n");
return NULL;
}
if (rc == 0) { // IRQ timeout
printf("IRQ timeout!\n");
}
if (fdset[0].revents & POLLPRI) { // Прерывание действительно произошло
printf("Interrupt!\n");
if (read(fdset[0].fd, buf, POLL_FD_MAX_BUF) < 0) printf("Interrupt handler problem: %s\n", strerror(errno));
}
printf("GPIO_%d: %d\n", gpio, gpio_get_value(gpio));
}
}
[править] Использование связки GDB/GDBSERVER
Описано тут.
[ Хронологический вид ]Комментарии
Войдите, чтобы комментировать.