Программинг микроконтроллеров / STM32F1xx — Инструменты разработчика и FreeRTOS

в 19:04, , рубрики: freertos, stm, stm32, stm32F, микроконтроллеры, метки: , , , , , ,

Добрый день, уважаемыее. В своих прошлых статьях (STM32F1xx — лечимся от ардуинозависимости вместе, STM32F1хх — продолжаем лечение от ардуинозависимости при помощи LCD) я постарался осветить вопросы перехода с 8-битных микроконтроллеров на новые 32-битные STM32F1xx.
В процессе работы с ними, я, разумеется выбирал инструменты себе «по руке» — то есть, старался найти наиболее удобные для меня отладочные платы, программаторы, IDE. В этой статье я хочу поделиться с вами несколькими соображениями на этот счет, а также описать процесс сборки в выбранной IDE операционной системы реального времени FreeRTOS.

Железо

Традиционно начнем с железа.
В прошлых статьях ядром системы была плата STM32VLDISCOVERY.
Плата, безусловно, хороша, и подкупает тем, что ее цена всего 300 рублей. В принципе, хороший инструмент для того, чтобы начать знакомиться с этим семейством микроконтроллеров. Но.
Дело в том, что при выборе отладочной платы, всегда хочется соблюсти баланс количества и качества, то есть с одной стороны, иметь все необходимое для работы, с другой – не хочется чтобы плата превращалась в огромного и дорогого монстра. У STM32VLDISCOVERY баланс смещен в сторону дешевизны и минимализма.
Полазив по e-bay, я нашел для себя более удобную, на мой взгляд, плату, которую и представляю вашему вниманию. Вот она:
Mini-STM32

image

За 46 долларов нам предлагают:

  1. Микроконтроллер STM32F103VE, имеющий на борту 512 килобайт флеша и 64 килобайта RAM, USB, SDIO (то есть с карточки читать будет намного быстрее, чем по SPI). Кроме того, так как на плате установлена 100-ногая его версия, у него хватает внешних пинов для управления памятью через FSMC. FSMC – это Flexible Static Memory Controller, очень удобный контроллер статической памяти, начиная с, собственно, SRAM и заканчивая NAND флешками. Настроив его и подключив память к управляющим пинам, мы получаем нашу память, мапированную на адресное пространство контроллера. То есть, с этого момента, все взаимодействия с ней будут для нас прозрачны и эквивалентны простой записи в RAM.
  2. Цветной TFT-дисплей с разрешением 320х240 и предустановленным резистивным тач-скрином. Дисплей ставится на плату в виде модуля, при желании, можно отвинтить стоечки, к которым он крепится, и использовать плату без него. В дисплейный модуль, помимо дисплея входит еще и повышающий преобразователь для питания его подсветки, а также контроллер тач-скрина, который управляется по SPI. Кроме того, разъем подключен к упомянутому выше FSMC, что делает взаимодействие с ним в разы удобнее.
    Для примера – вот так выглядит запись в регистры дисплея, а после – в его память, с использованием DMA:
    #define LCDRegister 					(*((volatile u16*) 0x60000000)) #define LCDMemory 					(*((volatile u16*) 0x60020000)) //…  void LCDWriteRegister(unsigned short reg, unsigned short data) { 	LCDRegister=reg; 	LCDMemory=data; }  void LCDBeginRAMWrite() { 	LCDRegister=CTR_WRITE_DATA; }  int main(void) { //… 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  	DMA_InitTypeDef DMA_InitStructure; 	DMA_DeInit(DMA1_Channel1); 	 	//Адрес буфера-источника графики 	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32) PlasmaBuffer1;	  	//Адрес «точки мапирования»  дисплейной памяти 	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)(&LCDMemory);		 	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 	DMA_InitStructure.DMA_BufferSize = PLASMA_WIDTH*PLASMA_HEIGHT; 	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; 	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; 	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; 	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; 	DMA_InitStructure.DMA_Priority = DMA_Priority_High; 	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable; 	DMA_Init(DMA1_Channel1, &DMA_InitStructure); //… 	//Собственно, вывод 	LCDBeginRAMWrite(); 	DMA_SetCurrDataCounter(DMA1_Channel1, PLASMA_WIDTH*PLASMA_HEIGHT); 	DMA_Cmd(DMA1_Channel1, ENABLE); } 

    После выполнения последней строчки, управление возвращается программе, так что можно сразу же начинать просчет следующего кадра, пока первый выводится через DMA.

  3. Слот micro-SD, подключенный к управляющим пинам SDIO-контроллера STMки. Ситуация та же, что и с дисплеем – в нашем распоряжении быстрый и удобный способ общаться с карточкой памяти, намного быстрее, чем SPI.
  4. USB-разъем и сопутствующая схематика. Как известно, USB-хост определяет наличие устройства на шине по подтягивающему резистору. Соответственно, чтобы иметь возможность сначала совершить какие-либо действия и только потом дать сигнал хосту «я подключен!» нужно подсоединить подтягивающий резистор через транзисторный ключ
    На плате это уже сделано за нас, управляющий транзистор подключен к одному из GPIO-пинов, подтягивающие резисторы настроены на Full-Speed USB.
  5. Неиспользованные пины выведены на двухрядный сорокапиновый разъем, второй разъем – дисплейный, третий – разъем под программатор, J-Link совместимый.
  6. Из приятных бонусов – на плате также присутствует разъем для батарейки, питающей RTC, двухметровая SPI-флешка, один управляемый светодиод, одна кнопка, хороший стабилизатор для питания от USB и max232-конвертер, вместе с разъемом для ком-порта.
    Последний, конечно, по моему мнению – самая лишняя часть во всей плате, только занимающая место, но ладно уж, пусть будет.

