Поработав некоторое время с платой К1986ВЕ92FI-Mini и оценив ее возможности я задался вопросом: реально ли ее использовать в экосистеме Arduino? Как оказалось, на сегодняшний день эта ниша несправедливо пустует. Ни производитель, ни сообщество не предложили готового решения, которое позволило бы писать код под этот МК в привычном Arduino-стиле.
Справедливости ради стоит упомянуть, что в природе существуют (или существовали) платы Миландруино и Miluino. Однако это лишь аппаратные решения, чья связь с Arduino ограничена разве что совместимостью по форм-фактору. Полноценного программного ядра, которое можно было бы установить в Arduino IDE и использовать на любой плате с этим чипом, до сих пор не было.
Я попытался закрыть этот пробел и предлагаю вашему вниманию мою версию ядра Arduino для Milandr. Мой подход иной: не создавать новое железо, а адаптировать существующее. Я беру плату К1986ВЕ92FI-Mini и описываю её распиновку так, как если бы это была Arduino.

| Пин Arduino | Пин MCU | Функция | PWM (~) | ADC | DAC | Tone (♫) |
|---|---|---|---|---|---|---|
| D0 | PE3 | GPIO | — | — | — | Tone0 |
| D1 | PE2 | GPIO | PWM0 | — | — | — |
| D2 | PE1 | GPIO | — | — | — | — |
| D3 | PE0 | GPIO | — | — | DAC2 | — |
| D4 | PD7 | GPIO | — | A0 | — | — |
| D5 | PD4 | GPIO | — | A1 | — | — |
| D6 | PD2 | GPIO/SPI2_MISO | — | A2 | — | — |
| D7 | PD3 | GPIO/SPI2_SS | — | A3 | — | — |
| D8 | PD5 | GPIO/SPI2_SCK | — | A4 | — | — |
| D9 | PD6 | GPIO/SPI2_MOSI | — | A5 | — | — |
| D10 | PC2 | GPIO | PWM1 | — | — | — |
| D11 | PC1 | GPIO/I2C1_SDA | — | — | — | — |
| D12 | PC0 | GPIO/I2C1_SCL | — | — | — | — |
| D13 | PB0 | GPIO | — | — | — | — |
| D14 | PB1 | GPIO | — | — | — | — |
| D15 | PB2 | GPIO | PWM2 | — | — | — |
| D16 | PB3 | GPIO | — | — | — | — |
| D17 | PB4 | GPIO | — | — | — | — |
| D18 | PB5 | GPIO | PWM3 | — | — | — |
| D19 | PB6 | GPIO/BUTTON | — | — | — | — |
| D20 | PB7 | GPIO/LED | PWM4 | — | — | — |
| D21 | PB8 | GPIO | — | — | — | — |
| D22 | PB9 | GPIO | — | — | — | — |
| D23 | PB10 | GPIO | — | — | — | — |
| D24 | PA7 | GPIO/SERIAL1_TX | — | — | — | — |
| D25 | PA6 | GPIO/SERIAL1_RX | — | — | — | — |
| D26 | PA5 | GPIO | PWM5 | — | — | — |
| D27 | PA4 | GPIO | — | — | — | Tone1 |
| D28 | PA3 | GPIO | PWM6 | — | — | — |
| D29 | PA2 | GPIO | — | — | — | Tone2 |
| D30 | PA1 | GPIO | PWM7 | — | — | — |
| D31 | PA0 | GPIO | — | — | — | — |
| D32 | PF0 | GPIO/SPI1_MOSI/SERIAL2_RX | — | — | — | — |
| D33 | PF1 | GPIO/SPI1_SCK/SERIAL2_TX | — | — | — | — |
| D34 | PF2 | GPIO/SPI1_SS | — | — | — | — |
| D35 | PF3 | GPIO/SPI1_MISO | — | — | — | — |
| D36 | PF4 | GPIO | — | — | — | — |
| D37 | PF5 | GPIO | — | — | — | — |
| D38 | PF6 | GPIO | PWM8 | — | — | — |
analogWrite возможно только на пинах, имеющих функцию PWM (D1, D10, D15, D18, D20, D26, D28, D30, D38), либо на пинах с функцией DAC (D3). Вызов функции analogWrite на пинах PWM задает коэффициент заполнения ШИМ (duty cycle). Функция analogWrite на пине DAC2 управляет аппаратным ЦАП и задает на выходе аналоговое напряжение в диапазоне 0…3.3 В.
tone возможно только на пинах, имеющих функцию Tone (D0, D27, D29). Реализация ядра позволяет одновременно выполнять функцию tone только на одном из выбранных пинов до тех пор, пока не будет вызвана функция noTone либо не истечет интервал времени duration (если этот параметр был передан в функцию tone).
tone и analogWrite делят между собой ресурсы аппаратного таймера TIMER_2, то при вызове tone становится недоступной генерация PWM на пинах D1 и D30. Соответственно и при вызове функции analogWrite на пинах D1, D30 прекращает работу функция tone на пинах D0, D27, D29. Захват ресурсов таймера осуществляет та функция, которая была вызвана последней.
Serial используется модуль UART_1 (пины D24 — TX, D25 — RX).
SPI используется модуль SSP_1 (пины D32 — MOSI, D33 — MISO, D34 — SCK, D35 — CS).
analogRead на виртуальном пине AIN_TEMP (D39). Параметры температурного датчика не регламентируются. В зависимости от необходимой точности может быть достаточно провести градуировку в двух – трех точках. При необходимости более точных измерений необходимо построить градуировочную таблицу. Градуировка производится индивидуально для каждой микросхемы.
- Цифровой ввод/вывод:
pinMode,digitalRead,digitalWrite - Аналоговый ввод/вывод:
analogRead(10-битный АЦП),analogWrite(ШИМ и ЦАП) - Интерфейсы связи:
Serial(UART с кольцевыми буферами),SPI,Wire(I2C) - Таймеры:
delay,delayMicroseconds,millis,micros - Прерывания:
attachInterrupt,detachInterrupt - Звук:
tone,noTone - Случайные числа:
random,randomSeed
Установка ядра
Чтобы установить ядро Arduino для Milandr в среде Arduino IDE, необходимо выполнить несколько стандартных действий:
- Откройте меню Файл/Настройки в Arduino IDE.
- В поле Дополнительные ссылки для Менеджера плат вставьте:
https://raw.githubusercontent.com/unsi9ned/Arduino_Core_Milandr/master/package_milandr_index.json
- Перейдите в меню Инструменты/Плата/Менеджер плат.
- Найдите в списке Milandr MDR32FxFI и нажмите Установить.
- Выберите плату Milandr К1986ВЕ92FI-Mini в меню Инструменты/Плата/Milandr MDR32FxFI
- В качестве программатора выберите OpenOCD (CMSIS-DAP).
Основные характеристики и ограничения
- Прерывания: Не поддерживаются аппаратные прерывания (EXT_INT1 — EXT_INT4) в связи с их ограниченным функционалом. Вместо этого реализована программная эмуляция внешних прерываний: опрос всех известных пинов D0…D38 по таймеру SysTick с периодом 1 мс. Там, где нужна быстрая реакция программы на внешние прерывания, используйте пожалуйста функции
NVIC_EnableIRQ()и обработчики прерыванийEXT_INTn_IRQHandler(), при условии, что есть возможность сброса прерываний внешнего устройства посредством отправки команд по SPI, I2C и т.д. В противном случае наличие активной 1 на входе EXT_INTn будет приводить к зацикливанию вызова обработчикаEXT_INTn_IRQHandler()и как следствие все ресурсы CPU будут уходить только на обработку данного прерывания. - Тактирование: Микроконтроллер тактируется от внешнего источника — кварцевого резонатора с частотой 8 МГц. Далее входная частота умножается с помощью аппаратного PLL до значение HCLK = 80 МГц. При наличии проблем с внешним кварцевым резонатором, микроконтроллер использует менее точный внутренний источник тактирование HSI = 8 МГЦ. При этом частота тактирования CPU по прежнему равна HCLK = 80 МГц.
- ADC: Разрядность АЦП всегда равна 12-бит. Программно эмулируется разрядность в диапазоне 10…12 бит (при разрядности 10 бит просто отбрасываются 2 младших бита измерений). Изменить разрешение АЦП можно с помощью функции
analogWriteResolution. Частота дискретизации АЦП составляет 312,5 кГц. В качестве опорного источника напряжение используется напряжение питания AUCC. В данной версии ядра нет возможно выбрать другой источник опорного напряжения. - PWM: Разрядность таймеров, генерирующих ШИМ всегда равна 12-бит. Программно эмулируется разрядность в диапазоне 8…12 бит. Когда выбранная разрядность канала ШИМ меньше аппаратной, происходит автоматическо масштабирование устанавливаемого значения Duty Cycle (дополняется нужным количеством младших бит до 12-битного значения). Изменить разрядность аналогового выхода можно с помощью функции
analogWriteResolution. Частота генерации ШИМ составляет 19531,25 Гц. - Tone: Для генерации сигнала Tone используется аппаратный таймер TIMER_2, что налагает ограничение в использовании аналоговых выходов ШИМ D1 и D30 (см. Примечание 3). Частота сигнала Tone может быть установлена в диапазоне 0…65535 Гц, скважность сигнала всегда равна 2.
- I2C: Библиотека Wire позволяет использовать модуль I2C только в режиме Master, что связано с аппаратными ограничениями. Поддерживаемые скорости передачи данных: 100 кГц, 400 кГц, 1 МГц. Поддерживается только 7-битная адресация Slave-устройств на шине. Синхронный режим работы драйвера I2C
- SPI: Поддерживается 4 режима работы шины (MODE0…MODE3). Управление пином CS только программное (на уровне пользовательского кода
pinMode(CS_PIN, OUTPUT);). Максимальная частота шины SPI — до 40 МГц. Текущая версия драйвера SPI работает в синхронном режиме. - UART: Поддерживаются все стандартные режимы Serial. В текущей версии драйвера UART реализованы кольцевые буферы приема/передачи, позволяющие выполнять обмен данными асинхронно, не блокируя выполнение основной программы.
- RTOS: Данная реализация ядра не поддерживает режим многозадачности. Код выполняется в главном цикле
loop(). Некоторые асинхронные операции достигаются посредством использования прерываний. - Память: Максимальный размер кода ядра во Flash при использовании всех функции и библиотек составляет ~ 48 кБайт. Минимальный объем: ~ 8 кБайт (пример Blink).
- Отладка и программирование: В данной реализации ядра программирование и отладка кода возможна только посредством интерфейса SWD (пины PD0, PD1) и отладчика CMSIS-DAP
На этом пока всё. Ядро находится в разработке, поэтому следите за обновлениями в репозитории. Буду рад обратной связи, замечаниям и предложениям — пишите в Issues на GitHub.

Хорошая работа
Спасибо. Думаю, что еще много чего предстоит сделать по данному проекту, если он окажется нужным
Ознакомились с проектом в отделе технической поддержки компании Миландр. Если не возражаете, подскажите, пожалуйста, мы можем в качестве справки направлять ссылки на статью и репозиторий нашим пользователям?
Здравствуйте, Илья. Благодарю за внимание к проекту и обратную связью. Я не против того, чтобы компания Миландр использовала ссылки на статью и репозиторий в качестве справки для своих пользователей.