Вступление
В предыдущей статье мы помигали светодиодом, но сделали это не совсем правильно. Дело в том, что в качестве задержки мы использовали пустой цикл, который под каждый временной интервал приходится подбирать. Такой способ так же не подходит, когда нам нужны точные временные интервалы. Для решения данной проблемы в нашем микроконтроллере имеются сразу три полноценных таймера и один системный. Для начала поставим небольшую подзадачу. Нам нужно получить мигание светодиодом с интервалом в одну секунду. Задача очень простая и для этого нам вполне хватит системного таймера. Из-за своей легкости он идеально подходит для такого рода задач. Рассмотрим его по подробнее.
Изучение и настройка
Посмотрим, что о данном таймере написано в документации.
Процессор имеет 24-х разрядный системный таймер, SysTick, который считает вниз от загруженного в него значения до нуля; перезагрузка (возврат в начало) значения в регистр LOAD происходит по следующему фронту синхросигнала, затем счёт продолжается по последующему фронту.
Когда процессор остановлен для отладки, таймер не декрементируется.
Не густо, но нам вполне хватит. Пора настроить его. Взглянем на карту регистров.
Всего четыре регистра, но понадобятся нам всего два из них. Рассмотрим их поподробнее.
Это основной регистр настройки. Как мы видим, нам нужно выбрать источник тактового сигнала, разрешить прерывания и включить счетчик. Тактировать таймер мы будем от HCLK. По умолчанию на этом источнике частота тактирования ядра, то есть 8 Мгц. Прерывания нам будут нужны для создания собственной функции точной задержки. В виду того, что я не смог найти define-ы бит регистра, я решил прописать их самостоятельно.
Создаем отдельную функцию настройки таймера и вписываем в нее нашу конструкцию.
void Init_SysTick (void)
{
SysTick->CTRL |= CLKSOURCE|TCKINT|ENABLE;
}
Смотрим следующий регистр:
Именно сюда мы должны загрузить значение задержки. Чтобы сделать нашу функцию более универсальной, настроим таймер на прерывание раз в одну миллисекунду. Мы настроили наш таймер на тактирование от HCLK – это частота тактирования ядра микроконтроллера. По умолчанию она равна 8 Мгц = 8000000 тактов в секунду. В одной секунде тысяча миллисекунд => 8000000(количество тактов в секунду)/1000(количество миллисекунд в секунде) = количество тактов в одной миллисекунде. Но нужно не забыть отнять «1», как описано в примере выше. Выходит следующее.
SysTick->LOAD |= (8000000/1000)-1;
Думаю, что данную настройку правильнее будет разместить перед включением таймера.
Итого, наша функция приобретает следующий вид.
Пишем функцию задержки.
Мы настроили наш таймер на появление прерываний, но нигде не упомянули, что же будет происходить, когда данное прерывание появится. Для этого заходим в стартап файл.
Чуть пролистав вниз, мы можем увидеть длинный столбец.
Это так называемые «вектора прерываний». Когда появляется какое-то внешнее/внутреннее неотложное событие (прерывание), микроконтроллер прерывает выполнение основной программы, переходит к этой таблице и смотрит, куда ему нужно перейти дальше. Например, когда появляется прерывание от нашего таймера, он переходит к пункту с именем «SysTick_Handler». В случае, если вектор не прописан нами в программе (нет функции с таким именем) – контроллер игнорирует его и продолжает выполнение своей программы. Но если в программе есть функция с этим именем, то он переходит к ее выполнению.
Создадим в папке main функцию с именем SysTick_Handler. Далее объявим глобальную переменную 32-х битной разрядности. Для того, чтобы при «сжатии» проекта ее не трогал компилятор, перед ней добавляем «volatile». Если этого не сделать, то в будущем мы будем наблюдать различные ошибки. В самой функции прописываем условие: если переменная еще не равна нулю – отнять 1.
Таким образом, каждую миллисекунду контроллер будет бросать основную программу и проверять, если еще счетчик не пуст – отнять от него 1.
Теперь нужно написать саму функцию задержки, которая будет использовать данное прерывание.
Данная функция принимает длительность задержки (в миллисекундах), копирует ее в переменную, из которой по прерываниям системного таймера функция отнимает 1 каждую миллисекунду. После того, как задержка исчерпана, программа продолжает свою работу. Данная задержка является далеко не самым лучшим вариантом траты процессорного времени. Но на начальных этапах ее хватает.
Теперь дело за малым. Заменить циклы нашими задержками.
На этом с системным таймером пока все. Исходник проекта можно взять здесь.
Разбираясь с системным таймером, я основывался на знаниях, полученных при изучении STM32 из данного видео-урока.
Автор: Vadimatorikda