Кроме того, по отдельной просьбе, за 28 долларов, продавец приложит к плате J-Link-совместимый программатор (а попросту, настоящий клон Segger J-Link, беззастенчиво под него маскирующийся), со шлейфом, полностью совместимым с разъемом на плате.
Подводя итог вышесказанному, я считаю данную плату вещью первой необходимости для человека, который решил начать изучение STM32F1xx микроконтроллеров.

Софт

Теперь то, что касается IDE. Изначально я выбрал Keil uVision, так как когда-то уже работал с ней.
Ну, что я могу сказать – я проработал в кейле достаточно, и в принципе с ним можно примириться. Но, положа руку на сердце – ИДЕ там ужасна. Также ужасна как и ИДЕ IAR’a, на мой взгляд.
IAR и Keil – признанные лидеры в разработке компиллеров, этого у них не отнять, но я до сих пор не могу понять, почему, имея такие компиллеры, они продолжают тянуть свои IDE, застрявшие по удобству на уровне 2002 года. Как пример могу привести Texas Instruments – у них раньше тоже была своя IDE, в довесок к компиллеру. Потом им это надоело, они взяли Eclipse, допилили его, прикрутили к своим компиллеру и профайлеру, и получили отличный продукт. Почему так не поступят Keil и IAR для меня остается загадкой, но на мой взгляд их IDE не такие удобные, как могли бы быть. Раздражает не очень удобная подсветка синтаксиса, полное отсутствие code-completion’а, не самая удобная навигация по коду. Плюс, uVision частенько у меня падала, но это можно списать на драйвер программатора.
Как бы то ни было, я стал искать альтернативу и нашел ее в виде CooCox IDE.

image

Это бесплатная среда разработки на базе эклипса, которая призвана работать, разумеется, с GCC.
Из плюсов отмечу все достоинства эклипса – удобная навигация, есть автозавершение кода и т.п.
Кроме того прикручен удобный просмотрщик периферии процессора, мне понравился больше чем Кейловский. Очень удобно наличие репозитория компонентов – говоря по-простому, при старте проекта мы как в визарде выбираем нужный нам процессор из списка, после чего отмечаем галочками те из модулей Standard Peripheral Library, которые хотели бы использовать, и они автоматически подключаются к проекту (Об этом чуть подробнее в следующем разделе статьи). Также сразу же можно просмотреть примеры, идущие в комплекте с этим модулем SPL и справку по его функциям.
Минусы CooCox IDE вобрала также из Eclipse, к коим относится тяжеловесность – у меня она потребляет около 180 метров оперативной памяти, занимая на диске 800 мегабайт.
Еще одним минусом является ее работа с этим самым J-Link-ком. Отладка происходит через приложение от создателей J-Link’a, предоставляющее стандартный gdb-шный интерфейс, но почему-то среда при каждом дебаге это приложение перезапускает, в отличие от того же кейла (который вообще работает через свои дллки).
Поэтому старт отладки в Кейле начинается через секунду, в CooCox же – через секунд 20. Возможно, это можно как-нибудь исправить настройками, но я пока таких настроек не видел, поэтому буду благодарен, если кто подскажет.
Тем не менее, я все таки остановился на CooCox — если вас тоже не устраивает IDE от Keil или IAR, или вы не хотите пользоваться ломанным ПО и предпочитаете опенсорс – качайте не задумываясь.

CooCox и FreeRTOS

