Forger 26 29 мая, 2018 Опубликовано 29 мая, 2018 · Жалоба Ещё раз: грабли там видите только Вы. Опыт! ;) Операции записи базовых типов данных в Cortex-M - все атомарные.Да это тут при чем? Речь совсем про другое! Про атомарность записи таких маленьких переменных никто ни спорит. Речь про ИЗМЕНЕНИЕ одного и того же объекта в фоне задач и прерываниях. Причем в фоне задач еще и производится чтение-модификация-запись, которую можно безопасно прервать в прерывании в одном случае - в нем лишь ЧИТАЕТСЯ объект, но НЕ ИЗМЕНЯЕТСЯ. Я понимаю, если в прерываниях мы только читаем некую volatile переменную, а меняем ее ТОЛЬКО в фоне задач. Это вполне безопасно. Повторюсь, если в прерываниях мы НЕ меняем значения этой переменной, а ТОЛЬКО ЧИТАЕМ. В приведенном примере ситуация другая: одна и та же переменная ИЗМЕНЯЕТСЯ и в фоне задач и в прерываниях. Я уже не знаю как еще более доходчиво объяснить эти очевидные вещи ((( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
maxntf 0 29 мая, 2018 Опубликовано 29 мая, 2018 (изменено) · Жалоба PPS: ТСу можно разве что посоветовать объявить IrState.ctim10ms с модификатором volatile. Хотя возможно что он уже есть, так как объявления её не приведено. Проблема не с переменной. Я не пойму (от незнания) почему висит ОС вот здесь: xSemaphoreGiveFromISR(IrState.xSemNx, &xHigherPriorityTaskWoken); portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); В том случае если Task2 еще не дошла до обработки семафора IrState.xSemNx. То есть глючить начинает когда в прерывании первый раз семафор установили, в задаче его еще не забрали (не успели) и потом в прерывании срабатывают снова эти строки каждые 10мкс. (так как IrState.ctim10ms = 0 и его никто не устанавливает). Изменено 29 мая, 2018 пользователем maxntf Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 29 мая, 2018 Опубликовано 29 мая, 2018 · Жалоба Проблема не с переменной. Я не пойму (от незнания) почему висит ОС вот здесь: В том случае если Task2 еще не дошла до обработки семафора IrState.xSemNx. То есть глючить начинает когда в прерывании первый раз семафор установили, в задаче его еще не забрали (не успели) и потом в прерывании срабатывают снова эти строки каждые 10мкс. (так как IrState.ctim10ms = 0 и его никто не устанавливает). Для начала попробуйте упростить код до минимум, оставив в прерывании только безусловный сигнал семафора, а в задаче ждать этот семафор по достаточному таймауту. Таймер всегда работает, задача всегда ждет. Для наглядности удобно подключить светодиод и правильно настроить моргания, или в прерываниях какому-нить пину делать toggle и смотреть на осциллографе меандр. Вариантов - вагон! Проверять условие таймаута. Если хотя бы раз не дождались семафора, разбираемся, почему косячит примитивный код. Если код не косячит, добавлять свой код по чуть-чуть, каждый раз проверяя, все ли правильно. В итоге, вы доберетесь до косячного места. Этот как готовить первый раз какое-нить новое блюдо - что-то положили, пробуем, анализируем. Но никогда не кладем все сразу скопом к одну кастрюлю ))) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
maxntf 0 29 мая, 2018 Опубликовано 29 мая, 2018 · Жалоба Для начала попробуйте упростить код до минимум... Этим и планирую заняться. Попробую добиться глюков на простом коде (если получиться), а потом опишу непонятные мне моменты. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 183 29 мая, 2018 Опубликовано 29 мая, 2018 (изменено) · Жалоба PS: Кто-нить ещё, кроме Forger, видит проблему с работой IrState.ctim10ms ? :rolleyes: Я вижу. Пример. Есть задача, да пусть будет вообще без 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: P.S. Можно не запрещать прерываний (аля критические секции), а использовать для этого специальные конструкции LDREX/STREX, регламентирующие эксклюзивный доступ к памяти. Изменено 29 мая, 2018 пользователем Arlleex Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 29 мая, 2018 Опубликовано 29 мая, 2018 · Жалоба ... Что получилось? RingBuffer_DataCnt == 9, а по факту их было 10, один байт потеряли. Классическое нарушение атомарности. И никакие атомарные записи LDR/STR тут не при чем, это не 8-битные МК, где требовалось защищать даже саму запись многобайтного числа. Логично? :rolleyes: P.S. Можно не запрещать прерываний (аля критические секции), а использовать для этого специальные конструкции LDREX/STREX, регламентирующие эксклюзивный доступ к памяти. Всё что Вы написали, справедливо для случая если операции чтения-модификации-записи есть в прерываемом коде (т.е. - фоновой задаче). Я уже это писал выше, прочитайте. Здесь совсем не тот случай. Посмотрите внимательнее на исходный код: в задаче переменная только записывается!. Я на этот уже тоже несколько раз указал Forger. И Вы что-ли узрели чтение-модификацию-запись в задаче ТС-а? Где??? Неужто только я вижу там только операции записи. Приведите выдержку где там в фоновой задаче чтение-модификация-запись??? Причем в фоне задач еще и производится чтение-модификация-запись Где там чтение-модификация-запись в фоновой задаче??? Приведите выдержку. Я не пойму (от незнания) почему висит ОС вот здесь: Проблем может быть вагон. Например - переполнение стека. Или нехватка быстродействия о чём я писал выше. Снижайте частоту таймера раз в 10 и запрещайте все остальные прерывания. И проверяйте. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 29 мая, 2018 Опубликовано 29 мая, 2018 · Жалоба Где там чтение-модификация-запись в фоновой задаче??? Спор уходит в другое русло, начинаются придирки к отдельным словам, хотя, очевидно, что все поняли о чем и идет речь. Если Вы привыкли писать код на грани фола, то это ваше дело, но в данном случае ТС лишь начинает этот путь и его код уже глючит, и никому не ведомо что будет дальше, если все оставить как есть. Я указал на потенциально опасное место. Надеюсь, мои советы пригодятся. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
maxntf 0 30 мая, 2018 Опубликовано 30 мая, 2018 (изменено) · Жалоба В общем выкинул все лишнее из программы. ТО таймера ставил от 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); } } } #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); } Изменено 30 мая, 2018 пользователем IgorKossak [codebox] для длинного кода. [code]-для короткого!!! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба В общем выкинул все лишнее из программы. Поменяйте в своем посте теги quote на code, иначе очень трудно читать этот сплошной текст )) Не совсем понятна фраза "раскомментировать вкл/выкл прерывания TIM10", поясните о каких именно кусках кода идет речь. Или покажите ДВА куска кода: работающий и неработающий. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
maxntf 0 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба Поменяйте в своем посте теги quote на code, иначе очень трудно читать этот сплошной текст )) Не совсем понятна фраза "раскомментировать вкл/выкл прерывания TIM10", поясните о каких именно кусках кода идет речь. Или покажите ДВА куска кода: работающий и неработающий. ну в смысле включать и выключать прерывания enableInterruptTIM10() и disableInterruptTIM10() (они в примере закомментированы) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба ну в смысле включать и выключать прерывания enableInterruptTIM10() и disableInterruptTIM10() (они в примере закомментированы) Если закомментировать enableInterruptTIM10(), которые я увидел только в одном месте, то безусловно прерывания от этого таймера работать не будут )) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
maxntf 0 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба Если закомментировать enableInterruptTIM10(), которые я увидел только в одном месте, то безусловно прерывания от этого таймера работать не будут )) Не в одном месте. в прерывании отключаем при установке семафора, в задаче включаем после того как забрали семафор и выполнили обработку. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба Не в одном месте. Я смотрю Ваш код, по поиску (CTRL-F) вижу enableInterruptTIM10 только в одном месте (( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
maxntf 0 30 мая, 2018 Опубликовано 30 мая, 2018 (изменено) · Жалоба Привожу проблемные участки кода (инициализация та что в пост №68): 1. Не работает. 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. Работает 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.Работает 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);//пин контроля работы программы } } Изменено 30 мая, 2018 пользователем IgorKossak [codebox] для длинного кода. [code]-для короткого!!! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба Если оставить как в коде ниже, не работает. Если раскомментировать вкл/выкл прерывания TIM10 - работает. Я не знаю что делает xSemaphoreGiveFromISR() во FreeRTOS, подозреваю что переводит семафор в сигнальное состояние. Если так, то зачем Вы её вызываете всегда когда ctim10ms==0 ? ctim10ms обнулилась и после этого вы начинаете долбить семафор постоянно. Зачем??? Достаточно один раз вызвать xSemaphoreGiveFromISR(). При частоте таймера ==100кГц и частоте ядра==16МГц, у Вас как только обнуляется ctim10ms, то потом в каждом прерывании вызывается xSemaphoreGiveFromISR(), а так как она наверняка длительная (и следующая за ней функция - скорей всего ещё более длительная, так как скорей всего в ней делается решедулинг задач), то к моменту выхода из ISR успевает пройти >= 160 тактов ядра. А значит - ждёт уже новое прерывание таймера и сразу снова входит в ISR и всё повторяется. Естественно, что всё блокируется на постоянных входах в ISR и на выполнение фоновой задачи времени не остаётся Я Вам на это намекал ещё в самом первом посте, когда советовал посчитать кол-во тактов, но Вы почему то не читаете советы (чукча не читатель? зачем тогда спрашивать?). Вызывать xSemaphoreGiveFromISR() нужно только один раз. Например так: 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. Иначе ваши портянки трудно пролистывать. И меньше народу будут читать сообщения. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться