June 11th, 2020

core5277, пример многопоточности

  Здесь я привожу дампы логики при параллельном выполнении нескольких задач на микроконтроллере ATmega328. Да, тот самый, с которым работает большинство Arduino поклонников восхваляя торговую марку вместо производителей чипа.
  Здесь чип работает на частоте 16МГц, среди прочего он имеет один аппаратный UART(который в данном примере не используется), и аппаратную поддержку TWI(I2C) - она задействована.

  На первом скрине показаны три события:
  - чтение с микросхемы реального времени DS3231(аппаратный TWI)
  - логирование в порт(программный UART 115200 8N1)
  - передача пакета данных(своя реализация шины на базе программного UART 19200 8N1)

Чтение микросхемы и логирование выполняются в одной задаче, передача пакета в другой, и еще есть две: мигание сетодиодом и анализ портов(открыта/закрыта дверь), но в дампе их не видно.




Далее немного увеличим дамп каждого события:

  3-ий канал - передача пакета:


  0-ой и 1-ый канал - TWI:


  2-ой канал - логирование в порт:


Кстати в логе указано текущее время, количество свободной динамической памяти(1018 байт) и динамической памяти всего(1246 байт). На данном чипе всего 2048 байта памяти, 802 байта было потрачено на нужды ядра, из которых только 368 байт выделено под 16 байт переменных 23-х задач и 253 байта под заголовки этих задач. Т.е. реально на нужды ядра выделено 181 байт, из них 64 байта под стек, 75 байт под таблицу прерываний и 20 байт под таблицу программных таймеров. При этом в данной прошивке проинициализировано 6 драйверов и 5 задач.

  Думаю отдельно стоит рассмотреть точность таймингов для передачи данных. К примеру канал 3, скорость не большая 19200, но, здесь важно то, что таймер программный, предоставляется ядром, при этом для данной скорости расходуется ровно 1 тик таймера. Все используемые таймеры в ядре основаны на TIMER0, он же отсчитывает тики ядра(кванты).



  А здесь скорость 115200, не используются таймеры и прерывания. Логирование выполняется как обычный код задачи, просто подсчитаны такты и на время передачи октета прерывания блокируются.


Блокировка прерываний, особенно в многопоточной ОС может привести к плачевным последствиям, например такое логирование при передаче или приеме данных по программному UART обязательно приведет к ошибкам. Для этого будет введен функционал похожий на мьютексы.

  Главное, что мне доставляет удовольствие в данном увлечении, это написание самих исполняемых задач, они просты, там нет никакой сложной логики для контролирования ресурсов.
  Вот например код задачи для тестирования драйвера моей шины:
;---CONSTANTS--------------------------------------------
_BUS5277_TEST_HELLO:
    .db   "Hello",0x00

BUS5277_TEST_INIT:
    MCALL CORE5277_TASK_READY
;--------------------------------------------------------
_BUS5277_TEST_INFINITE_LOOP:
    LDI TEMP,TID_BUS5277_DRV
    LDI YH,high(_BUS5277_TEST_HELLO)|0x80
    LDI YL,low(_BUS5277_TEST_HELLO)
    LDI TEMP_EH,BUS5277_RESP_OK
    LDI TEMP_EL,0x06
    MCALL CORE5277_DRIVER_EXEC
 
    LDI TEMP,0x01
    MCALL CORE5277_TASK_WAIT_1S

    MJMP _BUS5277_TEST_INFINITE_LOOP
    RET



И да, core5277 имеет прямое отношение к текущему проекту, так как прошивки всех устройств будут реализованы на моем ядре. Это, по большему счету сводит программирование на ассемблере к уровню программирования на Бейсике.

Update: А вто как выглядят три задачи типа
BURN_INIT:
    MCALL CORE5277_TASK_READY
    SBI PINB,PB2
    RJMP PC-0x01