RomeoGolf

Вт 27 Сентябрь 2016

USB-polygon-4: Обмен по USB, начало

Выбор класса устройства

Продолжаю разговор про самодельную отладочную плату на контроллере AT90USB162.

В примененном контроллере есть возможность подключения к компьютеру по USB. Прежде чем использовать эту возможность, нужно определиться с классом устройства. Операционная система будет общаться с устройством посредством драйверов и выбирать соответствующий драйвер в зависимости от класса. Так что же выбрать?

В той же Википедии насчитывается порядка двух десятков классов USB-устройств. Для сделанной отладочной платы можно сразу исключить узкоспецифичные, например, Audio, Video, Image, Printer и прочее подобное, а также Wireless Controller, поскольку к беспроводной связи этот макет не приспособлен совсем. Тогда выбор ограничивается такими классами:

  • 00 Unspesified
  • 02 Communication Device Class & 0A CDC-data
  • 03 Human Interface Device
  • 05 Physical Interface Device
  • 08 MassStorage Device Class
  • DC Diagnostic Device
  • EF Miscellaneous
  • FE Application-specific
  • FF Vendor-specific

Классы Unspesified, Application-specific, Vendor-specific, Miscellaneous страшновато даже рассматривать. Явно придется делать много работы вручную — писать кучу кода для устройства, наверняка писать драйвер для операционной системы. Хотелось бы для начала что-то попроще.

Про Diagnostic Device и Physical Interface Device не нашел информации. То есть, составить представление, что это за штуки такие — можно, а как это сделать самому, да попроще — не нашел.

CDC — вроде бы неплохой класс, предназначенный для передачи приличных объемов информации в обоих направлениях. И теоретически эту информацию можно использовать не только таким способом, какой предполагает класс устройства, то есть, не только для преобразования в другие виды интерфейсов (типа COM), но и для управления самим устройством и даже для хранения на устройстве. Но все же этот класс тоже достаточно специфичен и узконаправлен. Не понравился он мне.

HID — ничего себе штучка для реализации устройства с небольшими объемами и скоростями циркулирующей информации. В Windows работа с ним довольно проста благодаря встроенным драйверам. Я даже попробовал как-то сделать простенькую макетку на AT89C5131 по книге Агурова. Все оказалось довольно просто, где-то за неделю (занимаясь вечерами) устройство заработало. Однако у этого класса есть несколько минусов, которые мне сильно не понравились. Во-первых, хотя драйвер такому устройству не нужен, все же нужен INF-файл. То есть, воткнуть в любой компьютер, чтобы сразу заработало, не получится. Во-вторых, скорости обмена весьма дохленькие. Для моргания светодиодиком — более чем достаточно, но я еще не знаю, во что выльется мой макет. Есть предположение, что в инструмент для настройки и отладки другого прибора с передачей приличных объемов информации, с записью промежуточных результатов работы в файл с последующим считыванием и все такое. Кроме того, после беглого обзора по форумам у меня сложилось впечатление, что в Linux работа с HID далеко не так проста, как в Windows, а как с этим делом в Android, я вообще не понял.

MassStorage Device — на первый взгляд самое оно. Рассчитано на большие объемы информации, удовлетворительные скорости, в современных ОС не требует драйверов для установки. Да что там, современных — где-то с Windows Me уже не требует. Тоже, конечно, не без недостатков. Во-первых, обмен может вестись только по инициативе хост-устройства, то есть, компьютера. Во-вторых, не мешало бы реализовать файловую систему, даже если она не нужна вовсе. Хотя, можно и обойтись, но придется заниматься костылевелосипедостроением. Опять же, строить файловую систему там, где она не нужна — тоже велосипедокостыли те еще. И в-третьих, забегая вперед, уже после реализации этого класса был обнаружен неприятный факт: работа с MassStorage кэшируется ОС, и изменения на устройстве, видимые под видом файлов, системой игнорируются, так как данные читаются один раз ив дальнейшем берутся не из устройства, а из кэша. Это, конечно, можно объехать на кривой козе, но придется что-то писать вручную, а что именно — я пока не знаю.

