Современные микроконтроллеры имеют достаточно большую производительность и это дает многим программистом возможность думать в примерно следующем ключе: — «Ничего страшного, если 1-5% производительности уйдут на обслуживание операционной системы. Зато мой код будет легко отлаживаемый и явный!». Эти мысли подкрепляются большим количеством энергонезависимой (flash) памяти для хранения кода операционной системы и оперативной (RAM/SRAM) памяти для выделения под каждую задачу своего стека. Однако в большинстве случаев эта мысль ошибочна. И в данной статье я расскажу, почему.Читать полностью »
Рубрика «freertos» - 2
О злоупотреблении использования операционной системы в проектах под микроконтроллеры
2019-07-25 в 11:21, admin, рубрики: freertos, nvic, личный опыт, микроконтроллеры, микроконтроллеры stm, операционные системы, прерывания, программирование микроконтроллеров, эффективное использованиеСтатическое распределение объектов FreeRTOS
2019-07-07 в 13:18, admin, рубрики: c++, freertos, static allocation, программирование микроконтроллеров, Разработка под ArduinoПо умолчанию все объекты в системе FreeRTOS распределяются динамически — очереди, семафоры, таймеры, задачи (потоки), и мьютексы. Программист видит только «кучу» — область где динамически выделяется память по запросу программы или системы, а что там творится внутри – не ясно. Сколько еще осталось? Неизвестно. Не занимает ли что нибудь больше чем нужно? Кто его знает? Лично я предпочитаю решать вопросы организации памяти еще на этапе написания прошивки, не доводя до ошибок во время выполнения, когда память неожиданно закончилась.
Эта статья является логическим продолжением вчерашней про статическое распределение объектов в памяти микроконтроллера, только теперь применительно к объектам FreeRTOS. Сегодня мы научимся размещать объекты FreeRTOS статически, что позволит более четко понимать что происходит в оперативной памяти микроконтроллера, как именно расположены и сколько занимают наши объекты.
Но просто взять и начать размещать объекты FreeRTOS статически много ума не требуется — FreeRTOS начиная с версии 9.0 как раз предоставляет функции создания объектов размещенных статически. Такие функции имеют суффикс Static в названии и на эти функции имеется отличная документация с примерами. Мы же напишем удобные и красивые C++ обертки над функциями FreeRTOS, которые не только будут размещать объекты статически, но и скрывать все потроха, а также предоставлять более удобный интерфейс.
Статья рассчитана на начинающих программистов, но которые уже знакомы с основами FreeRTOS и примитивами синхронизации многопоточный программ. Поехали.
Читать полностью »
С++ обертка для «всех» Операционных Систем Реального Времени для CortexM4
2018-08-21 в 14:25, admin, рубрики: c++, C++14, embos, freertos, STM32F4, Программирование, программирование микроконтроллеров
Я уже рассказывал о том как можно использовать FreeRtos для проектов, написанных на С++ в статье STM32, C++ и FreeRTOS. Разработка с нуля. Часть 1. С тех пор прошло целых 3 года, я серьезно постарел, потерял кучу нейронных связей, поэтому решил встряхнуть стариной для того, чтобы эти связи восстановить и замахнуться на обертку для «любой» популярной ОСРВ. Это конечно шутка, я намеренно взял «всех» в кавычки, но в каждой шутке есть доля правды.
Читать полностью »
Отладка многопоточных программ на базе FreeRTOS
2018-04-05 в 10:46, admin, рубрики: c++, eclipse, freertos, memory management, openocd, отладка, программирование микроконтроллеров
Отладка многозадачных программ дело не простое, особенно если ты сталкиваешься с этим впервые. После того, как прошла радость от запуска первой задачи или первой демо программы, от бесконечно волнительного наблюдения за светодиодами, каждый из которых моргает в своей собственной задаче, наступает время, когда ты осознаешь, что довольно мало понимаешь (вообще не врубаешься) о том, что на самом деле происходит. Классика жанра: «Я выделил целых 3КБ операционной системе и запустил всего 3 задачи со стеком по 128Б, а на четвертую уже почему-то не хватает памяти» или «А сколько вообще стека я должен выделить задаче? Столько достаточно? А столько?». Многие решают данные задачи путем проб и ошибок, поэтому в этой статье я решила объединить большинство моментов, которые, в настоящее время, значительно упрощают мне жизнь и позволяют более осознанно отлаживать многопоточные программы на базе FreeRTOS.
Данная статья рассчитана, в первую очередь, на тех, кто только недавно начал осваивать FreeRTOS, однако вполне вероятно, что и хорошо знакомые с этой операционной системой читатели найдут для себя здесь что-то интересное. Кроме того, несмотря на то, что статья ориентирована на разработчиков встраиваемого ПО, прикладным программистам она тоже будет интересна, т.к. много слов будет сказано о FreeRTOS как таковой, безотносительно микроконтроллерной романтики.
В данной статье я расскажу о следующих моментах:
Как сделать context switch на STM32
2017-06-05 в 11:24, admin, рубрики: C, cortex-m, embox, freertos, Nuttx, open source, stm32, Блог компании Embox, микроконтроллеры, операционные системы, осрв, программирование микроконтроллеров, системное программирование
Добрый день!
Потоки… Переключение контекстов… Базовая сущность ОС. И конечно, при разработке библиотек и приложений мы всегда полагаемся на то, что реализация потоков безошибочна. Поэтому было неожиданно найти грубую ошибку в переключении потоков для STM32 на ОСРВ Embox, когда уже продолжительное время работали и сеть, и файловая система и многие сторонние библиотеки. И мы даже успели похвастаться о своих достижениях на хабре.
Я бы хотел рассказать про то, как мы делали переключение потоков для Cortex-M, и тестировали на STM32. Кроме того, постараюсь рассказать о том как это сделано в других ОС — NuttX и FreeRTOS.
Читать полностью »
Установка FreeRTOS для Stm32vlDiscovery
2016-12-14 в 10:24, admin, рубрики: diy или сделай сам, freertos, stm32vldiscoveryДоброго времени суток.
Разбираясь с программированием микроконтроллеров Stm32, решил попробовать установить (операционную систему реального времени) ОСРВ — FreeRTOS и столкнулся с рядом трудностей. В интернете есть множество статей, но внятной инструкции я не нашёл, поэтому всех заинтересовавшихся прошу под кат.
Читать полностью »
STM32: FreeRTOS и пьезокерамический излучатель
2016-12-08 в 6:00, admin, рубрики: buzzer, freertos, stm32, программирование микроконтроллеров
Керамический пьезоизлучатель (buzzer) — простая деталь, наравне со светодиодом требующая минимального набора ресурсов для управления и настолько же легко подключаемая к микроконтроллеру. Как и светодиоду с возможностью плавной регулировки яркости, от микроконтроллера ему требуется не более одного канала таймера и внешний вывод.
Много в интернете уроков «Подключаем пищалку к ардуино», только вот заканчиваются они проигрыванием «В траве сидел кузнечик» или озвучкой срабатывания RFID датчика. Наверное тем, кто занят этим профессионально и серьезно, не до ведения блогов и записи видеоуроков.
А ведь миниатюрный керамический динамик — шаг в сторону более дружелюбного интерфейса с человеком. Нажатия кнопок, касания сенсорной панели, реакция на различные события… Такая вот обратная связь в виде звукового отклика!
Под катом попробуем сделать с этим что нибудь, а именно напишем драйвер пьезодинамика и заставим его параллельно озвучивать несколько разных внешних событий.
Читать полностью »
Измерим гармонию — анализатор звукового спектра на STM32L4 Discovery
2016-03-08 в 19:46, admin, рубрики: diy или сделай сам, dsp, fft, freertos, ili9341, open source, stm32, гаджеты, Носимая электроника, Программирование, цифровая обработка сигналовВ предыдущей публикации мы подключали дешевый китайский LCD экран к плате STM32L4 Discovery. Теперь мы попробуем реализовать на этой комбинации что-то выходящее за рамки традиционного моргания светодиодом, а именно анализатор звукового спектра, который использует имеющийся на плате микрофон. Заодно я расскажу, как пользоваться операционной системой FreeRTOS, и зачем она нужна, а также почему в нотной октаве 12 нот, и чем 53 ноты лучше, чем 12.

