Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: FreeRTOS - минимальное время тика?
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > FreeRTOS
Страницы: 1, 2, 3
jcxz
Цитата(ViKo @ May 29 2018, 14:04) *
1600

А если подумать? rolleyes.gif
Forger
Любого ждет анафема, кто замечен за подобными злодеяниями - изменение глобальных объектов в фоне задачи и в прерываниях.
Допустимо, если будет только чтение таких объектов и оно атомарно (8...32-битное слово).
Как минимум непредсказуемые баги будут на самом ровном месте, а симптомы ни разу не однозначные. Поэтому найти такие баги крайне сложно!

Вот наглядный пример в Вашем коде:

процедура записи нового значения (между знаком "=") прервана прерыванием
Код
//Task2
...
        IrState.ctim10ms = IrState.bRxTx[pValue->nComm][0];
...


в котором будет выполнено это условие?

Код
void TIM10_IRQHandler(void)
{
....
        if(IrState.ctim10ms) IrState.ctim10ms--;
....
}


Зы. ту не поможет ни какая volatile.
ViKo
Цитата(jcxz @ May 29 2018, 14:07) *
А если подумать? rolleyes.gif

10 кГц -> 100 мкс
100 мкс * 16 МГц = 1600 тактов
jcxz
Цитата(ViKo @ May 29 2018, 14:11) *
10 кГц -> 100 мкс
100 мкс * 16 МГц = 1600 тактов

Цитата(maxntf @ May 29 2018, 13:17) *
Есть таймер настроенный на 10мкс,
ViKo
Я думал, речь идет по-прежнему про частоту работы планировщика задач.
jcxz
Цитата(Forger @ May 29 2018, 14:11) *
Любого ждет анафема, кто замечен за подобными злодеяниями - изменение глобальных объектов в фоне задачи и в прерываниях.

Ничего там криминального нет с IrState.ctim10ms, так как в задаче выполняется только запись в неё (хоть объявления IrState.ctim10ms не видно, но думаю что она - одного из встроенных типов), а запись встроенного типа 8/16/32/64 бита на Cortex-M - атомарна.
В ISR выполняется чтение-модификация-запись, приоритет ISR заведомо выше любого таска, так что таск не сможет прерывать ISR. А значит и операции внутри ISR для тасков - атомарны.

Цитата(Forger @ May 29 2018, 14:11) *
Допустимо, если будет только чтение таких объектов и оно атомарно (8...32-битное слово).

Для чтения/записи 64-битных переменных например IAR использует LDRD/STRD, а они тоже атомарны.

Цитата(Forger @ May 29 2018, 14:11) *
процедура записи нового значения (между знаком "=") прервана прерыванием
в котором будет выполнено это условие?
...

И что? Пускай прервана. Она не прервёт посередине операции записи (запись атомарна), а только перед или после.
К тому же как я понимаю: указанный участок внутри таска начнёт выполняться только когда IrState.ctim10ms == 0. А при этом в ISR никакого декремента не делается, только чтение.
Forger
Я же все объяснил на конкретном примере.

Еще раз, но на пальцах:
Пусть IrState.ctim10ms равно, скажем 10, но прерывание еще не возникло и в этот момент выполняется в задаче это кусок:

IrState.ctim10ms = IrState.bRxTx[pValue->nComm][0];

скажем нас прервали как раз перед записью нового значения в IrState.ctim10ms, собирались записать туда 100, которые тока что вычитали из IrState.bRxTx[pValue->nComm][0]

и тут вдруг возникает прерывание, где мы вполняем эту строчку:
if(IrState.ctim10ms) IrState.ctim10ms--;

логично, что после выхода из прерывания будет IrState.ctim10ms = 9,
а тут мы выходим из прерывания и тут же загоняем в IrState.ctim10ms = 100

Не знаю, как это скажется на логике работы прерывания, но явно, что в следующем вызове прерывания вместо 9 вдруг получить 100 - очень странно.
Как себя поведет прога в таком случае остается только гадать.... smile3046.gif


Цитата
К тому же как я понимаю: указанный участок внутри таска начнёт выполняться только когда IrState.ctim10ms == 0.

Ну-ну. Но чуть-чуть поменяли логику и привет? Ловим баги на ровном месте, через час/сутки/год? Тут кому как повезет.

Существуют неписанные правила: доступ к глобальным объектам при чтении-модификации-записи (из задачи) должен быть защищен от любых прерываний, которые могут этот объект изменить посреди этой цепочки.
Для таких случае в простейшем варианте можно использовать критическую секцию.
А в идеале - вообще избегать "дергать" один и тот же глобальный объект из задачи и прерываний. Есть решения.
К тому же нужно постоянно держать в голове - "а что будет, если ... ". Кому нужна доп. головная боль на ровном месте?

В той же MISRA вообще рекомендуют избегать или на край очень осторожно использовать глобальные объекты.
Разумеется, если используются прерывания. Но где их нынче не используют? wink.gif
jcxz
Цитата(Forger @ May 29 2018, 16:01) *
а тут мы выходим из прерывания и тут же загоняем в IrState.ctim10ms = 100

И что???

Цитата(Forger @ May 29 2018, 16:01) *
Не знаю, как это скажется на логике работы прерывания, но явно, что в следующем вызове прерывания вместо 9 вдруг получить 100 - очень странно.

А сколько там должно быть после записи 100? 200 что-ли? smile3046.gif
В чём проблема-то??
В программе написано "записать 100", код это делает? Делает. Никакие прерывания этому не мешают. Так в чём проблема, что Вас смущает?

Цитата(Forger @ May 29 2018, 16:01) *
Существуют неписанные правила: доступ к глобальным объектам при чтении-модификации-записи (из задачи) должен быть защищен от любых прерываний, которые могут этот объект изменить посреди этой цепочки.

Проблема тут только в том, что чтение-модификацию-запись (в задаче) переменной IrState.ctim10ms в указанном примере видите здесь похоже только Вы.... laughing.gif
Forger
Цитата(jcxz @ May 29 2018, 16:37) *
В программе написано "записать 100", код это делает? Делает. Никакие прерывания этому не мешают. Так в чём проблема, что Вас смущает?

Это мешает самим прерываниям, точнее, нарушает логику работы кода внутри этих самых прерываний.

Короче, я указал на потенциально опасное место в коде.
И связано оно именно с обращениям к одним и тем же глобальным объектам из фона задачи и прерываний БЕЗ соотв. защиты.

Даже, если в самом коде это в данный момент работает, ну, какое-то определенное время работает. Хотя бы в текущей версии.
Если игнорировать эти вещи, то все может стать только хуже, особенно, когда проект практически готов, но сбоит на ровном месте и в самый неожиданный момент.
К тому же, судя по всему, ТС только-только осваивает RTOS и поэтому много может просто не знать. Особенно про такие очень скверные грабли с глобальными объектами.
Или Вы считаете, что это - не грабли, и так делать нормально и безопасно? wink.gif
jcxz
Цитата(Forger @ May 29 2018, 16:51) *
Или Вы считаете, что это - не грабли, и так делать нормально и безопасно? wink.gif

Ещё раз: грабли там видите только Вы.
Никаких проблем (по крайней мере с работой этой переменной) в указанном фрагменте нет.
Сериализация доступа необходима только в случае неатомарных операций с переменной в прерываемом коде. Операции записи базовых типов данных в Cortex-M - все атомарные. Т.е. - в Task2 нет неатомарных операций с IrState.ctim10ms. Критические секции (или подобное) там как собаке пятая нога.

PS: Кто-нить ещё, кроме Forger, видит проблему с работой IrState.ctim10ms ? rolleyes.gif

PPS: ТСу можно разве что посоветовать объявить IrState.ctim10ms с модификатором volatile. Хотя возможно что он уже есть, так как объявления её не приведено.
Forger
Цитата(jcxz @ May 29 2018, 17:00) *
Ещё раз: грабли там видите только Вы.

Опыт! wink.gif

Цитата
Операции записи базовых типов данных в Cortex-M - все атомарные.
Да это тут при чем? Речь совсем про другое!
Про атомарность записи таких маленьких переменных никто ни спорит.
Речь про ИЗМЕНЕНИЕ одного и того же объекта в фоне задач и прерываниях.
Причем в фоне задач еще и производится чтение-модификация-запись, которую можно безопасно прервать в прерывании в одном случае - в нем лишь ЧИТАЕТСЯ объект, но НЕ ИЗМЕНЯЕТСЯ.

Я понимаю, если в прерываниях мы только читаем некую volatile переменную, а меняем ее ТОЛЬКО в фоне задач. Это вполне безопасно.
Повторюсь, если в прерываниях мы НЕ меняем значения этой переменной, а ТОЛЬКО ЧИТАЕМ.

В приведенном примере ситуация другая: одна и та же переменная ИЗМЕНЯЕТСЯ и в фоне задач и в прерываниях.

Я уже не знаю как еще более доходчиво объяснить эти очевидные вещи (((
maxntf
Цитата(jcxz @ May 29 2018, 17:00) *
PPS: ТСу можно разве что посоветовать объявить IrState.ctim10ms с модификатором volatile. Хотя возможно что он уже есть, так как объявления её не приведено.

Проблема не с переменной.
Я не пойму (от незнания) почему висит ОС вот здесь:
Цитата
xSemaphoreGiveFromISR(IrState.xSemNx, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);

В том случае если Task2 еще не дошла до обработки семафора IrState.xSemNx.
То есть глючить начинает когда в прерывании первый раз семафор установили, в задаче его еще не забрали (не успели) и потом в прерывании срабатывают снова эти строки каждые 10мкс. (так как IrState.ctim10ms = 0 и его никто не устанавливает).
Forger
Цитата(maxntf @ May 29 2018, 17:27) *
Проблема не с переменной.
Я не пойму (от незнания) почему висит ОС вот здесь:

В том случае если Task2 еще не дошла до обработки семафора IrState.xSemNx.
То есть глючить начинает когда в прерывании первый раз семафор установили, в задаче его еще не забрали (не успели) и потом в прерывании срабатывают снова эти строки каждые 10мкс. (так как IrState.ctim10ms = 0 и его никто не устанавливает).


Для начала попробуйте упростить код до минимум, оставив в прерывании только безусловный сигнал семафора,
а в задаче ждать этот семафор по достаточному таймауту. Таймер всегда работает, задача всегда ждет.
Для наглядности удобно подключить светодиод и правильно настроить моргания, или в прерываниях какому-нить пину делать toggle и смотреть на осциллографе меандр. Вариантов - вагон!
Проверять условие таймаута. Если хотя бы раз не дождались семафора, разбираемся, почему косячит примитивный код.
Если код не косячит, добавлять свой код по чуть-чуть, каждый раз проверяя, все ли правильно.
В итоге, вы доберетесь до косячного места.

Этот как готовить первый раз какое-нить новое блюдо - что-то положили, пробуем, анализируем. Но никогда не кладем все сразу скопом к одну кастрюлю )))
maxntf
Цитата(Forger @ May 29 2018, 17:39) *
Для начала попробуйте упростить код до минимум...

Этим и планирую заняться. Попробую добиться глюков на простом коде (если получиться), а потом опишу непонятные мне моменты.
Arlleex
Цитата(jcxz @ May 29 2018, 18:00) *
PS: Кто-нить ещё, кроме Forger, видит проблему с работой IrState.ctim10ms ? rolleyes.gif

Я вижу.

