Проблема с таймером на STM32F7 - неустойчивое поведение

Во-первых, я полный n00b и только начинаю работать с STM32. Я столкнулся с каким-то странным поведением при настройке TIM1, а также при использовании внешнего прерывания EXTI3. Я пытаюсь измерить низкую входную частоту внешнего прерывания и использую TIM1 для увеличения счетчика на 1 МГц, поэтому я могу измерять в микросекундах. Я делаю это, так как кажется, что SysTick недостаточно быстр (1 мс), чтобы дать мне точное измерение.

Тактовая частота ядра составляет 16 МГц, а TIM1 настроен следующим образом:

htim1.Instance = TIM1;
htim1.Init.Prescaler = 16; // 16 MHz / 16 = 1 MHz
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 1; // 1Mhz no sub-dividing
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
...
HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
...
void TIM1_UP_TIM10_IRQHandler(void)
{
    timerTicks++;
    HAL_TIM_IRQHandler(&htim1);
}
...
void EXTI1_IRQHandler(void)
{
    previousTimerTicks = currentTimerTicks;
    currentTimerTicks = timerTicks;
}
...
// Start the timer as interrupt
HAL_TIM_Base_Start_IT(&htim1);
...
while(1){
    // Blink a light so we know the program is running
    HAL_GPIO_TogglePin(GPIOB, Led_Green_Pin);
    HAL_Delay(100);
}

Однако программа в значительной степени блокируется, и цикл выполнения while(), похоже, не выполняется. Тем не менее, я вижу, что счетчик TIM1 увеличивается. Если я замедляю период, скажем, до 100, основной цикл приложения работает нормально.

Это проблема с TIM1 и EXTI3, конфликтующими на этой скорости, или здесь происходит что-то еще? Я полагаю, что у устройства не должно быть проблем со счетчиком 1 МГц, но, возможно, я делаю все это неправильно.

Если вы хотите разделить входные часы на 16, вам следует установить значение Prescaler на 15.
Я не уловил этого в примерах, которым следовал, но вы, кажется, правы, очень полезно знать!

Ответы (1)

Ты все делаешь неправильно.
Невозможно выполнить обработчик прерывания таймера всего за 16 тактов - не говоря уже о цикле while(1).

Что вы должны делать, так это позволить таймеру работать свободно и обрабатывать прерывания только при его переполнении. Мы скоро вернемся к этому.

Затем настройте обработчик прерывания для вашего EXTI и в нем зафиксируйте значение таймера и сохраните его.
В следующем прерывании вычислите разницу между двумя значениями — и вот оно — время между двумя последовательными событиями EXTI, кратное периоду вашего таймера (1 мкс, если вы оставите его таким же).

Но что, если таймер сбрасывается — вот почему мы хотим обрабатывать прерывания таймера при сбрасывании.
Здесь вы можете установить флаг, чтобы сообщить вашему обработчику EXTI, что таймер перешел, и он должен учитывать это при выполнении вычислений.

Хммм, я думал, что это именно то, что я делаю. В моем коде прерывания EXTI все, что я делаю, это получаю значение timerTicks при генерации прерывания, а также сохраняю предыдущее значение. Мой основной цикл выполняет фактический расчет на основе разницы двух значений.
Это корень вашей проблемы. Не используйте переменную, увеличенную ISR таймера — используйте собственный внутренний регистр аппаратного счетчика таймера — он уже есть и доступен для чтения и использования.
Ясно, я думал, что это будет реализовано так же внизу - спасибо, я попробую.
Основное отличие в этом случае состоит в том, что собственный счетчик таймера уже увеличивается без какого-либо вмешательства ЦП, тогда как ваша реализация ISR использует инструкции ЦП.