GPIO Extender-T (TOIC)
Миниатюрный программируемый контроллер с USB
Как это работает?
GPIO Extender - устройство для простого управления дискретными сигналами.
GPIO Extender-T - более продвинутая версия, которая имеет встроенный механизм программирования устройства и более развитый функционал.

Для чего нужен программируемый USB GPIO Extender
Возможность изменить встроенную программу позволяет значительно расширить возможности применения такого устройства
  • 1
    Очень простое управление сигналами
    GPIO Extender - в первую очередь устройство простого управления сигналами: позволяет с помощью команд в (виртуальный) последовательный порт изменять состояние 5 выходов и 5 входов (настройка по умолчанию).
  • 2
    Гибкость
    В отличие от базовой версии. Версия с TOIC позволяет переназначить используемые контакты:
    1. сделать 10 выходов, либо 10 входов или как-то разделить их, например как 2 выхода и 8 входов.
    2. использовать, как генератор PWM сигналов или, наоборот, как измеритель частоты сигнала.
    3. Использовать, как преобразователь в SPI.
    4. Использовать, как АЦП.
  • 3
    Программирование
    Устройство можно запрограммировать на выполнение своей логики: подсчет поступающих сигналов за определённое время или, наоборот, формирование каких-то специальных сигналов в зависимости от команд с ПК.
    Реализация параллельного интерфейса и т.д.
  • 4
    Новые интерфейсы
    В отличие от оригинального GPIO Extender, данная версия позволяет также использовать PWM и SPI. Так что, устройство может быть преобразователем интерфейсов USB-SPI, PWM генератором или PWM анализатором.
Характеристики
1. Интерфейс подключения: USB
2. Рабочие интерфейсы: GPIO, SPI, PWM
3. Количество выводов: 25, управляемых - 10
4. Габариты: 54х15х10мм
5. Работает в Win7/Linux/macOS и других ОС
6. Встроенный язык программирования TOIC для написания своих программ.

Установка USB GPIO Extender
  1. Устройство устанавливается в USB разъём управляющего ПК.
  2. Устройство управляется текстовыми командами согласно кода загруженной пользователем программы.
Программируем сами
Модификация встроенного ПО
Сердцем устройства является микроязык TOIC, который позволяет легко модифицировать встроенное программное обеспечение устройства.
Чтобы подключиться к устройству нужно открыть среду ToicIDE, для этого необходимо нажать на кнопку в левом верхнем углу и выбрать USB HID интерфейс устройства.
Работа с готовым проектом
1. Скачать архив по из раздела "Дополнительные ресурсы".
2. В ToicIDE выбрать Проект->Импорт.
3. В ToicIDE нажать Проект->Открыть и найти в папке импортированный проект.
4. В ToicIDE подключиться к устройству.
5. В ToicIDE нажать кнопку "Компиляция".
6. В ToicIDE нажать кнопку "Загрузить скрипт в устройство".
7. В ToicIDE нажать кнопку запуска скрипта, если не включен автозапуск.
Демонстрационный код 1
Пример работы по логике, близкой со стандартным GPIO Extender
Настроенные пины:
IO1 - PA4 - выход 1
IO2 - PA2 - выход 2
IO3 - PA0 - выход 3
IO4 - PA7 - выход 4
IO5 - PA6 - выход 5

IO6 - PA3 - Вход1
IO7 - PA1 - Вход2
IO8 - PA13 - Вход3
IO9 - PB1 - Вход4
IO10 - Контакт входа в загрузчик

IOx - PA5 - Доп. вход с подтяжкой резистром 10КОм к земле
PF0 - управляемый светодиод

Демонстрационный код поддерживает следующие команды:
~Sx Установка выхода в x в 1
~Rx Установка выхода в x в 0
~Gx Чтение текущего значения входа x
~A Чтение значений всех входов в виде «xxxxx». Пример ответа «~A11001»
Прим.: т.к. IO10 используется для перехода в загрузчик, то он не используется в программе и вместо него отображается еще раз сигнал IO9.
~Pxxxxх Запись значений всех выходов в виде «xxxxx». Например «~P11001»
~B Перезагрузка модуля.
~I Запросить информация о прошивке.
Код демонстрационной программы:
#define FW_STR "1.0 Extender-T"
  