Пример.
Есть задача, да пусть будет вообще без RTOS для упрощения - просто бесконечный цикл while(1). В этом цикле крутится код обработки кольцевого буфера UART-а. В функции чтения одного байта из кольцевого буфера уменьшается переменная количества символов в буфере.
Также среди функций работы с кольцевым буфером есть функция помещения символа в буфер. В ней эта же самая переменная количества текущих данных в буфере увеличивается при каждом вызове. И эта функция вызывается в прерывании по приему UART.

Теперь допустим, что переменная RingBuffer_DataCnt была равна 10, и сейчас код выполняется в потоке, вызывается функция чтения байта из кольцевой очереди, и следующая инструкция будет --RingBuffer_DataCnt.
Процессор выполняет чтение данных из памяти в регистр, считал 10, но... Возникло прерывание. Оно вызвало функцию помещения данных в очередь, и выполнила ++RingBuffer_DataCnt.
После выхода из прерывания RingBuffer_DataCnt == 11. Теперь продолжаем выполнять прерванный код - декрементируем число в регистра (получилось 9), и записываем обратно в переменную RingBuffer_DataCnt.

Что получилось? RingBuffer_DataCnt == 9, а по факту их было 10, один байт потеряли. Классическое нарушение атомарности. И никакие атомарные записи LDR/STR тут не при чем, это не 8-битные МК, где требовалось защищать даже саму запись многобайтного числа.
Логично? rolleyes.gif
P.S. Можно не запрещать прерываний (аля критические секции), а использовать для этого специальные конструкции LDREX/STREX, регламентирующие эксклюзивный доступ к памяти.
jcxz
Цитата(Arlleex @ May 29 2018, 17:50) *
...
Что получилось? RingBuffer_DataCnt == 9, а по факту их было 10, один байт потеряли. Классическое нарушение атомарности. И никакие атомарные записи LDR/STR тут не при чем, это не 8-битные МК, где требовалось защищать даже саму запись многобайтного числа.
Логично? rolleyes.gif
P.S. Можно не запрещать прерываний (аля критические секции), а использовать для этого специальные конструкции LDREX/STREX, регламентирующие эксклюзивный доступ к памяти.

Всё что Вы написали, справедливо для случая если операции чтения-модификации-записи есть в прерываемом коде (т.е. - фоновой задаче).
Я уже это писал выше, прочитайте.
Здесь совсем не тот случай. Посмотрите внимательнее на исходный код: в задаче переменная только записывается!. Я на этот уже тоже несколько раз указал Forger.
И Вы что-ли узрели чтение-модификацию-запись в задаче ТС-а? Где??? Неужто только я вижу там только операции записи.
Приведите выдержку где там в фоновой задаче чтение-модификация-запись???

Цитата(Forger @ May 29 2018, 17:15) *
Причем в фоне задач еще и производится чтение-модификация-запись

Где там чтение-модификация-запись в фоновой задаче???
Приведите выдержку.

Цитата(maxntf @ May 29 2018, 17:27) *
Я не пойму (от незнания) почему висит ОС вот здесь:

Проблем может быть вагон. Например - переполнение стека. Или нехватка быстродействия о чём я писал выше.
Снижайте частоту таймера раз в 10 и запрещайте все остальные прерывания. И проверяйте.
Forger
Цитата(jcxz @ May 29 2018, 18:27) *
Где там чтение-модификация-запись в фоновой задаче???

Спор уходит в другое русло, начинаются придирки к отдельным словам, хотя, очевидно, что все поняли о чем и идет речь.
Если Вы привыкли писать код на грани фола, то это ваше дело, но в данном случае ТС лишь начинает этот путь и его код уже глючит, и никому не ведомо что будет дальше, если все оставить как есть.
Я указал на потенциально опасное место. Надеюсь, мои советы пригодятся.
maxntf
В общем выкинул все лишнее из программы.
ТО таймера ставил от 10мкс до 1мсек, вообще не влияет.
Если оставить как в коде ниже, не работает. Если раскомментировать вкл/выкл прерывания TIM10 - работает.
Если закомментированть обработку переменно ctim10ms в обработчике прерываний таймера, просто устанавливать семафор на каждом прерывании - то же работает.
Код
void TIM10_IRQHandler(void)
{
    static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
    
    if(TIM_GetFlagStatus(TIM10, TIM_FLAG_Update) != RESET)
    {
        TIM_ClearFlag(TIM10, TIM_FLAG_Update);
        if(ctim10ms) ctim10ms--;
        else
        {
//disableInterruptTIM10();
            xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken);
            portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
        }
    }
}

CODE
#define enableInterruptTIM10() NVIC->ISER[TIM10_IRQn >> 0x05] = (uint32_t)0x01 << (TIM10_IRQn & (uint8_t)0x1F)
#define disableInterruptTIM10() NVIC->ICER[TIM10_IRQn >> 0x05] = (uint32_t)0x01 << (TIM10_IRQn & (uint8_t)0x1F)

xSemaphoreHandle xSemNx; //управление задачей
volatile uint32_t ctim10ms; //счетчик отсчетов по 10 мксек

void Tx_Init(void);
void Tx_DeInit (void);

void Task_temp(void *pParams);

int main(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;

RCC_HSICmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);

RCC_HSEConfig(RCC_HSE_OFF);
while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) != RESET);

SystemCoreClockUpdate();
SysTick_Config(SystemCoreClock/1000);//TO=1мс

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
NVIC_InitStruct.NVIC_IRQChannel = TIM10_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
//NVIC_InitStruct.NVIC_IRQChannelCmd = DISABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 11;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStruct);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);


xTaskCreate(Task_temp, "Temp-handler", configMINIMAL_STACK_SIZE, NULL, 2, NULL);

vTaskStartScheduler();

}

