Знакомство с RTOS OSA на STM8
Во время поисков RTOS для интересующих меня архитектур (об этом была предыдущая статья) совершенно случайно наткнулся на кооперативную RTOS OSA. Изначально она была написана для PIC'ов, но сейчас в комплекте есть порты для AVR (компиляторы AVR-GCC, IAR, CodeVision) и STM8 (компиляторы Cosmic, Raisonance, IAR).Проект заинтересовал хорошей документацией, русскоязычным автором, приличной функциональностью и отсутствием информации о применении ОС на AVR и STM8. Из-за этого идея попробовать RTOS с вытесняющей многозадачностью была временно отложена :).
Для того, чтобы разобраться с OSA собрал небольшой пример из трёх задач. Две из них мигают двумя светодиодами с разной частотой, третья — попискивает пьезопищалкой с помощью встроенного в STM8 контроллера.
Железо
Эксперименты проводились на моей отладочной плате для STM8, но пример заработает и на STM8S-Discovery если указать другой контроллер в настройках проекта. Светодиоды зажигаются нулём на PD0 и PD2 (эти порты на STM8S105 в корпусе LQFP32 отмечены как HS), пьезопищалка подключена к PD4 через транзистор BC337. Вывод контроллера пишалки BEEP на PD4 включается с путём изменения option bytes с помощью программатора STVP (бит AFR7 в OPT2), эту процедуру достаточно выполнить один раз. Кстати, выставить этот бит можно и программно. В качестве генератора тактовой частоты использовался внешний кварц на 16 МГц (HSE в терминах STM8).
Программная часть
Понадобится следующее:
- Среда STVD
- Компилятор Cosmic (я использовал бесплатную версию с ограничением до 32Кб)
- STVP для изменения option bytes
- Дистрибутив OSA RTOS
- stm8s.h из STM8S/A Standard Peripherals Library
- macros_stm8.h для простых операций с портами от ZiB (Денис, привет! :))
Драйверы периферии из STM8S/A Standard Peripherals Library напрямую не использовались, но в них был подсмотрена работа с BEEP'ром.
Подготовительный этап: настройки проекта и option bytes
Необходимое для stm8s.h макроопределение типа контроллера я указал в настройках проекта:

Для подключения контроллера пищалки на PD4 нужно включить в option byte OPT2 бит AFR7:

