|
FreeRTOS - минимальное время тика? |
|
|
|
 |
Ответов
(60 - 74)
|
May 29 2018, 14:15
|

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

|
Цитата(jcxz @ May 29 2018, 17:00)  Ещё раз: грабли там видите только Вы. Опыт!  Цитата Операции записи базовых типов данных в Cortex-M - все атомарные. Да это тут при чем? Речь совсем про другое! Про атомарность записи таких маленьких переменных никто ни спорит. Речь про ИЗМЕНЕНИЕ одного и того же объекта в фоне задач и прерываниях. Причем в фоне задач еще и производится чтение-модификация-запись, которую можно безопасно прервать в прерывании в одном случае - в нем лишь ЧИТАЕТСЯ объект, но НЕ ИЗМЕНЯЕТСЯ. Я понимаю, если в прерываниях мы только читаем некую volatile переменную, а меняем ее ТОЛЬКО в фоне задач. Это вполне безопасно. Повторюсь, если в прерываниях мы НЕ меняем значения этой переменной, а ТОЛЬКО ЧИТАЕМ. В приведенном примере ситуация другая: одна и та же переменная ИЗМЕНЯЕТСЯ и в фоне задач и в прерываниях. Я уже не знаю как еще более доходчиво объяснить эти очевидные вещи (((
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
May 29 2018, 14:27
|
Частый гость
 
Группа: Участник
Сообщений: 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
|
|
|
|
|
May 29 2018, 14:39
|

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

|
Цитата(maxntf @ May 29 2018, 17:27)  Проблема не с переменной. Я не пойму (от незнания) почему висит ОС вот здесь:
В том случае если Task2 еще не дошла до обработки семафора IrState.xSemNx. То есть глючить начинает когда в прерывании первый раз семафор установили, в задаче его еще не забрали (не успели) и потом в прерывании срабатывают снова эти строки каждые 10мкс. (так как IrState.ctim10ms = 0 и его никто не устанавливает). Для начала попробуйте упростить код до минимум, оставив в прерывании только безусловный сигнал семафора, а в задаче ждать этот семафор по достаточному таймауту. Таймер всегда работает, задача всегда ждет. Для наглядности удобно подключить светодиод и правильно настроить моргания, или в прерываниях какому-нить пину делать toggle и смотреть на осциллографе меандр. Вариантов - вагон! Проверять условие таймаута. Если хотя бы раз не дождались семафора, разбираемся, почему косячит примитивный код. Если код не косячит, добавлять свой код по чуть-чуть, каждый раз проверяя, все ли правильно. В итоге, вы доберетесь до косячного места. Этот как готовить первый раз какое-нить новое блюдо - что-то положили, пробуем, анализируем. Но никогда не кладем все сразу скопом к одну кастрюлю )))
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
May 29 2018, 14:44
|
Частый гость
 
Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008

|
Цитата(Forger @ May 29 2018, 17:39)  Для начала попробуйте упростить код до минимум... Этим и планирую заняться. Попробую добиться глюков на простом коде (если получиться), а потом опишу непонятные мне моменты.
|
|
|
|
|
May 29 2018, 14:50
|

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

|
Цитата(jcxz @ May 29 2018, 18:00)  PS: Кто-нить ещё, кроме Forger, видит проблему с работой IrState.ctim10ms ?  Я вижу. Пример. Есть задача, да пусть будет вообще без 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-битные МК, где требовалось защищать даже саму запись многобайтного числа. Логично?  P.S. Можно не запрещать прерываний (аля критические секции), а использовать для этого специальные конструкции LDREX/STREX, регламентирующие эксклюзивный доступ к памяти.
Сообщение отредактировал Arlleex - May 29 2018, 14:53
|
|
|
|
|
May 29 2018, 15:27
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Arlleex @ May 29 2018, 17:50)  ... Что получилось? RingBuffer_DataCnt == 9, а по факту их было 10, один байт потеряли. Классическое нарушение атомарности. И никакие атомарные записи LDR/STR тут не при чем, это не 8-битные МК, где требовалось защищать даже саму запись многобайтного числа. Логично?  P.S. Можно не запрещать прерываний (аля критические секции), а использовать для этого специальные конструкции LDREX/STREX, регламентирующие эксклюзивный доступ к памяти. Всё что Вы написали, справедливо для случая если операции чтения-модификации-записи есть в прерываемом коде (т.е. - фоновой задаче). Я уже это писал выше, прочитайте. Здесь совсем не тот случай. Посмотрите внимательнее на исходный код: в задаче переменная только записывается!. Я на этот уже тоже несколько раз указал Forger. И Вы что-ли узрели чтение-модификацию-запись в задаче ТС-а? Где??? Неужто только я вижу там только операции записи. Приведите выдержку где там в фоновой задаче чтение-модификация-запись??? Цитата(Forger @ May 29 2018, 17:15)  Причем в фоне задач еще и производится чтение-модификация-запись Где там чтение-модификация-запись в фоновой задаче??? Приведите выдержку. Цитата(maxntf @ May 29 2018, 17:27)  Я не пойму (от незнания) почему висит ОС вот здесь: Проблем может быть вагон. Например - переполнение стека. Или нехватка быстродействия о чём я писал выше. Снижайте частоту таймера раз в 10 и запрещайте все остальные прерывания. И проверяйте.
|
|
|
|
|
May 29 2018, 16:51
|

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

|
Цитата(jcxz @ May 29 2018, 18:27)  Где там чтение-модификация-запись в фоновой задаче??? Спор уходит в другое русло, начинаются придирки к отдельным словам, хотя, очевидно, что все поняли о чем и идет речь. Если Вы привыкли писать код на грани фола, то это ваше дело, но в данном случае ТС лишь начинает этот путь и его код уже глючит, и никому не ведомо что будет дальше, если все оставить как есть. Я указал на потенциально опасное место. Надеюсь, мои советы пригодятся.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
May 30 2018, 07:05
|
Частый гость
 
Группа: Участник
Сообщений: 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]-для короткого!!!
|
|
|
|
|
May 30 2018, 07:13
|

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

|
Цитата(maxntf @ May 30 2018, 10:05)  В общем выкинул все лишнее из программы. Поменяйте в своем посте теги quote на code, иначе очень трудно читать этот сплошной текст )) Не совсем понятна фраза "раскомментировать вкл/выкл прерывания TIM10", поясните о каких именно кусках кода идет речь. Или покажите ДВА куска кода: работающий и неработающий.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
May 30 2018, 07:17
|
Частый гость
 
Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008

|
Цитата(Forger @ May 30 2018, 10:13)  Поменяйте в своем посте теги quote на code, иначе очень трудно читать этот сплошной текст )) Не совсем понятна фраза "раскомментировать вкл/выкл прерывания TIM10", поясните о каких именно кусках кода идет речь. Или покажите ДВА куска кода: работающий и неработающий. ну в смысле включать и выключать прерывания enableInterruptTIM10() и disableInterruptTIM10() (они в примере закомментированы)
|
|
|
|
|
May 30 2018, 07:25
|
Частый гость
 
Группа: Участник
Сообщений: 107
Регистрация: 13-05-09
Пользователь №: 49 008

|
Цитата(Forger @ May 30 2018, 10:23)  Если закомментировать enableInterruptTIM10(), которые я увидел только в одном месте, то безусловно прерывания от этого таймера работать не будут )) Не в одном месте. в прерывании отключаем при установке семафора, в задаче включаем после того как забрали семафор и выполнили обработку.
|
|
|
|
|
May 30 2018, 07:35
|
Частый гость
 
Группа: Участник
Сообщений: 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]-для короткого!!!
|
|
|
|
|
May 30 2018, 07:36
|
Гуру
     
Группа: Свой
Сообщений: 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. Иначе ваши портянки трудно пролистывать. И меньше народу будут читать сообщения.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|