Есть еще казалось бы заманчивый вариант: MTP, Media Transfer Protocol. Активно используется современными смартфонами на Android. Вроде бы позволяет и обновившиеся данные в файлах перечитывать, и потоковые данные передавать/забирать. Вот только беглый обзор реализации на устройстве немного пугает, а поддержка в ОС огорчает: мне, к сожалению, надо еще пользоваться местами Windows XP, а я не смог по-человечески подключить к ней свой смартфон с четвертым андроидом. В семерке — вопросов нет, в восьмом Debian — тоже, но мне-то и XP надо. Опять же, вопрос — можно ли подключить MTP-устройство к, скажем, планшету с Android? Пока не знаю, а MassStorage — однозначно можно. Плюс к этому относительно невысокая скорость обмена большими объемами и разработчик — Microsof, что настораживает.

В общем, остановился на MassStorage Device, попросту говоря, флэшка.

Выбор библиотеки

Несколько лет назад, как я уже упоминал, попробовал сделать простую макетку на AT89C5131. Никаких библиотек не использовал, но тогда и выбора особого не было, и с HID-устройствами работать несколько проще, и я тогда предпочитал собственные велосипеды готовым решениям даже в ущерб простоте и скорости написания кода. Сегодня я сначала посмотрю, нельзя ли подтянуть что-то готовое. Конечно, не всегда использование библиотек является оптимальным, например, если огромный монстр имеет тучу возможностей, а надо только моргнуть диодиком, или наоборот, дохленький кусочек кода для своих целей еще пилить и пилить, так что проще сразу с нуля начать. Ну, посмотрим, что есть готового.

Во-первых, конечно, родная атмеловская библиотека. В ней привлекает то, что она все же родная, под эти контроллеры заточенная, да еще и самим производителем. Документация опять же на сайте Atmel имеется, и на первый взгляд даже неплохая и с примерами.

Во-вторых, одна из самых популярных (по встречаемости на форумах и в результатах выдачи поисковиков) библиотека LUFA. Очень хорошие отзывы на форумах, широкий спектр применения, уже образовавшееся сообщество с возможностью найти ответы на возникающие вопросы, очень нетребовательная лицензя MIT (если вдруг макет перерастет во что-то значительное). Заточена под AVR-GCC, входящий в состав AVR-Toolchain, то есть, с чем я и так собирался работать.

В-третьих, V-USB. Библиотека с обилием примеров применения, но ее основной фишкой является программная эмуляция USB для контроллеров, не имеющих аппаратной реализации, кроме того, с ее использованием в основном встречались HID-устройства. Впрочем, достаточно и того, что это программная эмуляция. Не нужно.

В-четвертых, все прочие, типа Dr. Stefan Salewski’s AT90USB1287 Stack, PJRC Teensy Stack и все такое. Не очень популярные решения разного рода, имеющие крайне мало примеров, где можно было бы подсмотреть особенности практического применения. Не вижу причин использовать что-то из них, поскольку это именно тот случай, когда допиливать может оказаться не проще, чем собрать с нуля самому.

Посмотрел бегло по диагонали документацию к USB-стеку Atmel и описание примеров. Потом заглянул на страничку LUFA, где ознакомился с кратким описанием, скачал библиотеку и документацию. И LUFA понравилась больше. Порылся немножко по форумам: там тоже решение от Atmel больше ругают, чем хвалят, а LUFA — наоборот. Ладно, выбираю LUFA.

Подготовка

На упомянутой чуть выше страничке LUFA скачал саму библиотеку и документацию к ней. В архиве с библиотекой есть папка LUFA — собственно, библиотека, и папка DEMOS — это не «народ» по-гречески, это примеры применения. Примеры замечательные и на самые разные случаи жизни, для устройств, хостов и приборов, которые способны и на то, и на другое. В папке lufa-LUFA-151115\Demos\Device\LowLevel\ можно найти и аудиоустройство, и мышь с клавиатурой, и HID, но, самое главное, там есть MassStorage.

Делаю отдельную папку для проекта, скажем, lufa-test, и копирую в нее упомянутые LUFA и MassStorage. Разумеется, библиотеку можно не копировать, на то она и библиотека, но мало ли… Вдруг захочется поковыряться немного в ее исходниках, так чтоб не запороть оригинал. А уж в MassStorage буду ковыряться по полной программе, для того и копирую. Это будет основная папка проекта для макета.