void Task_temp(void *pParams)
{
uint32_t c = 0;
vSemaphoreCreateBinary(xSemNx);

while(1){
xSemaphoreTake(xSemNx, 0);
ctim10ms = 100;
Tx_Init();

while(1)
{
//enableInterruptTIM10();
if(xSemaphoreTake(xSemNx, 10) != pdTRUE) break;
c++;
ctim10ms = 100;
GPIO_ToggleBits(GPIOB, GPIO_Pin_5);//пин контроля работы программы
if(c > 200) break;
}
//disableInterruptTIM10();
TIM_DeInit(TIM10);

}
}

void Tx_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10, ENABLE);

TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 160-1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 0;
TIM_TimeBaseInit(TIM10, &TIM_TimeBaseInitStruct);
TIM_ITConfig(TIM10, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM10, ENABLE);
}
Forger
Цитата(maxntf @ May 30 2018, 10:05) *
В общем выкинул все лишнее из программы.

Поменяйте в своем посте теги quote на code, иначе очень трудно читать этот сплошной текст ))
Не совсем понятна фраза "раскомментировать вкл/выкл прерывания TIM10", поясните о каких именно кусках кода идет речь.
Или покажите ДВА куска кода: работающий и неработающий.
maxntf
Цитата(Forger @ May 30 2018, 10:13) *
Поменяйте в своем посте теги quote на code, иначе очень трудно читать этот сплошной текст ))
Не совсем понятна фраза "раскомментировать вкл/выкл прерывания TIM10", поясните о каких именно кусках кода идет речь.
Или покажите ДВА куска кода: работающий и неработающий.

ну в смысле включать и выключать прерывания enableInterruptTIM10() и disableInterruptTIM10() (они в примере закомментированы)
Forger
Цитата(maxntf @ May 30 2018, 10:17) *
ну в смысле включать и выключать прерывания enableInterruptTIM10() и disableInterruptTIM10() (они в примере закомментированы)

Если закомментировать enableInterruptTIM10(), которые я увидел только в одном месте, то безусловно прерывания от этого таймера работать не будут ))
maxntf
Цитата(Forger @ May 30 2018, 10:23) *
Если закомментировать enableInterruptTIM10(), которые я увидел только в одном месте, то безусловно прерывания от этого таймера работать не будут ))

Не в одном месте.
в прерывании отключаем при установке семафора, в задаче включаем после того как забрали семафор и выполнили обработку.
Forger
Цитата(maxntf @ May 30 2018, 10:25) *
Не в одном месте.