STM32, C++ и FreeRTOS. Разработка с нуля. Часть 4 (Прерывания, UART и недоHART)
2015-08-02 в 19:51, admin, рубрики: diy или сделай сам, freertos, HART, stm32, программирование микроконтроллеров, С++Ведение
Попав в отпуске в город на Неве и посетив множество красивых мест, я все таки, вечерами за чашкой пива, разбирался с UARTом. Тем более, что я купил не плохие наушники Fisher FA011, к которым пришлось прикупить USB SOUND BLASTER X-FI HD и хотел послушать музыку.
Предыдущие статьи вначале переехали на Geektime потом я обратно их перегнал, даже и не знаю, куда теперь их деть :)
Но так на всякий случай они тут:
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 1
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 2 и
STM32, C++ и FreeRTOS. Разработка с нуля. Часть 3 (LCD и Экраны)
UART
После детального изучения микроконтроллера, мне казалось, что все просто. Настройка и тестовая посылка байта в порт прошла без задоринки, все работало как часы, и тут я решил использовать прерывания. Нужно было сделать так, чтобы обработчик прерывания был статическим методом класса. И IAR в руководстве на компилятор, так и писал:
Special function types can be used for static member functions. For example, in the
following example, the function handler is declared as an interrupt function:class Device { static __irq void handler(); };
Но вот незадача, для Cortex M такой способ не подходит и
On ARM Cortex-M, an interrupt service routine enters and returns in the same way as a
normal function, which means no special keywords are required. Thus, the keywords
__irq, __fiq, and __nested are not available when you compile for ARM Cortex-M.
These exception function names are defined in cstartup_M.c and cstartup_M.s.
They are referred to by the library exception vector code:
NMI_Handler
HardFault_Handler
MemManage_Handler
BusFault_Handler
…
The vector table is implemented as an array. It should always have the name
__vector_table,
Или по простому, ваш обработчик прерывания должен иметь такое же имя, какое он имеет в таблице векторов определенной в startup файле. Это делается с помощью специального ключевого слова — слабой ссылки __weak (в ассемблере PUBWEAK), которая означает, что данное определение будет использоваться до тех пора, пока не найдется хотя бы одно совпадающее по написанию без ключевого слова __week. Ну т.е., если вы определите функцию с точно таким же именем без этой директивы, то компилятро будет использовать это определение, а если не определите, то которое помечено __weak.
Понятное дело, что я не могу в файл startup_stm32l1xx_md.s или startup_stm32l1xx_md.с вставить С++ имя статического метода типа cUart::USART2_IRQHandler(), ассемблер его просто не поймет.
А просто «USART2_IRQHandler» не совпадает с определением «cUart::USART2_IRQHandler()».
Можно использовать extern «C» { void USART2_IRQHandler(void) {...}}, но это означает, что я тут буду делать встаки из Си, что мне совсем не надо, и вообще доступа из такой функции к атрибутам моего класса, например буферу — не будет, и надо будет городить кучу некрасивого кода :).
Поэтому, я решил пойти другим путем и создать файл startup_stm32l1xx_md.cpp. Поиск в интернете обнаружил, что точно такая же проблема была у некоторых людей
В общем идея заключается в следующем: Определяем startup_stm32l1xx_md.cpp в классы со статическими методами (которые и будут являться обработчиками прерываний), создаем таблицу __vector_table, где на каждом из векторов прерываний стоит указатель на на эти статические методы. Дальше делаем __weak определение каждого метода
И теперь когда в коде компилятор видет реализацию void cUart1::handler(), он не задумываясь берет её. Конечно же при этом ваши классы и методы должны называться точь в точь так, как они определены в startup_stm32l1xx_md.cpp.
Нужно еще не забыть про функции FreeRtos: vPortSVCHandler, xPortPendSVHandler, xPortSysTickHandler и поставить их на нужное прерывание и вуаля — все работает:
#pragma language = extended
#pragma segment = "CSTACK"
extern "C" void __iar_program_start( void );
extern "C" void vPortSVCHandler(void);
extern "C" void xPortPendSVHandler(void);
extern "C" void xPortSysTickHandler(void);
class cNMI
{
public:
static void handler(void);
};
class cHardFault
{
public:
static void handler(void);
};
class cMemManage
{
public:
static void handler(void);
};
class cBusFault
{
public:
static void handler(void);
};
class cUsageFault
{
public:
static void handler(void);
};
class cDebugMon
{
public:
static void handler(void);
};
class cWindowWatchdog
{
public:
static void handler(void);
};
class cPvd
{
public:
static void handler(void);
};
class cTamperTimeStamp
{
public:
static void handler(void);
};
class cRtcWakeup
{
public:
static void handler(void);
};
class cFlash
{
public:
static void handler(void);
};
class cRcc
{
public:
static void handler(void);
};
class cExti
{
public:
static void line0Handler(void);
static void line1Handler(void);
static void line2Handler(void);
static void line3Handler(void);
static void line4Handler(void);
static void line9Handler(void);
static void line15_10Handler(void);
};
class cDma
{
public:
static void channellHandler(void);
static void channel2Handler(void);
static void channel3Handler(void);
static void channel4Handler(void);
static void channel5Handler(void);
static void channel6Handler(void);
static void channel7Handler(void);
};
class cAdc
{
public:
static void handler(void);
};
class cDac
{
public:
static void handler(void);
};
class cUsb
{
public:
static void highPriorityHandler(void);
static void lowPriorityHandler(void);
static void fsWakeupHandler(void);
};
class cComp
{
public:
static void handler(void);
};
class cLcdDriver
{
public:
static void handler(void);
};
class cTim9
{
public:
static void handler(void);
};
class cTim2
{
public:
static void handler(void);
};
class cTim3
{
public:
static void handler(void);
};
class cTim4
{
public:
static void handler(void);
};
class cTim10
{
public:
static void handler(void);
};
class cTim6
{
public:
static void handler(void);
};
class cTim7
{
public:
static void handler(void);
};
class cTim11
{
public:
static void handler(void);
};
class cI2C1
{
public:
static void eventHandler(void);
static void errorHandler(void);
};
class cI2C2
{
public:
static void eventHandler(void);
static void errorHandler(void);
};
class cSpi1
{
public:
static void handler(void);
};
class cSpi2
{
public:
static void handler(void);
};
class cUart1
{
public:
static void handler(void);
};
class cUart2
{
public:
static void handler(void);
};
class cUart3
{
public:
static void handler(void);
};
class cRtcAlarm
{
public:
static void handler(void);
};
typedef void( *intfunc )( void );
typedef union { intfunc __fun; void * __ptr; } intvec_elem;
// The vector table is normally located at address 0.
// When debugging in RAM, it can be located in RAM, aligned to at least 2^6.
// If you need to define interrupt service routines,
// make a copy of this file and include it in your project.
// The name "__vector_table" has special meaning for C-SPY:
// it is where the SP start value is found, and the NVIC vector
// table register (VTOR) is initialized to this address if != 0.
#pragma location = ".intvec"
extern "C" const intvec_elem __vector_table[] =
{
{ .__ptr = __sfe( "CSTACK" ) },
__iar_program_start,
cNMI::handler,
cHardFault::handler,
cMemManage::handler,
cBusFault::handler,
cUsageFault::handler,
0,
0,
0,
0,
vPortSVCHandler, //функции freeRTOS не трогать!
cDebugMon::handler,
0,
xPortPendSVHandler, //функции freeRTOS не трогать!
xPortSysTickHandler, //функции freeRTOS не трогать!
//External Interrupts
cWindowWatchdog::handler, //Window Watchdog
cPvd::handler, //PVD through EXTI Line detect
cTamperTimeStamp::handler, //Tamper and Time Stamp
cRtcWakeup::handler, //RTC Wakeup
cFlash::handler, //FLASH
cRcc::handler, //RCC
cExti::line0Handler, //EXTI Line 0
cExti::line1Handler, //EXTI Line 1
cExti::line2Handler, //EXTI Line 2
cExti::line3Handler, //EXTI Line 3
cExti::line4Handler, //EXTI Line 4
cDma::channellHandler, //DMA1 Channel 1
cDma::channel2Handler, //DMA1 Channel 2
cDma::channel3Handler, //DMA1 Channel 3
cDma::channel4Handler, //DMA1 Channel 4
cDma::channel5Handler, //DMA1 Channel 5
cDma::channel6Handler, //DMA1 Channel 6
cDma::channel7Handler, //DMA1 Channel 7
cAdc::handler, //ADC1
cUsb::highPriorityHandler, //USB High Priority
cUsb::lowPriorityHandler, //USB Low Priority
cDac::handler, //DAC
cComp::handler, //COMP through EXTI Line
cExti::line9Handler, //EXTI Line 9..5
cLcdDriver::handler, //LCD
cTim9::handler, //TIM9
cTim10::handler, //TIM10
cTim11::handler, //TIM11
cTim2::handler, //TIM2
cTim3::handler, //TIM3
cTim4::handler, //TIM4
cI2C1::eventHandler, //I2C1 Event
cI2C1::errorHandler, //I2C1 Error
cI2C2::eventHandler, //I2C2 Event
cI2C2::errorHandler, //I2C2 Error
cSpi1::handler, //SPI1
cSpi2::handler, //SPI2
cUart1::handler, //USART1
cUart2::handler, //USART2
cUart3::handler, //USART3
cExti::line15_10Handler, //EXTI Line 15..10
cRtcAlarm::handler, //RTC Alarm through EXTI Line
cUsb::fsWakeupHandler, //USB FS Wakeup from suspend
cTim6::handler, //TIM6
cTim7::handler //TIM7
};
__weak void cNMI::handler() { while (1) {} }
__weak void cHardFault::handler() { while (1) {} }
__weak void cMemManage::handler() { while (1) {} }
__weak void cBusFault::handler() { while (1) {} }
__weak void cUsageFault::handler() { while (1) {} }
__weak void cDebugMon::handler() { while (1) {} }
__weak void cWindowWatchdog::handler() { while (1) {} }
__weak void cPvd::handler() { while (1) {} }
__weak void cTamperTimeStamp::handler() { while (1) {} }
__weak void cRtcWakeup::handler() { while (1) {} }
__weak void cFlash::handler() { while (1) {} }
__weak void cRcc::handler() { while (1) {} }
__weak void cExti::line0Handler() { while (1) {} }
__weak void cExti::line1Handler() { while (1) {} }
__weak void cExti::line2Handler() { while (1) {} }
__weak void cExti::line3Handler() { while (1) {} }
__weak void cExti::line4Handler() { while (1) {} }
__weak void cExti::line9Handler() { while (1) {} }
__weak void cExti::line15_10Handler() { while (1) {} }
__weak void cDma::channellHandler() { while (1) {} }
__weak void cDma::channel2Handler() { while (1) {} }
__weak void cDma::channel3Handler() { while (1) {} }
__weak void cDma::channel4Handler() { while (1) {} }
__weak void cDma::channel5Handler() { while (1) {} }
__weak void cDma::channel6Handler() { while (1) {} }
__weak void cDma::channel7Handler() { while (1) {} }
__weak void cAdc::handler() { while (1) {} }
__weak void cUsb::fsWakeupHandler() { while (1) {} }
__weak void cUsb::highPriorityHandler() { while (1) {} }
__weak void cUsb::lowPriorityHandler() { while (1) {} }
__weak void cDac::handler() { while (1) {} }
__weak void cComp::handler() { while (1) {} }
__weak void cLcdDriver::handler() { while (1) {} }
__weak void cTim2::handler() { while (1) {} }
__weak void cTim3::handler() { while (1) {} }
__weak void cTim4::handler() { while (1) {} }
__weak void cTim6::handler() { while (1) {} }
__weak void cTim7::handler() { while (1) {} }
__weak void cTim9::handler() { while (1) {} }
__weak void cTim10::handler() { while (1) {} }
__weak void cTim11::handler() { while (1) {} }
__weak void cI2C1::errorHandler() { while (1) {} }
__weak void cI2C1::eventHandler() { while (1) {} }
__weak void cI2C2::errorHandler() { while (1) {} }
__weak void cI2C2::eventHandler() { while (1) {} }
__weak void cSpi1::handler() { while (1) {} }
__weak void cSpi2::handler() { while (1) {} }
__weak void cUart1::handler() { while (1) {} }
__weak void cUart2::handler() { while (1) {} }
__weak void cUart3::handler() { while (1) {} }
__weak void cRtcAlarm::handler() { while (1) {} }
extern "C" void __cmain( void );
extern "C" __weak void __iar_init_core( void );
extern "C" __weak void __iar_init_vfp( void );
#pragma required=__vector_table
void __iar_program_start( void )
{
__iar_init_core();
__iar_init_vfp();
__cmain();
}