Там уже лежит Makefile, значит, можно попробовать запустить make, но не просто так, а в предварительно настроенном cygwin (о выборе и настройке компилятора — в предыдущем выпуске цикла). Сперва захожу в соответствующую папку:

$ cd /cygdrive/k/radio/asm/lufa-test/MassStorage/

и командую make. Получаю ответ:

makefile:38: ../../../../LUFA/Build/lufa_atprogram.mk: No such file or directory
make: *** Нет правила для сборки цели «../../../../LUFA/Build/lufa_atprogram.mk».  Останов.

Что не удивительно. Надо было все-таки сначала открыть и отредактировать Makefile под свои нужды. Для начала надо найти в нем переменную LUFA_PATH и оставить в ней только одну пару точек в пути: ../LUFA.

Вот теперь make замечательно запускается, проект компилируется, в папке проекта создаются всякого рода файлы, которых не было. Даже появился MassStorage.hex, который можно прошить в устройство посредством FLIP. Вот только не стоит пока этого делать — бесполезно.

Первая же переменная в Makefile — MCU — указывает на контроллер at90usb1287, а у меня at90usb162. Третья переменная — BOARD — указывает на какую-то отладочную плату. Не важно, какую, важно, что у меня — никакая, в списках не значится.

Однако начало положено, есть какая-то заготовка.

«… доработать напильником»

Начинаю подгонять заготовку под то, что имеется.

Для начала заменяю в Makefile значение MCU с at90usb1287 на at90usb162, а значение BOARD — на NONE, чищу результаты компиляции при помощи make clean и снова запускаю make. Компиляция сваливается с ошибкой. Да не с одной, а с целой кучей:

... до сих пор более-менее нормально ...

Lib/DataflashManager.c: In function 'DataflashManager_WriteBlocks':
Lib/DataflashManager.c:52:77: warning: division by zero [-Wdiv-by-zero]
  uint16_t CurrDFPage          = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) / D
ATAFLASH_PAGE_SIZE);
                                                                             ^
Lib/DataflashManager.c:53:77: warning: division by zero [-Wdiv-by-zero]
  uint16_t CurrDFPageByte      = ((BlockAddress * VIRTUAL_MEMORY_BLOCK_SIZE) % D
ATAFLASH_PAGE_SIZE);
                                                                             ^
Lib/DataflashManager.c:68:21: error: 'DF_CMD_BUFF1WRITE' undeclared (first use i
n this function)
  Dataflash_SendByte(DF_CMD_BUFF1WRITE);
                     ^
Lib/DataflashManager.c:68:21: note: each undeclared identifier is reported only 
once for each function it appears in
Lib/DataflashManager.c:98:44: error: 'DF_CMD_BUFF2TOMAINMEMWITHERASE' undeclared
 (first use in this function)
     Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_
CMD_BUFF1TOMAINMEMWITHERASE);
                                            ^
Lib/DataflashManager.c:98:77: error: 'DF_CMD_BUFF1TOMAINMEMWITHERASE' undeclared
 (first use in this function)
     Dataflash_SendByte(UsingSecondBuffer ? DF_CMD_BUFF2TOMAINMEMWITHERASE : DF_
CMD_BUFF1TOMAINMEMWITHERASE);
                                                                             ^
In file included from ../LUFA/../LUFA/Drivers/USB/USB.h:382:0,
                 from Lib/../Descriptors.h:40,
                 from Lib/../MassStorage.h:45,
                 from Lib/DataflashManager.h:42,
                 from Lib/DataflashManager.c:40:
../LUFA/../LUFA/Drivers/Board/Dataflash.h:141:60: error: 'DATAFLASH_CHIP0' undec
lared (first use in this function)
    #define DATAFLASH_CHIP_MASK(index)      CONCAT_EXPANDED(DATAFLASH_CHIP, inde
x)
                                                            ^
../LUFA/../LUFA/Drivers/USB/../../Common/Common.h:210:37: note: in definition of
 macro 'CONCAT'
     #define CONCAT(x, y)            x ## y
                                     ^
... и тому подобное ...

