реклама на сайте
подробности

 
 
> FreeRTOS - минимальное время тика?
maxntf
сообщение May 22 2018, 09:36
Сообщение #1


Частый гость
**

Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008



Всем привет.
На FreeRTOS вертятся 3 задачи, больше ничего из инструментария ОС не пользую.
Так вот когда configTICK_RATE_HZ установлен 1000Hz, все работает без проблем. Меняю это значение на 10000Hz, после ресета все задачи какое то время работают, а потом работает только одна - 2-я задача (скорее работает просто функция). Шедуллер перестает работать вовсе - vApplicationTickHook перестает вызываться и задачи не переключаются.
МК STM32L152rc работает на 16MHz.
В чем может быть дело?
Как отследить где планировщик заткнулся и почему?
Go to the top of the page
 
+Quote Post
9 страниц V  « < 3 4 5 6 7 > »   
Start new topic
Ответов (60 - 74)
Forger
сообщение May 29 2018, 14:15
Сообщение #61


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(jcxz @ May 29 2018, 17:00) *
Ещё раз: грабли там видите только Вы.

Опыт! wink.gif

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

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

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

Я уже не знаю как еще более доходчиво объяснить эти очевидные вещи (((


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
maxntf
сообщение May 29 2018, 14:27
Сообщение #62


Частый гость
**

Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008



Цитата(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 и его никто не устанавливает).

Сообщение отредактировал maxntf - May 29 2018, 14:28
Go to the top of the page
 
+Quote Post
Forger
сообщение May 29 2018, 14:39
Сообщение #63


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



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

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


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

Этот как готовить первый раз какое-нить новое блюдо - что-то положили, пробуем, анализируем. Но никогда не кладем все сразу скопом к одну кастрюлю )))


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
maxntf
сообщение May 29 2018, 14:44
Сообщение #64


Частый гость
**

Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008



Цитата(Forger @ May 29 2018, 17:39) *
Для начала попробуйте упростить код до минимум...

Этим и планирую заняться. Попробую добиться глюков на простом коде (если получиться), а потом опишу непонятные мне моменты.
Go to the top of the page
 
+Quote Post
Arlleex
сообщение May 29 2018, 14:50
Сообщение #65


Местный
***

Группа: Участник
Сообщений: 492
Регистрация: 12-11-11
Пользователь №: 68 264



Цитата(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, регламентирующие эксклюзивный доступ к памяти.

Сообщение отредактировал Arlleex - May 29 2018, 14:53
Go to the top of the page
 
+Quote Post
jcxz
сообщение May 29 2018, 15:27
Сообщение #66


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(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 и запрещайте все остальные прерывания. И проверяйте.
Go to the top of the page
 
+Quote Post
Forger
сообщение May 29 2018, 16:51
Сообщение #67


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(jcxz @ May 29 2018, 18:27) *
Где там чтение-модификация-запись в фоновой задаче???

Спор уходит в другое русло, начинаются придирки к отдельным словам, хотя, очевидно, что все поняли о чем и идет речь.
Если Вы привыкли писать код на грани фола, то это ваше дело, но в данном случае ТС лишь начинает этот путь и его код уже глючит, и никому не ведомо что будет дальше, если все оставить как есть.
Я указал на потенциально опасное место. Надеюсь, мои советы пригодятся.


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
maxntf
сообщение May 30 2018, 07:05
Сообщение #68


Частый гость
**

Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008



В общем выкинул все лишнее из программы.
ТО таймера ставил от 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);
}


Сообщение отредактировал IgorKossak - May 30 2018, 08:03
Причина редактирования: [codebox] для длинного кода. [code]-для короткого!!!
Go to the top of the page
 
+Quote Post
Forger
сообщение May 30 2018, 07:13
Сообщение #69


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(maxntf @ May 30 2018, 10:05) *
В общем выкинул все лишнее из программы.

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


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
maxntf
сообщение May 30 2018, 07:17
Сообщение #70


Частый гость
**

Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008



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

ну в смысле включать и выключать прерывания enableInterruptTIM10() и disableInterruptTIM10() (они в примере закомментированы)
Go to the top of the page
 
+Quote Post
Forger
сообщение May 30 2018, 07:23
Сообщение #71


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



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

Если закомментировать enableInterruptTIM10(), которые я увидел только в одном месте, то безусловно прерывания от этого таймера работать не будут ))


--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
maxntf
сообщение May 30 2018, 07:25
Сообщение #72


Частый гость
**

Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008



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

Не в одном месте.
в прерывании отключаем при установке семафора, в задаче включаем после того как забрали семафор и выполнили обработку.
Go to the top of the page
 
+Quote Post
Forger
сообщение May 30 2018, 07:32
Сообщение #73


Профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831



Цитата(maxntf @ May 30 2018, 10:25) *
Не в одном месте.

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



--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
Go to the top of the page
 
+Quote Post
maxntf
сообщение May 30 2018, 07:35
Сообщение #74


Частый гость
**

Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008



Привожу проблемные участки кода (инициализация та что в пост №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);//пин контроля работы программы
}

}


Сообщение отредактировал IgorKossak - May 30 2018, 08:04
Причина редактирования: [codebox] для длинного кода. [code]-для короткого!!!
Go to the top of the page
 
+Quote Post
jcxz
сообщение May 30 2018, 07:36
Сообщение #75


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(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. Иначе ваши портянки трудно пролистывать. И меньше народу будут читать сообщения.
Go to the top of the page
 
+Quote Post

9 страниц V  « < 3 4 5 6 7 > » 
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 23rd July 2025 - 10:36
Рейтинг@Mail.ru


Страница сгенерированна за 0.01539 секунд с 7
ELECTRONIX ©2004-2016