Я смотрю Ваш код, по поиску (CTRL-F) вижу enableInterruptTIM10 только в одном месте ((

maxntf
Привожу проблемные участки кода (инициализация та что в пост №68):
1. Не работает.
CODE
void TIM10_IRQHandler(void)
{
static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

if(TIM_GetFlagStatus(TIM10, TIM_FLAG_Update) != RESET)
{
TIM_ClearFlag(TIM10, TIM_FLAG_Update);
if(ctim10ms) ctim10ms--;
else
{
xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
}
}

void Task_temp(void *pParams)
{
vSemaphoreCreateBinary(xSemNx);

xSemaphoreTake(xSemNx, 0);
ctim10ms = 10;
Tx_Init();

while(1)
{
xSemaphoreTake(xSemNx, 10);
ctim10ms = 10;
GPIO_ToggleBits(GPIOB, GPIO_Pin_5);//пин контроля работы программы
}

}

2. Работает
CODE
void TIM10_IRQHandler(void)
{
static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

if(TIM_GetFlagStatus(TIM10, TIM_FLAG_Update) != RESET)
{
TIM_ClearFlag(TIM10, TIM_FLAG_Update);
if(ctim10ms) ctim10ms--;
else
{
disableInterruptTIM10();
xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
}
}

void Task_temp(void *pParams)
{
vSemaphoreCreateBinary(xSemNx);

xSemaphoreTake(xSemNx, 0);
ctim10ms = 10;
Tx_Init();

while(1)
{
enableInterruptTIM10();
xSemaphoreTake(xSemNx, 10);
ctim10ms = 10;
GPIO_ToggleBits(GPIOB, GPIO_Pin_5);//пин контроля работы программы
}

}

3.Работает
CODE
void TIM10_IRQHandler(void)
{
static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

if(TIM_GetFlagStatus(TIM10, TIM_FLAG_Update) != RESET)
{
TIM_ClearFlag(TIM10, TIM_FLAG_Update);
xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);

}
}

void Task_temp(void *pParams)
{
vSemaphoreCreateBinary(xSemNx);

xSemaphoreTake(xSemNx, 0);
Tx_Init();

while(1)
{
xSemaphoreTake(xSemNx, 10);
GPIO_ToggleBits(GPIOB, GPIO_Pin_5);//пин контроля работы программы
}

}
jcxz
Цитата(maxntf @ May 30 2018, 10:05) *
Если оставить как в коде ниже, не работает. Если раскомментировать вкл/выкл прерывания TIM10 - работает.

Я не знаю что делает xSemaphoreGiveFromISR() во FreeRTOS, подозреваю что переводит семафор в сигнальное состояние.
Если так, то зачем Вы её вызываете всегда когда ctim10ms==0 ? ctim10ms обнулилась и после этого вы начинаете долбить семафор постоянно. Зачем???
Достаточно один раз вызвать xSemaphoreGiveFromISR().
При частоте таймера ==100кГц и частоте ядра==16МГц, у Вас как только обнуляется ctim10ms, то потом в каждом прерывании вызывается xSemaphoreGiveFromISR(), а так как она наверняка длительная (и следующая за ней функция - скорей всего ещё более длительная, так как скорей всего в ней делается решедулинг задач), то к моменту выхода из ISR успевает пройти >= 160 тактов ядра. А значит - ждёт уже новое прерывание таймера и сразу снова входит в ISR и всё повторяется. Естественно, что всё блокируется на постоянных входах в ISR и на выполнение фоновой задачи времени не остаётся
Я Вам на это намекал ещё в самом первом посте, когда советовал посчитать кол-во тактов, но Вы почему то не читаете советы (чукча не читатель? зачем тогда спрашивать?).
Вызывать xSemaphoreGiveFromISR() нужно только один раз. Например так:
CODE
static u8 volatile ctim10ms = 0;
void ISR()
{
...
int i = ctim10ms;
if (--i < 0) return;
ctim10ms = i;
if (i) return;
xSemaphoreGiveFromISR(...);
portEND_SWITCHING_ISR(...);
}

PS: Всё сказанное справедливо если я догадался правильно и xSemaphoreGiveFromISR() - перевод семафора в сигнальное состояние, а portEND_SWITCHING_ISR() - уведомление к ОС о выходе из ISR с требованием решедулинга задач.

PPS: И как Вам уже посоветовали тут - используйте тег codebox для длинных примеров кода (как в этом сообщении), а не code. Иначе ваши портянки трудно пролистывать. И меньше народу будут читать сообщения.
maxntf
Цитата(Forger @ May 30 2018, 10:32) *
Я смотрю Ваш код, по поиску (CTRL-F) вижу enableInterruptTIM10 только в одном месте ((

Да так и есть. Включаем, когда готовы принимать семафор.

Цитата(jcxz @ May 30 2018, 10:36) *
При частоте таймера ==100кГц и частоте ядра==16МГц, у Вас как только обнуляется ctim10ms, то в каждом прерывании вызывается xSemaphoreGiveFromISR(), а так как она наверняка длительная (и следующая за ней функция - скорей всего ещё более длительная, так как скорей всего делается решедулинг задач), то к моменту выхода из ISR успевает пройти >= 160 тактов ядра. А значит - ждёт уже новое прерывание таймера и сразу снова входит в ISR и всё повторяется. Естественно, что всё блокируется на постоянных входах в ISR.


Ага. Но только если я долблю таймером xSemaphoreGiveFromISR без обработки переменной каждые 10мкс все работает. xSemaphoreGiveFromISR() не должна повторно устанавливать семафор пока задача его не заберет, соответственно возвращает FALSE и portEND_SWITCHING_ISR ничего не переключает.
Код
#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()




Я как бы увидел что происходит.
В неработающем варианте
Код
xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken);

всегда возвращается pdTREU - соответственно постоянно перевызывается планировщик - почему так?
Arlleex
Цитата(maxntf @ May 30 2018, 11:51) *
Да так и есть. Включаем, когда готовы принимать семафор.
Я как бы увидел что происходит.
В неработающем варианте
Код
xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken);

всегда возвращается pdTREU - соответственно постоянно перевызывается планировщик - почему так?

Потому что задача находится в состоянии READY и готова к выполнению.
Приведите пожалуйста файл настройки FreeRTOSConfig.h.

Цитата(maxntf @ May 30 2018, 11:51) *
xSemaphoreGiveFromISR() не должна повторно устанавливать семафор пока задача его не заберет, соответственно возвращает FALSE и portEND_SWITCHING_ISR ничего не переключает.

Нет. Это означает, что после выдачи семафора планировщик не видит задач, готовых к выполнению, находящихся в состоянии READY, поэтому решедулинга задач не производится.
maxntf
Все разобрался, не верно обрабатывал семафор.
Нужно было так:
Код
void TIM10_IRQHandler(void)
{
    static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
    
    if(TIM_GetFlagStatus(TIM10, TIM_FLAG_Update) != RESET)
    {
        TIM_ClearFlag(TIM10, TIM_FLAG_Update);
        if(ctim10ms) ctim10ms--;
        else
        {
            if(xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken) != pdFAIL) portEND_SWITCHING_ISR(1);
        }
    }
}


Осталось понять нафиг xHigherPriorityTaskWoken.
И почему во всех примерах так
Код
            xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken);
            portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);

А у меня не работает?

P.S.
Все понял.
У меня задача всегда READY. Но в нее не успеваем попасть, и все время выполняем решедулинг!
Arlleex
Код
if(xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken) != pdFAIL) portEND_SWITCHING_ISR(1);

Да не правильно так делать!

Проверять на возвращаемое значение выдачи семафора - зачем? Да и portEND_SWITCHING_ISR(1) не с 1 должно быть, а с xHigherPriorityTaskWoken.
Семафор Вы можете выдавать всегда сколько раз угодно - другое дело, что пока задача его не заберет, выдать его Вы не сможете.
Повысьте тактовую частоту CPU и проверьте еще раз свой нерабочий вариант. У меня на F4 приблизительно похожая задача, и все прекрасно успевает и обрабатывается как нужно.
maxntf
Цитата(Arlleex @ May 30 2018, 11:17) *
Код
if(xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken) != pdFAIL) portEND_SWITCHING_ISR(1);

Да не правильно так делать!

Проверять на возвращаемое значение выдачи семафора - зачем? Да и portEND_SWITCHING_ISR(1) не с 1 должно быть, а с xHigherPriorityTaskWoken.
Семафор Вы можете выдавать всегда сколько раз угодно - другое дело, что пока задача его не заберет, выдать его Вы не сможете.
Повысьте тактовую частоту CPU и проверьте еще раз свой нерабочий вариант. У меня на F4 приблизительно похожая задача, и все прекрасно успевает и обрабатывается как нужно.

В моем случае не совсем так.
у меня запускается планировщик, где то по середине прерывается, и запускается снова и так бесконечно. Так как задача всегда READY, но семафор обработать не успевает.
Конкретно в моем варианте нужно так:
Код
void TIM10_IRQHandler(void)
{
    static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE, fGiveResult = pdFALSE;
    
    if(TIM_GetFlagStatus(TIM10, TIM_FLAG_Update) != RESET)
    {
        TIM_ClearFlag(TIM10, TIM_FLAG_Update);
        if(ctim10ms) ctim10ms--;
        else
        {
            fGiveResult = xSemaphoreGiveFromISR(xSemNx, &xHigherPriorityTaskWoken);
            portEND_SWITCHING_ISR(xHigherPriorityTaskWoken & fGiveResult);
        }
    }
}


