Перейти к содержанию
    

FreeRTOS - минимальное время тика?

Ещё раз: грабли там видите только Вы.

Опыт! ;)

 

Операции записи базовых типов данных в Cortex-M - все атомарные.
Да это тут при чем? Речь совсем про другое!

Про атомарность записи таких маленьких переменных никто ни спорит.

Речь про ИЗМЕНЕНИЕ одного и того же объекта в фоне задач и прерываниях.

Причем в фоне задач еще и производится чтение-модификация-запись, которую можно безопасно прервать в прерывании в одном случае - в нем лишь ЧИТАЕТСЯ объект, но НЕ ИЗМЕНЯЕТСЯ.

 

Я понимаю, если в прерываниях мы только читаем некую volatile переменную, а меняем ее ТОЛЬКО в фоне задач. Это вполне безопасно.

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

 

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

PPS: ТСу можно разве что посоветовать объявить IrState.ctim10ms с модификатором volatile. Хотя возможно что он уже есть, так как объявления её не приведено.

Проблема не с переменной.

Я не пойму (от незнания) почему висит ОС вот здесь:

xSemaphoreGiveFromISR(IrState.xSemNx, &xHigherPriorityTaskWoken);

portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);

В том случае если Task2 еще не дошла до обработки семафора IrState.xSemNx.

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

Изменено пользователем maxntf

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Проблема не с переменной.

Я не пойму (от незнания) почему висит ОС вот здесь:

 

В том случае если Task2 еще не дошла до обработки семафора IrState.xSemNx.

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

 

Для начала попробуйте упростить код до минимум, оставив в прерывании только безусловный сигнал семафора,

а в задаче ждать этот семафор по достаточному таймауту. Таймер всегда работает, задача всегда ждет.

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

Проверять условие таймаута. Если хотя бы раз не дождались семафора, разбираемся, почему косячит примитивный код.

Если код не косячит, добавлять свой код по чуть-чуть, каждый раз проверяя, все ли правильно.

В итоге, вы доберетесь до косячного места.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для начала попробуйте упростить код до минимум...

Этим и планирую заняться. Попробую добиться глюков на простом коде (если получиться), а потом опишу непонятные мне моменты.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Изменено пользователем Arlleex

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...

Что получилось? RingBuffer_DataCnt == 9, а по факту их было 10, один байт потеряли. Классическое нарушение атомарности. И никакие атомарные записи LDR/STR тут не при чем, это не 8-битные МК, где требовалось защищать даже саму запись многобайтного числа.

Логично? :rolleyes:

P.S. Можно не запрещать прерываний (аля критические секции), а использовать для этого специальные конструкции LDREX/STREX, регламентирующие эксклюзивный доступ к памяти.

Всё что Вы написали, справедливо для случая если операции чтения-модификации-записи есть в прерываемом коде (т.е. - фоновой задаче).

Я уже это писал выше, прочитайте.

Здесь совсем не тот случай. Посмотрите внимательнее на исходный код: в задаче переменная только записывается!. Я на этот уже тоже несколько раз указал Forger.

И Вы что-ли узрели чтение-модификацию-запись в задаче ТС-а? Где??? Неужто только я вижу там только операции записи.

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

 

Причем в фоне задач еще и производится чтение-модификация-запись

Где там чтение-модификация-запись в фоновой задаче???

Приведите выдержку.

 

Я не пойму (от незнания) почему висит ОС вот здесь:

Проблем может быть вагон. Например - переполнение стека. Или нехватка быстродействия о чём я писал выше.

Снижайте частоту таймера раз в 10 и запрещайте все остальные прерывания. И проверяйте.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Где там чтение-модификация-запись в фоновой задаче???

Спор уходит в другое русло, начинаются придирки к отдельным словам, хотя, очевидно, что все поняли о чем и идет речь.

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

Я указал на потенциально опасное место. Надеюсь, мои советы пригодятся.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем выкинул все лишнее из программы.

ТО таймера ставил от 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);
}

Изменено пользователем IgorKossak
[codebox] для длинного кода. [code]-для короткого!!!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем выкинул все лишнее из программы.

Поменяйте в своем посте теги quote на code, иначе очень трудно читать этот сплошной текст ))

Не совсем понятна фраза "раскомментировать вкл/выкл прерывания TIM10", поясните о каких именно кусках кода идет речь.

Или покажите ДВА куска кода: работающий и неработающий.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поменяйте в своем посте теги quote на code, иначе очень трудно читать этот сплошной текст ))

Не совсем понятна фраза "раскомментировать вкл/выкл прерывания TIM10", поясните о каких именно кусках кода идет речь.

Или покажите ДВА куска кода: работающий и неработающий.

ну в смысле включать и выключать прерывания enableInterruptTIM10() и disableInterruptTIM10() (они в примере закомментированы)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ну в смысле включать и выключать прерывания enableInterruptTIM10() и disableInterruptTIM10() (они в примере закомментированы)

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Не в одном месте.

в прерывании отключаем при установке семафора, в задаче включаем после того как забрали семафор и выполнили обработку.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не в одном месте.

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

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

}

Изменено пользователем IgorKossak
[codebox] для длинного кода. [code]-для короткого!!!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если оставить как в коде ниже, не работает. Если раскомментировать вкл/выкл прерывания 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. Иначе ваши портянки трудно пролистывать. И меньше народу будут читать сообщения.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...