И при беглом просмотре похоже на то, что все ошибки связаны с DataflashManager, модулем, расположенным тут же в папке Lib. Модуль явно отвечает за управление флэш-памятью, это видно даже из названия, но написано и внутри в Doxygen-комментарии. А у меня на плате флэш нету и в ближайшем будущем не ожидается. Попробую тупо вырезать это дело: убираю в Makefile из значения переменной SRC запись «Lib/DataflashManager.c», в h-файлах закрываю «include "DataflashManager.h"», в MassStorage.c и SCSI.c нахожу и закрываю (символами комментария) все строки с функциями, начинающимися с Dataflash. Причем, естественно, не только сами строки, но всю содержащую конструкцию, типа if, скажем.

$ make
 [INFO]    : Begin compilation of project "MassStorage"...

avr-gcc.exe (AVR_8_bit_GNU_Toolchain_3.5.2_1680) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 [GCC]     : Compiling C file "SCSI.c"
avr-gcc -c -pipe -gdwarf-2 -g2 -mmcu=at90usb162 -fshort-enums -fno-inline-small-
functions -fpack-struct -Wall -fno-strict-aliasing -funsigned-char -funsigned-bi
tfields -ffunction-sections -I. -I../LUFA/.. -DARCH=ARCH_AVR8 -DBOARD=BOARD_NONE
-DF_USB=8000000UL -DF_CPU=8000000UL -mrelax -fno-jump-tables -x c -Os -std=gnu9 
9 -Wstrict-prototypes -DUSE_LUFA_CONFIG_HEADER -IConfig/  -MMD -MP -MF Lib/SCSI.
d Lib/SCSI.c -o Lib/SCSI.o
Lib/SCSI.c: In function 'SCSI_Command_Read_Capacity_10':
Lib/SCSI.c:216:23: error: 'LUN_MEDIA_BLOCKS' undeclared (first use in this funct
ion)
  Endpoint_Write_32_BE(LUN_MEDIA_BLOCKS - 1);
                       ^
Lib/SCSI.c:216:23: note: each undeclared identifier is reported only once for ea
ch function it appears in
Lib/SCSI.c:219:23: error: 'VIRTUAL_MEMORY_BLOCK_SIZE' undeclared (first use in t
his function)
  Endpoint_Write_32_BE(VIRTUAL_MEMORY_BLOCK_SIZE);
                       ^
Lib/SCSI.c: In function 'SCSI_Command_ReadWrite_10':
Lib/SCSI.c:298:22: error: 'LUN_MEDIA_BLOCKS' undeclared (first use in this funct
ion)
  if (BlockAddress >= LUN_MEDIA_BLOCKS)
                      ^
Lib/SCSI.c:320:62: error: 'VIRTUAL_MEMORY_BLOCK_SIZE' undeclared (first use in t
his function)
  CommandBlock.DataTransferLength -= ((uint32_t)TotalBlocks * VIRTUAL_MEMORY_BLO
CK_SIZE);
                                                              ^
make: *** [../LUFA/Build/lufa_build.mk:295: Lib/SCSI.o] Ошибка 1

После компиляции стало чуть получше, то есть, ошибок поменьше, но «чуть» не считается, потому что все равно не компилируется. А это потому, что теперь неизвестны константы, объявленные в DataflashManager. Ладно, include возвращаю. Функции из этого модуля закрыты, компиляции модуль тоже не подлежит, но нужное из него возьмем.

Чищу результаты неудачной компиляции, запускаю make — Ура! Откомпилировано!

... много-премного букв ...

avr-size --mcu=at90usb162 --format=avr MassStorage.elf
AVR Memory Usage
----------------
Device: at90usb162

Program:    4054 bytes (24.7% Full)
(.text + .data + .bootloader)

Data:        113 bytes (22.1% Full)
(.data + .bss + .noinit)


 [INFO]    : Finished building project "MassStorage".

Запускаю FLIP. Не забываю на подключенной плате нажать Reset, не отпуская, нажать HWB, отпустить Reset, отпустить HWB. После чего в окне FLIP подключаюсь к плате, загружаю hex-файл и жму Run. Прошивка загружается.

Теперь плата отображается в Windows XP и в семерке в виде флэшки «LUFA Dataflash Disk USB Device», ему выделяется буква диска, можно посмотреть свойства. Он пустой, без файловой системы, и Windows 7 все порывается при подключении его отформатировать.

new-dew-lufa lufa-pref

И в диспетчере устройств плата прописалась аж в трех местах (на примере Windows XP): «Дисковые устройства», «Контроллеры универсальной последовательной шины USB» и «Тома запоминающих устройств».