P.S.
На своей плате поднять частоту я не могу (на данный момент), а пропустить несколько отсчетов по 10мкс. в данном случае не критично и всегда можно подкорректировать.
Задача в первую очередь была понять почему так, а путей решения всегда бывает больше чем один!
jcxz
Цитата(maxntf @ May 30 2018, 11:25) *
На своей плате поднять частоту я не могу (на данный момент), а пропустить несколько отсчетов по 10мкс. в данном случае не критично и всегда можно подкорректировать.
Задача в первую очередь была понять почему так, а путей решения всегда бывает больше чем один!

Я вам уже несколько раз объяснил и пример дал, но вы так и не поняли.... и опять всё то же самое накатали. Да ещё какой-то бред добавили с ПОБИТОВЫМ ИЛИ между логическими (видимо) типами...
maxntf
На семафоре полностью развязать задачу я не смог, а вот с очередями получилось.
Реальная задача динамически запускается и удаляется примерно на сотню миллисекунд - передает команду через ИК диод.
В общем сделал так, все работает без проблем на любой частоте таймера.
Код
void TIM10_IRQHandler(void)
{
    static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE, fGiveResult = pdFALSE;
    
    if(TIM_GetFlagStatus(TIM10, TIM_FLAG_Update) != RESET)
    {
        TIM_ClearFlag(TIM10, TIM_FLAG_Update);
        if(ctim10ms) ctim10ms--;
        else
        {
            fGiveResult = xQueueSendToBackFromISR(xQueue, (const void *)0, &xHigherPriorityTaskWoken);
            if(xHigherPriorityTaskWoken & fGiveResult)
            {
                portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
            }
                else cal_ctim10ms++;
        }
    }
}
void Task_temp(void *pParams)
{
    uint32_t newValue = 100;
    uint32_t q_buf;
    
    xQueue = xQueueCreate(1, sizeof(uint32_t));


    ctim10ms = newValue;
    Tx_Init();

    while(1)
    {
        xQueuePeek(xQueue, &q_buf, 10);
    taskENTER_CRITICAL();
        ctim10ms = newValue - cal_ctim10ms;
        cal_ctim10ms = 0;
    taskEXIT_CRITICAL();
        GPIO_ToggleBits(GPIOB, GPIO_Pin_5);//пин контроля работы программы
        xQueueReceive(xQueue, &q_buf, 0);
    }
}

Если кто объяснит почему так нельзя приведя реальный пример как нужно - респект!
Forger
Цитата(maxntf @ May 30 2018, 16:03) *
Если кто объяснит почему так нельзя приведя реальный пример как нужно - респект!

Очередь (Queue) в данном случае - как из пушки по воробьям.


Чтобы избежать таких детских граблей:
Код
if(xHigherPriorityTaskWoken & fGiveResult)

это следует писать хотя бы так:
Код
if( (xHigherPriorityTaskWoken != 0) && (fGiveResult != 0))

Вы вообще понимаете разницу между операторами & и && ?

Самый главный косяк, разрешать себе делать так: if(some) { ... }.
Это допустимо только для одного случая some есть bool, но в таких случаях эту some лучше называть иначе, чтобы было проще читать, например: bool isRxDone = что_то_что_возвращает true/false;
Тогда всякие условия читаются в разы проще: if (isRxDone) { ... }

Если же речь идет про все остальные типы, которые анализируются в блоке if {} else, то их сравнение следует указывать явно (number != 0), и обязательно брать в скобки, если в одном блоке if проверяются более одного значения.
maxntf
Цитата(Forger @ May 30 2018, 16:06) *
Очередь (Queue) в данном случае - как из пушки по воробьям.

Я спроси конкретный пример! А то все пишете плохо да плохо.

Цитата(Forger @ May 30 2018, 16:06) *
Вы вообще понимаете разницу между операторами & и && ?
Самый главный косяк, разрешать себе делать так: if(some) { ... }.

Отлично понимаю и вообще косяка не вижу. Поразрядное И с однотипными данными в условии, что тут такого!
Forger
Цитата(maxntf @ May 30 2018, 16:29) *
Я спроси конкретный пример!

Конкретные примеры скачивайте с сайта freeRTOS.

Цитата
А то все пишете плохо да плохо.
Все просто - так оно и есть wink.gif

Цитата
Отлично понимаю и вообще косяка не вижу. Поразрядное И с однотипными данными в условии, что тут такого!
Вот с этого бреда и нужно было начинать тему. Дальше можно не продолжать, все ясно.

Напоследок. Вот так будет работать правильно:
Код
if ( (xHigherPriorityTaskWoken != pdFALSE) && (fGiveResult != pdFALSE) )
maxntf
Цитата(Forger @ May 30 2018, 16:33) *
Напоследок. Вот так будет работать правильно:
Код
if ( (xHigherPriorityTaskWoken != pdFALSE) && (fGiveResult != pdFALSE) )


#define pdFALSE ( ( BaseType_t ) 0 )
#define pdTRUE ( ( BaseType_t ) 1 )
typedef long BaseType_t;

xHigherPriorityTaskWoken и fGiveResult могут быть только 0x00000000 или 0x00000001

(xHigherPriorityTaskWoken & fGiveResult) в результате даст либо 0 либо 1
if(0) нет
if(1) да

P.S.
Ваш вариант с точки зрения читабельности, верный.
Фактически в данном случае мой вариант рабочий и перед тем чтоб такое написать я проверил что там за типы данных и какие значения принимают. Так пишу потому что последнее время писал только под pic с очень малым flash и приходилось очень много вот так подтачивать чтоб все влезло. Ваш код займет больше места, особенно если таких сравнений будет много.
Forger
Цитата(maxntf @ May 30 2018, 16:50) *
xHigherPriorityTaskWoken и fGiveResult могут быть только 0x00000000 или 0x00000001
Ну-ну ...
Мануалы читать - не Ваше.
Вот: https://www.freertos.org/xQueueSendToBackFromISR.html
Цитирую строчку для особе упертых: "pdTRUE if the semaphore was successfully given, otherwise errQUEUE_FULL"

