Перейти к содержанию

CH32V

  • записей
    8
  • комментариев
    12
  • просмотров
    3 085

Формирование интервалов времени


zhevak

2 674 просмотра

Это короткая заметка про то, как с помощью аппаратного таймера, который имеется в микроконтроллере CH32V003, сформировать пару сигналов.

Поставленная задача чем-то напоминает задачу ШИМ (широтно-импульсной модуляции). Но в отличие от классической ШИМ-задачи, в описываемой здесь задаче не используется модуляция (то есть ширина импульса не меняется) и не предполагается использование (аппаратного) сигнала, который обычно выводится на ножки МК и который подаётся на управляющие выводы силовых транзисторов, например, в блоке питания. Хотя, на самом деле в решении поставленной задачи, используются выводы портов и на них действительно выводится сигнал, но это не является целью программного кода, а сделано лишь для индикации того, что программный код работает правильно -- как задумано.

 

Вот временная диаграмма того, что нужно получить в программе:

 photo_2023-09-01_00-17-55.jpg.0ed7c149d867f91adee2b7f20db12fe3.jpg 

 

Здесь LED1 и LED2 -- это состояние светодиодов, которые подключены на ножки МК. Ещё раз: светодиоды нужны -- это только для отладки, только для индикации правильности процессов.

Мне нужно сфомировать два промежутка времени. Один с короткой продолжительностью (75 мс), другой -- с длительной продолжительностью (125 мс). Оба промежутка времени должны начинаться в одно и тоже время. Период повторения промежутков времени -- 500 мс.

Я буду использовать таймер TIM2. Период повторения будет формироваться счётчиком таймера CNT, который будет тактироваться входным сигналом с частотой 1 кГц. Входную частоту счетчика я получу из тактовой частоты шины AHB1, на которой "висит" этот счётчик.

Тактовая частота ядра (SysClock) в проекте 24 МГц. Делитель частоты для шины AHB1 равен единице, то есть тактовая частота таймера -- тоже 24 МГц. У счётчика таймера имеется предделитель. Чтобы из входной частоты таймера (24 МГц) получить входную частоту для счётчика таймера (1 кГц) необходимо в предделитель загрузить значение, равное (24000 - 1). В результате, если запустить таймер, его счётчик начнет считать со скоростью 1 кГц.

Чтобы сформировать период, равный 500 мс, необходимо в регистр автозагрузки записать число 500. Счетчик увеличивает своё значение каждую миллисекунду (частота 1 кГц), поэтому ему нужно досчитать до 500. После этого счётчик сбросится в ноль, поднимет в регистре INTFR флаг прерывания UIF и начнёт следующий период.

Для формирования заданных промежутков времени я использую регистры захвата/сравнения CH3CVR и CH4CVR. В регистр CH3CVR я записываю число 75, а в регистр CH4CVR -- число 125. Поскольку счётчик инкрементируется со скоростью один раз в миллисекунду, то третьего канала (CH3) возникнет прерывание на 75 миллисекунде, а от четвёртого канала (CH4) -- на 125 миллисекунде.

 

Вот код инициализации таймера TIM2:

void init_tim2(void)
{
  RCC->APB1PCENR |= RCC_TIM2EN;  // Включаю тактирование модуля таймера
 
  NVIC_EnableIRQ(TIM2_IRQn);     // Разрешаю прерывания от таймера TIM2
 
  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);

  TIM2->CTLR1 = TIM_CEN;  // И наконец запускаю таймер в работу 
}

 

Формирование промежутков осуществляется в обработчике прерываний от таймера. Обработчик прерывания от таймера TIM2 может обслуживать несколько прерываний. В моём случае это три прерывания -- одно от счётчика и два от каналов захвата/сравнения.

Вот как это делается:
 

__attribute__((interrupt("WCH-Interrupt-fast")))
void TIM2_IRQHandler(void)
{
  // Прерывание от счётчика. Отрабатываю период.
  if ((TIM2->DMAINTENR & TIM_UIE) && (TIM2->INTFR & TIM_UIF))
  {
    TIM2->INTFR &= ~TIM_UIF;  // Снимаю флаг прерывания

    // Начало периода. Включаю оба светодиода. 
    led1_on();  // Светодиоды нужны только для отладки кода программы
    led2_on();

    ... // Выполняю некоторую полезную работу
  }
 
  // Прерывание от канала CH3. Отрабатываю короткий промежуток времени.
  if ((TIM2->DMAINTENR & TIM_CC3IE) && (TIM2->INTFR & TIM_CC3IF))
  {  
    TIM2->INTFR &= ~TIM_CC3IF;   // Снимаю флаг прерывания
    led1_off();  // Выключаю светодиод LED1

    ... // Выполняю некоторую полезную работу
  }

  // Прерывание от канала CH4. Отрабатываю длинный промежуток времени.
  if ((TIM2->DMAINTENR & TIM_CC4IE) && (TIM2->INTFR & TIM_CC4IF))
  {
    TIM2->INTFR &= ~TIM_CC4IF;   // Снимаю флаг прерывания
    led2_off();  // Выключаю светодиод LED2

    ... // Выполняю некоторую полезную работу
  }
}

В начале периода обработчик прерывания зажигает оба светодиода. Каждый светодиод гасится своим каналом согласно заданному промежутку времени.

 

Не пытайтесь понять назначение этой задачи и не пытайтесь оптимизировать её решение. Задача несколько синтезированная. Реальная задача, из которой получилась эта статья, несколько иная. Если бы я описывал реальную задачу, то потерялась бы суть и простота описания работы с таймером. Поэтому я упростил задачу на столько, что она почти выродилась. Мне было важно показать, что работать с таймером не так уж тяжело.

В реальной задаче мне нужны времена не в миллисекундах, а в микросекундах. Но это сути не меняет. Да, я понимаю, что время входа в прерывание может составлять около микросекунды. Но в реальной задаче допускается джиттер до примерно 5 мкс. И, как я уже говорил, в реальной задаче не используется вывод сигналов на порты. Реальная задача работает не с внешними сигналами, а со значениями внутренних переменных.

На всякий случай сообщу, что на Telegram имеется канал по микроконтроллерам CH32V t.me/ch32v003 и есть группа https://t.me/ch32v, в которой можно пообщаться с коллегами.

 

Изменено пользователем zhevak

0 Комментариев


Рекомендуемые комментарии

Комментариев нет

Присоединяйтесь к обсуждению

Вы публикуете как гость. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Добавить комментарий...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...

×
×
  • Создать...