/*
IO1 - PA4 - OUTPUT
IO2 - PA2 - OUTPUT
IO3 - PA0 - OUTPUT
IO4 - PA7 - OUTPUT
IO5 - PA6 - OUTPUT

IO6 - PA3 - INPUT
IO7 - PA1 - INPUT
IO8 - PA13 - INPUT
IO9 - PB1 - INPUT
IO10 - BOOT

IOx - PA5 - INPUT PULLED DONW R10K
LED - PF0

 */
 
var GPIO_OUT = [PA4.VALUE, PA2.VALUE, PA0.VALUE, PA7.VALUE, PA6.VALUE];
var GPIO_IN = [PA3.VALUE, PA1.VALUE, PA13.VALUE, PB1.VALUE, PB1.VALUE];

var io_setup() {
    // OUTPUTS
    PA4.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
    PA2.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
    PA0.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
    PA7.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
    PA6.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
    // INPUTS
    PA3.MODE = GPIO_MODE_INPUT | GPIO_PULL_UP;
    PA1.MODE = GPIO_MODE_INPUT | GPIO_PULL_UP;
    PA13.MODE = GPIO_MODE_INPUT | GPIO_PULL_UP;
    PB1.MODE = GPIO_MODE_INPUT | GPIO_PULL_UP;
    PA5.MODE = GPIO_MODE_INPUT | GPIO_PULL_DOWN; // unused
    // LED - PF0
    PF0.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_HIGH|GPIO_OTYPE_PP;
    // UART
    UART0.CFG = UART_MODE_PLAIN | UART_CONFIG_START;
    __enable_irq();
}

var _msg() {
    if(MSG.PORT == 4) {
        memcpy(&RING.BUF, &MSG.RX, MSG.SIZE);
    }
}

var main() {
    var c;
    var pstate;
    var p_index;
    var p;
    io_setup();
    if((RING.ALLOC = 16) != 16) {
        return 1;
    }
    SYS.WDG = 1;
    while(1){
        if(RING.QUEUE > 0) {
            c = RING.PULL;
            if (c == '~') {
                pstate = 1;
                p_index = 0;
            } else if (pstate == 1) {
                pstate = 0;
                switch (c) {
                    case 'I':
                        sprintf(&UART0.TX, "~I%s\n", FW_STR);
                        pstate = 0;
                        break;
                    case 'B':
                        SYS.RESET = 1;
                        break;
                    case 'A':
                        sprintf(&UART0.TX, "~A%d%d%d%d%d\n", *GPIO_IN[0], *GPIO_IN[1], *GPIO_IN[2], *GPIO_IN[3], *GPIO_IN[4]);
                        pstate = 0;
                        break;
                    case 'S':
                    case 'R':
                    case 'G':
                    case 'P':
                        pstate = c;
                        break;
                    default:
                        break;
                }
            } else if (pstate) {
                p = c - '0';
                if (pstate == 'S' || pstate == 'R') { // ~S or ~R
                    if (p > 0 && p < 6) {
                        GPIO_OUT[p - 1] = (pstate == 'S');
                        sprintf(&UART0.TX, "~%c%d\n", pstate, p);
                    } else {
                        sprintf(&UART0.TX, "Err\n");
                    }
                    pstate = 0;
                } else if (pstate == 'G') { // ~G
                    if (p > 0 && p < 6) {
                        sprintf(&UART0.TX, "~G%d\n", *GPIO_IN[p - 1]);
                    } else {
                        sprintf(&UART0.TX, "Err\n");
                    }
                    pstate = 0;
                } else if (pstate == 'P') { // ~P
                    GPIO_OUT[p_index++] = (c == '1');
                    if (p_index == 5) {
                        sprintf(&UART0.TX, "~P%d%d%d%d%d\n", *GPIO_OUT[0], *GPIO_OUT[1], *GPIO_OUT[2], *GPIO_OUT[3], *GPIO_OUT[4]);
                        pstate = 0;
                    }
                }
            }
        } else {
            delay(10);
        }
        SYS.WDG = 42;
    }

    return 0;
}
Демонстрационный код 2
Пример работы с интерфейсами
Данная программа позволяет подключиться к устройству по виртуальному последовательному порту и взаимодействовать следующим образом:

Настроенные пины:
IO1 - PA4 - АЦП (0-3.3В)
IO2 - PA2 - Дискретный выход
IO3 - PA0 - SPI CS
IO4 - PA7 - SPI MOSI
IO5 - PA6 - SPI MISO
IO6 - PA3 - Дискретный вход (до 3.3В)
IO7 - PA1 - PWM выход с фиксированной частотой
IO8 - не используется
IO9 - PB1 - PWM вход (до 3.3В)
IOx - PA5 - SPI SCK