dev-man-lufa

Маленькое расширение программы

Теперь компьютер думает, что моя плата пусть хреновенькая, но все-таки флэшка. Однако, кроме как создавать видимость флэшки, плата больше ничего пока не может. А у нее ведь есть светодиодики и кнопки! Зря, что ли?

И вообще смешно получается — взял готовый пример и только выкидывал из него лишнее. Надо же что-то и добавить!

Открываю основной файл проекта — MassStorage.c. Ага, а в нем еще есть что убрать. Закрываю все строки с функциями, начинающимися на «LEDs», потому что у меня хоть и есть светодиоды, но совсем не такие и не там. Кстати, заодно в MassStorage.h закрываю «include <LUFA/Drivers/Board/LEDs.h>», «include <LUFA/Drivers/Board/Dataflash.h>» и «include <LUFA/Platform/Platform.h>», потому что у меня несколько не тот BOARD, что предполагался в демонстрационном примере.

Но вернмся к основному сишному файлу. Главная функция, как очевидно следует из названия, main. В ней все, что идет перед началом бесконечного цикла — инициализация всякого рода сущностей, от программных переменных до аппаратных регистров. В самом цикле for (;;) будет крутиться то, что нам собственно и надо. Сейчас там вертится обработка событий USB, не требующая вот-прям-щаз-реакции, то, что не надо делать в прерываниях.

Инициализирую порты: порт D, на котором у меня светодиоды, и порт C, на котором кнопки. После имеющейся в заготовке строчки GlobalInterruptEnable(); пишу:

    PORTD = 0x00;    // начальное значение - все нули
    DDRD = 0xFF;     // все линии порта на вывод
    PORTC = 0x00;    // без "подтяжки" (есть внешняя)
    DDRC = 0x00;     // все линии порта на ввод

И объявляю переменные:

    unsigned char cnt = 0;        // тупо счетчик
    unsigned char cnt_bt = 0;     // счетчик нажатий на кнопки
    unsigned char mode_out = 0;   // режим вывода
    unsigned char bt_now = 0;     // состояние кнопок
    unsigned char bt_old = 0;     // состояние кнопок в прошлый раз

Дальше в цикле for (;;) после строчки USB_USBTask(); можно писать всякую отсебятину:

        cnt++;                              // инкремент счетчика - чтобы что-то изменялось
        bt_now = PINC;                      // считывание порта с кнопками
        if (bt_now != bt_old) {             // если состояние порта изменилось
            if ((bt_now & 0x30) == 0) {     // если нажаты сразу две верхние кнопки на разрядах 3 и 4
            mode_out++;                 // циклически изменить режим отображения,
            mode_out = mode_out & 3;    // которых всего 4 - 0, 1, 2 и 3 (2 разряда по маске)
            } else {                        // в противном случае
            if ((bt_now & 0x10) == 0) {cnt_bt++;}  // верхняя кнопка увеличивает счет нажатий
            if ((bt_now & 0x20) == 0) {cnt_bt--;}  // а вторая сверху - уменьшает
            }
            bt_old = bt_now;                // и сохраняем состояние порта для следующей проверки
        }

А дальше вывожу в порт D, то есть, на светодиоды, некоторые данные в зависимости от режима:

        switch (mode_out) {
            case 0 : 
            PORTD = cnt;     // просто счетчик
            break;
            case 1 : 
            PORTD = bt_now;  // состояние кнопок
            break;
            case 2 :
            PORTD = cnt_bt;  // счетчик нажатий
            break;
            default:
            PORTD = 0x55;    // просто константа
        }

Теперь если я нажму на две верхние кнопки одновременно, то могу переключать выводимые данные:

  • В режиме счетчика светодиоды горят все. Потому что счетчик на такой частоте успевает прокручиваться с таким темпом обновления, что глазом уследить за морганием светодиода практически нереально.
  • В режиме состояния кнопок горят светодиоды, соответствующие ненажатой кнопке, потому что нажатые кнопки замыкают подтянутый вывод на корпус.
  • В режиме счетчика нажатий верхняя кнопка увеличивает значение, выдаваемое на светодиодах, а вторая сверху кнопка — уменьшает.
  • В режиме константы светодиоды горят через один, что соответствует шестнадцатиричной константе 0x55.

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

