Timer16 Использование прерываний: различия между версиями
Sh-sergey (обсуждение | вклад) Нет описания правки |
Sh-sergey (обсуждение | вклад) Нет описания правки |
||
Строка 2: | Строка 2: | ||
Флаг прерывания по сравнению выставляется, если значение счета таймера совпадает со значением сравнения. Из-за этого возможна ситуация, когда после сброса флага прерывания по сравнению внутри обработчика флаг снова выставляется на следующем такте, если счетчик таймера не успел увеличиться. В таком случае, после выхода из обработчика прерывания происходит повторное срабатывание. | Флаг прерывания по сравнению выставляется, если значение счета таймера совпадает со значением сравнения. Из-за этого возможна ситуация, когда после сброса флага прерывания по сравнению внутри обработчика флаг снова выставляется на следующем такте, если счетчик таймера не успел увеличиться. В таком случае, после выхода из обработчика прерывания происходит повторное срабатывание. | ||
Пример ниже демонстрирует эту ситуацию. Для воспроизведения | Пример ниже демонстрирует эту ситуацию. Для воспроизведения потребуется использовать источник счета с частотой значительно ниже частоты тактирования системы, например, один из низкоскоростных таймеров.<syntaxhighlight lang="c"> | ||
#include "mik32_hal.h" | |||
#include "mik32_hal_timer16.h" | |||
#include "mik32_hal_irq.h" | |||
#include "uart_lib.h" | |||
#include "xprintf.h" | |||
Timer16_HandleTypeDef htimer16_1; | |||
void SystemClock_Config(void); | |||
static void Timer16_1_Init(void); | |||
void GPIO_Init(); | |||
int main() | |||
{ | |||
HAL_Init(); | |||
SystemClock_Config(); | |||
GPIO_Init(); | |||
Timer16_1_Init(); | |||
HAL_Timer16_Counter_Start(&htimer16_1, 0x7); | |||
HAL_Timer16_SetCMP(&htimer16_1, 0x7 / 2); | |||
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) | |||
{ | |||
} | |||
} | |||
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_16; | |||
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_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; | |||
GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; | |||
HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); | |||
GPIO_InitStruct.Pin = GPIO_PIN_1; | |||
HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); | |||
} | |||
void trap_handler() | |||
{ | |||
if (TIMER16_1->ISR & TIMER16_FLAG_CMPM) | |||
{ | |||
GPIO_0->SET = 1; | |||
TIMER16_1->ICR = TIMER16_FLAG_CMPM; | |||
GPIO_0->CLEAR = 1; | |||
} | |||
if (TIMER16_1->ISR & TIMER16_FLAG_ARRM) | |||
{ | |||
GPIO_0->SET = 2; | |||
TIMER16_1->ICR = TIMER16_FLAG_ARRM; | |||
GPIO_0->CLEAR = 2; | |||
} | |||
} | |||
</syntaxhighlight> | |||
[[Файл:Timer16 Многократные срабатывания прерываний.png|мини|492x492пкс]] | |||
На временной диаграмме видны многократные срабатывания: | |||
Один из возможных вариантов исправления: временное отключение прерывания по сравнению, как в примере ниже:<syntaxhighlight lang="c"> | |||
#include "mik32_hal.h" | #include "mik32_hal.h" | ||
#include "mik32_hal_timer16.h" | #include "mik32_hal_timer16.h" | ||
Строка 29: | Строка 150: | ||
Timer16_1_Init(); | Timer16_1_Init(); | ||
HAL_Timer16_Counter_Start(&htimer16_1, | HAL_Timer16_Counter_Start(&htimer16_1, 0x7); | ||
HAL_Timer16_SetCMP(&htimer16_1, | HAL_Timer16_SetCMP(&htimer16_1, 0x7 / 2); | ||
HAL_Timer16_SetInterruptMask(&htimer16_1, | HAL_Timer16_SetInterruptMask(&htimer16_1, TIMER16_FLAG_CMPM | TIMER16_FLAG_ARRM); | ||
/* Включать прерывания Timer16 рекомендуется после его инициализации */ | /* Включать прерывания Timer16 рекомендуется после его инициализации */ | ||
Строка 39: | Строка 160: | ||
while (1) | while (1) | ||
{ | { | ||
if ((TIMER16_1->CNT != TIMER16_1->CMP) && ((TIMER16_1->IER & TIMER16_FLAG_CMPM) == 0)) | |||
{ | |||
TIMER16_1->IER |= TIMER16_FLAG_CMPM; | |||
TIMER16_1->ICR = TIMER16_FLAG_CMPM; | |||
} | |||
if ((TIMER16_1->CNT != TIMER16_1->ARR) && ((TIMER16_1->IER & TIMER16_FLAG_ARRM) == 0)) | |||
{ | |||
TIMER16_1->IER |= TIMER16_FLAG_ARRM; | |||
TIMER16_1->ICR = TIMER16_FLAG_ARRM; | |||
} | |||
} | } | ||
} | } | ||
Строка 65: | Строка 196: | ||
/* Настройка тактирования */ | /* Настройка тактирования */ | ||
htimer16_1.Clock.Source = | htimer16_1.Clock.Source = TIMER16_SOURCE_INTERNAL_OSC32K; | ||
htimer16_1.CountMode = TIMER16_COUNTMODE_INTERNAL; /* При тактировании от Input1 не имеет значения */ | htimer16_1.CountMode = TIMER16_COUNTMODE_INTERNAL; /* При тактировании от Input1 не имеет значения */ | ||
htimer16_1.Clock.Prescaler = | htimer16_1.Clock.Prescaler = TIMER16_PRESCALER_16; | ||
htimer16_1.ActiveEdge = TIMER16_ACTIVEEDGE_RISING; /* Выбирается при тактировании от Input1 */ | htimer16_1.ActiveEdge = TIMER16_ACTIVEEDGE_RISING; /* Выбирается при тактировании от Input1 */ | ||
Строка 102: | Строка 233: | ||
GPIO_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; | GPIO_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; | ||
GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; | GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; | ||
HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); | |||
GPIO_InitStruct.Pin = GPIO_PIN_1; | |||
HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); | HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); | ||
} | } | ||
Строка 107: | Строка 240: | ||
void trap_handler() | void trap_handler() | ||
{ | { | ||
GPIO_0-> | if (TIMER16_1->ISR & TIMER16_FLAG_CMPM) | ||
{ | |||
GPIO_0->SET = 1; | |||
TIMER16_1->IER &= ~TIMER16_FLAG_CMPM; | |||
TIMER16_1->ICR = TIMER16_FLAG_CMPM; | |||
GPIO_0->CLEAR = 1; | |||
} | |||
if (TIMER16_1->ISR & TIMER16_FLAG_ARRM) | |||
{ | |||
GPIO_0->SET = 2; | |||
TIMER16_1->IER &= ~TIMER16_FLAG_ARRM; | |||
TIMER16_1->ICR = TIMER16_FLAG_ARRM; | |||
GPIO_0->CLEAR = 2; | |||
} | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
[[Файл:Timer16 | [[Файл:Timer16 Однократные срабатывания прерываний.png|мини|461x461пкс]] | ||
На временной диаграмме видны однократные срабатывания каждого из прерываний: |
Версия от 06:21, 21 января 2025
Особенность установки флагов прерываний
Флаг прерывания по сравнению выставляется, если значение счета таймера совпадает со значением сравнения. Из-за этого возможна ситуация, когда после сброса флага прерывания по сравнению внутри обработчика флаг снова выставляется на следующем такте, если счетчик таймера не успел увеличиться. В таком случае, после выхода из обработчика прерывания происходит повторное срабатывание.
Пример ниже демонстрирует эту ситуацию. Для воспроизведения потребуется использовать источник счета с частотой значительно ниже частоты тактирования системы, например, один из низкоскоростных таймеров.
#include "mik32_hal.h" #include "mik32_hal_timer16.h" #include "mik32_hal_irq.h" #include "uart_lib.h" #include "xprintf.h" Timer16_HandleTypeDef htimer16_1; void SystemClock_Config(void); static void Timer16_1_Init(void); void GPIO_Init(); int main() { HAL_Init(); SystemClock_Config(); GPIO_Init(); Timer16_1_Init(); HAL_Timer16_Counter_Start(&htimer16_1, 0x7); HAL_Timer16_SetCMP(&htimer16_1, 0x7 / 2); 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) { } } 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_16; 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_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_1; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); } void trap_handler() { if (TIMER16_1->ISR & TIMER16_FLAG_CMPM) { GPIO_0->SET = 1; TIMER16_1->ICR = TIMER16_FLAG_CMPM; GPIO_0->CLEAR = 1; } if (TIMER16_1->ISR & TIMER16_FLAG_ARRM) { GPIO_0->SET = 2; TIMER16_1->ICR = TIMER16_FLAG_ARRM; GPIO_0->CLEAR = 2; } }
На временной диаграмме видны многократные срабатывания:
Один из возможных вариантов исправления: временное отключение прерывания по сравнению, как в примере ниже:
#include "mik32_hal.h" #include "mik32_hal_timer16.h" #include "mik32_hal_irq.h" #include "uart_lib.h" #include "xprintf.h" Timer16_HandleTypeDef htimer16_1; void SystemClock_Config(void); static void Timer16_1_Init(void); void GPIO_Init(); int main() { HAL_Init(); SystemClock_Config(); GPIO_Init(); Timer16_1_Init(); HAL_Timer16_Counter_Start(&htimer16_1, 0x7); HAL_Timer16_SetCMP(&htimer16_1, 0x7 / 2); 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) { if ((TIMER16_1->CNT != TIMER16_1->CMP) && ((TIMER16_1->IER & TIMER16_FLAG_CMPM) == 0)) { TIMER16_1->IER |= TIMER16_FLAG_CMPM; TIMER16_1->ICR = TIMER16_FLAG_CMPM; } if ((TIMER16_1->CNT != TIMER16_1->ARR) && ((TIMER16_1->IER & TIMER16_FLAG_ARRM) == 0)) { TIMER16_1->IER |= TIMER16_FLAG_ARRM; TIMER16_1->ICR = TIMER16_FLAG_ARRM; } } } 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_16; 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_InitStruct.Mode = HAL_GPIO_MODE_GPIO_OUTPUT; GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_1; HAL_GPIO_Init(GPIO_0, &GPIO_InitStruct); } void trap_handler() { if (TIMER16_1->ISR & TIMER16_FLAG_CMPM) { GPIO_0->SET = 1; TIMER16_1->IER &= ~TIMER16_FLAG_CMPM; TIMER16_1->ICR = TIMER16_FLAG_CMPM; GPIO_0->CLEAR = 1; } if (TIMER16_1->ISR & TIMER16_FLAG_ARRM) { GPIO_0->SET = 2; TIMER16_1->IER &= ~TIMER16_FLAG_ARRM; TIMER16_1->ICR = TIMER16_FLAG_ARRM; GPIO_0->CLEAR = 2; } }
На временной диаграмме видны однократные срабатывания каждого из прерываний: