Время срабатывания таймеров и их точность
Добавлено: Ср авг 31, 2022 13:32:05
Добрый день, довольно простая и обыденная задача вызвала большие затруднения с ее решением. Надо от одного таймера запустить 3 других и получить результат, как на рисунке 1.
TIM4 просто циклично задает начало отсчета (зеленая линия) с постоянной частотой. Далее мне надо получить картину как на рисунке, где все параметры (T1, T2, T3, P1, P2, P3) динамически могу меняться путем задания их длительности с ПК.
Основная проблема в том, что я не могу добиться нужной мне точности срабатывания таймеров и установки логических уровней на пинах мк. Для этого попробовал разные подходы:
1. Изначально была идея сделать на прерываниях. Довольно банально, от TIM4 стартуем все таймеры и в их прерываниях выставляем логические уровни и делаем их перезапуск.
Запускаем все таймеры
Коллбек всех 3х таймеров делаем по такому примеру:
В таком виде это работает, но с ошибками. Если выставить тайминги довольно большими, например T7 = 29 мк сек, то в целом картина более менее стабильна (на логическом анализаторе он будет 31.75), т.е. все тайминги по факту будут больше на порядка 1.5 - 2 мк сек, полагаю из за задержки на время запуска таймеров. Хотя такая задержка вызывает вопросы, не много ли?
Но когда я уменьшаю время срабатывания таймера допустим T7 до 0.1 мк сек от старта, то тут во-первых этот тайминг не выставится меньше 2.5 мк сек (Рисунок 2), во вторых почему-то начинает сильно ошибаться величина P7, может уменьшиться в 1.5 раза и тд, происходит какое-то влияние одной величины на другую.
Такая не предсказуемость меня не сильно устроила, поэтому попробовал второй вариант.
2. Решил я переделать все на one pulse mode. В целом идея та же в коллбеке TIM4 стартуем 3 таймера, но уже настроенные в этом режиме. Для эксперимента взял TIM11 и TIM12
Запускаю я их вместе, времена у них заданы одинаковые, а срабатывают они в разное время. А именно таймер 12 опаздывает на 2.5 мк сек. Измерил я, ради интереса, этот участок кода:
По длительности он как раз занимает порядка 2.2 мк сек. Отсюда следует, если таким образом запускать 3 таймера подряд, то 3й будет отставать от первого на 5 мк сек. Это трындец будет. Ну и вопрос, опять, почему так много уходит времени на запуск таймера?
3. Третьей моей идеей является взять один таймер с 4мя каналами, запустить его в коллбеке TIM4 и в прерывании от каждого канала запускать еще по одному таймеру, чтобы выдержать необходимые длительности импульсов (P1, P2, P3). Берем пока TIM12 на 2 канала.
Ставлю такие параметры:
Для канала 1 - TIM_OC_InitStruct.CompareValue = 1;
Для канала 2 - TIM_OC_InitStruct.CompareValue = 2500;
Т.е. при запуске в том же самом коллбеке TIM4
Я должен получить, что первый канал сработает сразу же, а второй как отсчитает 2500 тиков, что равно 25 мксек.
По факту первый стартует спустя 1.5 мк сек, а второй 25 + 1.5 мк сек. Т.е. опять задержка от времени старта единицы мк секунд.
Исходя из этого:
1) Почему такие большие задержки запуска таймеров?
2) Какой подход для решения данной задачи будет самым правильным?
3) Может просто я что-то делаю не так?
Буду благодарен за любые подсказки, ибо не понимаю причину такой работы мк.
Лирическое отступление: мк - stm32f746. Настройка в CubeMX (для таймеров LL), так как используется FreeRTOS, но приоритет таймеров выше чем у любой задачи ОС. Таймеры функции ОС не используют
TIM4 просто циклично задает начало отсчета (зеленая линия) с постоянной частотой. Далее мне надо получить картину как на рисунке, где все параметры (T1, T2, T3, P1, P2, P3) динамически могу меняться путем задания их длительности с ПК.
Основная проблема в том, что я не могу добиться нужной мне точности срабатывания таймеров и установки логических уровней на пинах мк. Для этого попробовал разные подходы:
1. Изначально была идея сделать на прерываниях. Довольно банально, от TIM4 стартуем все таймеры и в их прерываниях выставляем логические уровни и делаем их перезапуск.
Запускаем все таймеры
Спойлер
Код: Выделить всё
void TIM4_Callback(void)
{
if(LL_TIM_IsActiveFlag_UPDATE(TIM4))
{
// START
GPIOC->BSRR = GPIO_BSRR_BS0;
// START TIMER 7
TIM7->ARR = SetTime7 - 1;
TIM7->SR &= ~ TIM_SR_UIF;
TIM7->CNT = 0;
TIM7->DIER = TIM_DIER_UIE;
LL_TIM_EnableCounter(TIM7);
// START TIMER 3
TIM3->ARR = SeеTime3 - 1;
TIM3->EGR = TIM_EGR_UG;
TIM3 ->SR &= ~ TIM_SR_UIF;
TIM3->CNT = 0;
TIM3->DIER = TIM_DIER_UIE;
LL_TIM_EnableCounter(TIM3);
// START TIMER 5
TIM5->ARR = SetTime5 - 1;
TIM5->EGR = TIM_EGR_UG;
LL_TIM_ClearFlag_UPDATE(TIM5);
TIM5->CNT = 0;
TIM5->DIER = TIM_DIER_UIE;
LL_TIM_EnableCounter(TIM5);
}
else
{
__NOP();
}
LL_TIM_ClearFlag_UPDATE(TIM4);
}
Спойлер
Код: Выделить всё
void TIM7_Callback(void)
{
if(LL_TIM_IsActiveFlag_UPDATE(TIM7))
{
switch (StateTim7)
{
case 0:
StateTim7 = 1;
// SET PIN LEVEL HIGH
GPIOC->BSRR = GPIO_BSRR_BS3;
TIM7->ARR = SetPeriod7 - 1;
break;
case 1:
StateTim7 = 0;
// SET PIN LEVEL LOW
GPIOC->BSRR = GPIO_BSRR_BR3;
LL_TIM_DisableCounter(TIM7);
break;
}
}
else
{
__NOP();
}
LL_TIM_ClearFlag_UPDATE(TIM7);
}
Но когда я уменьшаю время срабатывания таймера допустим T7 до 0.1 мк сек от старта, то тут во-первых этот тайминг не выставится меньше 2.5 мк сек (Рисунок 2), во вторых почему-то начинает сильно ошибаться величина P7, может уменьшиться в 1.5 раза и тд, происходит какое-то влияние одной величины на другую.
Такая не предсказуемость меня не сильно устроила, поэтому попробовал второй вариант.
2. Решил я переделать все на one pulse mode. В целом идея та же в коллбеке TIM4 стартуем 3 таймера, но уже настроенные в этом режиме. Для эксперимента взял TIM11 и TIM12
Спойлер
Код: Выделить всё
void TIM4_Callback(void)
{
if(LL_TIM_IsActiveFlag_UPDATE(TIM4))
{
TIM11->ARR = 4000;
TIM11->CCR1 = 1000;
TIM11->EGR = TIM_EGR_UG;
TIM11->CNT = 0;
TIM11->SR &= ~ TIM_SR_UIF;
TIM11->DIER = TIM_DIER_UIE;
HAL_TIM_Base_Start(&htim11);
HAL_TIM_PWM_Start(&htim11, TIM_CHANNEL_1);
TIM12->ARR = 4000;
TIM12->CCR2 = 1000;
TIM12->EGR = TIM_EGR_UG;
TIM12->CNT = 0;
TIM12->SR &= ~ TIM_SR_UIF;
TIM12->DIER = TIM_DIER_UIE;
HAL_TIM_Base_Start(&htim12);
HAL_TIM_PWM_Start(&htim12, TIM_CHANNEL_2);
}
else
{
__NOP();
}
LL_TIM_ClearFlag_UPDATE(TIM4);
}
Код: Выделить всё
TIM11->ARR = 4000;
TIM11->CCR1 = 1000;
TIM11->EGR = TIM_EGR_UG;
TIM11->CNT = 0;
TIM11->SR &= ~ TIM_SR_UIF;
TIM11->DIER = TIM_DIER_UIE;
HAL_TIM_Base_Start(&htim11);
HAL_TIM_PWM_Start(&htim11, TIM_CHANNEL_1);
3. Третьей моей идеей является взять один таймер с 4мя каналами, запустить его в коллбеке TIM4 и в прерывании от каждого канала запускать еще по одному таймеру, чтобы выдержать необходимые длительности импульсов (P1, P2, P3). Берем пока TIM12 на 2 канала.
Ставлю такие параметры:
Для канала 1 - TIM_OC_InitStruct.CompareValue = 1;
Для канала 2 - TIM_OC_InitStruct.CompareValue = 2500;
Т.е. при запуске в том же самом коллбеке TIM4
Код: Выделить всё
TIM12->EGR = TIM_EGR_UG;
LL_TIM_EnableIT_CC1(TIM12);
LL_TIM_EnableIT_CC2(TIM12);
LL_TIM_EnableCounter(TIM12);
Спойлер
Код: Выделить всё
void TIM12_Callback(void)
{
if(LL_TIM_IsActiveFlag_UPDATE(TIM12))
{
if(LL_TIM_IsActiveFlag_CC1(TIM12))
{
LL_TIM_ClearFlag_CC1(TIM12);
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_2); // Test
LL_TIM_DisableIT_CC1(TIM12);
}
else if(LL_TIM_IsActiveFlag_CC2(TIM12))
{
LL_TIM_ClearFlag_CC2(TIM12);
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_2); // Test
LL_TIM_DisableIT_CC2(TIM12);
LL_TIM_DisableCounter(TIM12);
}
else
{
__NOP();
}
}
else
{
__NOP();
}
}
По факту первый стартует спустя 1.5 мк сек, а второй 25 + 1.5 мк сек. Т.е. опять задержка от времени старта единицы мк секунд.
Исходя из этого:
1) Почему такие большие задержки запуска таймеров?
2) Какой подход для решения данной задачи будет самым правильным?
3) Может просто я что-то делаю не так?
Буду благодарен за любые подсказки, ибо не понимаю причину такой работы мк.
Лирическое отступление: мк - stm32f746. Настройка в CubeMX (для таймеров LL), так как используется FreeRTOS, но приоритет таймеров выше чем у любой задачи ОС. Таймеры функции ОС не используют