Cortex-M0+ под ногами или бесплатная «девборда»
Увлекся я микроконтроллерами в качестве хобби давно. И тогда они не валялись на дороге. Ошибка установки битов-предохранителей в AVR (так я себе представлял fuse-bits) непременно вели на Колхозную площадь города Смоленска в радиолюбительские ряды за новенькой микросхемой в обмен на деньги. Но мир изменился.
После доставки находки в радиолюбительское логово, предстояла разборка устройства методом декомпозиции с соблюдением требований безопасности. Отделив всё вредное для здоровья (и избавившись от лишнего согласно правилам сортировки мусора), предо мной предстало это.
Зёрна отделены от плевел
Рассмотрим детали. LP4068 — контроллер зарядки аккумуляторов Li-Po Li-Ion на 800 мА, G1808 — судя по расположению транзистор (я так и не опознал деталь по маркировке), через который управляется нагреватель, символьный светодиодный индикатор с шестью выводами и микроконтроллер PUYA py32f002a, плюс модный Type-C разъем для зарядки. Ну чем не девборда для разработки поделки выходного дня? Быстрый поиск по маркировке черного квадратика привел к листу технической информации, в котором было указано что это 32-bit Arm Cortex-M0+ @ 24 MHz с таймерами, АЦП, различными периферийными интерфейсами, портами ввода-вывода общего назначения. А ножки SWD и SWC намекали на то что, программы в него можно прошивать при помощи распространенного ST-Link V2.
Цоколёвка
Но, как оказалось, работать с этим контроллером через этот программатор невозможно. Нужен J-Link. По запросам Puya programmer находились какие-то программаторы по заоблачным ценам. С виду клоны J-Link’а. В сети мне попадались инструкции по переделке ST-Link V2 в J-Link. На ближайшем к дому маркетплейсе за 250 р был заказан «свисток» (свой основной, с оригинальным STM32, добытый на хакатоне по Флипперу было жалко). А пока покупка шла к покупателю, я не спешно (аж целых два дня) изучал тему. Оказывается, еще контроллеры Puya можно шить при помощи DAP-Link, который можно получить из blue pill. Забегая вперед, у меня сработал первый вариант.
Изучая просторы сети, я наткнулся на репозиторий замечательного человека IOSettings, который уже прошел этот путь и создал шаблон. Еще он подробно расписал все шаги для таких как я, привыкших к GUI и плохо знакомым с цепочками инструментов, компиляторами, make-файлами. Далее идем по шагам.
1. Подготовка ПО
Первым делом нам нужен Python. Устанавливаем с официального сайта с обязательным включением пути к исполняемым файлам в системную переменную Path (поставить галочку в мастере установки). В последнее время мне нравится такой способ получения доступа к этой настройке: Нажимаем «Win+R», вводим sysdm.cpl и получаем окно системных настроек. Проверяем доступность Python из командной строки.
C:\Users\xxx>python --version
Python 3.11.1
Для загрузки программы в контроллер будем использовать pyocd (инструмент для отладки, программирования и исследования Arm Cortex микроконтроллеров). Чтобы его установить нужен менеджер пакетов pip. Если менеджер не установлен, нужно загрузить специальный скрипт get-pip.py с официального сайта и выполнить его. Так же проверяем.
C:\Users\xxx>pip --version
pip 23.0.1 from C:\python\Lib\site-packages\pip (python 3.11)
Теперь можно установить pyocd выполнив pip install pyocd
.
Для компиляции исходного кода и последующей компоновки исполняемых файлов нужна утилита make. Я использовал её из состава набора инструментов разработки ПО mingw64. В поисковике ищем бинарники (исполняемые файлы), скачиваем, копируем, прописываем в Path.
Пути к исполняемым файлам
Проверяем доступность.
C:\Users\xxx>make -v
GNU Make 4.4.1
Built for x86_64-w64-mingw32
Copyright (C) 1988-2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
В моем комплекте mingw утилита называлась mingw64-make.exe, и вызов этой утилиты не пройдет с таким именем. Я не знаю есть ли в windows механизм трансляции имен файлов (cast), вопрос решил простым переименованием.
Настало время воспользоваться шаблоном от IOSettings. По ссылке из его руководства загружаем GNU Arm Embedded Toolchain для Windows, распаковываем, добавляем в path (вторая выделенная строка на снимке экрана выше). Так же клонируем/загружаем репозиторий с шаблоном.
Для редактирования кода предлагается использовать MSVS Code. Скачиваем и устанавливаем, запускаем из консоли C:\Users\xxx>code
(в этом случаи подтянутся значения из переменной Path). Добавляем в MSVS Сode папку с шаблоном. Третий пункт из инструкции IOSettings по настройке pyocd пропускаем и переходим к редактированию make файла. Тут прописываем свой микроконтроллер и путь к инструментам GNU Arm Embedded Toolchain. Сохраняем изменения.
# MCU types:
MCU_TYPE = PY32F002Ax5
##### Toolchains #######
ARM_TOOCHAIN ?= /opt/arm-gnu-toolchain-13.2.Rel1-mingw-w64-i686-arm-none-eabi/bin
В каталоге User находится тот код, который будет компилироваться. Открываем main.c (классический Blink) и немного правим его под реалии нашей платы. Нужно удалить все, что касается вывода по UART и использовать выводы 8 и 12 порта A.
Важное замечание! В первых экспериментах не используйте PA13 и PA14! Если вы сконфигурируете их под свои задачи, у вас пропадет интерфейс SWD. Я таким образом окирпичил один контроллер (благо он был добыт бесплатно путем древнего навыка собирательства). Причем мои попытки использовать вывод NRST, чтобы как-то стереть память до загрузки программы, не к чему не привели.
#include "py32f0xx_bsp_printf.h"
static void APP_LedConfig(void);
int main(void)
{
HAL_Init();
APP_LedConfig();
while (1)
{
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8); //переключем состояние анода одного сегмента
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12,0); //катод постоянно подключен к минусу
}
}
static void APP_LedConfig(void) //тут конфигурируем порты
{
GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_8; //заполняем структуру конфигурации для 8 выхода
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //передаем структуру
GPIO_InitStruct.Pin = GPIO_PIN_12; //заполняем структуру конфигурации для 12 выхода
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //передаем структуру
}
void APP_ErrorHandler(void)
{
while (1);
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
while (1)
{
}
}
#endif /* USE_FULL_ASSERT */
Настало время ввести команду make в терминале VS Code. И эта утилита возьмет все, что нужно, подставит куда надо и сделает всю работу (прямо магия какая-то). Но у меня выпал ряд ошибок. Как оказалось make файл написан для *nix системы, и для запуска нужен терминал bash.
На этом этапе наша цепочка инструментов работает!
2. Подготовка ST Link V2
Тем временем прибыл программатор. Приступим к его конверсии в J Link. Для преобразования ищем и скачиваем утилиту STLinkReflash.exe. Тут вроде все понятно, но читая материалы по этим преобразованиям я наткнулся на вероятность сбоя при прошивке и отказе работы с микроконтроллерами, отличными от STM. Утилиту STLinkReflash нужно слегка доработать. Приведу цитату человека с ником Miragui:
If you have tried to flash a clone ST-Link V2 to J-Link you must have encountered the following message;
«Unsupported ST-LINK hardware variant»
The solution is to change the following offsets in STLinkReflash.exe
I used the HxD hex editor. But you can use any other you like.offset 2566 3C > 38
offset 2567 40 > C0
offset 26B2 3C > 38
offset 26B3 4A > C0
Я пошел его путем, только смещения 26B2 и 26B3 у меня не совпали. Они были сдвинуты на 16 байт выше. Тем не менее, после изменений и сохранения копии все получилось с первого раза.
Тут надо отметить, что у меня уже была установлена STM32CubeIDE и в процессе экспериментов операционная система никакие драйвера не запрашивала (они были установлены ранее). Возможно, вам отдельно будет нужно установить драйверы на программатор.
Проверяем наличие программатора командой pyocd list
.
Вывод списка программаторов
Добавим поддержку микроконтроллеров puya в pyocd. Командой pyocd pack find PY32
обновим индекс и командой pyocd pack install PY32
добавим поддержку Puya. Проверить установку пакета можно командой pyocd list --targets
.
3. Первая загрузка
Припаиваем к заботливо оставленным пятачкам мой любимый МГТФ. Кстати, SWD и SWC выведены на Type-С разъем. Думаю на заводе изделие прошивается через него.
Пациент готов к жизни в новых качествах
Все готово к выполнению команды make flash
. Но тут есть нюанс ©. После подачи питания, изделие выдает количество оставшихся использований, заряд аккумулятора и уходит в глубокий сон делая недоступным SWD. Я пробовал различные комбинации reset из Eclipse IDE, добавлял кнопку…, но ничего не вышло. Получилось тогда, когда устройство только включилось и ему сразу же была стерта заводская прошивка командой pyocd erase -t py32f002ax5 --chip --config ./Misc/pyocd.yaml
(соединил общий провод платы с общим проводом программатора и нажал ввод). После этого процесс прошивки проходит как обычно.
3. Хитрый экранчик
Символьный светодиодный экран имеет 6 ножек и 19 сегментов, символы капли и молнии двухцветные. Так как управлять? Конкретно на этот индикатор я документацию не нашел, но нашел на похожий. Там два диода для двух сегментов включены параллельно и разнонаправленно. Подавая напряжение в одну сторону зажигаем один, меняем полярность — горит второй. Я сконфигурировал порты PA12, PB1, PA8, PB0, PA7, PA6 как выходы и установил их в ноль. Потом на один из портов подавал единицу, и у меня загоралось сразу три сегмента. Явно ещё что-то соединено. Выпаяв индикатор, попробовал подавать на выводы 3 В с ограничением по току. Ожидаемо загорался один сегмент. Тут стало ясно, что при работе с одним сегментом, все остальные порты должны «висеть в воздухе» — так называемый вход Hi-Z. Для зажигания нужного сегмента нужно динамически конфигурировать GPIO. Для этого была написана некрасивая switch-case функция (показывать её тут не будем). Красиво можно сделать через библиотеку LL, управляя регистрами. Но я пока этого не умею. И да, на плате нет никаких токоограничивающих резисторов — только динамическая индикация. При помощи этой функции, (на входе порт, пин и направление) я нашел комбинации для включения нужных сегментов и создал анимацию «вечной» загрузки.
В качестве заключения хочется сказать следующее. Да, сейчас есть дешевые платы для разработки, удобные IDE. Да, 20 кБ много, для того, чтобы помигать светодиодами (в конфигурационном файле подключено много лишнего). Однако, мне понравилось разбираться с тем, с чем я вроде знаком, но как так сделать чтобы оно заработало я не знаю. И решая микрозадачи по достижению цели, получаешь удовольствие от результата. Наверное это часть DIY движения. Поздравляю с наступающими праздниками!
Hidden text
Приветствуются замечания по статье с целью увеличения её технической ценности