Подключение символьного ЖКИ к плате от WD MyBook Live на AppliedMicro APM82181. Окончание

    Добрый день! Продожим работу с платой от NAS WesternDigital MyBook Live и подключенным к ней ЖК индикатором. Итак, в предыдущей части мы нашли на плате место для подключения к шине I2C, подключили расширитель портов с индикатором, убедились что все работает. Сегодня выведем на индикатор состояние системы.
    image
    image
    Начало было тут: Подключение символьного ЖКИ к плате от WD MyBook Live на AppliedMicro APM82181

    Содержание первой части:

    1. Подключение консоли
    2. Загрузка без диска
    3. Компиляция в LEDE
    4. Управление портами (через LuCI и консоль)
    5. Подключение к шине I2C
    6. Подключение расширителя портов PCF8574

    Сегодня рассмотрим:

    7. Инициализация HD44780 через i2cset
    8. Символьное устройство для записи данных в шину I2C
    9. Добавление драйвера HD44780 в ядро
    10. Добавление обработки необходимых команд VT100 в драйвер HD44780
    11. Добавление дисплея с некоторыми командами VT100 в LCD4Linux
    12. Добавление команды программирования знакогенератора в драйвер HD44780
    13. Оптимизация передачи данных по шине I2C

    Как и прежде, дополнения и замечания приветствуются.

    Итак, к этому моменту подключен в систему расширитель портов, которыми мы можем управлять. К расширителю присоединен символьный ЖК индикатор на клоне контроллера HD44780. Теоретически мы можем им управлять, включив все порты на вывод и зная их назначение. Подсветкой помигать уже удалось, дергая третий порт.

    7. Инициализация HD44780 через i2cset


    Соединение между контроллером HD44780 и расширителем портов организовано так:

    RS — P0
    R/W — P1
    E — P2
    BL — P3
    D4 — P4
    D5 — P5
    D6 — P6
    D7 — P7

    Это один из встречающихся вариантов. Контроллер при этом переводится и работает в 4-битном режиме, а байт передается по частям.

    Имея в распоряжении все порты расширителя, можно выдавать на него данные побитно и таким образом управлять дисплеем. Думаю согласитесь, что это не очень удобно.

    Попробуем напрямую управлять через шину I2C. Простой вариант для проверки такой возможности — использовать набор утилит I2C-tools. В LEDE они в разделе Utilites. В набор входит i2cdetect, i2cdump, i2cget, i2cset. Нас интересует последняя и немного первая (для диагностики).

    С помощью i2cdetect можно обнаружить подключенные на шину устройства и определить их адрес.

    В нашем случае занят только адрес 0x27:
    root@lede: i2cdetect 0
    WARNING! This program can confuse your I2C bus, cause data loss and worse!
    I will probe file /dev/i2c-0.
    I will probe address range 0x03-0x77.
    Continue? [Y/n] y
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- --
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- --
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
    70: -- -- -- -- -- -- -- --
    


    Утилита i2cset используется для вывода данных устройству с заданным адресом на шине I2C.
    Зная последовательность инициализации для вашего ЖКИ, можно без проблем ее выполнить и вывести символы на экран.

    Чтобы не изобретать велосипед, рекомендую скачать отсюда: I2C hd44780 модуль на расширителе PCF8574 «тестилку i2c lcd». Вот прямая ссылка. Внутри архива shell скрипт, который работает с индикатором через команду i2cset и выдает на экран символы поочередно. Единственно перед использованием надо закоментировать или удалить строки в начале файла:

    insmod i2c-dev
    insmod i2c-gpio-custom bus0=0,$sda_gpio,$scl_gpio
    

    Они создают программный порт I2C на любых свободных портах ввода-вывода, а у нас уже есть аппаратный. Ну и кроме того она рассчитана на индикатор размерностью 4*40, но для проверки работоспособности и понимания использования утилиты i2cset это ни чуть не помешает.

    Результат:

    image

    Немного пояснений по ее реализации. Процедуры write_CMD и print_LCD выводят соответственно на индикатор команду или данные. Это зависит от сигнала RS, в нашем случае находящегося на нулевой бите.

    Процедура init_LCD последовательно выдает команды для инициализации индикатора согласно его datasheet'у, широко распространенному в интернет. Например вот. Далее последовательно выдаются различные символы на экран.

    8. Символьное устройство для записи данных в шину I2C


    Все замечательно, однако хотелось бы уйти от использования утилит, и иметь символьное устройство, выводя на который байты, они бы попадали прямо на шину I2C, конечно с заданным адресом.

    К сожалению мне не удалось найти такой драйвер для шины I2C в LEDE. Поэтому с экспериментальными целями было решено переделать один из существующих. Понятно что при желании его использовать и далее, надо было не переделывать, а хотя бы создать на его основе новый.

    Подходящим для опытов оказался драйвер EEPROM под шину I2C. В ядре LEDE был подключен драйвер kmod-eeprom-at24, после обновления системы сделана попытка добавления устройства:

    root@lede: echo 24c00 0x27 > /sys/bus/i2c/devices/i2c-0/new_device
    root@lede: echo 24c00 0x27 > 
    [   33.335472] at24 0-0027: 16 byte 24c00 EEPROM, writable, 1 bytes/write
    [   33.342102] i2c i2c-0: new_device: Instantiated device 24c00 at 0x27
    

    Успешно подключилось. Теперь если вывести что-то в устройство:

    root@lede: echo "1111" > /sys/bus/i2c/devices/i2c-0/0-0027/eeprom

    то на шине мы увидим следующую последовательность байт:

    0x4e-0x00-0x31 0x4e-0x01-0x31 0x4e-0x02-0x31 0x4e-0x03-0x31 0x4e-0x04-0x0a

    Первый байт в каждой тройке — это адрес устройства, умноженный на 2 (0x27 x 2). Второй — адрес ячейки в EEPROM, третий — данные. Драйвер вполне подходит для передачи данных на ЖКИ, за исключением выдачи адреса ячейки.

    Чтобы убрать это исправим файл драйвера build_dir/target-powerpc_464fp_musl-1.1.15/linux-apm821xx_sata/linux-4.4.21/drivers/misc/eeprom/at24.c. Закомментируем несколько строк в процедуре at24_eeprom_write (335-337):

    //if (at24->chip.flags & AT24_FLAG_ADDR16)
    //  msg.buf[i++] = offset >> 8;
    //msg.buf[i++] = offset;

    Компилируем-обновляем, добавляем устройство, смотрим вывод

    root@lede: echo 24c00 0x27 > /sys/bus/i2c/devices/i2c-0/new_device
    [ 2708.782356] at24 0-0027: 16 byte 24c00 EEPROM, writable, 1 bytes/write
    [ 2708.788891] i2c i2c-0: new_device: Instantiated device 24c00 at 0x27
    root@lede: echo "1111" > /sys/bus/i2c/devices/i2c-0/0-0027/eeprom
    

    Все правильно, вывод без адреса ячейки, только того что нам надо:

    image

    Теперь можно переделать тестовую программу, убрав оттуда вызов утилиты i2cset:
    #!/bin/sh
    
    i2c_adres=0x27
    i2c_dev=/sys/bus/i2c/devices/i2c-0/0-0027/eeprom
    
    led=8
    ansi=0
    
    to_octal () {
    	hh3=$(($hh / 64))
    	hh1=$(($hh - $hh3 * 64))
    	hh2=$(($hh1 / 8))
    	hh1=$(($hh1 - $hh2 * 8))
    }
    
    write_CMD () {
    : $((hb = $c & 240))
    : $((lb = ($c << 4)  & 240 ))
    
    	hh=$((4 + $hb + $led))
    	to_octal 
    	echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev
    	hh=$((0 + $hb + $led))
    	to_octal 
    	echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev
    	hh=$((4 + $lb + $led))
    	to_octal 
    	echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev
    	hh=$((0 + $lb + $led))
    	to_octal 
    	echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev
    }
    
    print_LCD () {
    : $((hb = $c & 240))
    : $((lb = ($c << 4)  & 240 ))
    
    	hh=$((5 + $hb + $led))
    	to_octal 
    	echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev
    	hh=$((1 + $hb + $led))
    	to_octal 
    	echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev
    	hh=$((5 + $lb + $led))
    	to_octal 
    	echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev
    	hh=$((1 + $lb + $led))
    	to_octal 
    	echo -e -n \\$hh3$hh2$hh1 >> $i2c_dev
    }
    
    ##########  init LCD  #####################
    init_LCD () {
    if [[ ! -w $i2c_dev ]]
    then 
    	echo 24c00 0x27 > /sys/bus/i2c/devices/i2c-0/new_device
    	sleep 0.5
    fi
    sleep 0.5
    c=3
    write_CMD
    c=3
    write_CMD
    c=2
    write_CMD
    c=40 #28
    write_CMD
    c=44 #2C
    write_CMD
    c=44 #2C
    write_CMD
    c=12 #0C
    write_CMD
    c=1
    write_CMD
    sleep 0.2
    c=6
    write_CMD
    c=2
    write_CMD
    }
    ###############################
    
    init_LCD
    
    	c=0x80 # stroka - 1
    	write_CMD
    	for i in `seq 32 63`; do
    		if [ "$i" == 48 ]; then
    			c=0xC0 # stroka - 2
    			write_CMD
    		fi
    	c=$(($i + $ansi))
    	print_LCD
    	done
            sleep 3
    
    	c=0x80 # stroka - 1
    	write_CMD
    	for i in `seq 64 95`; do
    		if [ "$i" == 80 ]; then
    			c=0xC0 # stroka - 2
    			write_CMD
    		fi
    	c=$(($i + $ansi))
    	print_LCD
    	done
            sleep 3
    
    	c=0x80 # stroka - 1
    	write_CMD
    	for i in `seq 96 127`; do
    		if [ "$i" == 112 ]; then
    			c=0xC0 # stroka - 2
    			write_CMD
    		fi
    	c=$(($i + $ansi))
    	print_LCD
    	done
    


    Заодно теперь программа рассчитана только на нашу геометрию экрана — 2х16 символов. Понятно, что изменяя исходник в каталоге build_dir, следует ожидать что в ближайшем времени файл будет восстановлен из оригинальных пакетов при сборке. Для создания постоянных исправлений следует использовать возможность применения патчей на этапе сборки.

    9. Добавление драйвера HD44780 в ядро


    После изучения вопроса работоспособности данного варианта подключения ЖКИ было решено попробовать возложить на индикатор некоторый функционал. Например отображение какой-то части состояния операционной системы.

    Такой пакет уже существует и даже включен в состав LEDE. Это LCD4Linux. Он позволяет получать необходимую информацию о компонентах ОС и располагать ее на индикаторе в нужном месте. Естественно, обновление в реальном времени.

    Однако использование его с нашим индикатором на шине I2C вызвало некоторые затруднения.
    Подключение дисплея на HD44780-I2C из комплекта LCD4Linux

    в файле \etc\lcd4linux.conf
    Display HD44780-I2C {
        Driver 'HD44780'
        Model 'generic'
        Bus 'i2c'
        Port '/dev/i2c-0'
        Device '0x27'
        Bits '4'
        Size '16x2'
        asc255bug 0
        Icons 1
        Wire {
            RW     'DB1'
            RS     'DB0'
            ENABLE 'DB2'
            GPO    'GND'
        }
    }
    
    вызывало ошибку:
    root@lede: /usr/bin/lcd4linux -v -F
    
    LCD4Linux 0.11.0-SVN-1193 starting
    HD44780: $Rev: 1202 $
    HD44780: using model 'generic'
    HD44780: using I2C bus
    HD44780: using 1 Controller(s)
    HD44780: using 4 bit mode
    udelay: using gettimeofday() delay loop
    Segmentation fault


    Было также опробованы почти все возможные варианты дисплеев из пакета, в том числе и для использования символьного устройства на базе драйвера EEPROM, сделанного в предыдущей главе. Не заработало.

    Тогда было решено идти другим путем. Добавить в систему драйвер именно этого индикатора, принимающий для отображения символы и команды управления, а затем добавить новый дисплей, использующий этот драйвер, в LCD4Linux, благо в нем есть для этого руководство.

    Итак, берем готовый драйвер для HD44780 на I2C отсюда: Linux driver for Hitachi HD44780 LCD attached to I2C bus via PCF8574 I/O expander. Драйвер испытывался на Raspberry Pi, понимает пару команд управления терминала VT100, настраивается под разную геометрию индикатора, умеет отображать, гасить и мигать курсором. Осталось его интегрировать в LEDE и немного доработать.
    Скачиваем, распаковываем в папку package/hd44780/src.

    Дерево
    ls -l
    -rw-r--r-- 1 root root 18092 Feb 21  2016 LICENSE
    -rw-r--r-- 1 root root    60 Nov  9 06:17 Makefile
    -rw-r--r-- 1 root root  1945 Feb 21  2016 README.md
    -rw-r--r-- 1 root root 10316 Nov 16 04:33 hd44780-dev.c
    -rw-r--r-- 1 root root  7756 Feb 21  2016 hd44780-i2c.c
    -rw-r--r-- 1 root root  1122 Nov 16 03:28 hd44780.h
    -rw-r--r-- 1 root root   235 Feb 21  2016 make.sh


    Оставляем в Makefile только это:

    obj-m := hd44780.o
    hd44780-y := hd44780-i2c.o hd44780-dev.o

    И создаем новый Makefile, только папкой выше, в package/hd44780, по аналогии с файлами в других пакетах LEDE:

    package/hd44780/Makefile
    include $(TOPDIR)/rules.mk
    include $(INCLUDE_DIR)/kernel.mk
    
    PKG_NAME:=hd44780
    PKG_RELEASE:=1
    PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
    
    include $(INCLUDE_DIR)/package.mk
    
    define KernelPackage/hd44780
    SUBMENU:=Other modules
    TITLE:=I2C HD44780 driver
    FILES:=$(PKG_BUILD_DIR)/hd44780.ko
    AUTOLOAD:=$(call AutoLoad,70,hd44780)
    KCONFIG:=
    endef
    
    define Package/hd44780/description
    Big comments....
    ...
    endef
    
    MAKE_OPTS:= \
    	ARCH="$(LINUX_KARCH)" \
    	CROSS_COMPILE="$(TARGET_CROSS)" \
    	SUBDIRS="$(PKG_BUILD_DIR)" \
    	EXTRA_CFLAGS="$(EXTRA_CFLAGS)"
    
    define Build/Prepare
    	mkdir -p $(PKG_BUILD_DIR)
    	$(CP) ./src/* $(PKG_BUILD_DIR)/
    endef
    
    define Build/Compile
    	$(MAKE) -C "$(LINUX_DIR)" \
    		$(MAKE_OPTS) \
    		modules
    endef
    
    $(eval $(call KernelPackage,hd44780))


    Строку с автозагрузкой (AUTOLOAD:=$(call AutoLoad,70,hd44780)) можно добавить позже, когда драйвер будет протестирован. Теперь при вызове конфигуратора LEDE

    make menuconfig

    Драйвер появится в модулях ядра (kmod-hd44780), и его можно добавить в конфигурацию:

    LEDE Configuration
    image

    После компиляции, обновления и перезагрузки, если не включена автозагрузка модуля, то пробуем загрузить, смотрим результат:

    root@lede: insmod hd44780
    root@lede: lsmod |grep 44780
    hd44780                 5450  0
    

    Пробуем добавить устройство:

    root@lede: echo hd44780 0x27 > /sys/bus/i2c/devices/i2c-0/new_device
    [ 9463.913178] i2c i2c-0: new_device: Instantiated device hd44780 at 0x27

    На индикаторе, как заложено в драйвере, при инициализации выдается адрес созданного устройства: "/dev/lcd0", с мигающим курсором в конце.

    На это устройство можно отправлять символы, которые будут отображаться на индикаторе:

    root@lede: echo -n 123 > /dev/lcd0

    Также можно управлять режимами работы через sysfs (/sys/class/hd44780/lcd0). По этому пути есть следующие имена файлов: backlight, cursor_display, geometry, cursor_blink. Через них можно настраивать геометрию экрана, управлять режимами курсора и подсветкой. Например, для выключения мигания курсора достаточно дать команду:

    root@lede: echo -n 0 > /sys/class/hd44780/lcd0/cursor_blink

    Кроме того поддерживаются две команды терминала VT100, это очистка экрана и установка курсора в начальную позицию. Подать их можно так:

    root@lede: echo -n -e '\x1b'[2J > /dev/lcd0
    root@lede: echo -n -e '\x1b'[H > /dev/lcd0

    Установку необходимых режимов также можно сделать при загрузке ОС. Для этого добавляем файл target/linux/apm821xx/base-files/etc/board.d/03_lcd в LEDE с содержимым:

    #!/bin/sh
    echo hd44780 0x27 > /sys/bus/i2c/devices/i2c-0/new_device
    echo -n 16x2 > /sys/class/hd44780/lcd0/geometry
    echo -n 0 > /sys/class/hd44780/lcd0/cursor_display
    echo -n 0 > /sys/class/hd44780/lcd0/cursor_blink
    echo -n -e '\x1b'[2JHello! > /dev/lcd0
    exit 0
    

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

    10. Добавление обработки необходимых команд VT100 в драйвер HD44780


    Итак, драйвер работает, но для использования в LCD4Linux он должен уметь размещать символы в любой позиции экрана. Согласно списка команд терминала выбираем нужную:

    Esc[Line;ColumnH — Move cursor to screen location v,h

    Находим файл package/hd44780/src/hd44780-dev.c и добавляем обнаружение и выполнение новой команды. Надо доработать процедуру обработки esc-последовательностей:

    Оригинал:
    static void hd44780_handle_esc_seq_char(struct hd44780 *lcd, char ch)
    {
    	int prev_row, prev_col;
    
    	lcd->esc_seq_buf.buf[lcd->esc_seq_buf.length++] = ch;
    
    	if (!strcmp(lcd->esc_seq_buf.buf, "[2J")) {
    		prev_row = lcd->pos.row;
    		prev_col = lcd->pos.col;
    
    		hd44780_clear_display(lcd);
    		hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (lcd->geometry->start_addrs[prev_row] + prev_col));
    
    		hd44780_leave_esc_seq(lcd);
    	} else if (!strcmp(lcd->esc_seq_buf.buf, "[H")) {
    		hd44780_write_instruction(lcd, HD44780_RETURN_HOME);
    		lcd->pos.row = 0;
    		lcd->pos.col = 0;
    
    		hd44780_leave_esc_seq(lcd);
    	} else if (lcd->esc_seq_buf.length == ESC_SEQ_BUF_SIZE) {
    		hd44780_flush_esc_seq(lcd);
    	}
    }

    Доработанный вариант:
    static void hd44780_handle_esc_seq_char(struct hd44780 *lcd, char ch)
    {
    	int prev_row, prev_col;
    	struct hd44780_geometry *geo = lcd->geometry;
    
    	lcd->esc_seq_buf.buf[lcd->esc_seq_buf.length++] = ch;
    
    	if (!strcmp(lcd->esc_seq_buf.buf, "[2J")) {
    		prev_row = lcd->pos.row;
    		prev_col = lcd->pos.col;
    
    		hd44780_clear_display(lcd);
    		hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (lcd->geometry->start_addrs[prev_row] + prev_col));
    
    		hd44780_leave_esc_seq(lcd);
    	} else if (!strcmp(lcd->esc_seq_buf.buf, "[H")) {
    		hd44780_write_instruction(lcd, HD44780_RETURN_HOME);
    		lcd->pos.row = 0;
    		lcd->pos.col = 0;
    
    		hd44780_leave_esc_seq(lcd);
    	} else if ((lcd->esc_seq_buf.buf[0]=='[') && (lcd->esc_seq_buf.buf[4]=='H') && // Esc[ Line ; Column H
    		(lcd->esc_seq_buf.buf[2]==';' ) && (lcd->esc_seq_buf.length == 5)) {
    		lcd->pos.row = lcd->esc_seq_buf.buf[1] % geo->rows;
    		lcd->pos.col = lcd->esc_seq_buf.buf[3] % geo->cols;
    		hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR
    		    | (geo->start_addrs[lcd->pos.row] + lcd->pos.col));
    		hd44780_leave_esc_seq(lcd);
    	} else if (lcd->esc_seq_buf.length == ESC_SEQ_BUF_SIZE) {
    		hd44780_flush_esc_seq(lcd);
    	}
    }

    И надо изменить длину буфера для накопления и анализа esc-последовательностей. Находим в файле hd44780.h строку:

    #define ESC_SEQ_BUF_SIZE   4

    И исправляем значение на с 4 на 6. Можно компилировать и проверять. Можно компилировать только один пакет из LEDE.

    root@debian:/apm82181-lede-master# make package/hd44780/compile
     make[1] package/hd44780/compile
     make[2] -C package/hd44780 compile

    Если ошибок нет, то компилируем весь проект, обновляем, перезагружаемся. Проверяем:

    root@lede:/ echo -n -e '\x1b[1;6H' > /dev/lcd0

    Курсор перемещается во вторую строку и 7-ю позицию (нумерация с нуля):
    image


    11. Добавление дисплея с некоторыми командами VT100 в LCD4Linux


    Драйвер ЖКИ выполняет свой функционал. Теперь его можно задействовать в пакете LCD4Linux для отображения состояния системы. Однако я в нем не нашел дисплея, работающего с драйвером по протоколу терминала.

    Значит пишем свой. Согласно инструкции How to write new display drivers.
    Исходные файлы можно взять в каталоге build_dir/target-powerpc_464fp_musl-1.1.15/lcd4linux-custom/lcd4linux-r1203, либо из пакета dl/lcd4linux-r1203.tar.bz2.

    Все как в руководстве:

    1. Из файла drv_Sample.c drv делаем копию drv_vt100.c
    2. Редактируем drv_vt100.c, удаляем все связанное с графическим режимом, с GPIO
    3. Добавляем новый драйвер в drv.c
    4. Добавляем в Makefile.am

    5. Добавляем в drivers.m4
      if test "$VT100" = "yes"; then
         TEXT="yes"
         I2C="yes"
         DRIVERS="$DRIVERS drv_vt100.o"
         AC_DEFINE(WITH_VT100,1,[vt100 driver])
      fi

    6. Добавляем в Makefile.am

    Далее пишем свои процедуры в файл drv_vt100.c.

    drv_vt100_open:
    static int drv_vt100_open(const char *section)
    {
        char *s;
        int f = -1;
        s = cfg_get(section, "Port", NULL);
        if (s == NULL || *s == '\0' || strlen(s) > 80) {
    	error("%s: no '%s.Port' entry from %s", Name, section, cfg_source());
    	return -1;
        }
        strcpy(Port, s);
        f = open(Port, O_WRONLY);
        if (f == -1) {
    	error("open(%s) failed: %s", Port, strerror(errno));
    	return -1;
        }
        close (f);
        return 0;
    }
    


    drv_vt100_send:
    static void drv_vt100_send(const char *data, const unsigned int len)
    {
        unsigned int i;
        int f;
        f = open(Port, O_WRONLY);
        write (f, data, len);
        close (f);
    }

    drv_vt100_clear:
    static void drv_vt100_clear(void)
    {
        char cmd[4];
        cmd[0] = 0x1B; // ESC
        cmd[1] = '[';  // [
        cmd[2] = '2';  // 2
        cmd[3] = 'J';  // J
        drv_vt100_send(cmd, 4);
        cmd[2] = 'H';  // H
        drv_vt100_send(cmd, 3);
    }

    drv_vt100_write:
    static void drv_vt100_write(const int row, const int col, const char *data, int len)
    {
        char cmd[6];
        cmd[0] = 0x1B; 		// ESC
        cmd[1] = '[';  		// [
        cmd[2] = row & 0xff;	// Line
        cmd[3] = ';';		// ;
        cmd[4] = col & 0xff;	// Column
        cmd[5] = 'H';		// H
        drv_vt100_send(cmd, 6);
    }

    drv_vt100_close оставляем пустой.

    Редактируем и создаем файлы в отдельной от проекта LEDE папке. Затем, так как при компиляции проекта файлы LCD4linux обновляются из архива, то изменять их в папке build_dir/… бессмысленно. Необходимо пользоваться возможность применять патчи. Патчи для LCD4Linux располагаются в папке feeds/packages/utils/lcd4linux/patches. Свой, добавляющий новый драйвер дисплея VT100 нужно разместить тут же.

    Для создания патча делаем рядом две папки. В одной (пусть 1/) размещаем оригинальные файлы, в другой (пусть 2/) те же, но измененные. Затем выполняем команду diff:

    diff -Naur ./1 ./2 > 180-vt100.patch

    В результате имеем файл 180-vt100.patch примерно с таким содержанием:
    diff -Naur ./vt100/Makefile.am ./vt100-f/Makefile.am
    --- ./vt100/Makefile.am	2016-11-28 11:01:56.000000000 +0000
    +++ ./vt100-f/Makefile.am	2016-11-14 07:33:41.000000000 +0000
    @@ -125,6 +125,7 @@
     drv_USBHUB.c                  \
     drv_USBLCD.c                  \
     drv_vnc.c                     \
    +drv_vt100.c                   \
     drv_WincorNixdorf.c           \
     drv_X11.c                     \
                                   \
    diff -Naur ./vt100/drivers.m4 ./vt100-f/drivers.m4
    --- ./vt100/drivers.m4	2016-11-14 11:54:41.000000000 +0000
    +++ ./vt100-f/drivers.m4	2016-11-14 07:37:00.000000000 +0000
    @@ -39,7 +39,7 @@
       [                        Newhaven, Noritake, NULL, Pertelian, PHAnderson,]
       [                        PICGraphic, picoLCD, picoLCDGraphic, PNG, PPM, RouterBoard,]
       [                        Sample, SamsungSPF, serdisplib, ShuttleVFD, SimpleLCD, st2205, T6963,]
    -  [                        TeakLCM, TEW673GRU, Trefon, ULA200, USBHUB, USBLCD, VNC, WincorNixdorf, X11],
    +  [                        TeakLCM, TEW673GRU, Trefon, ULA200, USBHUB, USBLCD, VNC, vt100, WincorNixdorf, X11],
       drivers=$withval,
       drivers=all
     )
    @@ -114,6 +114,7 @@
     	 USBHUB="yes"
              USBLCD="yes"
              VNC="yes"
    +         VT100="yes"
     	 WINCORNIXDORF="yes"
              X11="yes"
              ;;
    @@ -279,6 +280,9 @@
           VNC)
              VNC=$val
              ;;
    +      vt100)
    +         VT100=$val
    +         ;;
           WincorNixdorf)
              WINCORNIXDORF=$val
              ;;
    @@ -869,6 +873,13 @@
        fi
     fi
     
    +if test "$VT100" = "yes"; then
    +   TEXT="yes"
    +   I2C="yes"
    +   DRIVERS="$DRIVERS drv_vt100.o"
    +   AC_DEFINE(WITH_VT100,1,[vt100 driver])
    +fi
    +
     if test "$WINCORNIXDORF" = "yes"; then
        TEXT="yes"
        SERIAL="yes"
    

    Создается один патч для всех файлов. Если посмотреть в патчах, которые уже есть в папке feeds/packages/utils/lcd4linux/patches, внутри файлов отсутствуют строки, которые показывают выполняемую команду «diff -Naur...». Приводим наш патч в такое же состояние и копируем в папку.
    Заходим в конфигуратор LEDE.

    Видим появление нашего драйвера дисплея в LCD4Linuc-custom:
    image

    Включаем его в проект, сохраняем настройки, компилируем, обновляем, перезагружаем. Подключаем наш драйвер в файле конфигурации:

    lcd4linux.conf
    Variables {
       tick 500
       tack 100
       minute 60000
    }
    Display VT100 {
        Driver 'vt100'
        Size '16x2'
        Port '/dev/lcd0'
    }
    Widget Test {
        class 'Text'
        expression '1234567890123456'
        width 16
    }
    Layout Test {
        Row01.Col1 'Test'
        Row02.Col1 'Test'
    }
    Display 'VT100'
    Layout 'Test'
    


    Проверяем:
    root@lede:/# /usr/bin/lcd4linux -v -F
    LCD4Linux 0.11.0-SVN-1193 starting
    vt100: $Rev: 001 $
    initializing layout 'Test'
    Creating new timer group (1000 ms)
     widget 'Test': Class 'text', Parent '<root>', Layer 1, Row 0, Col 0 (to 0,16)
     widget 'Test': Class 'text', Parent 'Test', Layer 1, Row 1, Col 0 (to 1,16)


    На индикаторе мы видим, как и планировали в конфиге, цифры.

    image

    12. Добавление команд программирования знакогенератора в драйверы HD44780 и VT100


    Индикатор на базе контроллера HD44780 имеет зашитый латинский алфавит с цифрами и знаками и свободно 8 программируемых символов (бывает есть и русские символы, но не в этом случае). Меняя отображение программируемых символов можно получить несложную анимацию. Ее варианты представлены в примере конфига LCD4Linux, но пока наши драйверы не поддерживают эту функцию, использовать их мы не можем.

    Ничего сложного в этом нет, надо в одном драйвере принять 8 байт и отправить с командой в знакогенератор, в другом их отправить.

    Снова исправляем
    процедуру hd44780_handle_esc_seq_char в hd44780-dev.c
    static void hd44780_handle_esc_seq_char(struct hd44780 *lcd, char ch)
    {
    	int prev_row, prev_col;
    	struct hd44780_geometry *geo = lcd->geometry;
    
    	if (lcd->is_in_set_char == 0) {
    		lcd->esc_seq_buf.buf[lcd->esc_seq_buf.length++] = ch;
    		if (!strcmp(lcd->esc_seq_buf.buf, "[2J")) {
    			prev_row = lcd->pos.row;
    			prev_col = lcd->pos.col;
    			hd44780_clear_display(lcd);
    			hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (lcd->geometry->start_addrs[prev_row] + prev_col));
    			hd44780_leave_esc_seq(lcd);
    		} else if (!strcmp(lcd->esc_seq_buf.buf, "[H")) {
    			hd44780_write_instruction(lcd, HD44780_RETURN_HOME);
    			lcd->pos.row = 0;
    			lcd->pos.col = 0;
    			hd44780_leave_esc_seq(lcd);
    		} else if ((lcd->esc_seq_buf.buf[0]=='[') && (lcd->esc_seq_buf.buf[4]=='H') && // Esc[ Line ; Column H
    			(lcd->esc_seq_buf.buf[2]==';' ) && (lcd->esc_seq_buf.length == 5)) {
    			lcd->pos.row = lcd->esc_seq_buf.buf[1] % geo->rows;
    			lcd->pos.col = lcd->esc_seq_buf.buf[3] % geo->cols;
    			hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR
    			    | (geo->start_addrs[lcd->pos.row] + lcd->pos.col));
    			hd44780_leave_esc_seq(lcd);
    		} else if (!strcmp(lcd->esc_seq_buf.buf, "(S")) { // Esc(S code matrix(8)
    			lcd->is_in_set_char = 1;
    		} else if (lcd->esc_seq_buf.length == ESC_SEQ_BUF_SIZE) {
    			hd44780_flush_esc_seq(lcd);
    		}
    
    	} else if (lcd->is_in_set_char == 1) { // start set CGRAM code
    		hd44780_write_instruction(lcd, HD44780_CGRAM_ADDR | 8 * (ch & 0x07));
    		lcd->is_in_set_char++;
    	} else {
    		hd44780_write_data(lcd, ch & 0x1f); // set 8 bytes CGRAM code
    		lcd->is_in_set_char++;
    		if (lcd->is_in_set_char == 10){ // go to DDRAM mode
    			hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR
    			    | (geo->start_addrs[lcd->pos.row] + lcd->pos.col));
    			hd44780_leave_esc_seq(lcd);
    	    }
    	}
    }

    Добавляем в нее режим приема 8-и байтов знакогенератора и запись их в CGRAM (знакогенератор). Так как в VT100 я не нашел ESC-последовательность программирования символа, то пришлось что-то придумать. Пусть это будет Esc(S 8 байт, то есть код ESC, затем открывающая круглая скобка, латинская буква S и 8 байт матрицы. В нашем случае, при размере знакоместа 8*5 будет использоваться только 5 младших бит каждого байта.

    В процедуре hd44780_write добавляем сброс режима приема знакогенератора (строка lcd->is_in_set_char = 0).

    hd44780_write
     void hd44780_write(struct hd44780 *lcd, const char *buf, size_t count)
    ...
     			case '\e':
     				lcd->is_in_esc_seq = true;
    				lcd->is_in_set_char = 0;
     				break;
     			default:
     				hd44780_write_char(lcd, ch);
    ...
    

    И описываем это поле структуры (is_in_set_char) в файле заголовков hd44780.h.

    struct hd44780
    struct hd44780 {
    	struct cdev cdev;
    	struct device *device;
    	struct i2c_client *i2c_client;
    	struct hd44780_geometry *geometry;
    	struct {
    		int row;
    		int col;
    	} pos;
    	char buf[BUF_SIZE];
    	struct {
    		char buf[ESC_SEQ_BUF_SIZE];
    		int length;
    	} esc_seq_buf;
    	bool is_in_esc_seq;
    	int is_in_set_char;
    	bool backlight;
    	bool cursor_blink;
    	bool cursor_display;
    	bool dirty;
    	struct mutex lock;
    	struct list_head list;
    };

    Теперь добавим этот функционал в драйвер дисплея LCD4Linux. Функция drv_vt100_defchar файла drv_vt100.c:

    drv_vt100_defchar
    static void drv_vt100_defchar(const int ascii, const unsigned char *matrix)
    {
        char cmd[12];
        int i;
    
        /* call the 'define character' function */
        cmd[0] = 0x1B; 		// ESC
        cmd[1] = '(';  		// (
        cmd[2] = 'S';		// S
        cmd[3] = ascii & 0x07;	// code
    
        /* send bitmap to the display */
        for (i = 0; i < 8; i++) {
    	cmd[i + 4] = (*matrix++) & 0x1f;
        }
        drv_vt100_send(cmd, 12);
    }
    

    Компилируем, обновляем, перезагружаем. Меняем снова конфиг LCD4linux.

    lcd4linux.conf
    Variables {
       tick 500
       tack 100
       minute 60000
    }
    Display VT100 {
        Driver 'vt100'
        Size '16x2'
        Port '/dev/lcd0'
        Icons 1
    }
    Widget RAM {
        class  'Text'
        expression meminfo('MemFree')/1024
        postfix ' MB RAM'	
        width  11
        precision 0
        align  'R'
        update tick
    }
    Widget Busy {
        class 'Text'
        expression proc_stat::cpu('busy', 500)
        prefix 'Busy'	
        postfix '%'	
        width 9	
        precision 1
        align 'R'	
        update tick	
    }	
    Widget Uptime {
        class 'Text'
        expression uptime('%d days %H:%M:%S')
        width 20
        align 'R'
        prefix 'Up '
        update 1000
    }
    Widget Uptime {
        class 'Text'
        expression 'Up '.uptime('%d %H:%M:%S')
        width 16
        align 'L'
        update 1000
    }
    # Icons
    Widget Timer {
        class 'Icon'
        speed 83
        Bitmap {
    	Row1 '.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|'
    	Row2 '.***.|.*+*.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.+++.|.+*+.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|'
    	Row3 '*****|**+**|**++*|**+++|**++.|**++.|**+++|**+++|**+++|**+++|**+++|+++++|+++++|++*++|++**+|++***|++**.|++**.|++***|++***|++***|++***|++***|*****|'
    	Row4 '*****|**+**|**+**|**+**|**+++|**+++|**+++|**+++|**+++|**+++|+++++|+++++|+++++|++*++|++*++|++*++|++***|++***|++***|++***|++***|++***|*****|*****|'
    	Row5 '*****|*****|*****|*****|*****|***++|***++|**+++|*++++|+++++|+++++|+++++|+++++|+++++|+++++|+++++|+++++|+++**|+++**|++***|+****|*****|*****|*****|'
    	Row6 '.***.|.***.|.***.|.***.|.***.|.***.|.**+.|.*++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.+++.|.++*.|.+**.|.***.|.***.|.***.|.***.|'
    	Row7 '.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|'
    	Row8 '.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|'
        }
    }
    Layout L16x2 {
        Row1 {
    	Col1  'Uptime'
    	col16 'Timer'
        }
        Row2 {
    	Col1  'Busy'
    	Col11 'RAM'
        }
    }
    Display 'VT100'
    Layout 'L16x2'
    

    Проверяем:

    root@lede: /usr/bin/lcd4linux  -v -F
    LCD4Linux 0.11.0-SVN-1193 starting
    vt100: $Rev: 001 $
    vt100: reserving 1 of 8 user-defined characters for icons
    initializing layout 'L16x2'
    Creating new timer group (1000 ms)
     widget 'Uptime': Class 'text', Parent '<root>', Layer 1, Row 0, Col 0 (to 0,16)
    Creating new timer group (83 ms)
     widget 'Timer': Class 'icon', Parent '<root>', Layer 1, Row 0, Col 15 (to 1,16)
    Creating new timer group (500 ms)
     widget 'Busy': Class 'text', Parent '<root>', Layer 1, Row 1, Col 0 (to 1,9)
     widget 'RAM': Class 'text', Parent '<root>', Layer 1, Row 1, Col 10 (to 1,21)
    

    На экране видим время работы платы с последней загрузки, загрузку системы, свободной память и символ анимации в виде заполняющегося и очищающегося диска.

    image

    Добавив исправление конфига LCD4Linux в патч 180-vt100.patch, мы получим такой же вид индикатора сразу при загрузке:

    180-vt100.patch
    --- a/lcd4linux.conf.sample	2016-11-15 09:47:46.000000000 +0000
    +++ a-f/lcd4linux.conf.sample	2016-11-18 03:18:22.000000000 +0000
    @@ -567,7 +567,14 @@
         HttpPort	 '5800'
     }
     
    -
    +Display VT100 {
    +    Driver 'vt100'
    +    Size '16x2'
    +    Port '/dev/lcd0'
    +    Icons 1
    +}
    +            
    +            
     Display FutabaVFD {
         Driver 'FutabaVFD'
         Port '/dev/parport0'	
    @@ -674,7 +681,7 @@
     
     Widget RAM {
         class  'Text'
    -    expression meminfo('MemTotal')/1024
    +    expression meminfo('MemFree')/1024
         postfix ' MB RAM'	
         width  11
         precision 0
    @@ -828,6 +835,14 @@
         update 1000
     }
     
    +Widget Uptime {
    +    class 'Text'
    +    expression 'Up '.uptime('%d %H:%M:%S')
    +    width 16
    +    align 'L'
    +    update 1000
    +}
    +
     Widget mpris_TrackPosition_bar {
         class 'Bar'
         expression  mpris_dbus::method_PositionGet('org.kde.amarok')
    @@ -1015,7 +1030,7 @@
     
     Widget Timer {
         class 'Icon'
    -    speed 50
    +    speed 83
         Bitmap {
     	Row1 '.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|.....|'
     	Row2 '.***.|.*+*.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.*++.|.+++.|.+*+.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|.+**.|'
    @@ -1225,6 +1240,17 @@
         }
     }
     
    +Layout L16x2-2 {
    +    Row1 {
    +	Col1  'Uptime'
    +	col16 'Timer'
    +    }
    +    Row2 {
    +	Col1  'Busy'
    +	Col11 'RAM'
    +    }
    +}
    +
     Layout L20x2 {
         Row1 {
             Col1  'CPUinfo'
    @@ -1323,7 +1349,7 @@
     
     
     
    -Display 'ACool'
    +#Display 'ACool'
     #Display 'SerDispLib'
     #Display 'LCD-Linux'
     #Display 'LCD2041'
    @@ -1354,7 +1380,7 @@
     #Display 'IRLCD'
     #Display 'USBLCD'
     #Display 'BWCT'
    -#Display 'Image'
    +Display 'Image'
     #Display 'TeakLCD'
     #Display 'Trefon'
     #Display 'LCD2USB'
    @@ -1363,15 +1389,17 @@
     #Display 'ctinclud'
     #Display 'picoLCD'
     #Display 'VNC'
    +Display 'VT100'
     #Display 'FutabaVFD'
     #Display 'GLCD2USB'
     
    -#Layout 'Default'
    -Layout 'TestLayer'
    +Layout 'Default'
    +#Layout 'TestLayer'
     #Layout 'TestImage'
     #Layout 'L8x2'
     #Layout 'L16x1'
     #Layout 'L16x2'
    +Layout 'L16x2-2'
     #Layout 'L20x2'
     #Layout 'L40x2'
     #Layout 'Test'
    


    13. Оптимизация передачи данных по шине I2C


    Теперь, когда все работает как планировалось, немного о скорости передачи данных. Хочется обратить внимание на два момента.

    Во-первых, данный по шине I2C передаются очень маленькими блоками, а конкретно по одному байту. А каждому блоку добавляется адрес ведомого устройства. Логично предположить, что передача адреса устройства с блоком бОльшего размера увеличит утилизацию шины и уменьшит время передачи.

    Так выглядит передача отдельными байтами
    image
    Видно что каждый второй — байт адреса (0x4E).

    Проведем частичную оптимизацию. Для этого вспомним как передается один байт данных на индикатор. ЖКИ работает в 4-битном режиме, т.е. получает за раз по полбайта. Эти полбайта должны подтверждаться выдачей сигнала «Enable». В итоге для передачи одного байте из процессора на индикатор по шине I2C идет 6 байт:

    1. Старшие полбайта без сигнала «Enable»
    2. Старшие полбайта c сигналом «Enable»
    3. Старшие полбайта без сигнала «Enable»
    4. Младшие полбайта без сигнала «Enable»
    5. Младшие полбайта с сигналом «Enable»
    6. Младшие полбайта без сигнала «Enable»

    И так как каждый сопровождается адресом, то реально это составляет 12 байт, при скорости шины 100 КГц это 1.2 мс.

    Предлагается передавать те же 6 байт, но одним блоком, с одним байтом адреса, т.е. 7 байт вместо 12. Оригинальные процедуры передачи данных из драйвера HD44780.

    hd44780_write_data, hd44780_write_nibble, pcf8574_raw_write из hd44780-dev.c
    static void pcf8574_raw_write(struct hd44780 *lcd, u8 data)
    {
    	i2c_smbus_write_byte(lcd->i2c_client, data);
    }
    
    static void hd44780_write_nibble(struct hd44780 *lcd, dest_reg reg, u8 data)
    {
    	data = (data << 4) & 0xF0;
    	if (reg == DR)
    		data |= RS;
    	data = data | (RW & 0x00);
    	if (lcd->backlight)
    		data |= BL;
    	pcf8574_raw_write(lcd, data);
    	pcf8574_raw_write(lcd, data | E);
    	pcf8574_raw_write(lcd, data);
    }
    
    static void hd44780_write_data(struct hd44780 *lcd, u8 data)
    {
    	u8 h = (data >> 4) & 0x0F;
    	u8 l = data & 0x0F;
    	hd44780_write_nibble(lcd, DR, h);
    	hd44780_write_nibble(lcd, DR, l);
    	udelay(37 + 4);
    }

    Процедура из драйвера HD44780, исправленная для пакетной передачи данных.

    hd44780_write_data из hd44780-dev.c
    static void hd44780_write_data(struct hd44780 *lcd, u8 data)
    {
    	u8 h = (data >> 4) & 0x0F;
    	u8 l = data & 0x0F;
    	u8 buf[5];
    	h = (h << 4) & 0xF0;
    	l = (l << 4) & 0xF0;
    	h |= RS;
    	l |= RS;
    	h = h | (RW & 0x00);
    	l = l | (RW & 0x00);
    	if (lcd->backlight){
    		h |= BL;
    		l |= BL;
    	}
    	buf[0] = h | E;
    	buf[1] = h;
    	buf[2] = l;
    	buf[3] = l | E;
    	buf[4] = l;
    	i2c_smbus_write_i2c_block_data(lcd->i2c_client, h, 5, (const u8 *)(&buf[0]));
    	udelay(37 + 4);
    }

    Вот так выглядит теперь передача байта 0x1F
    image

    И занимает 0,67 мс.

    Во-вторых, скорость шины по умолчанию 100 КГц, а это не максимум. Конечно именно такая скорость рекомендуется для расширителя портов на ЖКИ. Но в тоже время многие разработчики говорят о бесперебойной работе и на 400 КГц. Конечно, использование нестандартного режима оправдано при необходимости и тщательном тестирование на отсутствие сбоев, а я всего лишь могу сказать как это сделать и что получится.

    Информации в интернете о включении режима найти не удалось, пришлось пересматривать исходники LEDE. В итоге есть два варианта включения режима fast, т.е. 400 КГц.

    Первый, это передать параметр модулю ядра. Модуль это i2c-ibm_iic. Параметр — iic_force_fast. В итоге надо к параметрам ядра при запуске добавить i2c-ibm_iic.iic_force_fast=1. Это можно сделать в загрузчике U-boot например так:

    setenv addmisc 'setenv bootargs ${bootargs} i2c-ibm_iic.iic_force_fast=1'

    После загрузки системы мы имеем:

     root@lede: dmesg | grep i2c
    [    0.000000] Kernel command line: root=/dev_nfs rw nfsroot=192.168.1.10:/nfs/debian_ppc/rootfs ip=dhcp console=ttyS0,115200 i2c-ibm_iic.iic_force_fast=1
    [    4.770923] i2c /dev entries driver
    [    4.774742] ibm-iic 4ef600700.i2c: using fast (400 kHz) mode
    [   10.456041] i2c i2c-0: new_device: Instantiated device hd44780 at 0x27
    

    Второй — указать режим работы шины в дереве устройств (apollo3g.dtsi, параметр fast-mode):

    IIC0: i2c@ef600700 {
    	compatible = "ibm,iic";
    	reg = <0xef600700 0x00000014>;
    	interrupt-parent = <&UIC0>;
    	interrupts = <0x2 0x4>;
    	fast-mode;
    	#address-cells = <1>;
    	#size-cells = <0>;
    };

    После компиляции не забудьте обновить дерево устройств на TFTP сервере. И результат:

    root@lede: dmesg | grep i2c
    [    4.774585] i2c /dev entries driver
    [    4.778396] ibm-iic 4ef600700.i2c: using fast (400 kHz) mode
    [   10.464396] i2c i2c-0: new_device: Instantiated device hd44780 at 0x27
    root@lede: ls -al /proc/device-tree/plb/opb/i2c@ef600700
    -r--r--r--    1 root     root             4 Nov 18 04:13 #address-cells
    -r--r--r--    1 root     root             4 Nov 18 04:13 #size-cells
    drwxr-xr-x    2 root     root             0 Nov 18 04:13 .
    drwxr-xr-x   12 root     root             0 Nov 18 04:13 ..
    -r--r--r--    1 root     root             8 Nov 18 04:13 compatible
    -r--r--r--    1 root     root             0 Nov 18 04:13 fast-mode
    -r--r--r--    1 root     root             4 Nov 18 04:13 interrupt-parent
    -r--r--r--    1 root     root             8 Nov 18 04:13 interrupts
    -r--r--r--    1 root     root             4 Nov 18 04:13 name
    -r--r--r--    1 root     root             8 Nov 18 04:13 reg
    

    И скорость передачи байта 0,19 мс:

    image

    Что почти на порядок лучше исходной.

    В качестве вывода можно сказать, что в результате проделанной работы мы получили возможность использовать плату со слабодокументированным в открытых источниках процессором в проектах, где применим Linux (LEDE). Основной интерфейс Ethernet, хранение на SATA, управление через I2C и несколько портов дают широкие возможности для разработчиков.

    Ну и напоследок, для дублирования всего вышесказанного, файлы из LEDE, согласно структуры каталогов (вроде все вспомнил) доступны тут.
    Поделиться публикацией

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.