Устройство основано на USB-CAN переходнике. Функционал платы расширен: от части контактов МК выведены площадки, к которым возможно подпаять проводники, позволяющие задействовать функции GPIO (вход, выход push-pull или opendrain; с подтяжками или без), АЦП, I2C, USART и SPI. При подключении устройство создает два интерфейса. При использовании соответствующего udev-скрипта (см. в конце) автоматически возникают ссылки, по умолчанию: `/dev/USB-CANx` для "старого" интерфейса CAU-USB и `/dev/USB-GPIOx` для "нового". Устройство эмулирует два CDC-ACM, имея простейший текстовый протокол.

## Общий формат команд

- Команды чувствительны к регистру, команды-геттеры и команды-сеттеры отличаются наличием в последних знака "=".
- Параметры сеттера разделяются пробелом или запятой.
- Если не оговорено иное, числовые значения параметров могут иметь десятичный, шестнадцатеричный (`0x...`), восьмеричный (`0x...`) или двоичный (`b...`) формат.
- В таких командах, как `SPI` или `I2C` все данные в шестнадцатеричном формате без префикса.
- Ввод всегда построчный: символ '\n' означает конец строки и парсер, встретив этот символ, начинает разбор предыдущих символов.

## Список команд

`canspeed [=value]`
  установить скорость CAN-интерфейса по умолчанию (при запуске CAN будет работать на этой скорости). В процессе работы с CAN-USB ее можно временно изменить в соответствии с протоколом (описан на страничке оригинального устройства).
`curcanspeed`
  Отобразить текущую скорость CAN.