Об операционной системе FreeRTOS было сказано много, в частности, на хабре (вот, например, одна из статей: FreeRTOS: введение)
Я решил тоже приобщиться к этой технологии и расширить свой арсенал инструментов, тем более, что FreeRTOS не навязывает никакого HAL (Hardware Abstraction Layer, слой абстракции от оборудования, драйверы то бишь), и предоставляет только средства работы с задачами, синхронизацию и меж-процессное взаимодействие, поэтому во многих случаях будет очень удобна.
Рассмотрим поподробнее, что же нам необходимо, чтобы использовать FreeRTOS вместе с CooCox IDE на нашей плате Mini-STM32.
На самом деле, все очень просто. Архитектурное портирование (портирование кода, требуемого шедулером под архитектуру Cortex M3) уже давно выполнено, и нам нужно, по сути, просто правильно составить проект для CooCox.
Начинаем с того, что скачиваем исходники FreeRTOS с их официального сайта.
Вот прямая ссылка: http://sourceforge.net/projects/freertos/files/.
Тем временем создаем новый проект в CooCox, я назвал его FreeRTOS-Mini.

Программинг микроконтроллеров / STM32F1xx — Инструменты разработчика и FreeRTOS

Выбираем в визарде производителя ST, в списке чипов – чип, на котором построена отладочная плата, STM32F103VE.

Программинг микроконтроллеров / STM32F1xx — Инструменты разработчика и FreeRTOS

Программинг микроконтроллеров / STM32F1xx — Инструменты разработчика и FreeRTOS

После этого перед нами открывается уже упомянутое окошечко репозитория компонентов, представляющих собой части SPL.

Программинг микроконтроллеров / STM32F1xx — Инструменты разработчика и FreeRTOS

Выбираем там CMSIS Core и CMSIS Boot – это собственно ядро CMSIS и стартовый код, который производит настройку и дергает main()
Кстати, обратите внимание – в CooCox стартовый код написан целиком на C, ни одной асмовой строчки. Лежит в файле cmsis_bootstartupstartup_stm32f10x_hd.c – запомните этот путь, нам нужно будет там кое-что подправить. Заодно добавляем в проект модуль GPIO, чтобы было что поделать в тестовом таске FreeRTOS. Этот модуль автоматом потянет за собой зависимый RCC, отвечающий за настройку клоков.
Теперь возвращаемся к скаченным исходникам FreeRTOS. Для начала скопируем всю папку в папку с нашим проектом. После, начнем удалять лишнее. Итак, лично у меня под нож сразу пошло содержимое папки Demo – там лежат демо-приложения для разных контроллеров, вся папка вам не нужна в любом случае, но при желании можно оставить то, что относится к STM32F103. Единственное, что нам оттуда обязательно понадобится – файл настроек ядра FreeRTOS, который можно взять из любого подходящего проекта, допустим отсюда: DemoCORTEX_STM32F103_Primer_GCCFreeRTOSConfig.h
Его можно скопировать в любую папку инклудов, я лично положил в самый корень проекта, рядом с main.c
Далее, в папке sourceportable есть множество под-папок, где лежит код, рассчитанный на разные компиллеры и среды. Заходим в папку sourceportableGCCARM_CM3, копируем ее двумя уровнями выше, в sourceportable. Обращаем внимание на папку sourceportableMemMang – она нам тоже понадобится. Поэтому удаляем все, кроме sourceportableMemMang и свежескопированной sourceportableARM_CM3
После этого кликаем правой кнопкой в прожект эксплорере CooCox, нажимаем Add Linked Folder и добавляем нашу папку с подготовленными исходниками FreeRTOS. В итоге должно получиться вот такое дерево проекта:

Программинг микроконтроллеров / STM32F1xx — Инструменты разработчика и FreeRTOS

Теперь начинаем править файлы проекта. Начнем с настроек ядра. Оттуда нам нужно будет убрать только пару строчек, связанных со старым проектом – левый, ненужный нам инклуд.

/* Library includes. */ #include "stm32f10x_lib.h" 

Все остальное можно оставить без изменений, а можно прочитать статью о настройке ядра FreeRTOS и поменять опции по необходимости, этим мы сейчас заниматься не будем.
Теперь идем в стартап код (cmsis_bootstartupstartup_stm32f10x_hd.c) и делаем следующее: находим строки:

/*----------Function prototypes-----------------------------------------------*/   extern int main(void);           /*!< The entry point for the application.    */ extern void SystemInit(void);    /*!< Setup the microcontroller system(CMSIS) */ void Default_Reset_Handler(void);   /*!< Default reset handler                */ static void Default_Handler(void);  /*!< Default exception handler            */ 

У меня это строки 114-122, и добавляем после них такой код:

extern void xPortPendSVHandler( void ) __attribute__ (( naked )); extern void xPortSysTickHandler( void ); extern void vPortSVCHandler( void ) __attribute__ (( naked )); 