Цитата
Ваш код займет больше места, особенно если таких сравнений будет много.

Мля, что за идиотская привычка пошла у начинающих программеров экономить на спичках, плодя потенциальны места для будущих багов!!! cranky.gif
Забудьте про PIC и начинайте уже писать код так, как это делают остальные, без такой адской самодеятельности и оптимизаций!
maxntf
Не тот пример был.
esaulenka
Цитата(Forger @ May 30 2018, 16:06) *
Очередь (Queue) в данном случае - как из пушки по воробьям.

Расскажите пожалуйста, чем отличается очередь и семафор.
Напомню (и это важно!) мы по прежнему про FreeRTOS, а не о сферическом коне, как тут принято.
Forger
Цитата(esaulenka @ May 30 2018, 18:30) *
Расскажите пожалуйста, чем отличается очередь и семафор.
Напомню (и это важно!) мы по прежнему про FreeRTOS, а не о сферическом коне, как тут принято.

RTFM
esaulenka
Никакой конкретики. Отлично.
Ещё раз. Пожалуйста, расскажите своё видение отличий очереди и семафора.
Что вижу я: макрос vSemaphoreCreateBinary вызывает xQueueGenericCreate, макрос xSemaphoreGive вызывает функцию xQueueGenericSend.
Т.е. внутри что семафор, что очередь из одного элемента отличаются минимально.

А теперь расскажите, почему эту очередь maxntf ни в коем случае использовать не должен.
Forger
Цитата(esaulenka @ May 30 2018, 20:15) *
...

"Разжевать и положить в рот"? Нет, я пас. Извините, но мы не в ясельной группе!
Если вы сами до сих пор еще не разобрались, где какой сервис RTOS нужно использовать, а где какой будет избыточен, то я ни как Вам не смогу помочь.
В качестве "домашнего задания": подумайте, зачем в RTOS существует такое разноообразие сервисов?
maxntf
Цитата(esaulenka @ May 30 2018, 20:15) *
Никакой конкретики. Отлично.
Ещё раз. Пожалуйста, расскажите своё видение отличий очереди и семафора...
А теперь расскажите, почему эту очередь maxntf ни в коем случае использовать не должен.

Опыта с freertos мало, точнее нет. Работал только с кооперативной ОС OSA, под pic_и, там все проще. По этому не смог сделать нужное мне на семафорах, в частности мне нужно было задачу разблокировать , а семафор забрать уже так, чтоб посреди этой функции снова не влететь в прерывание. А вот с очередями получилось, так как там можно прочитать сообщение не забирая его тем самым разблокировать задачу, а потом забрать когда задача будет к этому готова.

Цитата(Forger @ May 30 2018, 20:31) *
В качестве "домашнего задания": подумайте, зачем в RTOS существует такое разноообразие сервисов?

Чтоб народ с ума сводить sm.gif. Чем дальше углубляюсь, тем больше вижу, что там куча всего через дефайны дублируется.
Forger
Цитата(maxntf @ May 30 2018, 20:58) *
Чтоб народ с ума сводить sm.gif. Чем дальше углубляюсь, тем больше вижу, что там куча всего через дефайны дублируется.

Это во FreeRTOS они все сделаны можно сказать "через одну щель", потому и не славится эта RTOS особой производительностью и малым размером. Но зато под нее существует порт практически под любой проц.
Увы, такова плата за универсальность и поддержку немереного числа платформ.

В самом начале пути можно делать как угодно: и через зад лечить зубы и через нос зашивать пятку.
Но потом все равно придется делать как надо ))
Arlleex
Взял отладку на STM32F429, повторил код автора топика.
Код
int main(void)
{
    HW_MCUInit();
    
    SampleSemaphoreHandle = xSemaphoreCreateBinary();
    
    xTaskCreate(&SampleHandlingTask, "SampleHandlingTask", 512, NULL, SAMPLE_HANLING_TASK_PRIORITY, &SampleHandlingTaskHandle);
    
    vTaskStartScheduler();
    
    return 0;
}

Код
void SampleHandlingTask(void *Parameters)
{
    HW_TIMER_START();
    
    while(1)
    {
        xSemaphoreTake(SampleSemaphoreHandle, 10);
        
        unsigned int ReshedulingTime = DWT_CYCCNT - CYCCNT_ISR_Vale;

        ctim10ms = 10;
        
        GPIOG->ODR ^= GPIO_Pin_13;
    }
}

Код
volatile unsigned int ctim10ms = 10000;
volatile unsigned int CYCCNT_ISR_Vale = 0;

void TIM1_UP_TIM10_IRQHandler(void)
{
    static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    if(TIM1->SR & TIM_IT_Update)
    {
        if(ctim10ms)
            ctim10ms--;
        else
        {
            xSemaphoreGiveFromISR(SampleSemaphoreHandle, &xHigherPriorityTaskWoken);
            portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
            
            CYCCNT_ISR_Vale = DWT_CYCCNT;
        }
        TIM1->SR = (uint16_t)~TIM_IT_Update;
    }
}

Не работает (вернее работает через ж*пу - светодиод еле видно - но, видимо, переключается).
Ставлю таймер на 100мкс, все работает. Отлично - открываю отладчик, запускаю код в нем. Отключаю работу таймера при отладке, чтобы не поломалась система и результаты были честными.

В теле задачи ставлю точку останова на
Код
ctim10ms = 10;

и результат 256 тактов CPU тратит на работу переключателя контекста.

256/16000000Гц = 16мкс.

