Timer16 Использование прерываний: различия между версиями
Sh-sergey (обсуждение | вклад) Нет описания правки |
Sh-sergey (обсуждение | вклад) Нет описания правки Метка: визуальный редактор отключён |
||
Строка 129: | Строка 129: | ||
На временной диаграмме видны многократные срабатывания прерывания по сравнению (CMP). Однократные прерывания по перезагрузке (ARR) связаны с реализацией Timer16. | На временной диаграмме видны многократные срабатывания прерывания по сравнению (CMP). Однократные прерывания по перезагрузке (ARR) связаны с реализацией Timer16. | ||
=== Варианты обхода === | |||
Повторная установка флага потребует подбора обходного пути в зависимости от задачи. Основная идея - отключение прерывания на время счета, равного значению сравнения. Рассмотрим некоторые из возможных обходных путей работы с прерываниями по сравнению. | |||
==== Отключение прерывания по сравнению и повторное включение в основном цикле ==== | |||
Один из возможных вариантов обхода: временное отключение прерывания по сравнению на время до смены значения счетчика в другой части программы, например, в основном цикле, как в примере ниже: | Один из возможных вариантов обхода: временное отключение прерывания по сравнению на время до смены значения счетчика в другой части программы, например, в основном цикле, как в примере ниже: | ||
{{#spoiler:show=Развернуть код|hide=Свернуть код| | {{#spoiler:show=Развернуть код|hide=Свернуть код| | ||
Строка 264: | Строка 268: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
}} | }} | ||
[[Файл:Timer16 Однократные срабатывания прерываний.png|мини|375x375px]] | [[Файл:Timer16 Однократные срабатывания прерываний.png|мини|375x375px]]На временной диаграмме видны однократные срабатывания прерываний по сравнению (CMP) и перезагрузке (ARR): | ||
На временной диаграмме видны однократные срабатывания прерываний по сравнению (CMP) и перезагрузке (ARR): | |||
Такой вариант можно использовать, если программа работает в режиме pooling'а, регулярно вызывая основной цикл. | |||
==== Отключение прерывания по сравнению и повторное включение в прерывании по ==== | |||
Возможный вариант обхода: отключение прерывания по сравнению и повторное включение в прерывании по переполнению. Пример представлен ниже: | |||
{{#spoiler:show=Развернуть код|hide=Свернуть код| | |||
<syntaxhighlight lang="c"> | |||
#include "mik32_hal.h" | |||
#include "mik32_hal_timer16.h" | |||
#include "mik32_hal_irq.h" | |||
#include "uart_lib.h" | |||
#include "xprintf.h" | |||
/* | |||
* В примере демонстрируется обход повторного входа в прерывание по CMP. | |||
* Обход заключается в отключении прерывания CMP при внутри обработчика флага CMP | |||
* и его включении внутри обработчика прерывания ARR. | |||
* */ | |||
extern unsigned long __TEXT_START__; | |||
#define ARR_VALUE 6 | |||
#define CMP_VALUE (ARR_VALUE / 2) | |||
Timer16_HandleTypeDef htimer16_1; | |||
void SystemClock_Config(void); | |||
static void Timer16_1_Init(void); | |||
void GPIO_Init(); | |||
int main() | |||
{ | |||
write_csr(mtvec, &__TEXT_START__); | |||
HAL_Init(); | |||
SystemClock_Config(); | |||
UART_Init(UART_0, 3333, UART_CONTROL1_TE_M | UART_CONTROL1_M_8BIT_M, 0, 0); | |||
xprintf("\nStart\n"); | |||
GPIO_Init(); | |||
Timer16_1_Init(); | |||
HAL_Timer16_Counter_Start(&htimer16_1, ARR_VALUE); | |||
HAL_Timer16_SetCMP(&htimer16_1, CMP_VALUE); | |||
HAL_Timer16_SetInterruptMask(&htimer16_1, TIMER16_FLAG_CMPM | TIMER16_FLAG_ARRM); | |||
/* Включать прерывания Timer16 рекомендуется после его инициализации */ | |||
HAL_EPIC_MaskLevelSet(HAL_EPIC_TIMER16_1_MASK); | |||
HAL_IRQ_EnableInterrupts(); | |||
while (1) | |||
{ | |||
GPIO_0->OUTPUT = (GPIO_0->OUTPUT & ~(0b111 << 7)) | (TIMER16_1->CNT << 7); // Вывод текущего счета таймера | |||
} | |||
} | |||
void SystemClock_Config(void) | |||
{ | |||
PCC_InitTypeDef PCC_OscInit = {0}; | |||
PCC_OscInit.OscillatorEnable = PCC_OSCILLATORTYPE_ALL; | |||
PCC_OscInit.FreqMon.OscillatorSystem = PCC_OSCILLATORTYPE_OSC32M; | |||
PCC_OscInit.FreqMon.ForceOscSys = PCC_FORCE_OSC_SYS_UNFIXED; | |||
PCC_OscInit.FreqMon.Force32KClk = PCC_FREQ_MONITOR_SOURCE_OSC32K; | |||
PCC_OscInit.AHBDivider = 0; | |||
PCC_OscInit.APBMDivider = 0; | |||
PCC_OscInit.APBPDivider = 0; | |||
PCC_OscInit.HSI32MCalibrationValue = 128; | |||
PCC_OscInit.LSI32KCalibrationValue = 8; | |||
PCC_OscInit.RTCClockSelection = PCC_RTC_CLOCK_SOURCE_AUTO; | |||
PCC_OscInit.RTCClockCPUSelection = PCC_CPU_RTC_CLOCK_SOURCE_OSC32K; | |||
HAL_PCC_Config(&PCC_OscInit); | |||
} | |||
static void Timer16_1_Init(void) | |||
{ | |||
htimer16_1.Instance = TIMER16_1; | |||
/* Настройка тактирования */ | |||
htimer16_1.Clock.Source = TIMER16_SOURCE_INTERNAL_OSC32K; | |||
htimer16_1.CountMode = TIMER16_COUNTMODE_INTERNAL; /* При тактировании от Input1 не имеет значения */ | |||
htimer16_1.Clock.Prescaler = TIMER16_PRESCALER_128; | |||
htimer16_1.ActiveEdge = TIMER16_ACTIVEEDGE_RISING; /* Выбирается при тактировании от Input1 */ | |||
/* Настройка режима обновления регистра ARR и CMP */ | |||
htimer16_1.Preload = TIMER16_PRELOAD_AFTERWRITE; | |||
/* Настройка триггера */ | |||
htimer16_1.Trigger.Source = TIMER16_TRIGGER_TIM1_GPIO1_9; | |||
htimer16_1.Trigger.ActiveEdge = TIMER16_TRIGGER_ACTIVEEDGE_SOFTWARE; /* При использовании триггера значение должно быть отлично от software */ | |||
htimer16_1.Trigger.TimeOut = TIMER16_TIMEOUT_DISABLE; /* Разрешить повторное срабатывание триггера */ | |||
/* Настройки фильтра */ | |||
htimer16_1.Filter.ExternalClock = TIMER16_FILTER_NONE; | |||
htimer16_1.Filter.Trigger = TIMER16_FILTER_NONE; | |||
/* Настройка режима энкодера */ | |||
htimer16_1.EncoderMode = TIMER16_ENCODER_DISABLE; | |||
/* Выходной сигнал */ | |||
htimer16_1.Waveform.Enable = TIMER16_WAVEFORM_GENERATION_DISABLE; | |||
htimer16_1.Waveform.Polarity = TIMER16_WAVEFORM_POLARITY_NONINVERTED; | |||
HAL_Timer16_Init(&htimer16_1); | |||
} | |||
void GPIO_Init() | |||
{ | |||
GPIO_InitTypeDef GPIO_InitStruct = {0}; | |||
__HAL_PCC_GPIO_0_CLK_ENABLE(); | |||
__HAL_PCC_GPIO_1_CLK_ENABLE(); | |||
__HAL_PCC_GPIO_2_CLK_ENABLE(); | |||
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9; | |||
GPIO_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; | |||
GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; | |||
HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); | |||
} | |||
void trap_handler() | |||
{ | |||
GPIO_0->OUTPUT = (GPIO_0->OUTPUT & ~(0b111 << 7)) | (TIMER16_1->CNT << 7); // Вывод текущего счета таймера | |||
if ((TIMER16_1->ISR & TIMER16_FLAG_CMPM) && (TIMER16_1->IER & TIMER16_FLAG_CMPM)) | |||
{ | |||
GPIO_0->SET = 1 << 0; | |||
TIMER16_1->IER &= ~TIMER16_FLAG_CMPM; // Запретить прерывание по CMP. | |||
/* Обработчик CMP */ | |||
TIMER16_1->ICR = TIMER16_FLAG_CMPM; | |||
GPIO_0->CLEAR = 1 << 0; | |||
} | |||
if ((TIMER16_1->ISR & TIMER16_FLAG_ARRM) && (TIMER16_1->IER & TIMER16_FLAG_ARRM)) | |||
{ | |||
GPIO_0->SET = 1 << 2; | |||
TIMER16_1->ICR = TIMER16_FLAG_CMPM; | |||
TIMER16_1->IER |= TIMER16_FLAG_CMPM; // Разрешить прерывание по CMP. | |||
/* Обработчик ARR */ | |||
TIMER16_1->ICR = TIMER16_FLAG_ARRM; | |||
GPIO_0->CLEAR = 1 << 2; | |||
} | |||
} | |||
</syntaxhighlight> | |||
}} |
Версия от 08:11, 5 марта 2025
Особенность установки флагов прерываний
Флаг прерывания по сравнению выставляется, если значение счета таймера совпадает со значением сравнения. Из-за этого возможна ситуация, когда после сброса флага прерывания по сравнению внутри обработчика флаг снова выставляется на следующем такте, если счетчик таймера не успел увеличиться. В таком случае, после выхода из обработчика прерывания происходит повторное срабатывание.
Пример ниже демонстрирует эту ситуацию. В этом примере Timer16 тактируется от встроенного низкочастотного источника тактирования.
На временной диаграмме видны многократные срабатывания прерывания по сравнению (CMP). Однократные прерывания по перезагрузке (ARR) связаны с реализацией Timer16.
Варианты обхода
Повторная установка флага потребует подбора обходного пути в зависимости от задачи. Основная идея - отключение прерывания на время счета, равного значению сравнения. Рассмотрим некоторые из возможных обходных путей работы с прерываниями по сравнению.
Отключение прерывания по сравнению и повторное включение в основном цикле
Один из возможных вариантов обхода: временное отключение прерывания по сравнению на время до смены значения счетчика в другой части программы, например, в основном цикле, как в примере ниже:
На временной диаграмме видны однократные срабатывания прерываний по сравнению (CMP) и перезагрузке (ARR):
Такой вариант можно использовать, если программа работает в режиме pooling'а, регулярно вызывая основной цикл.
Отключение прерывания по сравнению и повторное включение в прерывании по
Возможный вариант обхода: отключение прерывания по сравнению и повторное включение в прерывании по переполнению. Пример представлен ниже: