CH32V003. Формирование временнЫх интервалов

Аватар пользователя zhevak

Делать так, как описано в этой короткой статье, я бы не рекомендовал. Эта статья предназначена не ради готового примера для применения в каких-либо коммерческих программах, а ради "первой ступеньки" в освоении модуля таймера.

Таймеры в STM32 и в CH32V по сравнению с другими микроконтроллерами (например, MSP430, ATMEGA и другими) сильно навороченные, и разобраться сходу, как с ними работать, -- довольно-таки трудно. По себе сужу.

Документация в интернете в основном представлена на английском языке. На русском тоже есть, но есть один момент. Документации по таймерам конкретно для CH32V нет. Хотя таймеры в CH32V и STM32 очень похоже, но состав, названия регистров, названия битов по отношению к STM32 несколько различаются. Поэтому у разработчиков возникают определённые трудности, которые выливаются в затягивание сроков разработки программ.

Представленный в статье пример помогает быстрее начать с таймером работать.

В микроконтроллерах CH32V реализованы два таймера -- таймер общего назначения (General Purpose)  TIM2 и продвинутый таймер (Advanced) TIM1. В примере используется таймер общего назначения TIM2, но представленный код пригоден и для продвинутого таймера TIM1.

У таймера много функций, которые он может выполнять. Начать освоения таймера лучше с самой простой функции -- формирование временнЫх промежутков. Что это значит?

Допустим, мы пишем программу, которая выполняет какие-то действия (например, измеряет температуру). Поскольку программа измеряет температуру значительно быстрее, чем температура обычно меняется, то измерять температуру каждую миллисекунду нет смысла. Допустим, что нам нужно измерять температуру с периодом один раз в секунду. Само же время измерения и время передачи полученного значения температуры во внешний мир (на LCD или по последовательному каналу в компьютер) составляет 10 мс.

В этом случае главный цикл программы будет выглядеть как-то так:

int main(void)
{
  ...
  tim_init();  // Настраиваю таймер

  // Главный цикл программы
  while (1)
  {
    temperature = get_temperature();  // Измеряю температуру
    send_value(temperature);          // Передаю показания
    wait();                           // Жду секунду
  }
}

В функции tim_init() производится настройка таймера на формирование секундных промежутков времени, а функция wait() тупо останавливявает выполнение программы до начала следующего промежутка времени.

Вот, эти-то функции мы сейчас и рассмотрим более подробно. Начнём с функции tim_init().

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

RCC->APB1PCENR |= RCC_TIM2EN;  // Включаю таймер

Следующим шагом нужно настроить работу таймера. Допустим, что тактовая частота SysClock, на которой работает ядро микроконтроллера, равно 24 МГц, а предделитель для шины мы не используем (то есть тактовая частота шины APB1 тоже 24 МГц). Тогда оставшийся код инициализации таймера будет выглядеть так:

TIM2->INTFR = 0;        // Предочистка
TIM2->PSC = 24000;      // Входная частота таймера = 1 кГц
TIM2->ATRLR = 1000;     // Соответствует одной секунде
TIM2->CTLR1 = TIM_CEN;  // Запускаю таймер в работу

У каждого таймера есть свой предделитель. Он делить входную частоту на заданное значение и потом подает её на счетчик таймера. В нашем случае мы записываем коэффициент деления 24000 в регистр предделителя (PSC). Это значит, что после предделителя частота, котораяубдет поступать на счётчик таймера, будет равна 1 кГц.

У каждого счётчика так же имеется регистр автозагрузки. Работа этого регистра зависит от направления счёта счетчика -- увеличивает ли счетчик свое значение или же уменьшает. Значение из этого регистра либо загружается в счётчик каждый раз при достижении счётчиком нулевого значения, либо наоборот -- при достижении счётчиком значения, равного записанному в регистре PSC, счётчик обнуляется. В обоих случая счётчик формирует событие UIF, которое мы и будем отслеживать в функции wait().

Код функции wait() ещё проще:

void wait(void)
{
  while (!(TIM2->INTFR & TIM_UIF))
    ;  // Ожидаю поднятия флага UIF

  TIM2->INTFR = 0;  // Сбрасываю флаг
}

Как можно понять из приведённого кода, функция тормозит выполнение программы до тех пор, пока не сработает таймер и не будет взведён флаг UIF. После этого происходит очистка этого флага и программа может продолжить своё выполнение.

Еще раз отмечу, что не смотря на то, что программа, построенная по предложенному способу, будет вполне рабочей, делать так не надо. Код программы был приведён только в учебных целях. С чего-то же нужно начинать?

Авторство: 
Авторская работа / переводика

Комментарии

Аватар пользователя Офисный планктон

Данная тема, конечно, интересна, но только для программирующих микроконтроллеры. Профиль данного сайта совсем другой. Может статью надо было на сайте по программированию микроконтроллеров выложить?

Аватар пользователя zhevak
zhevak(4 года 3 месяца)

Простите великодушно. Я знаю, что делаю не совсем правильные вещи.

У меня есть блог на wordpress.com, но там верстка чрезвычайно неудобная. А aftershok я периодически читаю. Вот и решил выкладывать здесь же.

Короче говоря, совсем плохие дела. Не знаю, куда приткнуться.

Извините!

Аватар пользователя Офисный планктон

Да не надо извиняться! Просто если спец по МК будет искать информацию по профилю, он не будет её искать на АШ. Здесь размещают статьи на разные темы, но в основном про политику, экономику, историю. А Вашу статью (и другие подобные) хорошо бы скопировать на «киберфорум», «хабр», «схем.нет» иди что-то подобное.