By
zhevak in CH32V
2
Продолжаю публикацию материалов на тему работы с таймером.
У таймеров, которые встроены в CH32V003, имеется один полезный режим -- режим одиночного импульса (OPM, One Pulse Mode).
Этот режим работы характеризуется тем, что таймер создаёт одиночный кусочек времени, а не генерирует периодический сигнал. Иначе говоря, мы стартуем таймер, он один раз отрабатывает заданное время и на этом всё заканчивается. Чтобы повторить процедуру, нужно ещё раз стартануть таймер.
В задаче, код которой приведён ниже, таймер отрабатывает промежуток времени, равный 500 мс. По окончании этого промежутка таймер поднимает флаг UIF и возбуждает прерывание.
Кроме этого, у таймера, как по аналогии с предыдущей статьёй, также задействованы два канала захвата/сравнения -- 3-й и 4-й. Они также отрабатывают промежутки времени 75 и 125 мс.
На этот раз управление светодиодами (которые используются для диагностики и отладки) несколько изменилось. Добавился третий светодиод, который индицирует отрабатываемый таймером промежуток времени (500 мс).
Код обработчика прерывания выглядит следующим образом:
__attribute__((interrupt("WCH-Interrupt-fast")))
void TIM2_IRQHandler(void)
{
// Отрабатываю период
if ((TIM2->DMAINTENR & TIM_UIE) && (TIM2->INTFR & TIM_UIF))
{
TIM2->INTFR &= ~TIM_UIF; // Очищаю флаг UIF
led0_off(); // Гашу светодиод (500 мс)
... // Выполняю другую полезную работу, связанную с окончанием полного периода
}
// Отрабатываю короткий промежуток врмени
if ((TIM2->DMAINTENR & TIM_CC3IE) && (TIM2->INTFR & TIM_CC3IF))
{
TIM2->INTFR &= ~TIM_CC3IF; // Очищаю флаг CC3IF
led2_off(); // Гашу светодиод (75 мс)
... // Выполняю другую полезную работу, связанную с окончанием короткого промежутка времени
}
// Отрабатываю длинный промежуток времени
if ((TIM2->DMAINTENR & TIM_CC4IE) && (TIM2->INTFR & TIM_CC4IF))
{
TIM2->INTFR &= ~TIM_CC4IF; // Очищаю флаг CC4IF
led1_off(); // Гашу светодиод (125 мс)
... // Выполняю другую полезную работу, связанную с окончанием длинного промежутка времени
}
}
Светодиоды включаются в функции запуска таймера:
void tim2_start(void)
{
TIM2->CTLR1 |= TIM_CEN; // Запускаю формирование одиночного импульса
// Включаю все светодиоды
led0_on();
led1_on();
led2_on();
}
Функция инициализации таймера (по сравнению с примером из предыдущей статьи) тоже изменилась.
void tim2_init(void)
{
RCC->APB1PCENR |= RCC_TIM2EN; // Включаю тактирование модуля таймера
TIM2->CTLR1 = TIM_OPM; // Режим одиночного импульса
TIM2->PSC = 24000 - 1; // Таймер будет считать со скоростью 1 кГц
TIM2->ATRLR = 500; // Период 500 мс
TIM2->CH3CVR = 25; // Короткий импульс 25 мс
TIM2->CH4CVR = 125; // Длинный импульс 125 мс
TIM2->INTFR = 0; // Сбрасываю все флаги
// Разрешаю прерывания от 3-го и 4-го каналов, а также по окончанию счёта таймера
TIM2->DMAINTENR |= (TIM_CC4IE | TIM_CC3IE | TIM_UIE);
NVIC_EnableIRQ(TIM2_IRQn); // Разрешаю в системе прерывание от таймера TIM2
}
Как видите, режим одиночного импульса задаётся с помощью установки флага OPM в управляющем регистре CTLR1.
Теперь функция main() должна выглядеть как-то так:
int main(void)
{
uint32_t flag = 0;
system_init();
leds_init();
tim2_init(); // Настраиваю таймер TIM2 на генерацию одиночных импульсов
__enable_irq();
while (true)
{
if (system_1s() == 1)
{ // Сюда мы попадаем один раз в секунду
if (flag == 1)
{
flag = 0;
led4_off();
}
else
{
flag = 1;
led4_on();
tim2_start(); // Запускаю таймер
}
}
}
}
В начале функции main() происходит инициализация таймера на работу в режиме одиночных импульсов. В главном цикле программы периодически вызывается функция system_1s(), которая возвращает признак истечения односекундного интервала времени. Эта функция в этой статье не описана. Секундные интервалы времени для этой функции вырабатываются системным таймером SysTick. Согласно этим интервалам меняется значение переменной flag и через каждые две секунду происходит запуск таймера TIM2.
Вообще это не принципиально по какому критерию запускать таймер TIM2. Можно, например, по нажатию кнопки, а интервалы формировать с целью подавления дребезга.