`curpinconf`
  Текущая конфигурация выводов МК (может отличаться от сохраненной в конфигурации в случае ошибки при запуске команды `reinit`.
`dumpconf`
  Отображение текущей глобальной конфигурации (в т.ч. параметров CAN, SPI и т.п.). Внимание! Эта конфигурация содержит обновленные данные, которые вы могли изменять в рантайме, поэтому она может не соответствовать актуальной. Для сохранения во флеш-память используйте `saveconf`.
`eraseflash`
  Полная очистка области хранения конфигурации во флеш-памяти.
`help`
  Отображение справки.
`hexinput [=0/1]`
  Изменение режима ввода:
  - 0 — простой текст (данные вводятся и выводятся построчно);
  - 1 — шестнадцатеричный ввод (без префикса `0x`) с возможностью ввода текста в кавычках.
  Данный режим влияет лишь на команды `USART` и `sendcan`.
`iic=addr data...`
  Запись данных в I2C. Адрес `addr` — несмещенный 7-битный адрес устрйства, данные в шестнадцатеричном формате без разделителя, например: `iic=50 01 02`
`iicread=addr nbytes`
  Чтение `nbytes` данных по I2C. Данные выводятся в режиме "hexdump".
`iicreadreg=addr reg nbytes`
  Считать `nbytes` данных с регистра `reg`.
`iicscan`
  Сканирование шины I2C в поиске активных устройств. Найденные рабочие устройства отображаются асинхронно.
`mcutemp`
  Условная температура МК, ℃ с множителем 10.
`mcureset`
  Перезагрузка микроконтроллера (будьте внимательны: это отразится и на интерфейсе CAN-USB).
`PAx [= ...]` и `PBx [= ...]`
  Геттеры и сеттеры для выводов портов GPIOA и GPIOB соответственно (см. раздел «Конфигурация выводов»).
`pinout [=function1,function2,...]`
  Отображение всех настраиваемых выводов. В случае сеттера отобразятся лишь соответствующие функциональные группы (например, `pinout=ADC, PWM` отобразит выводы, доступные для конфигурирования как входы АЦП или выходы ШИМ). Если вы видите одинаковые функции (например, `PWM2_2` на разных выводах, это — потенциальные конфликты: использовать данный функционал можно лишь на одном из этих выводов.
`pwmmap`
  вариант команды `pinout` только для выводов с поддержкой ШИМ, конфликты отмечены явно.
`readconf`
  Перечитать последнюю сохраненную в флеш-памяти конфигурацию (полезно, если вы что-то испортили в текущей, но не успели ее сохранить, и хотите "откатиться"). Если устройство "девственно", т.е. ни одной конфигурации не сохранено, это не сработает.
`reinit`
  Проверка текущей конфигурации выводов и полная переинициализация. В случае ошибок настройки проблемных выводов будут сброшены в `FL IN`, что можно будет увидеть по вызову `curpinconf`.
`saveconf`
  Сохранить текущие настройки во флеш-память.
`sendcan`
  Отправить в USB-интерфейс устройства CAN-USB данные. Внимание! Эта команда не пересылает данные в CAN-шину (используйте для этого непосредственно соответствующий интерфейс), но позволяет передать какую-либо информацию процессу, работающую с этим интерфейсом.
`setiface=N [=name]`
  Команда позволяет переименовать интерфейсы (0 = CAN, 1 = GPIO). Используйте удобные для вас наименования, не превышающие 16 символов. Помните, что udev-скрипт будет создавать ссылки с соответствующими именами, поэтому не используйте недопустимых символов (пробелы, слэши и прочее, что не допустимо в именах файлов).
`SPI=data...` или `SPI=size`
  Обмен данными с устройством SPI. Первая команда работает в полнодуплексном режиме или режиме TX-only, передавая все введенные в шестнадцатеричном формате данные. В случае полнодуплексного режима считывается такое же количество данных с MISO и выводится как "hexdump". В режиме Rx-only используется второй вариант команды, считывая `size` байт с SPI.
`time`
  Отображение условного времени (в мс), прошедшего с последней перезагрузки МК.
`USART[=...]`
  Геттер считывает данные из приемного кольцевого буфера USART, сеттер отправляет перечисленные данные (их формат зависит от выбранного командой `hexinput` режима).
`vdd`
  Примерное напряжение питания МК (В с множителем 100).

## Конфигурация выводов (команды `PAx`/`PBx`)

Общий синтаксис настройки выводов следующий:

    PXx = MODE PULL OTYPE FUNC MISC ...

Здесь `PX` — соответствующий порт (`PA` или `PB`), `x` — номер вывода. После знака равенства идет последовательность ключевых слов с возможными числовыми аргументами. Кроме того, эта же команда работает как сеттер/геттер значения, если соответствующий вывод настроен как GPIO, АЦП (только геттер) или ШИМ. Например, если PA1 настроен как `OUT`, команда `PA1=1` установит его значение в высокое состояние, геттер же отобразит текущее состояние (из регистра `GPIOX->IDR`, т.е. для выхода с открытым стоком в состоянии "1" можно детектировать внешнюю верхнюю или нижнюю подтяжку). Для настройки ШИМ (`PWM`) можно изменять заполнение выходного сигнала, например: `PB2=128` установит заполнение в 50%.

### Ключевые слова

Ключевые слова могут идти в любой последовательности.

#### MODE

`AIN`
  Аналоговый вход (АЦП).
`IN`
  Цифровой вход.
`OUT`
  Цифровой выход.
`AF`
  Альтернативная функция (данное ключевое слово не обязательно указывать, т.к. этот флаг установится автоматически при выборе любой функции вроде `USART` и т.п.).

#### PULL

(только для режимов `GPIO`)

`PU`
  Включена верхняя подтяжка (как для входов, так и для выходов).
`PD`
  Включена нижняя подтяжка (-//-, хоть для выходов это бессмысленно).
`FL`
  Подтяжки выключены.

Верхняя и нижняя подтяжки взаимоисключающие.

#### OTYPE

(только для режимов `GPIO`) Настройка типа выхода (для входов не актуальна):

`PP`
  Push-pull.
`OD`
  Открытый сток.

#### FUNC

Выбор альтернативной функции:

`USART`
  Вывод будет частью USART (можно выбрать только Rx или Tx).
`SPI`
  Вывод — часть SPI (обязательно выбрать SCK и хотя бы один из MISO или MOSI).
`I2C`
  Вывод — часть I2C (обязательно выбрать как SCL, так и SDA).
`PWM`
  Вывод с функцией ШИМ (частота ШИМ около 23.5кГц, заполнение от 0 до 255).

#### MISC

`MONITOR`
  Асинхронный мониторинг функционала данного вывода: без запроса пользователя передаются данные, как только значение GPIO входа или АЦП изменится, либо же в случае USART — появятся новые данные.
`THRESHOLD`
  Пороговое значение (0..4095) для мониторинга аналогового входа. Изменение асинхронно отображается лишь при превышении текущим значением данного порога относительно предыдущего.
`SPEED`
  Установка скорости интерфейса (USART, SPI, I2C), в бодах, Гц или индексе.
`TEXT`
  (только USART) Текстовый режим USART: входные и выходные данные буферизуются построчно. В случае включенного мониторинга отображаются лишь новые строки целиком, но не их части (в случае, если длина строки не превышает размера входного буфера).
`HEX`
  (только USART) Шестнадцатеричный режим USART: блоки входных данных (не превышающих размер внутреннего буфера) разделяются по сигналу IDLE. Вывод — в формате "hexdump".
`LSBFIRST`
  (только SPI) Передача младшего бита первым. По умолчанию — MSBFIRST.
`CPOL`
  (только SPI) полярность данных.
`CPHA`
  (только SPI) выбор фронта активности данных.

При выборе функционала, недоступного на данном выводе, сразу же будет выведена ошибка. В случае, если вы неправильно настроите выводы интерфейсов (скажем, забудете указать SCK для SPI), после вызова команды `reinit` появится сообщение об ошибке настройки, а проблемные места будут сброшены в значения по умолчанию.

## Альтернативные функции

### USART

Данный интерфейс доступен на контактах PA9/PB6 (TX USART1), PA10/PB7 (RX USART1) или PA2 (TX USART2) и PA2 (RX USART2). Несмотря на наличие двух модулей USART, допускается использовать лишь один из них. Соответственно, нельзя настроить смесь для разных интерфейсов (скажем, PA9 и PA3). Прием и передача данных использует DMA, поэтому большие объемы данных, передаваемые по этому интерфейсу, не будут сказываться на работе преобразователя CAN-USB. Для данного интерфейса при конфигурации используются такие ключевые слова, как SPEED, TEXT/HEX и MONITOR. Например,

    PA9 = USART SPEED 115200 TEXT MONITOR
    PA10 = USART
    reinit

Данная последовательность команд настроит USART1 на выводах PA9/PA10. Скорость 115200 бод, текстовый режим с асинхронным выводом поступающих строк текста. Каждая новая строка будет выведена как `USART = ...`. Обратите внимание: для отличия реального символа конца строки от переноса строки в случае вывода неполного буфера (например, при переполнении входного буфера), "реальный" символ '\n' удваивается. Передача строк зависит от формата, например, в формате TEXT команда `USART=Hello, world!\n` отправит по USART строку "Hello, world!\n". А в формате HEX команда `USART=48 65 6c 6c 6f\n` отправит пять байт данных "Hello". Команда-геттер `USART` отображает содержимое приемного кольцевого буфера. В "текстовом" режиме оно будет выводиться построчно на каждый запрос. В "шестнадцатеричном" режиме — порциями по 256 байт. Если кольцевой буфер пуст, никакого ответа на эту команду не последует. Даже вне режима MONITOR возможен асинхронный вывод данных в случае переполнения буфера: вызывайте геттер чаще, чтобы этого не допустить. Длина входного буфера DMA ограничена, поэтому если пользователь настроит USART на слишком высокую скорость, возможна потеря данных (особенно при активной работе второго интерфейса — CAN-USB), т.к. опрос буфера DMA производится без прерываний.

### I2C

Вы можете выбрать интерфейс на контактах PB6/PB10 (SCL) и PB7/PB11 (SDA). Будьте внимательны: если вместо STM32F042 используется STM32F072, на контактах PB7/PB11 доступен лишь I2C2, но не I2C1. В этом случае настроить I2C на данных контактах будет невозможным (в коде нет поддержки I2C2). I2C не допускает активации только с одним из этих выводов, поэтому обязательно настроить и SCL, и SDA. Для данного интерфейса доступно лишь ключевое слово SPEED. Обратите внимание, что, в отличие от USART и SPI, здесь скорость задается индексом: 0=10kHz, 1=100kHz, 2=400kHz, 3=1MHz. Пример:

    PB6 = I2C SPEED 2
    PB7 = I2C
    reinit

Активирует I2C на PB6/PB7 со скоростью 400кГц. Прием и передача данных производится при помощи команд `iic`, `iicread`, `iicreadreg`. Командой `iicscan` можно произвести поиск активных устройств на шине. Все данные вводятся и выводятся в двоичном формате без префикса, в том числе адрес и длина данных.

### SPI

Доступен SPI на выводах PA5/PB3 (SCK), PA6/PB4 (MISO) и PA7/PB5 (MOSI). Не обязательно настраивать все три вывода, если вам нужна односторонняя передача данных. Ключевые слова:

- `SPEED` — скорость, Гц, при настройке будет подобран актуальный делитель системной тактовой частоты, наиболее близкий к заданной скорости (не ожидайте получить скорость 30МГц при тактовой 48МГц),
- `CPOL` — полярность SCK (1 — высокий в неактивном состоянии),
- `CPHA` — фронт SCK на актуальный уровень сигнала (1 — второй фронт),
- `LSBFIRST` — передача первым младшего бита.

По умолчанию флаги CPOL/CPHA/LSBFIRST считаются равными нулю. Пример, последовательность команд:

    PA5 = SPI SPEED 2000000 CPOL CPHA
    PA6 = SPI
    PA7 = SPI
    reinit

настроит SPI на контактах PA5/PA6/PA7, скорость 2МГц, режим 3 (CPOL=CPHA=1), полный дуплекс. В режиме полного дуплекса сеттер `SPI=...` передает перечисленные после знака "равно" данные (в шестнадцатеричном формате без разделителя) и принимает такое же количество данных. Принятые данные будут выведены после завершения операции в виде `SPI = ...`. В RX-only режиме (т.е. когда не сконфигурирован вывод MOSI), формат геттера другой: `SPI = datalen`, где указывается количеств байт для считывания (в любом формате числа!). Скажем, `SPI = 0x10` считает 16 байт данных, равно как и `SPI=16`, `SPI = 020` или `SPI=b10000`.

### ШИМ (PWM)

Доступные для работы в режиме ШИМ выводы можно отобразить запросом `pwmmap` или `pinout=PWM`. При настройке новых режимов командой `reinit` будет проведен анализ конфликтов, поэтому в случае выбора ШИМ на конфликтующих контактах (т.е. если один и тот же канал одного и того же таймера выбран в качестве ШИМ-выхода на разных кнтактах) будет отображено сообщение об ошибке со сбросом конфликтов в значение по умолчанию. Для конфигурации никаких дополнительных параметров указывать не нужно, лишь `PXx = PWM`. Сеттер позволяет установить требуемое заполнение (0..255), а геттер указывает текущее. Например,

    PA1 = PWM
    reinit
    PA1=128

установит PA1 в ШИМ-режим с заполнением 50%. Частота ШИМ фиксирована: около 23.5кГц и настройке не подлежит.

## Асинхронный мониторинг

При указании флага `MONITOR` в настройке контакта в случае, если выбранная функция это поддерживает, по USB будут выдаваться сообщения с новыми данными на этом контакте вне зависимости от запроса пользователя. Для входа или выхода с открытым стоком изменение значения будет отображаться, как, например `PB3 = 1` при смене низкого уровня сигнала на высокий. В случае входа АЦП дополнительным параметром `THRESHOLD` задается пороговое значение: если уровень сигнала на входе отличается от предыдущего больше, чем на это пороговое значение, будет выведено соответствующее сообщение, например `PA1 = 3456`. Также "мониторинг" доступен для асинхронного отображения входящих по USART сообщений (в текстовом режиме данные выводятся построчно, в "hex" — с буферизацией по блокам, разделенным IDLE). Если режим мониторинга отключен, то пользователь должен регулярными вызовами геттера `USART` считывать пришедшие данные из кольцевого буфера. В случае переполнения буфера он будет автоматически очищаться, а имеющаяся в нем информация — асинхронно передаваться пользователю. Интерфейсы I2C и SPI работают исключительно с блокирующими операциями, поэтому флаг мониторинга к ним не применим.

## Сохранение настроек

Если вы окончательно настроили все интерфейсы и контакты, и хотите, чтобы при следующем включении устройство был сконфигурировано точно так же, сохраните настройки во флеш-память при помощи команды `saveconf`. Если произойдет ошибка сохранения, попробуйте стереть весь отведенный под хранение настроек блок данных при помощи `eraseflash`, а затем сохранить снова. При включении устройства МК автоматически загружает последние сохраненные настройки, пользуясь бинарным поиском (что позволяет не тратить слишком много времени на перебор всего объема доступного хранилища, хотя, конечно, для STM32F042 это неактуально). Настройки во флеш-памяти хранятся последовательно. При достижении последней записи в отведенном регионе (все незанятые блоки флеш-памяти после микрокода устройства), стирается все хранилище, а затем новая запись пишется первой. В такие моменты будьте осторожными, т.к. сбой питания может повредить настройки, и их придется выставлять заново.

## Внутренние коды ошибок

Часть сеттеров, не возвращающих в свою очередь геттеры, выдает следующие коды ошибок:

`OK`
  Выполнение команды прошло успешно.
`BADCMD`
  Введена неправильная команда.
`BADPAR`
  Неправильный параметр команды (например, номер контакта вне диапазона 0..15).
`BADVAL`
  Неправильное значение сеттера (например, вы ошиблись в написании ключевого слова или ввели неверную скорость и т.п.).
`WRONGLEN`
  Неправильная длина сообщения (слишком большое или нулевой длины).
`CANTRUN`
  Ошибка выполнения команды (например, при отсутствии на шине I2C устройства с идентификатором, в который пытаются отправить данные).
`BUSY`
  Попробуйте вызвать команду позже (актуально лишь для USART: если вы попытаетесь послать следующее сообщение, пока еще не передано предыдущее).
`OVERFLOW`
  Переполнение длины входного буфера.

Некоторые команды не возвращают вообще ничего (например, геттер `USART`, если в буфере ничего нет). По запросу геттеров выводится имя команды и данные после знака "равно".

## Как различать несколько одинаковых подключенных устройств

Данное устройство представляет собой "составное устройство USB", состоящее из двух CDC-ACM интерфейсов. Поэтому по умолчанию при его включении в `/dev` вы увидите появление устройств `/dev/ttyACMx` и `/dev/ttyACM(x+1)` (где x зависит от количества уже подключенных устройств). Чтобы различать интерфейсы (особенно если вы включили несколько одинаковых таких преобразователей CAN-USB-GPIO), в USB-дескрипторах сохраняются символьные имена для соответствующих полей `iInterface`. Изменить их вы можете при помощи команды `setiface`. В терминале при помощи `lsusb -v` можно увидеть эти поля. Т.к. при подключении USB-устройств udev может "перехватить" их и выполнить какие-то действия, приведенный скрипт поможет создавать человекочитаемые симлинки. Например, если вы задаете интерфейсам названия «myCAN» и «myGPIO» и сохраняете конфигурацию во флеш-памяти МК, после переподключения устройства вы увидите симлинки: `/dev/ttymyCAN0` и `/dev/ttymyGPIO0`, что позволит быстро их отождествить (особенно это актуально для служб, запускаемых при старте системы). Просто добавьте этот файл в `/etc/udev/rules.d`: `

    ACTION=="add", DRIVERS=="usb", ENV{USB_IDS}="%s{idVendor}:%s{idProduct}"
    ACTION=="add", ENV{USB_IDS}=="067b:2303", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep $attr{interface} | wc -l \"", SYMLINK+="$attr{interface}%c", MODE="0666", GROUP="tty"
    ACTION=="add", ENV{USB_IDS}=="0483:5740", ATTRS{interface}=="?*", PROGRAM="/bin/bash -c \"ls /dev | grep $attr{interface} | wc -l \"", SYMLINK+="$attr{interface}%c", MODE="0666", GROUP="tty"

`