Программируем микроконтроллеры ESP32 и STM32 на C# (nanoFramework)
.NET nanoFramework — это бесплатная платформа с открытым исходным кодом, основанная на .NET и предназначена для малых встраиваемых устройств, микроконтроллеров. С её помощью можно разрабатывать различные устройства для Интернета вещей, носимые устройства, научные приборы, робототехнические устройства, можно создавать прототипы и даже использовать на промышленном оборудовании. В первой части мы познакомились с платформой .NET nanoFramework, её архитектурой, основными возможностями, посмотрели примеры программного кода. Теперь перейдем к практике, установим nanoFramework на микроконтроллеры серии ESP32 и STM32, напишем первый «Hello World!», поработаем с аппаратными интерфейсами, и оценим переносимость кода с «большого» .NET на платформу nanoFramework.
Сейчас на рынке представлено большое количество самых разнообразных микроконтроллеров. Одна из самых известных на сегодняшний день это платформа Arduino, которая использует язык программирования C/C++. По степени удобства, легкости, простоте, nanoFramework ничуть не уступает платформе Arduino. Несмотря на небольшой возраст, nanoFramework уже работает на многих микроконтроллерах, с поддержкой отладки кода и точками останова в удобной IDE MS Visual Studio. nanoFramework просто идеальное решение для существующих .NET разработчиков, которые не хотят останавливаться на достигнутом и им тоже интересна разработка для Интернета вещей (IoT). Ведь все, что необходимо знать и уметь для программирования на nanoFramework, они уже знают и умеют. Программный код, библиотеки, с большой степени переносимы один-к-одному, только необходимо учитывать малый объем RAM и низкую производительность процессора.
Отладка кода приложения на nanoFramework в Visual Studio 2019
Платформа nanoFramework отлично подходит и для сферы обучения. Нет ничего лучше, чем научиться разрабатывать приложения на C# для микроконтроллеров, где у тебя нет гигабайтов оперативной памяти и n-количества ядер процессора, вот тут начинаешь серьезно задумываться над оптимизацией алгоритмов.
И конечно nanoFramework подойдет всем тем, кто стремится к творчеству и хочет сделать свою жизнь более интересной и разнообразной, добавить в неё автоматизацию, роботов, и ИИ. Финансовые затраты для начала работы с nanoFramework составят не более стоимости чашки «кофа» в Starbucks (при условии покупки микроконтроллера ESP32). Для разработки используется бесплатная версия Visual Studio 2019 Community Edition. Все что от вас требуется, это терпение и упорство.
С момента публикации первой часто прошло более полугода, с того времени произошли серьезные изменения. Первое, команда разработчиков обновила логотип. Шутка, на самом деле его действительно обновили, но ещё добавили новые функции и существенно расширили поддержку различных микроконтроллеров.
Логотипы nanoFramework
Рассмотрим некоторые микроконтроллеры для платформы nanoFramework.
Поддерживаемые устройства делятся на две категории: основные платы и поддерживаемые сообществом (дополнительная ссылка — GitHub nanoframework/nf-Community-Targets). В зависимости от устройства будут доступны все или только некоторые сборки (.NET Assemblies). При выборе обращайте внимание на поддерживаемые функции, такие как: Gpio, Spi, I2c, Pwm, Adc, Dac, Serial, OneWire, CAN, Events, SWO, Networking, Large Heap и UI. Например, работа с графическими дисплеями в серии модулей ESP32 будет доступна только на тех, в состав которых входит память PSRAM (Pseudo static RAM).
Модуль ESP-WROOM-32 — самое простое и доступное решение для начала осваивания nanoFramework. На основе данного модуля построена отладочная плата ESP32 DevKit v1, стоимостью до $3.65. В основе модуля чип SoC ESP32-D0WDQ6 от компании Espressif, в него входит 2-ядерный 32-битный процессор Tensilica Xtensa LX6 с блоками памяти ROM на 448 КБ и SRAM на 520 КБ. На кристалле расположены беспроводные модули Wi-Fi/Bluetooth, датчик Холла и сенсор температуры, доступны интерфейсы: ШИМ, АЦП, ЦАП, I2C, SPI, UART/Serial, I2S. Дополнительно модуль содержит Flash-память на 4 МБ.
Отладочная плата ESP32 DevKit v1
При выборе модуля, возможно, вы встретите обозначение не ESP-WROOM-32, а
ESP32-WROOM-32U или ESP32-WROOM-32D. Разница заключается в следующем:
- ESP32-WROOM-32U — модуль ESP-WROOM-32 с подключением внешней антенны;
- ESP32-WROOM-32D — модуль ESP-WROOM-32 c встроенной антенной в виде змейки на самой плате.
Важно! Обычно на отладочной плате с модулем ESP-WROOM-32 размещают две кнопки EN и Boot. Кнопка EN — перезагрузка, Boot — ручной перевод микроконтроллера в режим загрузки прошивки. При нажатие кнопки Boot, контакт GPIO0 на микроконтроллере замыкается на Землю (GND), и в терминале можно увидеть следующий текст:
ets Jun 8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x7 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2))
waiting for download
Кнопки EN и Boot на плате с модулем ESP-WROOM-32
У некоторых пользователей модулей ESP-WROOM-32 без кнопки Boot, исходя из сообщений на форумах, возникают проблемы с переключением микроконтроллера в режим прошивки, поэтому лучше приобретайте модули с наличием кнопки Boot на плате. Но это касается модулей первой версии/ревизии, в третьей ревизии для прошивки нажимать кнопку Boot не обязательно. Далее этот момент будет подробно рассмотрен в пункте выбора прошивки.
Недавно команда разработчиков nanoFramework сообщила, что следующие прошивки для устройств ESP32 будут основаны на системе сборки ESP-IDF 4.3.1. Это означает поддержку микроконтроллеров всей серии ESP32, включая: ESP32_S2, ESP32_S3, ESP32_C3 (RISC-V). Новая система сборки позволяет легко добавлять новые библиотеки и функции в nanoFramework, которые доступны или предоставляются ESP-IDF. Теперь у двухъядерных чипов ESP32 задействованы оба ядра для работы кода. Был успешно перенесен компонент mbedTLS из ESP-IDF без потери оптимизации и использования аппаратных блоков (криптографические вычисления, генерации случайных чисел и т. д.). Конфигурация сети, профили Wi-Fi, CA корневые сертификаты и сертификаты устройств X509 теперь доступны для использования также на всей серии ESP32 устройств.
Если вам не хватает количества контактов GPIO, то можно выбрать один из микроконтроллеров поддерживаемым сообществом — STM32. Например, отладочная плата ST Nucleo64 F411RE на базе SoC Cortex-M4@100 MHz с блоками памяти ROM на 512 КБ и SRAM на 128 КБ. Дополнительно на плате размещены контакты для подключения Arduino-совместимых модулей. По стоимости плата уже существенно дороже ESP32, на торговых площадках продается от $29 и выше.
Плата ST Nucleo64 F411RE
В связи с малыми аппаратными возможностями микроконтроллеров, в отличие от .NET IoT, у каждого класса библиотек есть соответствующий Nuget пакет. Все пакеты добавляются с помощью системы Nuget, как это принято в .NET Core. Рассмотрим подробно библиотеки классов для представления возможностей платформы. Все библиотеки делятся на общие для всех устройств и на специальные, предназначенные для конкретных микроконтроллеров или серии устройств. Классы имеющие название, например System.Device.Gpio совместимы с .NET IoT для Linux, а с названием Windows.Device.Gpio совместимы с Windows IoT Core. Полный список библиотек в документации
Class Libraries. Documentation — nanoFramework.
Библиотеки .NET nanoFramework (xlsx, pdf)
Многие датчики, используемые для подключения к Arduino, также поддерживаются платформой, среди них: акселерометры, газоанализаторы, замер уровня освещения, барометры, термометры, инфракрасные датчики, гироскопы, компасы, драйверы двигателей, NFC-модули и т.д.
Полный список поддерживаемых датчиком представлен в документации List and category of devices. Devices — nanoFramework. С примерами GitHub nanoFramework.IoT.Device/devices.
Для разработки приложений для nanoFramework потребуется Microsoft Visual Studio Community 2019 Version 16.11, установленная платформа .NET, ОС Windows 10 (в Windows 7 не открывается панель Device Explorer). Расширение устанавливается стандартно, через меню: Extensions → Manage Extensions. Название расширения: .NET nanoFramework VS2019 Extension.
Расширение nanoFramework для Microsoft Visual Studio
После установки расширения появится окно Device Explorer (меню View → Other Windows → Device Explorer). В этом окне наиболее важные всего три кнопки: Ping Device, Device Capabilities, Erase App.
Панель Device Explorer расширения nanoFramework
Команды:
- Ping Device — проверяет доступность подключенного устройства/микроконтроллера;
- Device Capabilities — выводит в консоль список возможностей устройства;
- Erase App — удаляет загруженное приложение (nanoFramework остается на месте).
Для возможности исполнения .NET кода на микроконтроллере, необходимо его прошить соответствующей прошивкой nanoFramework. Прошивка осуществляется с помощью утилиты nanoFirmwareFlasher. Установим драйвера для микроконтроллера, утилиту и прошьем устройство.
Шаг 1 — Установка драйвера для моста CP2102 USB to UART
Если при подключение отладочной платы ESP32 DevKit v1 по USB, вы видите что в Диспетчере устройств микроконтроллер находится в узле Другие устройства, значит необходимо установить драйвер.
Диспетчер устройств
Загружаем драйвер с официального сайта CP210x USB to UART Bridge VCP Drivers и устанавливаем. После установки драйвера, микроконтроллер доступен на порту COM3.
Диспетчер устройств
Шаг 2 — Установка утилиты nanoFirmwareFlasher
Теперь устанавливаем утилиту nanoFirmwareFlasher, для этого запускаем командную строку от имени Администратора и выполняем команду:
dotnet tool install -g nanoff
Для вызова справки выполнить команду:
nanoff --help
Обновление утилиты выполняется следующей командой:
dotnet tool update -g nanoff
Шаг 3 — Вывод идентификационной информации для отладочной платы
Иногда бывает сложно понять, какой перед тобой микроконтроллер из серии ESP32, особенно если он пришел с китайской площадки. Поэтому до прошивки можно вывести идентификационную информацию на экран командой:
nanoff --platform esp32 --serialport COM3 --devicedetails
где параметр --platform указывает на Target платформы, --serialport — номер COM порта, в данном случае COM3.
Результат выполнения команды:
Идентификационная информация отладочной платы
Когда будет появляться фиолетовая строка »*** Hold down the BOOT/FLASH button in ESP32 board ***», то в этот момент необходимо нажать на плате кнопку Boot, иначе команда не выполнится.
Из вывода видим, что на плате установлен чип ESP32-D0WDQ6 с 4 Мб Flash памяти, на борту есть WiFi и BT.
Шаг 4 — Выбор прошивки
Теперь переходим к самому сложному шагу. Для серии ESP32 существует несколько различных прошивок (ESP32 Firmware versions). Espressif выпускает чипы SoC, затем на их основе формирует модули. Обратимся к описанию ESP32-S Series на официальном сайте. Модули серии ESP32 выпускаются на базе чипов:
- ESP32-D0WD-V3 (третья версия/ревизия);
- ESP32-D0WD (первая версия/ревизия — старый вариант);
- ESP32-D0WDQ6 (первая версия/ревизия — старый вариант);
- и другие.
Чипы ESP32-D0WD и ESP32-D0WDQ6 по функциональности ничем не отличаются, различие заключается только в форм-факторе самого чипа.
Рассмотрим различия между модулями. Существую модули версии ESP32-WROOM и ESP32-WROVER, эти модули выпускаются как на базе ESP32-D0WD-V3, так и на ESP32-D0WD. Модуль ESP32-WROVER отличается от ESP32-WROOM только наличием памяти PSRAM. PSRAM — это дополнительный напаянный чип RAM помимо встроенной памяти. Обычно указывается в описание к плате и бывает размером 2 или 8 Мб. Дополнительная оперативная память необходима, если подключаете дисплей для вывода UI используя WPF (поддерживается в модулях ESP-WROVER-KIT с LCD ILI9341 на SPI интерфейсе).
Доступные прошивки (target) nanoFramework:
- ESP32_REV0 — (DEV) подходит для всех плат ESP32 без поддержки PSRAM;
- ESP32_REV3 — (DEV) подходит для всех плат ESP32 с чипом ESP32 версии/ревизии 3 без поддержки PSRAM;
- ESP32_PSRAM_REV0 — (DEV) подходит для всех плат ESP32 с поддержкой PSRAM;
- ESP32_PSRAM_REV3 — (DEV) подходит для всех плат ESP32 с чипом ESP32 версии/ревизии 3 и поддержкой PSRAM;
- ESP32_WROOM_32 — подходит для всех плат ESP32 c поддержкой PSRAM, но не поддерживает Bluetooth BLE из-за ограничений памяти в разделе IRAM, вызванных исправлениями PSRAM;
- ESP32_WROOM_32_BLE — подходит для всех плат ESP32, поддерживает Bluetooth BLE, но PSRAM отключен;
- ESP32_WROOM_32_V3_BLE — подходит для всех плат ESP32 с чипом ESP32 версии/ревизии 3, одновременно поддерживается PSRAM и Bluetooth BLE;
- ESP32_PSRAM_REV3_ILI9341 — (упоминается в документации, но прошивка не найдена) подходит для всех плат ESP32 с чипом ESP32 версии/ревизии 3 и поддержкой PSRAM, включает драйвер для LCD ILI9341;
- ESP32_PSRAM_REV3_ILI9342 — (упоминается в документации, но прошивка не найдена) подходит для всех плат ESP32 с чипом ESP32 версии/ревизии 3 и поддержкой PSRAM, включает драйвер для LCD ILI9342;
- ESP_WROVER_KIT — специальный вариант для отладочной платы ESP WROVER KIT. Включает в себя функции UI, поддержку PSRAM и драйвер для SPI LCD ILI9341;
- ESP32_PICO — подходит для всех плат ESP32 с ESP32 PICO без поддержки PSRAM, например ESP32-PICO-KIT и M5Stack ATOM;
- ESP32_PICO_ST7735S — (упоминается в документации, но прошивка не найдена) подходит для платы ESP32 PICO, включает драйвер для LCD ST7735S;
- ESP32_PICO_ST7789V — (упоминается в документации, но прошивка не найдена) подходит платы для M5Stick C Plus, включает драйвер для LCD ST7789V;
- ESP32_REV0_ILI9342 — (упоминается в документации, но прошивка не найдена) подходит платы для M5Stack, включает драйвер для ILI9342;
- ESP32_LILYGO — специальный вариант для отладочной платы LilyGO ESP32, включает поддержку Ethernet PHY.
Перенесем из документации сводную таблицу основных прошивок с описанием опций:
Рассмотрим информацию о чипе модуля ESP32 DevKit v1 (скриншот в предыдущем шаге):
- Отладочная плата построена на чипе ESP32-D0WDQ6 первой ревизии;
- Включена поддержка Wi-Fi, BT;
- Модуль PSRAM недоступен.
Исходя из полученной информации подойдут прошивки: ESP32_REV0 и ESP32_WROOM_32_BLE. Из двух прошивок выбираем ESP32_WROOM_32_BLE т.к. поддержка Bluetooth BLE будет не лишней.
Если вы не знаете какую прошивку выбрать, то подойдет универсальная прошивка »ESP32_REV0» и »ESP32_PSRAM_REV0».
Полный каталог прошивок представлен в Repository: nanoframework-images. Список прошивок только для серии Espressif ESP32 Series. Общий список прошивок GitHub nanoframework/nf-interpreter.
Шаг 5 — Прошивка ESP-WROOM-32
Для прошивки необходимо подключить отладочную плату и выполнить команду:
nanoff --update --target ESP32_WROOM_32_BLE --serialport COM3
где параметр --target указывает на идентификатор прошивки, --serialport — номер COM порта, а данном случае COM3.
Результат выполнения команды:
Прошивка ESP-WROOM-32
Если прошивка не загрузится с репозитория, то можно повторить операцию или её загрузить со страницы ESP32_WROOM_32_BLE, и распаковать, например по пути: «c:\nanofw». В архиве будет несколько файлов:
Распакованная прошивка для ESP-WROOM-32
Для прошивки устройства, используя локальную прошивку, выполнить команду:
nanoff --update --clrfile "C:\nanofw\nanoCLR.bin" --serialport COM3
где параметр --clrfile путь к файлу nanoCLR.bin, --serialport — номер COM порта, а данном случае COM3.
Не забываем во время прошивки нажимать на кнопку «Boot». Устройство готово для загрузки .NET программ!
Запустим Visual Studio 2019 Community Edition и откроем окно Device Explorer. Выполним команду — Device Capabilities. В результате выполнения команды в консоль будет выведена информация о возможностях микроконтроллера.
Информация о возможностях ESP-WROOM-32
HAL build info: nanoFramework running @ ESP32
Target: ESP32_WROOM_32_BLE
Platform: ESP32
Firmware build Info:
Date: Aug 25 2021
Type: MinSizeRel build with IDF v3.3.5
CLR Version: 1.7.0.618
Compiler: GNU ARM GCC v5.2.0
OEM Product codes (vendor, model, SKU): 0, 0, 0
Serial Numbers (module, system):
00000000000000000000000000000000
0000000000000000
Target capabilities:
Has nanoBooter: NO
IFU capable: NO
Has proprietary bootloader: YES
AppDomains:
Assemblies:
Native Assemblies:
mscorlib v100.5.0.12, checksum 0×132BDB6F
nanoFramework.Runtime.Native v100.0.8.0, checksum 0×2307A8F3
nanoFramework.Hardware.Esp32 v100.0.7.2, checksum 0×1B75B894
nanoFramework.Hardware.Esp32.Rmt v100.0.3.0, checksum 0×9A53BB44
nanoFramework.Hardware.Esp32.Ble v100.0.0.0, checksum 0×1B75B894
nanoFramework.Devices.OneWire v100.0.3.4, checksum 0xA5C172BD
nanoFramework.Networking.Sntp v100.0.4.4, checksum 0xE2D9BDED
nanoFramework.ResourceManager v100.0.0.1, checksum 0xDCD7DF4D
nanoFramework.System.Collections v100.0.0.1, checksum 0×5A31313D
nanoFramework.System.Text v100.0.0.1, checksum 0×8E6EB73D
nanoFramework.Runtime.Events v100.0.8.0, checksum 0×0EAB00C9
EventSink v1.0.0.0, checksum 0xF32F4C3E
System.IO.FileSystem v1.0.0.0, checksum 0×3112D24C
System.Math v100.0.5.2, checksum 0xC9E0AB13
System.Net v100.1.3.4, checksum 0xC74796C2
Windows.Devices.Adc v100.1.3.3, checksum 0xCA03579A
System.Device.Dac v100.0.0.6, checksum 0×02B3E860
System.Device.Gpio v100.1.0.4, checksum 0xB6D0ACC1
Windows.Devices.Gpio v100.1.2.2, checksum 0xC41539BE
Windows.Devices.I2c v100.2.0.2, checksum 0×79EDBF71
System.Device.I2c v100.0.0.1, checksum 0xFA806D33
Windows.Devices.Pwm v100.1.3.3, checksum 0xBA2E2251
Windows.Devices.SerialCommunication v100.1.1.2, checksum 0×34BAF06E
System.IO.Ports v100.1.1.3, checksum 0×61B8380C
Windows.Devices.Spi v100.1.4.2, checksum 0×360239F1
System.Device.Spi v100.1.0.0, checksum 0×48031DC5
Windows.Devices.Wifi v100.0.6.2, checksum 0xA94A849E
Windows.Storage v100.0.2.0, checksum 0×954A4192
++++++++++++++++++++++++++++++++
++ Memory Map ++
++++++++++++++++++++++++++++++++
Type Start Size
++++++++++++++++++++++++++++++++
RAM 0×3ffe436c 0×00016800
FLASH 0×00000000 0×00400000
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++ Flash Sector Map ++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Region Start Blocks Bytes/Block Usage
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0 0×00010000 1 0×180000 nanoCLR
1 0×00190000 1 0×230000 Deployment
2 0×003C0000 1 0×040000 Configuration
+++++++++++++++++++++++++++++++++++++++++++++++++++
++ Storage Usage Map ++
+++++++++++++++++++++++++++++++++++++++++++++++++++
Start Size (kB) Usage
+++++++++++++++++++++++++++++++++++++++++++++++++++
0×003C0000 0×040000 (256kB) Configuration
0×00010000 0×180000 (1536kB) nanoCLR
0×00190000 0×230000 (2240kB) Deployment
Deployment Map
Empty
Из полученных данных наиболее важен список — Native Assemblies. Данный список содержит сборки, которые мы можем использовать в своем проекте, все остальные библиотеки необходимо включать в проект в виде исходного кода. Дополнительно можно сформировать прошивку со своими нативными сборками на C++, но как это сделать будет в продолжении.
Создадим новое приложение, выберем тип проекта: Blank Application (nanoFramework).
Шаблон проекта Blank Application (nanoFramework)
Шаблонный проект выведет отладочное сообщение в консоль.
Шаблонный проект nanoFramework
Запустим проект на отладку
Отладка проекта nanoFramework
В окне отладки будет выведено сообщение: «Hello from nanoFramework!».
Отлично, первое приложение запущено! Далее запустим стандартную программу мигания светодиодом.
На плате ESP32 DevKit v1 размещён встроенный светодиод, подключенный к контакту GPIO2. Запустим программу Blink, аналогичную как на Arduino. Для этого создадим новый проект nanoframework-esp32-blink, и добавим Nuget-пакет: nanoFramework.System.Device.Gpio. Исходный код возьмем из проекта GitHub nanoframework/Samples — Blinky.
Файл Program.cs.
public class Program
{
private static GpioController s_GpioController;
public static void Main()
{
s_GpioController = new GpioController();
// ESP32 DevKit: 4 is a valid GPIO pin in, some boards
// like Xiuxin ESP32 may require GPIO Pin 2 instead.
GpioPin led = s_GpioController.OpenPin(2,PinMode.Output);
led.Write(PinValue.Low);
while (true)
{
led.Write(PinValue.High);
Thread.Sleep(1000);
led.Write(PinValue.Low);
Thread.Sleep(1000);
}
}
}
Разберем пример:
- s_GpioController = new GpioController () — инициализирует объект контроллера доступа к GPIO;
- GpioPin led = s_GpioController.OpenPin (2, PinMode.Output) — метод OpenPin переводит контакт GPIO2 в режим вывода;
- led.Write (PinValue.Low) — подает логический »0» на контакт;
- led.Write (PinValue.High) — логическая »1», подано напряжение в 3.3V.
Демонстрация работы программного кода:
Любое устройство взаимодействует с внешним миром и нуждается в управлении, теперь добавим кнопку и реализуем новую логику.
К микроконтроллеру подключим кнопку и реализуем следующую логику: встроенный светодиод загорается и остается в положение включено при удержании нажатой кнопки. При отпускание кнопки светодиод гаснет. Светодиод будем использовать тот же контакт GPIO2. Кнопку подключим к контакту GPIO25, на плате обозначен как »D25». Создадим новый проект nanoframework-esp32-button, и добавим Nuget-пакет: nanoFramework.System.Device.Gpio.
Отладочная плата ESP32 DevKit v1 с подключенной кнопкой
Файл Program.cs.
using System;
using System.Diagnostics;
using System.Threading;
using System.Device.Gpio;
namespace nanoframework_esp32_button
{
public class Program
{
//Board: ESP32 DevKit
static GpioController s_GpioController;
static int s_BluePinNumber=2;
static int s_UserButtonPinNumber=25;
public static void Main()
{
s_GpioController = new GpioController();
//setup blue LED
s_GpioController.OpenPin(s_BluePinNumber, PinMode.Output);
s_GpioController.Write(s_BluePinNumber, PinValue.Low);
//setup user button
s_GpioController.OpenPin(s_UserButtonPinNumber, PinMode.Input);
//s_GpioController.OpenPin(s_UserButtonPinNumber, PinMode.InputPullUp);
//Event registration
s_GpioController.RegisterCallbackForPinValueChangedEvent(
s_UserButtonPinNumber,
PinEventTypes.Falling | PinEventTypes.Rising,
UserButton_ValueChanged);
//Infinite
Thread.Sleep(Timeout.Infinite);
}
private static void UserButton_ValueChanged(object sender, PinValueChangedEventArgs e)
{
// read Gpio pin value from event
Debug.WriteLine("USER BUTTON (event) : " + e.ChangeType.ToString());
Debug.WriteLine("USER BUTTON (event) : " + ((((int)e.ChangeType) == 1) ? "Rising" : "Falling"));
//if (e.ChangeType != PinEventTypes.Rising) //for DFRobot
if (e.ChangeType == PinEventTypes.Rising)
{
s_GpioController.Write(s_BluePinNumber, PinValue.High);
}
else
{
s_GpioController.Write(s_BluePinNumber, PinValue.Low);
}
}
}
}
Разберем пример:
- s_GpioController.OpenPin (s_UserButtonPinNumber, PinMode.Input) — открывает контакт кнопки и выставляет режим работы на ввод (сигнал поступает от кнопки). Если необходимо исключить считывание внешних наводок, то можно использовать подтяжку к питанию PinMode.InputPullUp (не все контакты ввода поддерживают);
- s_GpioController.RegisterCallbackForPinValueChangedEvent — обработка прерывания реализуется путем добавление Callback на изменение состояние контакта. Callback регистрируется в контроллере GPIO. Указываем номер контакта — s_UserButtonPinNumber, событие срабатывание — Falling (изменение с »1» на »0») или Rising (изменение с »0» на »1»), название функции обработки — UserButton_ValueChanged.
Демонстрация работы программного кода:
Основной принцип работы с nanoFramework понятен, теперь вернемся к микроконтроллеру ST Nucleo64 F411RE и прошьем его средой nanoFramework, и запустим простую программу использующую светодиод и кнопку.
Шаг 1 — Подключение ST Nucleo64 F411RE
До подключение микроконтроллеров STM32 необходимо установить драйвер для отладчика/программатора ST-Link, для этого устанавливаем драйвер ST-LINK USB driver signed for Windows 7–10 и подключаем устройство. После подключения в Диспетчере устройств должно появиться устройство ST-Link Debug.
Диспетчер устройств
Выполним команду обнаружения устройств ST-Link:
nanoff --listjtag
Результат выполнения команды:
Обнаруженные устройства ST-Link
Если плата появилась в списке, то можно переходить к следующему шагу.
Шаг 2 — Обновление прошивки ST-Link
Если мы попытаемся загрузить nanoFramework в микроконтроллер, то возможно появится подобная ошибка, которая говорит о необходимости обновления прошивки ST-Link Debug.
Сообщение о необходимости обновления прошивки ST-Link Debug
Загружаем и запускаем STM32 ST-LINK utility. Выбираем в меню ST-LINK → Firmware update.
STM32 ST-LINK utility
Нажимаем на кнопку Device Connect, затем Yes >>>>
STM32 ST-LINK utility
Если прошивка успешно загрузиться будет сообщение Update succesfull.
STM32 ST-LINK utility
Если на этом этапе возникнут трудности, то можете посмотреть видео-инструкцию ST Link Update — Web learning.
Шаг 3 — Прошивка ST Nucleo64 F411RE
Для прошивки необходимо выполнить команду:
nanoff --update --target ST_NUCLEO64_F411RE_NF --jtag
где параметр --target указывает на идентификатор прошивки ST_NUCLEO64_F411RE_NF.
Прошивка ST Nucleo64 F411RE
Теперь запустим Visual Studio Community и выполним команду Device Capabilities.
Информация о возможностях ST Nucleo64 F411RE
HAL build info: nanoCLR running @ ST_NUCLEO64_F411RE_NF
Target: ST_NUCLEO64_F411RE_NF
Platform: STM32F4
Firmware build Info:
Date: Oct 22 2021
Type: MinSizeRel build with ChibiOS v21.6.0.6
CLR Version: 1.7.0.96
Compiler: GNU ARM GCC v9.3.1
OEM Product codes (vendor, model, SKU): 0, 0, 0
Serial Numbers (module, system):
00000000000000000000000000000000
0000000000000000
Target capabilities:
Has nanoBooter: YES
nanoBooter: v21845.21845.21845.21845
IFU capable: NO
Has proprietary bootloader: NO
AppDomains:
Assemblies:
nanoframework_stm32_button, 1.0.0.0
mscorlib, 1.10.5.4
nanoFramework.Runtime.Events, 1.9.1.3
Windows.Devices.Gpio, 1.5.2.3
Native Assemblies:
mscorlib v100.5.0.12, checksum 0×132BDB6F
nanoFramework.Runtime.Native v100.0.8.0, checksum 0×2307A8F3
nanoFramework.Hardware.Stm32 v100.0.4.4, checksum 0×0874B6FE
nanoFramework.ResourceManager v100.0.0.1, checksum 0xDCD7DF4D
nanoFramework.System.Collections v100.0.0.1, checksum 0×5A31313D
nanoFramework.System.Text v100.0.0.1, checksum 0×8E6EB73D
nanoFramework.Runtime.Events v100.0.8.0, checksum 0×0EAB00C9
EventSink v1.0.0.0, checksum 0xF32F4C3E
System.Math v100.0.5.2, checksum 0xC9E0AB13
Windows.Devices.Adc v100.1.3.3, checksum 0xCA03579A
Windows.Devices.Gpio v100.1.2.2, checksum 0xC41539BE
Windows.Devices.I2c v100.2.0.2, checksum 0×79EDBF71
System.Device.I2c v100.0.0.1, checksum 0xFA806D33
Windows.Devices.SerialCommunication v100.1.1.2, checksum 0×34BAF06E
System.IO.Ports v100.1.2.0, checksum 0×564F2452
Windows.Devices.Spi v100.1.4.2, checksum 0×360239F1
System.Device.Spi v100.1.0.0, checksum 0×48031DC5
++++++++++++++++++++++++++++++++
++ Memory Map ++
++++++++++++++++++++++++++++++++
Type Start Size
++++++++++++++++++++++++++++++++
RAM 0×20000000 0×00020000
FLASH 0×08000000 0×00080000
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++ Flash Sector Map ++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Region Start Blocks Bytes/Block Usage
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0 0×08000000 1 0×004000 nanoBooter
1 0×08004000 3 0×004000 nanoCLR
2 0×08010000 1 0×010000 nanoCLR
3 0×08020000 1 0×020000 nanoCLR
4 0×08040000 2 0×020000 Deployment
+++++++++++++++++++++++++++++++++++++++++++++++++++
++ Storage Usage Map ++
+++++++++++++++++++++++++++++++++++++++++++++++++++
Start Size (kB) Usage
+++++++++++++++++++++++++++++++++++++++++++++++++++
0×08000000 0×004000 (16kB) nanoBooter
0×08004000 0×03C000 (240kB) nanoCLR
0×08040000 0×040000 (256kB) Deployment
Deployment Map
Empty
Из полученных данных видно, что nanoCLR работает на ChibiOS v21.6.
В отличие от ESP32, на плате дополнительно ещё размещена кнопка, которую можно программировать под свои задачи. Светодиод на плате обозначен меткой LD2 (Green LED), кнопка — B1 (USER). Добавим немного другую логику работы отличную от примера для ESP32, встроенный светодиод загорается и выключается по нажатию на кнопку.
Светодиод LD2 (Green LED) и кнопка B1 USER на плате ST Nucleo64 F411RE
Создадим новый проект nanoframework-stm32-button, и добавим Nuget-пакет: nanoFramework.Windows.Devices.Gpio. Обратите внимание, пакет отличается от предыдущего примера, название — nanoFramework.Windows.*. Как было сказано выше, API библиотек в которых присутствует Windows.*, совпадают с .NET на Windows IoT Core, которая работает на Raspberry Pi. Поэтому возьмем готовый код Push button — Raspberry Pi, с сайта MS. Из изменений в коде будут только другие номера контактов (c Raspberry Pi не совпадают).
Светодиод LD2 (Green LED) подключен к контакту Arduino D13, что соответствует STM32 I/O PA5 (pin 21). Контакт кнопки B1 USER соответствует STM32 I/O PC13 (pin 2). В STM32 принято обращение к контактам по названию. Контакт PA5, где P — port, A-литера порта, 5 — номер контакта. Поэтому в программный код дополнительно будет добавлена функция конвертации буквенного обозначения контактов в номерной — int PinNumber (char port, byte pin). Описание контактов GitHub nanoframework/nf-Community-Targets/ChibiOS/ST_NUCLEO64_F411RE_NF/.
Файл Program.cs.
using System;
using System.Diagnostics;
using System.Threading;
using Windows.Devices.Gpio;
namespace nanoframework_stm32_button
{
public class Program
{
private static int LED_PIN;
private static int BUTTON_PIN;
private static GpioPin ledPin;
private static GpioPin buttonPin;
private static GpioPinValue ledPinValue = GpioPinValue.High;
public static void Main()
{
// ST Nucleo64 F411RE: PA5 is LED_GREEN
LED_PIN = PinNumber('A', 5);
// ST Nucleo64 F411RE: PC13 is B1_USER
BUTTON_PIN = PinNumber('C', 13);
//Next as .NET for Raspberry Pi, Windows IoT Core
//Controller
var gpio = new GpioController();
//Init
ledPin = gpio.OpenPin(LED_PIN);
ledPin.SetDriveMode(GpioPinDriveMode.Output);
buttonPin = gpio.OpenPin(BUTTON_PIN);
buttonPin.SetDriveMode(GpioPinDriveMode.Input);
// Initialize LED to the OFF state by first writing a HIGH value
// We write HIGH because the LED is wired in a active LOW configuration
ledPin.Write(ledPinValue);
// Check if input pull-up resistors are supported
if (buttonPin.IsDriveModeSupported(GpioPinDriveMode.InputPullUp))
buttonPin.SetDriveMode(GpioPinDriveMode.InputPullUp);
else
buttonPin.SetDriveMode(GpioPinDriveMode.Input);
// Set a debounce timeout to filter out switch bounce noise from a button press
buttonPin.DebounceTimeout = TimeSpan.FromMilliseconds(50);
// Register for the ValueChanged event so our buttonPin_ValueChanged
// function is called when the button is pressed
buttonPin.ValueChanged += buttonPin_ValueChanged;
//Infinite
Thread.Sleep(Timeout.Infinite);
}
static void buttonPin_ValueChanged(object sender, GpioPinValueChangedEventArgs e)
{
Debug.WriteLine("USER BUTTON (event) : " + ((e.Edge ==GpioPinEdge.RisingEdge)? "RisingEdge" : "FallingEdge"));
// toggle the state of the LED every time the button is pressed
if (e.Edge == GpioPinEdge.FallingEdge)
{
ledPinValue = (ledPinValue == GpioPinValue.Low) ?
GpioPinValue.High : GpioPinValue.Low;
ledPin.Write(ledPinValue);
}
}
static int PinNumber(char port, byte pin)
{
if (port < 'A' || port > 'J')
throw new ArgumentException();
return ((port - 'A') * 16) + pin;
}
}
}
Разберем пример:
- int PinNumber (char port, byte pin) — функция конвертации буквенного обозначения контактов в номерной;
- gpio.OpenPin (LED_PIN) — открывает контакт для его использования;
- ledPin.SetDriveMode (GpioPinDriveMode.Output) — выставление режим работы контакта на — вывод GpioPinDriveMode.Output или ввод GpioPinDriveMode.Input;
- ledPin.Write (ledPinValue) — подает логический »0«GpioPinValue.Low или »1»(GpioPinValue.High) на контакт;
- buttonPin.IsDriveModeSupported (GpioPinDriveMode.InputPullUp) — проверка наличия подтягивающего резистора (pull-up);
- buttonPin.SetDriveMode (GpioPinDriveMode.InputPullUp) — включение режима работы контакта на ввод с поддержкой pull-up;
- buttonPin.DebounceTimeout — интервал в течение которого не будут регистрироваться события (необходимо для исключения дребезга контактов);
- buttonPin.ValueChanged — подпись на событие изменения состояния;
- void buttonPin_ValueChanged — функция, которая вызывается при изменение состояния кнопки. Свойство e.Edge принимает значение GpioPinEdge.RisingEdge (изменение с »0» на »1»), или GpioPinEdge.FallingEdge (изменение с »1» на »0»).
Демонстрация работы программного кода:
Модуль BME280 фирмы Bosch Sensortec предназначен для измерения атмосферного давления, температуры и влажности. По сравнению с первыми датчиками серии (BMP085 и BMP180), он имеет лучшие характеристики и меньшие размеры. Отличие от датчика BMP280 — наличие гигрометра, что позволяет измерять относительную влажность воздуха и создать на его основе маленькую метеостанцию.
Назначение контактов:
- VCC — питание модуля 3.3 В или 5 В;
- GND — Ground;
- SCL — линия тактирования (Serial CLock);
- SDA — линия данных (Serial Data).
Модуль работает по двухпроводному интерфейсу I2C, адрес по умолчанию 0×76. Данный датчик уже был использован в проекте Метеостанция на Banana Pi M64 (Linux, C#, Docker, RabbitMQ, AvaloniaUI), поэтому детальнее ознакомится с характеристиками и шиной I2C можно по вышеуказанной ссылке. Подключим датчик BME280 к отладочной плате ESP32 DevKit v1.
Отладочная плата ESP32 DevKit v1 с датчиком BME280
Создадим новый проект nanoframework-esp32-bme280, и добавим для работы с шиной I2C Nuget-пакет: nanoFramework.System.Device.I2c. Драйвер для работы с BME280 возьмем с каталога драйверов nanoFramework.IoT.Device — BMxx80 Device Family. Перенесем драйвер в папку /libs/. Дополнительно команда разработчиков nanoFramework для работы с физическими величинами используют библиотеку UnitsNet.
Файл Program.cs. Инициализация датчика BME280.
// when connecting to an ESP32 device, need to configure the I2C GPIOs
// used for the bus
Configuration.SetPinFunction(21, DeviceFunction.I2C1_DATA);
Configuration.SetPinFunction(22, DeviceFunction.I2C1_CLOCK);
// bus id on the MCU
const int busId = 1;
I2cConnectionSettings i2cSettings = new(busId, Bme280.SecondaryI2cAddress);
using I2cDevice i2cDevice = I2cDevice.Create(i2cSettings);
using Bme280 bme80 = new Bme280(i2cDevice)
{
// set higher sampling
TemperatureSampling = Sampling.LowPower,
PressureSampling = Sampling.UltraHighResolution,
HumiditySampling = Sampling.Standard,
};
// set this to the current sea level pressure in the area for correct altitude readings
Pressure defaultSeaLevelPressure = WeatherHelper.MeanSeaLevel;
Разберем пример:
- Configuration.SetPinFunction (21, DeviceFunction.I2C1_DATA) — функция из пространства имен nanoFramework.Hardware.Esp32. В связи с работой nanoFramework на различных микроконтроллерах, например для работы с I2C, требуется дополнительное конфигурирования контактов. В данном случае для шины I2C с идентификатором »1», передача данных DATA определяется на GPIO21;
- busId = 1 — идентификатор шины I2C — 1;
- new I2cConnectionSettings (busId, Bme280.SecondaryI2cAddress) — создание конфигурации устройства I2C, указывается идентификатор шины I2C и адрес устройства (0×76);
- I2cDevice.Create (i2cSettings) — создание I2C устройства;
- new Bme280(i2cDevice) — инициализация датчика BME280;
- TemperatureSampling, PressureSampling, HumiditySampling — указание точности измерений физических величин;
- WeatherHelper.MeanSeaLevel — для определения уровня высоты указывается точка отсчета от уровня моря.
Файл Program.cs. Чтение данных с датчика BME280.
// Perform a synchronous measurement
var readResult = bme80.Read();
// Note that if you already have the pressure value and the temperature, you could also calculate altitude by using
// var altValue = WeatherHelper.CalculateAltitude(preValue, defaultSeaLevelPressure, tempValue) which would be more performant.
bme80.TryReadAltitude(defaultSeaLevelPressure, out var altValue);
Debug.WriteLine($"Temperature: {readResult.Temperature.DegreesCelsius}\u00B0C");
Debug.WriteLine($"Pressure: {readResult.Pressure.Hectopascals}hPa");
Debug.WriteLine($"Altitude: {altValue.Meters}m");
Debug.WriteLine($"Relative humidity: {readResult.Humidity.Percent}%");
Разберем пример:
- bme80.Read () — чтение показаний датчика;
- bme80.TryReadAltitude — получение значения высоты над уровнем моря;
- readResult.Temperature, readResult.Pressure, readResult.Humidity — получение данных температуры, давления, влажности.
Запустим проект:
Чтение данных с датчика BME280 на nanoFramework
Для вывода данных планировал