Timer16 Использование прерываний: различия между версиями

Материал из MIK32 микроконтроллер
Нет описания правки
Нет описания правки
Строка 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 тактируется от встроенного низкочастотного источника тактирования.

Timer16 Многократные срабатывания прерываний.png

На временной диаграмме видны многократные срабатывания прерывания по сравнению (CMP). Однократные прерывания по перезагрузке (ARR) связаны с реализацией Timer16.

Варианты обхода

Повторная установка флага потребует подбора обходного пути в зависимости от задачи. Основная идея - отключение прерывания на время счета, равного значению сравнения. Рассмотрим некоторые из возможных обходных путей работы с прерываниями по сравнению.

Отключение прерывания по сравнению и повторное включение в основном цикле

Один из возможных вариантов обхода: временное отключение прерывания по сравнению на время до смены значения счетчика в другой части программы, например, в основном цикле, как в примере ниже:

Timer16 Однократные срабатывания прерываний.png

На временной диаграмме видны однократные срабатывания прерываний по сравнению (CMP) и перезагрузке (ARR):

Такой вариант можно использовать, если программа работает в режиме pooling'а, регулярно вызывая основной цикл.

Отключение прерывания по сравнению и повторное включение в прерывании по

Возможный вариант обхода: отключение прерывания по сравнению и повторное включение в прерывании по переполнению. Пример представлен ниже: