суббота, 10 июля 2021 г.

libopencm3 STM32

Как говорят парни на изиэлектроникс, сейчас не нужно обсуждать интерфейсы и принципы их работы. Весь нужный код выплюнет в нас автоконфигуратор. Но когда покопаешься в старых opensource проектах, то не обнаруживаешь там никаких сред, только make и горстка сишников. И такой подход смотрится отнюдь не архаичным, а более лаконичным и, следовательно, профессиональным. В общем как обычно, раньше было лучше. Хотя о таком параметре, как скорость разработки, в таком случае лучше не говорить совсем. 
Итак, libopencm3 - интерфейсная библиотека под все микроконтроллеры, основанные на ядре arm cortex M. Здесь же рассматривается частный случай с STM32. Начнем со структуры проекта:
  • cm3 - аналог CMSIS. Тот самый случай, когда религия противоречит здравому смыслу. Т.е. уже существует полноценная библиотека от создателя процессора, включающая в себя даже функции ЦОС. И в это время авторы проекта пишут её огрызок, только ради того, что бы она стала копилефтной.
  • регистры (например stm32/f1/gpio.h) - полный набор макросов на все регистры, эта часть отлажена полностью, ошибок никаких. Вызывает трудности только перемещение по проекту. Например, когда нужно подсмотреть регистры к файлу stm32/f1/flash.h, нужно заглянуть в stm32/common/flash_common_all.h, stm32/common/flash_common_f.h, и stm32/common/flash_common_f01.h. Что не очень то удобно.
  • стандартные функции (например rtc_set_prescaler(), gpio_set(GPIOC, GPIO8)) - бывают удобными, повторяют примеры кода из datasheet. Я же предпочитаю писать все то же самое еще раз.
  • api - на него даже есть описание, состоящее из doxygen. Как говорят сами разработчики, оно недописано, я же думаю что оно невозможно.
  • все остальное (скрипты компоновщика, таблицы векторов прерываний и т.д.)
Теория закончилась, теперь практика. Наименьший возможный код это блинк, его в конце и получим. Сперва скачаем и установим все что нужно (для ubuntu):

apt install gcc-arm-none-eabi libnewlib-arm-none-eabi apt install gdb-multiarch git stlink-tools python

Там компилятор, си библиотеки, отладчик, git, python (части библиотеки генерируются скриптами во время сборки) и прошивальщик. Теперь определитесь, куда нужно сложить проект и давайте получим сами libopencm3. Вместо your-project вставьте название вашего проекта:

git clone --recurse-submodules https://github.com/libopencm3/libopencm3-template.git your-project cd your-project make -C libopencm3

Ну вот, исходные коды библиотек скачаны с гитхаба и собраны. Теперь о структуре проекта с точки зрения программиста. Папка my-common-code синоним lib, там будут расположены основные файлы вашего проекта (все что кроме main.c).  Пути и константы, касающиеся не библиотеки, а вашего проекта описываются в my-project/Makefile. Редактируем, удалим ненужные строчки CFILES += api.c, AFILES += api-asm.S, это примеры того, как добавлять ваши файлы в проект. Переменная PROJECT это имя вашего проекта, влияет на название бинарника. Переменная DEVICE в моём случае равна stm32f103c8t6, т.к. на моей 300 рублёвой плате тот же контроллер что и в discovery. Данная библиотека использует информацию о типе контроллера для того, чтобы подключать только нужные файлы за счет условной компиляции.
Теперь код, файл my-project/my-project.c, туда копируем вот этот код:

#include "stdint.h" #include "stdio.h" #include "stdlib.h" #include "../libopencm3/include/libopencm3/stm32/rcc.h" #include "../libopencm3/include/libopencm3/stm32/flash.h" #include "../libopencm3/include/libopencm3/stm32/gpio.h" void rough_delay_us(uint16_t us); void delay_s(uint16_t s); void sysClk(void); void rough_delay_us(uint16_t us) { volatile uint32_t cnt = (uint32_t)us*(uint32_t)4; while(cnt-- > 0); } void delay_s(uint16_t s) { uint16_t cnt = s*20; while(cnt-- > 0) rough_delay_us(50000); } void sysClk() { // включаем кварц, ждем пока прогреется RCC_CR |= (uint32_t)RCC_CR_HSEON; uint32_t timeout = 1e8; while ( ((RCC_CR & RCC_CR_HSERDY) == 0) && (--timeout > 1) ); // рассчет на тактирование от кварца 8 мГц, на максимальную частоту в 72 мГц // AHB, APB1, APB2 36 (разрешенный максимум) // APB2 36 мГц (для таймеров 72), на АЦП забили, не используем. uint32_t cfgr = (RCC_CFGR_USBPRE_PLL_CLK_DIV1_5 << 22) \ | (RCC_CFGR_PLLMUL_PLL_CLK_MUL9 << RCC_CFGR_PLLMUL_SHIFT) \ | (RCC_CFGR_PLLXTPRE_HSE_CLK << 17) \ | (RCC_CFGR_PLLSRC_HSE_CLK << 16) \ | (RCC_CFGR_PPRE2_HCLK_NODIV << RCC_CFGR_PPRE2_SHIFT) \ | (RCC_CFGR_PPRE1_HCLK_NODIV << RCC_CFGR_PPRE1_SHIFT) \ | (RCC_CFGR_HPRE_SYSCLK_DIV2 << RCC_CFGR_HPRE_SHIFT); RCC_CFGR = cfgr; // что то с памятью, копипаста с функций stmhal FLASH_ACR |= (uint32_t)FLASH_ACR_PRFTBE; FLASH_ACR &= ((uint32_t)~FLASH_ACR_LATENCY_MASK); FLASH_ACR |= (uint32_t)FLASH_ACR_LATENCY_2WS; // передергиваем PLL, что бы точно все включилось timeout = 9e6; if ( (RCC_CFGR & RCC_CFGR_SWS) == (RCC_CFGR_SWS_SYSCLKSEL_PLLCLK << RCC_CFGR_SWS_SHIFT) ) { RCC_CFGR &= ~((uint32_t)RCC_CFGR_SW); while ( ((RCC_CFGR & RCC_CFGR_SWS) != (RCC_CFGR_SWS_SYSCLKSEL_HSICLK << RCC_CFGR_SWS_SHIFT) ) && \ (--timeout > 1) ); } RCC_CR &= ~(uint32_t)(RCC_CR_PLLON); timeout = 9e6; while( ((RCC_CR & RCC_CR_PLLRDY) != 0) && (--timeout > 1) ); RCC_CR |= (uint32_t)RCC_CR_PLLON; timeout = 9e6; while( ((RCC_CR & RCC_CR_PLLRDY) == 0) && (--timeout > 1) ); // включаем sysclk, ждем RCC_CFGR |= (uint32_t)(RCC_CFGR_SW_SYSCLKSEL_PLLCLK << RCC_CFGR_SW_SHIFT); timeout = 9e6; while( ((RCC_CFGR & RCC_CFGR_SWS) != (RCC_CFGR_SWS_SYSCLKSEL_PLLCLK << RCC_CFGR_SWS_SHIFT)) && (--timeout > 1) ); } int main(void) { // тактирование микроконтроллера sysClk(); // тактирование порта ввода вывода RCC_APB2ENR |= (uint32_t)RCC_APB2ENR_IOPBEN; // сконфигурировать 45 лапку контроллера как выход gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO8); // цикл дерганья лапкой while(1){ gpio_clear(GPIOB, GPIO8); rough_delay_us(1000); gpio_set(GPIOB, GPIO8); rough_delay_us(10000); } }

Здесь используется самописный вариант инициализации тактирования под максимальные частоты от кварца 8мГц и функции задержки с циклом, длительность которых подбиралась вручную. 
Теперь код нужно скомпилировать и прошить. В таких проектах это принято делать через отладчик. Создайте файл инициализации отладчика с названием gdbinit в корне, c текстом:

target remote localhost:5555 load my-project/awesomesauce.elf continue

Дальше подключаем отладчик к плате с контроллером. У меня это выглядит так.
А потом обнуляем на всякий случай проект, компилируем, чистим память микроконтроллера, и запускаем прошивальщик с отладчиком. В xubuntu они должны открыться в разных окнах.

make -C my-project clean make -C my-project st-flash erase xfce4-terminal -e "st-util -p 5555 -n" --tab -e "gdb-multiarch --init-command="initgdb" my-project/awesomesauce.elf"

Ну вот мы и получили генератор меандра из процессора, который мощнее чем чей нибудь первый телефон или компьютер.

Комментариев нет :

Отправить комментарий