Демонстрационный код поддерживает следующие команды:
S - Установить выход в 1,
R - Установить выход в 0,
G - Считать данные со входа,
A - Считать данные с АЦП,
K - Отправить в SPI данные и прочитать ответ,
F - Считать данные с определителя частоты PWM,
I - Считать информацию об устройстве.
Код демонстрационной программы:
#define FW_STR "2.1 02 Aug 2021"
  
/*
IO1 - PA4 - ADC
IO2 - PA2 - OUTPUT
IO3 - PA0 - SPI CS
IO4 - PA7 - SPI MOSI
IO5 - PA6 - SPI MISO
IO6 - PA3 - INPUT
IO7 - PA1 - PWM OUTPUT
IO8 - NC
IO9 - PB1 - PWM INPUT
IOx - PA5 - SPI SCK
LED - PF0
 */

var _msg() {
      var c = stoi(&MSG.RX, 'c');
      memcpy(SYS.RAM, &MSG.RX, MSG.SIZE);
      switch (c) {
          case 'S':
              PA2.VALUE = 1;
              break;
          case 'R':
              PA2.VALUE = 0;
              break;
          case 'A':
              sprintf(&UART0.TX, "A%d\n", PA4.VALUE);
              break;
         case 'K':
              PA0.VALUE = 0;
              memcpy(&SPI.DR, SYS.RAM+1, MSG.SIZE-1);
              sprintf(&UART0.TX, "K%d\n", SYS.RAM+1);
              PA0.VALUE = 1;
              break;
          case 'G':
              sprintf(&UART0.TX, "G%d\n", PA3.VALUE);
              break;
          case 'F':
              sprintf(&UART0.TX, "F%d\n", PB1.VALUE);
              break;
          case 'I':
              sprintf(&UART0.TX, "I%s\n", FW_STR);
              break;       
          default:
              break;
      }
}


var io_setup() {
    // IO1 - PA4 - ADC
    PA4.MODE = GPIO_MODE_ADC; 
    // IO2 - PA2 - OUTPUT
    PA2.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_LOW|GPIO_OTYPE_PP;
    // IO6 - PA3 - INPUT
    PA3.MODE = GPIO_MODE_INPUT | GPIO_PULL_DOWN;
    // IO7 - PA1 - PWM OUTPUT
    PA1.MODE = GPIO_MODE_OPWM|GPIO_INIT_LOW|GPIO_OTYPE_PP;
    // IO9 - PB1 - PWM INPUT
    PB1.MODE = GPIO_MEASURE_FREQ|GPIO_MODE_IPWM|GPIO_INIT_LOW; 
    // LED - PF0
    PF0.MODE = GPIO_MODE_OUTPUT|GPIO_INIT_HIGH|GPIO_OTYPE_PP;
    
    // IO3 - PA0 - SPI CS
    // IO5 - PA7 - SPI MOSI
    // IO5 - PA6 - SPI MISO
    // IOx - PA5 - SPI SCK
    PA0.MODE = GPIO_INIT_LOW|GPIO_OTYPE_PP|GPIO_TYPE_SOFTWARE|GPIO_MODE_OUTPUT; 
    SPI.SETUP = SPI_SETUP_POLARITY_LOW|SPI_SETUP_EDGE_LEADING|1000/*kBod*/;
    SPI.EN = 1;
    
    PA1.PWM = 128; 
    TIM2.FREQ = TIM3.FREQ = 2;                                
    TIM2.EN = TIM3.EN = 1;
    UART0.CFG = UART_MODE_PLAIN | UART_CONFIG_START;
    __enable_irq();
}


var main() {

    io_setup();
    while (1) {};
    return 0;
}
Дополнительные ресурсы

  • 1
    Документация
    Документация на оригинальный OD GPIO-Extender (без TOIC). Скачать.
  • 2
    Сайт TOIC
    Сайт проекта TOIC Platform, где можно подчерпнуть основные сведения по работе с платформой TOIC. Перейти.
  • 3
    TOIC book
    Книга-справочник по языку TOIC. Перейти.
  • 4
    Проект 1
    Архив с проектом 1 для устройства. Скачать.
  • 5
    Проект 2
    Архив с проектом 2 для устройства. Скачать.