Это обработчики прерываний из ядра ОС, которые объявлены в файле port.c. Теперь нам нужно запихнуть их в вектор прерываний, который идет ниже (строки 129-209):

__attribute__ ((section(".isr_vector"))) void (* const g_pfnVectors[])(void) = {          /*----------Core Exceptions-------------------------------------------------*/   (void *)&pulStack[STACK_SIZE-1],     /*!< The initial stack pointer         */   Reset_Handler,                /*!< Reset Handler                            */   NMI_Handler,                  /*!< NMI Handler                              */   HardFault_Handler,            /*!< Hard Fault Handler                       */   MemManage_Handler,            /*!< MPU Fault Handler                        */   BusFault_Handler,             /*!< Bus Fault Handler                        */   UsageFault_Handler,           /*!< Usage Fault Handler                      */   0,0,0,0,                      /*!< Reserved                                 */   vPortSVCHandler,                  /*!< SVCall Handler                           */   DebugMon_Handler,             /*!< Debug Monitor Handler                    */   0,                            /*!< Reserved                                 */   xPortPendSVHandler,               /*!< PendSV Handler                           */   xPortSysTickHandler,              /*!< SysTick Handler                          */ 

Соответственно, меняем вектор так, как написано в вышеизложенном коде, заменив строки, отмеченные SVCall Handler, PendSV Handler, SysTick Handler на vPortSVCHandler, xPortPendSVHandler и xPortSysTickHandler соответственно.
После открываем в дереве проекта папку SourceMemMang, и выбираем ту реализацию мемори менеджмента, которая нам подходит. Подробнее об этом написано тут: FreeRTOS Memory Management.
Если коротко – файл номер 1 это упрощенная реализация с выделением памяти, но без освобождения, файл номер 2 – более продвинутая реализация, позволяющая освобождение памяти, и номер 3 – реализация, которая потребует от вас библиотеки с реализованными malloc и free. Я выбрал вторую реализацию, оставшиеся два файла исключаем из компиляции, кликнув правой кнопкой на имя файла в дереве проекта и выбрав пункт Exclude from build.
Осталось совсем чуть-чуть – открываем файл main.c, добавляем туда нужные нам инклуды от SPL:

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" 

Инклуды от FreeRTOS:

#include "FreeRTOS.h" #include "task.h" #include "queue.h" 

После объявляем функцию, которая будет вызвана до старта шедулера, в соответствии с рекомендациями в таком виде:

static void prvSetupHardware( void ); 

И функцию, которая будет исполнять роль нашего тестового таска, вот так:

static void prvLedBlink( void *pvParameters ); 

Реализация функций выглядит так:

void prvSetupHardware() { 	GPIO_InitTypeDef  GPIO_InitStructure; 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 	GPIO_Init(GPIOB, &GPIO_InitStructure); }  void prvLedBlink( void *pvParameters ) { 	GPIO_SetBits(GPIOB,GPIO_Pin_5); 	while(1); } 

В тестовых целях ничего полезного не написал, задача просто зажигает светодиод на плате.
Осталась сама функция main(), которая стартанет задачу и шедулер:

int main(void) { 	prvSetupHardware();  	xTaskCreate(prvLedBlink,(signed char*)"LED",configMINIMAL_STACK_SIZE, 			NULL, tskIDLE_PRIORITY + 1, NULL);  	/* Start the scheduler. */ 	vTaskStartScheduler();     while(1); } 

Вот в принципе и все. Осталось настроить дебаг – для этого жмем «Debug configuration», во вкладке Debugger выбираем наш программатор (J-Link) и порт JTAG.
Ставим галочку Run To Main, чтобы не барахтаться в стартап-коде, в строке GDBServer cmdline tool указываем путь к экзешнику, идущему с программатором (скачать можно с сайта Segger), у меня это C:SEGGERJLinkARM_V440bJLinkGDBServerCL.exe
После жмем Apply и Close.
Теперь компиллим наш проект и жмем на дебаг – если все получилось, после аплода и выполнения, должен загореться светодиод.

Заключение

Правильный выбор инструментов разработчика, безусловно, обеспечит наиболее быстрое и комфортное освоение новых технологий. Я надеюсь, что, осветив в данной статье отладочную плату Mini-STM32 и CooCox IDE, я помог разработчикам приглядеться к новому инструменту. Что касается операционной системы FreeRTOS – это бесспорно очень мощное средство, и, на мой взгляд, хорошая ступень, для перехода от программирования прошивок «в лоб» к использованию эмбеддед операционных систем.

Ссылки

Страничка eBay где можно купить отладочную плату
Официальный сайт CooCox
Официальный сайт FreeRTOS
Русский мануал по FreeRTOS

Автор: Ariman

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js