Самое простое, что можно здесь сделать — это добавить задержку после конструкции switch, например, вставить библиотечную функцию _delay_ms(100);. Для того, чтобы оно заработала, надо не забыть добавить include <util/delay.h> после #include "MassStorage.h". Теперь после компиляции и светодиоды в режиме счетчика перемигиваются с частотой 0,1 с, и на кнопки реакция более-менее нормальная, даже немного заторможенная: если нажимать быстро, некоторые нажатия могут пропуститься.

Но некрасиво это. И главный цикл тормозить так грубо — очень плохо, и функция-задержка реализована так себе… В общем, пришло время попробовать таймеры.

Убираю _delay_ms(). Закрываю cnt++;. После объявления переменных вставляю запуск таймеров:

    /* запуск таймера 0 на период ~0.01 с */
    TCCR0B = 4;           /* 1 тик = 0.000032 с */
    TCNT0 = 0;            /* 256 раз ~ 0.008192 с */

    /* запуск таймера 1 на период 0.5 с */
    TCCR1B = 4;
    TCNT1 = 65536 - 15625;

После USB_USBTask(); проверяю таймер 1, и если он сработал — увеличиваю счетчик:

        /* проверка срабатывания таймера без прерываний */
        if ((TIFR1 & 1) == 1) {
            TCNT1 = 65536 - 15625;
            TIFR1 = 1;
            cnt++;
        }

А всю остальную конструкцию — от считывания порта с кнопками до отображения — выполняем после проверки срабатывания таймера 0, для чего сразу после проверки таймера 1 вставляю следующее:

        if ((TIFR0 & 1) == 1) {
                TCNT0 = 0;
            TIFR0 = 1;

и после завершающей switch фигурной скобки ставлю еще одну скобку, закрывающую эту проверку. Хорошо бы еще соответственно сдвинуть отступы в коде для красоты.

Теперь светодиоды в режиме счетчика моргают с частотой в полсекунды. А с таймером 0 можно поиграться, устанавливая разные варианты TCNT0 и TCCR0B, добиваясь такого времени опроса кнопок, чтобы и дребезг не влиял, и быстрые двойные щелчки по кнопке не пропускались.

Предупреждение
Вообще-то говоря, приведенный код — ни разу не пример для подражания. И разряды таким образом прописывать/маскировать нехорошо, и константы в коде числом задавать нездорово, и стиль оформления хромает. Но макет — он и есть макет: и плата, и код. Я считаю позволительным для вещей, служащих лишь для проверки работоспособности, отступать от правил и стандартов в угоду быстроте. Тем более, что это не будет повторно использоваться или развиваться до чего-то приличного, а будет стерто и забыто.

Итак, из заготовки (в качестве которой служит демонстрационный пример библиотеки LUFA) сделан «проект», который позволяет компьютеру увидеть в плате «флэшку», и при этом делает еще какую-то, пусть и не очень полезную, работу. Полноценного обмена с компьютером еще нет, хотя какой-то — служебными пакетами — уже есть.

Что дальше?

Раз обмен по USB как-то завелся, надо организовать обмен данными между платой и компьютером, причем, пользовательскими данными, которые нужны мне, а не компьютеру. Вижу три направления:

  1. Обмен «сырыми» данными с «железом», пользуясь дескриптором устройства, с помощью собственной программы.
  2. Организация «липовой» файловой системы на устройстве и обмен средствами операционной системы — посредством чтения/записи файлов, которых на самом деле нет, без написания программы для компьютере.
  3. Организация «липовой» файловой системы и обмен данными через ReadFile/WriteFile, но уже с дескрипторами несуществующих в реальности файлов, опять же с помощью собственной программы.

Забегая вперед, скажу, что первый вариант проще с точки зрения программы для устройства (она уже почти готова), но сложнее в плане написания программы для ПК. Кроме того, в нем нехорошо с кроссплатформенностью, есть проблемы даже при переносе с Windows XP на семерку, не говоря о Linux. Второй вариант хорош тем, что для ПК вообще писать не надо, но не получается перечитать обновленный файл (если данные изменились, пока устройство было подключено), потому что данные кешируются при первом же чтении. И похоже, что финишем будет вынужденный третий вариант, но посмотрим, что получится.


Теги: