Задача: найти багу в коде.
Описание:
TIM2-timebase таймер, 1000гц прерывания
TIM3-счётный таймер, подключен к TRGO TIM2, прерывания по сравнению по всем Compare регистрам
глобальный массив uint16_t Query_counter[8] для хранения значений требуемых задержек
глобальный массив void(*Pointer_query[8])(); для хранения указателей на функции-исполнители
глобальная переменная uint8_t Query_counter указывает количество валидных позиций в массивах
глобальная переменная uint8_t New_query_items количество необработанных позиций в массивах
из Main() вызываем функцию ADD_DELAY
Код
void ADD_DELAY(uint16_t Delay_ms, void (*Function)())
{
Pointer_query[Query_counter+New_query_items] = Function;
Value_query[Query_counter+New_query_items] = Delay_ms;
New_query_items++;
TIM2->DIER |= 0x0001; // UIE- Update interrupt enable
}
{
Pointer_query[Query_counter+New_query_items] = Function;
Value_query[Query_counter+New_query_items] = Delay_ms;
New_query_items++;
TIM2->DIER |= 0x0001; // UIE- Update interrupt enable
}
-закидываем значения в оба массива
-инкриментируем счётчик значений, которые были добавлены между двумя прерываниями TIM2, но ещё не были обработаны
-включаем прерывания TIM2 по перезагрузке
-возвращаемся к основной программе, ожидая TIM2 (флаг уже висит, поэтому технически ждать не придётся.)
Код
void TIM2_IRQHandler(void)
{
uint8_t i= 0;
void(*Complete_item)();
TIM2->SR = 0;
if(New_query_items>0)
{
TIM3->CR1 &= ~0x0001; // CEN- Counter disable
if((Query_counter-1)<3)
{
for(i=0; i<Query_counter; i++)
{
Value_query[i] -= TIM3->CNT;
*TIM3_CCR[i] = Value_query[i];
}
TIM3->CNT = 0;
}
while(New_query_items>0)
{
if((Query_counter-1)<4)
{
*TIM3_CCR[Query_counter] = Value_query[Query_counter];
Query_counter++;
New_query_items--;
}
else
{
Query_counter += New_query_items;
New_query_items = 0;
}
}
TIM3->CR1 |= 0x0001; // CEN- Counter enable
}
if((Query_counter>4) && (New_query_items==0))
{
for(i=4; i<Query_counter; i++)
{
Value_query[i]--;
if(Value_query[i]==0)
{
Query_counter--;
Complete_item = Pointer_query[i];
Complete_item();
}
}
}
if(Query_counter<5)
TIM2->DIER &= ~0x0001;
}
{
uint8_t i= 0;
void(*Complete_item)();
TIM2->SR = 0;
if(New_query_items>0)
{
TIM3->CR1 &= ~0x0001; // CEN- Counter disable
if((Query_counter-1)<3)
{
for(i=0; i<Query_counter; i++)
{
Value_query[i] -= TIM3->CNT;
*TIM3_CCR[i] = Value_query[i];
}
TIM3->CNT = 0;
}
while(New_query_items>0)
{
if((Query_counter-1)<4)
{
*TIM3_CCR[Query_counter] = Value_query[Query_counter];
Query_counter++;
New_query_items--;
}
else
{
Query_counter += New_query_items;
New_query_items = 0;
}
}
TIM3->CR1 |= 0x0001; // CEN- Counter enable
}
if((Query_counter>4) && (New_query_items==0))
{
for(i=4; i<Query_counter; i++)
{
Value_query[i]--;
if(Value_query[i]==0)
{
Query_counter--;
Complete_item = Pointer_query[i];
Complete_item();
}
}
}
if(Query_counter<5)
TIM2->DIER &= ~0x0001;
}
-сбрасываем флаг прерывания
-если прерывание было вызвано функцией ADD_DELAY то New_query_items больше нуля и тогда мы:
--отключаем счётный таймер, временно
--вычитаем из всех заполненных Compare регистров текущее значение счётчика, если ещё остались незаполненные хардварные Compare регистры и обнуляем счётчик
--пока счётчик необработанных запросов не опустел, заполняем хардварные Compare регистры TIM3, после чего просто складываем счётчик обработанных и нет элементов, получая готовое для работы значение Query_counter
--обратно включаем счётный таймер
-если прерывание не было вызвано функцией ADD_DELAY, значит остались задержки, не поместившиеся в CCR третьего таймера(это избыточно проверяется тут: if((Query_counter>4) && (New_query_items==0))) тогда:
--все элементы массива Value_query[], начиная с пятого декриментируем
--исполняем те, которые кончились.
-если все исполняемые в данный момент задержек умещаются в четыре Compare регистра, то прерывание timebase таймера просто отключается, до следующего исполнения ADD_DELAY(...)
соответственно, после внесения значения в соответствующий CCR мы ожидаем прерывание TIM3 по сравнению, это и есть полезная задержка.
Код
void TIM3_IRQHandler(void)
{
uint8_t Temp = (TIM3->SR>>1)&0x0F;
uint8_t Counter = 0;
void(*Complete_item)();
TIM3->SR = 0;
while(Temp!=0 && Counter<4)
{
if((Temp&0x01)==1)
{
Complete_item = Pointer_query[Counter];
Query_counter--;
if(Query_counter==0)
TIM3->CR1 &= ~0x0001; // CEN- Counter disable
else // If more than one task left, relocate latest task.
{
TIM3->CR1 &= ~0x0001; // CEN- Counter enable
if(Counter<=Query_counter)
{
Value_query[Counter] = Value_query[Query_counter+1];
*TIM3_CCR[Counter] = Value_query[Counter];
Pointer_query[Counter] = Pointer_query[Query_counter+1];
}
TIM3->CR1 |= 0x0001; // CEN- Counter enable
}
Complete_item();
}
Counter++;
Temp = Temp>>1;
}
}
{
uint8_t Temp = (TIM3->SR>>1)&0x0F;
uint8_t Counter = 0;
void(*Complete_item)();
TIM3->SR = 0;
while(Temp!=0 && Counter<4)
{
if((Temp&0x01)==1)
{
Complete_item = Pointer_query[Counter];
Query_counter--;
if(Query_counter==0)
TIM3->CR1 &= ~0x0001; // CEN- Counter disable
else // If more than one task left, relocate latest task.
{
TIM3->CR1 &= ~0x0001; // CEN- Counter enable
if(Counter<=Query_counter)
{
Value_query[Counter] = Value_query[Query_counter+1];
*TIM3_CCR[Counter] = Value_query[Counter];
Pointer_query[Counter] = Pointer_query[Query_counter+1];
}
TIM3->CR1 |= 0x0001; // CEN- Counter enable
}
Complete_item();
}
Counter++;
Temp = Temp>>1;
}
}
-сразу сохраняем биты CC4IF-CC1IF статусного регистра в переменной Temp
-после чего обнуляем флаги всех прерываний
-в цикле, подразумевающем возможность одновременного истечения нескольких задержек последовательно проверяем флаги для всех Compare регистров, начиная с CCR1
--если опрашиваемый в данный момент регистр выдал флаг прерывания, то:
---присваиваем указателю Complete_item значение элемента массива Pointer_query[] соответствующего опрашиваемому в данный момент регистру (для CCR1 это 0, для CCR2 это 1 и т.д.)
---заранее декриментируем счётчик валидных значений
---выключаем таймер на время операций с CCR. если задание не было последним- мы его опять включим
---если же остались ещё задания, то:
----если сработавший Compare регистр содержал в себе не последнюю в списке задачу, то:
-----перекладываем значение последней в списке задачи(Value_query[Query_counter]) в ячейку текущего Compare регистра (Value_query[Current_CCR]) для корректной работы вычитания в TIM2_IRQ и сразу же укладываем это значение в сам регистр
-----перекладываем указатель из последней ячейки в только что освободившуюся.
----в случае, если сработал таймер с последней в списке задачей, нам делать ничего не нужно и мы включаем третий таймер для дальнейшей работы
---теперь можно выполнить Complete_item()
--инкриментируем Current_CCR и сдвигаем Temp для доступа к следующему биту CCRxIF. если это был последний активный бит, выполнение процедуры заканчивается.
Проблема: код корректно отрабатывает только один запуск функции ADD_DELAY, повторный вызов приводит к остановке нормальной работы, т.е. тут
Код
ADD_DELAY(500, &FIRST_HANDLER);
_delay_ms(1000);
ADD_DELAY(1500, &SECOND_HANDLER);
GPIOA->ODR ^= 0x0004;
_delay_ms(1000);
ADD_DELAY(1500, &SECOND_HANDLER);
GPIOA->ODR ^= 0x0004;
светодиод на третьем пине не зажигается
но если посмотреть на проблему ближе, то
Код
void ADD_DELAY(uint16_t Delay_ms, void (*Function)())
{
Pointer_query[Query_counter+New_query_items] = Function;
Value_query[Query_counter+New_query_items] = Delay_ms;
New_query_items++;
GPIOA->ODR ^= 0x0008;
TIM2->DIER |= 0x0001; // UIE- Update interrupt enable
}
{
Pointer_query[Query_counter+New_query_items] = Function;
Value_query[Query_counter+New_query_items] = Delay_ms;
New_query_items++;
GPIOA->ODR ^= 0x0008;
TIM2->DIER |= 0x0001; // UIE- Update interrupt enable
}
так светодиод включается и выключается, а если опустить GPIOA->ODR ^= 0x0008 строчкой ниже, то уже не гаснет
первое, что я сделал, это добавил перед TIM2->DIER |= 0x0001; сброс флага, а в основном алгоритме в цикле ксорил ножку проца. еси верить моему логическому анализатору, дерготня завершалась через 0.996 мс- контроллер уходил в прерывание и больше не возвращался
пробовал сносить флаги TIM2 при входе/выходе из прерывания, пытался отключать второй таймер на время прерывания- ничего не помогает, контроллер остаётся в прерывании.
бесконечных циклов не обнаружил. что меняется во второй раз-загадка, первый раз отрабатывает на ура.
сердечно прошу помочь. две недели сижу на одном месте, рефакторю код. скорее всего чего-то не знаю, доку читал диагонально, но вдумчиво. а тут ещё и учёба- времени ни на что нет.
Перспективы:
когда найду и изничтожу багу, сделаю массив очереди изменяемым и закину в динамическую память. указатели на функции выполненных задержек буду складывать в список и исполнять все подряд, но в самом конце, чтобы это не мешало счёту третьего таймера.
серьёзно прошу помочь, а то я тут с катушек слечу