STM32F1xx — Инструменты разработчика и FreeRTOS

    Добрый день, уважаемые хабровчане. В своих прошлых статьях (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.



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





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



    Выбираем там CMSIS Core и CMSIS Boot – это собственно ядро CMSIS и стартовый код, который производит настройку и дергает main()
    Кстати, обратите внимание – в CooCox стартовый код написан целиком на C, ни одной асмовой строчки. Лежит в файле cmsis_boot\startup\startup_stm32f10x_hd.c – запомните этот путь, нам нужно будет там кое-что подправить. Заодно добавляем в проект модуль GPIO, чтобы было что поделать в тестовом таске FreeRTOS. Этот модуль автоматом потянет за собой зависимый RCC, отвечающий за настройку клоков.
    Теперь возвращаемся к скаченным исходникам FreeRTOS. Для начала скопируем всю папку в папку с нашим проектом. После, начнем удалять лишнее. Итак, лично у меня под нож сразу пошло содержимое папки Demo – там лежат демо-приложения для разных контроллеров, вся папка вам не нужна в любом случае, но при желании можно оставить то, что относится к STM32F103. Единственное, что нам оттуда обязательно понадобится – файл настроек ядра FreeRTOS, который можно взять из любого подходящего проекта, допустим отсюда: Demo\CORTEX_STM32F103_Primer_GCC\FreeRTOSConfig.h
    Его можно скопировать в любую папку инклудов, я лично положил в самый корень проекта, рядом с main.c
    Далее, в папке source\portable есть множество под-папок, где лежит код, рассчитанный на разные компиллеры и среды. Заходим в папку source\portable\GCC\ARM_CM3, копируем ее двумя уровнями выше, в source\portable. Обращаем внимание на папку source\portable\MemMang – она нам тоже понадобится. Поэтому удаляем все, кроме source\portable\MemMang и свежескопированной source\portable\ARM_CM3
    После этого кликаем правой кнопкой в прожект эксплорере CooCox, нажимаем Add Linked Folder и добавляем нашу папку с подготовленными исходниками FreeRTOS. В итоге должно получиться вот такое дерево проекта:



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

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

    Все остальное можно оставить без изменений, а можно прочитать статью о настройке ядра FreeRTOS и поменять опции по необходимости, этим мы сейчас заниматься не будем.
    Теперь идем в стартап код (cmsis_boot\startup\startup_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 соответственно.

    UPD:
    В комментариях мне подсказали более изящный вариант, чем ковыряние стартового файла. Достаточно просто переопределить обработчики следующими дефайнами:

    #define vPortSVCHandler SVC_Handler
    #define xPortPendSVHandler PendSV_Handler
    #define vPortSVCHandler SVC_Handler
    #define xPortSysTickHandler SysTick_Handler
    


    После открываем в дереве проекта папку Source\MemMang, и выбираем ту реализацию мемори менеджмента, которая нам подходит. Подробнее об этом написано тут: 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:\SEGGER\JLinkARM_V440b\JLinkGDBServerCL.exe
    После жмем Apply и Close.
    Теперь компиллим наш проект и жмем на дебаг – если все получилось, после аплода и выполнения, должен загореться светодиод.

    Заключение



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

    Ссылки


    Страничка eBay где можно купить отладочную плату
    Официальный сайт CooCox
    Официальный сайт FreeRTOS
    Русский мануал по FreeRTOS
    • +25
    • 81.8k
    • 9
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 9

      0
      Есть пара замечаний:

      1) CoIDE поддерживает только STM32F10x, с остальными STM32 придётся всё делать вручную (никаких визардов). В этом свете может помочь моя статья по настройке Eclipse. Да, сложновато для начала, но зато потом можно работать с STM32F10x, STM32F2xx, STM32F4xx и STM32L1xx. У меня описано только использование утилиты stlink, но я недавно осилил OpenOCD, и теперь вообще ни на что не жалуюсь.
      2) В сообществе Easyelectronics есть подобного рода статья, и в ней автор советует вместо ковыряния startup-файла забить #define'ы в FreeRTOSConfig.h (ему тоже подсказали в комментах :)

      #define vPortSVCHandler SVC_Handler
      #define xPortPendSVHandler PendSV_Handler
      #define vPortSVCHandler SVC_Handler
      #define xPortSysTickHandler SysTick_Handler

      Я считаю, так правильнее.
        0
        Да, весьма разумно с дефайнами.
        Почитал вашу статью, рад что вы пришли к такому же мнению что и я по поводу Кейла)
        Я про
        >А в популярной, не знаю, почему, среде Keil ещё и редактор кода, судя по его виду и удобству, написан во времена Windows 95.

        Не понимаю политику компании, давно бы уже могли поручить своим девелоперам uVision взяться за эклипс.
          0
          Есть тонкость. UVision использует линцензированый компилятор от ARM. А ARM выпускается свою среду со своим компилятором на базе eclipse. Чувствуете? UVision попала в неприятную ситуацию, когда выпустить среду на основе eclipse и старого компилятора им банально не дадут (отзовут лицензию на компилятор), т.к. это будет прямая конкуренция с авторами компилятора (ARM). А переходить на GCC тоже нельзя, т.к. теряет обратная совместимость с предыдущими версиями.
        0
        Особенно странно это смотрится сейчас, когда почти все IDE для разработки под ARM основаны на Eclipse и «искакопки» предлагают отличный редактор кода с поддержкой рефакторинга. А если брать голый Eclipse — так в Indigo вообще рефакторинг внесли пунктом меню по Ctrl+1 (Quick fix), а идентификатор правится прямо в окне редактора. Впрочем, за счёт широкой поддержки камней и прикольного отладчика команда Keil ещё будет держаться на плаву несколько лет.
          0
          Угу, ну тогда покажите, как в этом eclipse сделать сброс процессора без перезапуска отладчика, как выводить служебные регистры процессора и как выводить значения в шестнадцатеричном виде.
            0
            1) Нажать кнопку Reset на плате. У меня отлично работает на STM32VLDISCOVERY, STM32L-Discovery и STM32F4DISCOVERY.
            2) Про какие это регистры вы говорите? Приведите пример. Если вы про регистры типа r0-r12, lr и т.п., то они есть в окне Registers (я использую GNU ARM Eclisple plugin), а если про периферию — это можно в Expressions закинуть, там же и менять можно.
            3) Вот это не знаю. Но мне пока не было нужно ни разу, потому и не искал.
              0
              1) Reset на плате прерывает отладку через JTAG в некоторых случаях.
              2) CONTROL, MSP, PSP?
                0
                Да, кстати, первым пунктом я намекал на наследственный баг c monitor reset. Который присутствует с момента рождения eclipse и в ближайшее время исправляться не собирается
                А третьим пунктом на то, что eclipse не умеет сохранять настройки отображения переменных. Что тоже наследственный баг. А ещё есть баг с созданием hex-файла. И недоработок и багов там… И вместо их исправления eclipse обрастает очередными рюшками и багами.
            0
            Скажите, а нет ли аналогичной игрушки с ethernet'ом?
            Нашел пока такое, но это не совсем то.

            Only users with full accounts can post comments. Log in, please.