Далее точно также посчитаем, сколько выполняется тело цикла прерывания: 40 тактов в случае, если ctim10ms отличен от 0 и вызовов xSemaphoreGiveFromISR() не производится, и 356 тактов в случае, если ctim10ms достигло 0 и были вызваны сервисы RTOS выдачи семафора. Добавим ко всему этому по 12 тактов на вход/выход в прерывание (предполагаем, что tail-chaining и late-arriving не запускаются в связи с непериодичностью): итого 64 и 380 тактов соответственно. В микросекундах это 4 и 23.75 соответственно при синхронизации CPU от 16МГц. О какой нафиг тут периодичности прерываний 10мкс может идти речь - не пойму. У Вас за 23.75мкс сформируется еще 2 прерывания, которые Вы пропустите и обработаете как одно.

P.S. Поднял частоту работы системы - и теперь 10мкс прерывания работают отлично, что и требовалось доказать.
А Ваши старания с очередью... Ну не уверен я, что оно работает действительно так, как задумано.
jcxz
Цитата(Arlleex @ May 30 2018, 22:41) *
Взял отладку на STM32F429, повторил код автора топика.
...
О какой нафиг тут периодичности прерываний 10мкс может идти речь - не пойму. У Вас за 23.75мкс сформируется еще 2 прерывания, которые Вы пропустите и обработаете как одно.

Пролистайте тему выше. Ещё в 47-м, 49-м и 75-м сообщениях тут я писал об этом. Тут даже отладка не нужна - и так всё очевидно. Но "чукча не читатель, чукча писатель"...... laughing.gif
Arlleex
Цитата(jcxz @ May 30 2018, 23:05) *
Пролистайте тему выше. Ещё в 47-м, 49-м и 75-м сообщениях тут я писал об этом. Тут даже отладка не нужна - и так всё очевидно. Но "чукча не читатель, чукча писатель"...... laughing.gif

Мне просто было интересно поковырять, пока еда готовится biggrin.gif
Но где-то внутри тоже предполагал, что времени не хватает просто...
maxntf
Всем участникам спасибо за помощь и в некоторых случаях за "мат". Кое как вроде разобрался, что то может не до понял, в дальнейшем процессе освоения дойдет.
Остался один не решенный момент, а именно макрос
Код
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);


В начале я думал, что он работает следующим образом:
Task1 имеет более высокий приоритет чем Task2, но она заблокирована, а работает Task2.
Вдруг в каком то месте Task2 возникает прерывание, в котором происходит разблокировка Task1 (установили семафор например, который ждет Task1).
В макросе portYIELD() грубо говоря поменяли адрес (на задачу с большим приоритетом) куда нужно вернуться.
После выхода из функции обработки прерывания уже попадаем в Task1, а не возвращаемся в Task2.

Но судя по отладке это не так.
Кто может без нервов разжевать, как оно работает?
Arlleex
Цитата(maxntf @ May 31 2018, 17:15) *
В начале я думал, что он работает следующим образом.

Вы все правильно думаете. Прерывания - это асинхронные события, и всегда нужно стараться сократить время реакции системы на них.
Ваши домыслы, очевидно, верны.
Ну а при отладке Вы что-то делаете не так...
Еще раз прошу, скиньте файл FreeRTOSConfig.h, там много интересных настроек, и без включения некоторых из них действительно можно наблюдать чудеса.
maxntf
Цитата(Arlleex @ May 31 2018, 16:44) *
Еще раз прошу, скиньте файл FreeRTOSConfig.h

Вот пожалуйста.
Код
#define configTICK_RATE_HZ                        ( ( TickType_t ) 1000 )
#define configCPU_CLOCK_HZ                        ( SystemCoreClock )
#define configUSE_PREEMPTION                    1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION    1
#define configUSE_IDLE_HOOK                        1
#define configUSE_TICK_HOOK                        1
#define configMAX_PRIORITIES                    ( 5 )
#define configMINIMAL_STACK_SIZE                ( ( unsigned short ) 170 )
#define configTOTAL_HEAP_SIZE                    ( ( size_t ) ( 14 * 1024 ) )
#define configMAX_TASK_NAME_LEN                    ( 16 )
#define configUSE_TRACE_FACILITY                1
#define configUSE_16_BIT_TICKS                    0
#define configIDLE_SHOULD_YIELD                    1
#define configUSE_MUTEXES                        1
#define configQUEUE_REGISTRY_SIZE                5
#define configCHECK_FOR_STACK_OVERFLOW            2
#define configUSE_RECURSIVE_MUTEXES                1
#define configUSE_MALLOC_FAILED_HOOK            1
#define configUSE_APPLICATION_TASK_TAG            0
#define configUSE_COUNTING_SEMAPHORES            1

/* Software timer related definitions. */
#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY                ( configMAX_PRIORITIES - 1 )
#define configTIMER_QUEUE_LENGTH                10
#define configTIMER_TASK_STACK_DEPTH            configMINIMAL_STACK_SIZE

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES             0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

#define INCLUDE_vTaskPrioritySet        1
#define INCLUDE_uxTaskPriorityGet        1
#define INCLUDE_vTaskDelete                1
#define INCLUDE_vTaskCleanUpResources    0
#define INCLUDE_vTaskSuspend            1
#define INCLUDE_vTaskDelayUntil            1
#define INCLUDE_vTaskDelay                1

/* Standard assert semantics. */
//#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for(;; ); }    

/* Use the system definition, if there is one */
#ifdef __NVIC_PRIO_BITS
    #define configPRIO_BITS       __NVIC_PRIO_BITS
#else
    #define configPRIO_BITS       4        /* 15 priority levels */
#endif

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY            15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5

/* The lowest priority. */
#define configKERNEL_INTERRUPT_PRIORITY     ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Priority 5, or 95 as only the top four bits are implemented. */
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY     ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/* Run time stats related macros. */
#define configGENERATE_RUN_TIME_STATS    0

/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.