«ОС — это такой полосатый мух»
После скачивания дистрибутива OSA RTOS и настройки проекта по инструкции можно приступать к конфигурированию ОС c помощью файла OSAcfg.h:
#ifndef _OSACFG_H
#define _OSACFG_H
//------------------------------------------------------------------------------
// SYSTEM
//------------------------------------------------------------------------------
#define OS_TASKS 3 // Максимальное количество активных задач
#define OS_DISABLE_PRIORITY // Вылюкчаем приоритеты задач
//------------------------------------------------------------------------------
// ENABLE CONSTANTS
//------------------------------------------------------------------------------
#define OS_ENABLE_TTIMERS // Включаем службу таймеров (необходима для работы функций временных задержек)
#define OS_USE_INLINE_TIMER // Встраиваем функцию обработки таймеров прямо в обработчик прерываний как inline
//------------------------------------------------------------------------------
// BSEMS
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// STIMERS
//------------------------------------------------------------------------------
#endif
Системный таймер
Для работы функций задержек нужно выделить для RTOS отдельный таймер, для этого был исползован самый простой (basic timer) 8 битный таймер TIM4, за минимальный тик была принята 1мс. RTOS OSA не накладывает никаких ограничений ни на способ реализации вызова функций системного таймера, ни на длительность системного тика.
Инициализация таймера:
/*
TIM4 (basic timer) 16Mhz / 128 / 124 = 1kHz = 1ms interrupt
From \Project\STM8S_StdPeriph_Examples\TIM4\TIM4_TimeBase\main.c
- TIM4CLK is set to 16 MHz, the TIM4 Prescaler is equal to 128 so the TIM1 counter
clock used is 16 MHz / 128 = 125 000 Hz
- With 125 000 Hz we can generate time base:
max time base is 2.048 ms if TIM4_PERIOD = 255 --> (255 + 1) / 125000 = 2.048 ms
min time base is 0.016 ms if TIM4_PERIOD = 1 --> ( 1 + 1) / 125000 = 0.016 ms
- In this example we need to generate a time base equal to 1 ms
so TIM4_PERIOD = (0.001 * 125000 - 1) = 124
*/
TIM4->PSCR = TIM4_PRESCALER_128; // Prescaler
TIM4->ARR = 124; // Auto-reload value
TIM4->IER |= TIM4_IER_UIE; // Enable interrupt
TIM4->CR1 |= TIM4_CR1_CEN; // Enable timer
В обработчике прерывания по переполнению таймера был помещён вызов службы таймеров (в терминах OSA):
/*
Timer overflow handler
*/
INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
{
TIM4->SR1 = (uint8_t)~TIM4_SR1_UIF;
OS_Timer();
}
Обработчик описан в файле main.c, в стандартном для Cosmic'а файле c описанием обработчиков прерываний stm8_interrupt_vector.c эта функция описана так:
/*
Interrupts defined in main.c
*/
extern void INTERRUPT TIM4_UPD_OVF_IRQHandler(void);
Задачи
Задачи в OSA — это обычные функции. В этом примере из всего богатства возможностей RTOS по управлению ходом выполнения программы и «межпроцессорного» взаимодействия (бинарные семафоры, счётные семафоры, флаги и т.п.) используется только функция задержки OS_Delay(). В качестве параметра этой функции передаётся количество «тиков» системного таймера, в течении которых нужно подождать. Во время ожидания диспетчер ОС обслуживает другие задачи.
/*
Задачи
*/
void Task_T1(void)
{
for (;;)
{
OS_Delay(250);
Pin_Inv(LED1);
}
}
void Task_T2(void)
{
for (;;)
{
OS_Delay(1000);
Pin_Inv(LED2);
}
}
void Task_T3(void)
{
for (;;)
{
BEEP->CSR &= ~BEEP_CSR_BEEPSEL;
BEEP->CSR |= BEEP_FREQUENCY_2KHZ;
BEEP->CSR |= BEEP_CSR_BEEPEN;
OS_Delay(100);
BEEP->CSR &= ~BEEP_CSR_BEEPEN;
OS_Delay(250);
BEEP->CSR &= ~BEEP_CSR_BEEPSEL;
BEEP->CSR |= BEEP_FREQUENCY_1KHZ;
BEEP->CSR |= BEEP_CSR_BEEPEN;
OS_Delay(100);
BEEP->CSR &= ~BEEP_CSR_BEEPEN;
OS_Delay(2000);
}
}
Основная программа
Внутри функции main() кода остаётся совсем немного:
/*
Основная программа
*/
void main(void)
{
init(); // Инициализируем периферию
OS_Init(); // Инициализируем RTOS
OS_Task_Create(0, Task_T1); // Стартуем задачи (приоритеты отключены, поэтому первый параметр может быть любым)
OS_Task_Create(0, Task_T2);
OS_Task_Create(0, Task_T3);
OS_EI(); // Включаем прерывания
OS_Run(); // Запускаем шедулер
/*
Пустой цикл не нужен, всем занимается RTOS
for (;;) {}
*/
}
Сколько вешать в байтах?
При компиляции в Release-конфигурации map-файл выглядит так:
--------
Segments
--------
start 00008080 end 00008080 length 0 segment .const
start 00008083 end 000083ce length 843 segment .text
start 00004000 end 00004000 length 0 segment .eeprom
start 00000000 end 00000000 length 0 segment .bsct
start 00000000 end 00000023 length 35 segment .ubsct
start 00000023 end 00000023 length 0 segment .bit
start 00000023 end 00000023 length 0 segment .share
start 00000100 end 00000100 length 0 segment .data
start 00000100 end 00000100 length 0 segment .bss
start 00000000 end 00000270 length 624 segment .info.
start 00008000 end 00008080 length 128 segment .const
start 00008080 end 00008083 length 3 segment .init
Минимальный пример занял около 1Кб флеш-памяти (см. шпаргалку-бонус по секциям ниже).
Полный исходный код main.c:
/*
****************************************************************************
Тип контроллера для stm8s.h (например STM8S105), указывается
в директивах препроцессора в свойствах проекта
Вывод пишалки BEEP на PD4 включается с путём изменения option bytes
с помощью STVP (AFR7 в OPT2)
****************************************************************************
Bonus: как определить размер прошивки по .map-файлу Cosmic'a:
> .text, .const and .vector are ROM
> .bsct, .ubsct, .data and .bss are all RAM
> .info. and .debug are symbol tables that do not use target resources/memory.
*/
#include <iostm8s105.h> // Файл определений периферии от компилятора COSMIC, Px_ODR, Px_CR1 определены в этом файле
#include "macros_stm8.h" // Макросы для работы с портами
#include "stm8s.h" // Файл от Std. Periph. Library с определениями регистров периферии, целых типов и т.п.
#include "osa.h" // OSA RTOS
/*
Этих значений нет ни в одном заголовочном файле, взяты из Standard Peripherial Library
*/
#define CLK_SWR_HSE 0xB4
#define TIM4_PRESCALER_128 ((uint8_t)0x07)
#define BEEP_CALIBRATION_DEFAULT ((uint8_t)0x0B)
#define BEEP_FREQUENCY_1KHZ (uint8_t)0x00 /*!< Beep signal output frequency equals to 1 KHz */
#define BEEP_FREQUENCY_2KHZ (uint8_t)0x40 /*!< Beep signal output frequency equals to 2 KHz */
#define BEEP_FREQUENCY_4KHZ (uint8_t)0x80 /*!< Beep signal output frequency equals to 4 KHz */
/*
Светодиоды
*/
#define LED1 D, 0, Low, NoPullUp, NoOpenDrain, NoSpeedLimit, NoIT
#define LED2 D, 2, Low, NoPullUp, NoOpenDrain, NoSpeedLimit, NoIT
/*
Timer overflow handler
*/
INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
{
TIM4->SR1 = (uint8_t)~TIM4_SR1_UIF;
OS_Timer();
}
/*
Инициализация периферии
*/
@inline void init(void)
{
/*
Переключаемся на внешний кварц (HSE)
*/
CLK->CKDIVR = 0; // После этого начинаем работать от встроенного генератора на 16 МГц
CLK->SWCR |= CLK_SWCR_SWEN; // Начинаем переключение
CLK->SWR = CLK_SWR_HSE; // Инициируем переключение на HSE
// Никого не ждём, переключится, как только сможет
/*
TIM4 (basic timer) 16Mhz / 128 / 124 = 1kHz = 1ms interrupt
From \Project\STM8S_StdPeriph_Examples\TIM4\TIM4_TimeBase\main.c
- TIM4CLK is set to 16 MHz, the TIM4 Prescaler is equal to 128 so the TIM1 counter
clock used is 16 MHz / 128 = 125 000 Hz
- With 125 000 Hz we can generate time base:
max time base is 2.048 ms if TIM4_PERIOD = 255 --> (255 + 1) / 125000 = 2.048 ms
min time base is 0.016 ms if TIM4_PERIOD = 1 --> ( 1 + 1) / 125000 = 0.016 ms
- In this example we need to generate a time base equal to 1 ms
so TIM4_PERIOD = (0.001 * 125000 - 1) = 124
*/
TIM4->PSCR = TIM4_PRESCALER_128; // Prescaler
TIM4->ARR = 124; // Auto-reload value
TIM4->IER |= TIM4_IER_UIE; // Enable interrupt
TIM4->CR1 |= TIM4_CR1_CEN; // Enable timer
/*
Светодиоды
*/
Pin_Out(LED1);
Pin_Out(LED2);
/*
Пищалка
*/
BEEP->CSR &= ~BEEP_CSR_BEEPDIV; /* Clear bits */
BEEP->CSR |= BEEP_CALIBRATION_DEFAULT; /* Set a default calibration value if no calibration is done */
}
/*
Задачи
*/
void Task_T1(void)
{
for (;;)
{
OS_Delay(250);
Pin_Inv(LED1);
}
}
void Task_T2(void)
{
for (;;)
{
OS_Delay(1000);
Pin_Inv(LED2);
}
}
void Task_T3(void)
{
for (;;)
{
BEEP->CSR &= ~BEEP_CSR_BEEPSEL;
BEEP->CSR |= BEEP_FREQUENCY_2KHZ;
BEEP->CSR |= BEEP_CSR_BEEPEN;
OS_Delay(100);
BEEP->CSR &= ~BEEP_CSR_BEEPEN;
OS_Delay(250);
BEEP->CSR &= ~BEEP_CSR_BEEPSEL;
BEEP->CSR |= BEEP_FREQUENCY_1KHZ;
BEEP->CSR |= BEEP_CSR_BEEPEN;
OS_Delay(100);
BEEP->CSR &= ~BEEP_CSR_BEEPEN;
OS_Delay(2000);
}
}
/*
Основная программа
*/
void main(void)
{
init(); // Инициализируем периферию
OS_Init(); // Инициализируем RTOS
OS_Task_Create(0, Task_T1); // Стартуем задачи (приоритеты отключены, поэтому первый параметр может быть любым)
OS_Task_Create(0, Task_T2);
OS_Task_Create(0, Task_T3);
OS_EI(); // Включаем прерывания
OS_Run(); // Запускаем шедулер
/*
Пустой цикл не нужен, всем занимается RTOS
for (;;) {}
*/
}
Полный исходный код (включая дистрибутив OSA RTOS с документацией) прикреплён к посту.
- +8
- 04 июля 2011, 23:56
- artvolk
- 1
Файлы в топике:
osa_hello-world.zip
а вот мне интересно, почему никто не использует периферийные драйвера от STM. Есть же файл stm8ххх_gpio.h, где описаны все флаги и функции для работы с портами. Я вот тоже ковыряюсь сейчас с stm8, и никак не могу определиться, то ли использовать этот периферийный драйвер (что, собственно, логично, так как он был написан разработчиками мк для облегчения написания программ под него), или пойти по пути истинного джедайства и писать свою библиотеку с блекджеком и шлюхами, чтением даташитов и заучиванием регистров.
Для GPIO мне нравятся макросы (для AVR эти: snippets.crisp-studio.com/view/126/makrosy-na-c-dlya-raboty-s-gpio-mikrokontrollerov-avr, для STM8 в статье ссылочка). Баланс между удобством и размером кода (по сути, код после раскрытия макросов соответствует написанному вручную). В stm8ххх_gpio.h на каждую смену состояния ножки приходится дёргать функцию, что само по себе накладные расходы. Насколько я понимаю логику разработчиков контроллеров регистры как раз и нужны для того, чтобы дёргать ножками было быстрее и проще :)
Остальные функции периферийных драйверов я иногда использую, иногда — подсматриваю регистры. В принципе, работа с любой периферией (кроме GPIO) рано или поздно заворачивается в подобие библиотеки, поэтому использование её для этих случаев вполне оправдано :)
Остальные функции периферийных драйверов я иногда использую, иногда — подсматриваю регистры. В принципе, работа с любой периферией (кроме GPIO) рано или поздно заворачивается в подобие библиотеки, поэтому использование её для этих случаев вполне оправдано :)
Сег попробую на stm8s101f3 :)
Скачал проект, скомпилилось без проблем. Только добавь ещё ключик +split, тогда неиспользуемые функции не попадут в выходной файл: и станет
.text: hilo code, at address 0x8083
663 data bytes (0x0297)
Скачал проект, скомпилилось без проблем. Только добавь ещё ключик +split, тогда неиспользуемые функции не попадут в выходной файл: и станет
.text: hilo code, at address 0x8083
663 data bytes (0x0297)
Ты этот проект собирал?
Быстрые вопросы:
— Сколько не бился с STVD он мне всё равно абсолютные пути прописывал в настройках компилятора, у тебя проект завёлся без изменения путей?
— +split это ключик линкера? Не знаешь, почему STVD его не добавляет по дефолту?
— А что такое STM8S101? Я только STM8S103 видел (и прикупил себе в SO-8), а это что за серия? Ещё более младшая, чем STM8S103? Новая?
Быстрые вопросы:
— Сколько не бился с STVD он мне всё равно абсолютные пути прописывал в настройках компилятора, у тебя проект завёлся без изменения путей?
— +split это ключик линкера? Не знаешь, почему STVD его не добавляет по дефолту?
— А что такое STM8S101? Я только STM8S103 видел (и прикупил себе в SO-8), а это что за серия? Ещё более младшая, чем STM8S103? Новая?
блин с лькой спутал, конечно же STM8S103F3 :)
создал
\2011\2011-07-05-stm8-rtos-osa-demo\Прошивки\
распаковал, открыл, собрал, все ОК ни чего не менял
потом добавил ключик и ещё раз собрал, все.
пока не когда, дома гляну.
Почему не добавляет не знаю, не интересовался так как не пользуюсь средой.
— а ты купил в SO-8, они вроде только в SO-20?
создал
\2011\2011-07-05-stm8-rtos-osa-demo\Прошивки\
распаковал, открыл, собрал, все ОК ни чего не менял
потом добавил ключик и ещё раз собрал, все.
пока не когда, дома гляну.
Почему не добавляет не знаю, не интересовался так как не пользуюсь средой.
— а ты купил в SO-8, они вроде только в SO-20?
В этот раз я спутал — конечно SO-20, наверное, подсознательно мечтаю о STM8 в таком мелком корпусе :)
Хочу сделать отладочную платку на таком (с 8Кб), всё останавливает отсутствие такой детальки в Eagle, придётся свою делать…
а я совсем простецкую накидал, только мк и контакты на каждый вывод. за отладочную не сойдет. Можно скооперироваться по этому поводу, если хочешь мыльни мне. (меня знакомый попросил собрать www.getchip.net/posts/063-12-ti-kanalnyjj-generator-ehffektov-na-attiny2313/, так как ему не срочно, то я хочу переделать на stm-ке + добавить управление с ик-пульта)
Знающие люди подсказали, что Eagle есть ULP-скрипт для простого создания новых деталек из имеющихся корпусов. Корпус SO-20 в Eagle есть, нужно только выводы поназначать.
Я хотел сделать что-то наподобие моей платы для TQFP-32:
we.easyelectronics.ru/STM8/otladochnaya-plata-dlya-kontrollerov-stm8s-v-korpuse-lqfp32.html
Только хочу обойтись без перемычек по top. :)
Я хотел сделать что-то наподобие моей платы для TQFP-32:
we.easyelectronics.ru/STM8/otladochnaya-plata-dlya-kontrollerov-stm8s-v-korpuse-lqfp32.html
Только хочу обойтись без перемычек по top. :)
Я схалявил, не делал компонент: we.easyelectronics.ru/STM8/mini-plata-dlya-stm8103-v-korpuse-so-20-iz-perehodnika-so-28-dip-28.html
:)
:)
Чёт меня тож заитересовали ОС для МК, попробовал эту осу на AVR, ох… нно, аж плясать охото. пропёрло не по детски, думаю потом под STM32 и FreeRTOS с вытесняющим диспетчером протащится.

- Ageofenigma
- 14 июля 2011, 16:24
- ↓
Комментарии (30)
RSS свернуть / развернуть