Таймер SysTick, реализация задержек в программе |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Важнейшей функцией почти всех приложений на микроконтроллерах является вычисление задержек времени в программе (отслеживание таймаутов во время передачи данных, периодический опрос клавиатуры и т. п.). Когда отслеживание времени является важным для приложения, Вы ничего не сможете сделать без хорошей функции задержки. Во всех моих проектах, начиная с простейших 8-битных MCU, функции задержек всегда было непросто реализовать, если нужно было точно отсчитывать время. Часто время правильно вычислялось только для определенной тактовой частоты, но чтобы код работал на всех тактовых частотах, требовалось применять продвинутое программирование на ассемблере. К счастью, как и у всех других микроконтроллеров ARM Cortex, семейство STM32F4 имеет отличный 24-битный системный таймер (system timer, SysTick), который считает вниз до нуля от предварительно загруженной в счетчик величины. Благодаря этому таймеру можно довольно просто и качественно реализовать отсчет реального времени в программе. Функции для SysTick можно найти в заголовочном файле core_cm4.h, если Вы используете CoIDE. Если вы используете IAR, то функции для управления и использования таймера SysTick Вы сможете найти в примерах кода на основе стандартной библиотеки ST: в среде IAR 6.50, на стартовой странице выберите EXAMPLE PROJECTS -> ST -> STM32F4xx -> CMSIS and STM32F4xx stdperiph lib 1.1.0 (ищите функции SysTick_Config, SysTick_Handler). Все, что необходимо - просто вызвать функцию SysTick_Config, где в параметре будет указано количество тиков между прерываниями, и предоставить обработчик прерывания SysTick_Handler. В ядро процессора STM32 встроен 24-битный системный таймер, так называемый SysTick (STK), который считает в обратном направлении от загруженной в таймер величины до нуля. В момент достижения значения 0 счетчик автоматически на следующем тактовом перепаде перезагружает сам себя значением из регистра STK_LOAD, и далее продолжает счет вниз, считая каждый приходящий импульс тактирования. Когда процессор остановлен (halted) для отладки, то счетчик не декрементируется. Таймер SysTick (Cortex System Timer) специально предназначен для операционных систем реального времени (real-time operating system, RTOS), но может использоваться просто как стандартный счетчик вниз. Его возможности: • 24-битный счетчик с обратным отсчетом. Базовый адрес для блока регистров SysTick равен 0xE000E010. Таблица 54. Обзор регистров таймера SysTick.
[Описание регистров SysTick] STK_CTRL, SysTick control and status register (регистр статуса и управления).
Смещение адреса 0x00, значение после сброса 0x00000000. Требуемый режим доступа: привилегированный. Через регистр статуса и управления разрешаются фичи SysTick. Биты 31..17, 15..3 зарезервированы, их состояние должно сохраняться в нулевом значении. COUNTFLAG: этот бит читается как 1, если таймер досчитал до нуля после последнего чтения этого бита. CLKSOURCE: выбор тактовой частоты для счетчика SysTick. 0: частота AHB/8, 1: частота процессора (AHB). TICKINT: разрешение на запрос прерывания (exception request enable) SysTick. 0: счет продолжается вниз до нуля, но при этом не выставляется запрос на SysTick exception, 1: то же самое, но по достижении нуля выставляется запрос на SysTick exception. Примечание: программное обеспечение может использовать флаг COUNTFLAG, чтобы определить, что сосчитал ли когда-либо счетчик SysTick до нуля. ENABLE: разрешение работы счетчика, 0: счетчик запрещен, 1: разрешен. Когда в 1, то счетчик загружает в себя значение RELOAD из регистра STK_LOAD, когда счет достигает 0. В этот момент также устанавливается в 1 флаг COUNTFLAG, и может быть выставлен запрос от прерывания, в зависимости от значения флага TICKINT. После перезагрузки счетчика значением RELOAD счет продолжается, и цикл повторяется. STK_LOAD, SysTick reload value register (регистр для значения перезагрузки).
Смещение адреса 0x04, значение после сброса не определено. Требуемый режим доступа: привилегированный. Биты 31..24 зарезервированы, их состояние должно сохраняться в нулевом значении. RELOAD: в этих битах содержится значение для перезагрузки счетчика SysTick. Это начальное значение, которое автоматически загружается в регистр STK_VAL, когда счетчик разрешен, и когда счет достигает 0. Значение для RELOAD вычисляется в соответствии с целями программы: • Для генерации multi-shot таймера (т. е. у которого срабатывания повторяются снова и снова) с периодом N циклов процессора, используйте значение для RELOAD, равное N-1. Например, когда нужно, чтобы прерывание SysTick было каждые 100 тактовых импульсов, то установите значение RELOAD на величину 99. STK_VAL, SysTick current value register (регистр текущего значения счетчика).
Смещение адреса 0x08, значение после сброса не определено. Требуемый режим доступа: привилегированный. Биты 31..24 зарезервированы, их состояние должно сохраняться в нулевом значении. CURRENT: текущее значение счетчика SysTick. Чтение возвратит это значение. Запись любого значения очистит это поле в 0, и также очистит в 0 бит COUNTFLAG регистра STK_CTRL. STK_CALIB, SysTick calibration value register (регистр значения калибровки). Содержимое регистра показывает свойства калибровки SysTick.
Смещение адреса 0x0C, значение после сброса 0xC0000000. Требуемый режим доступа: привилегированный. Биты 29..24 зарезервированы, их состояние должно сохраняться в нулевом значении. NOREF: флаг NOREF, читается как 0. Показывает, что предоставляется отдельная опорная частота тактов. Частота этих тактов HCLK/8. SKEW: флаг SKEW (уход), показывает, точно ли установлено значение TENMS. Читается как 1. Значение калибровки для 1 мс не определено, потому что значение TENMS не известно. Это может повлиять на пригодность SysTick в качестве программных часов реального времени. TENMS: значение калибровки. Показывает значение калибровки, когда счетчик SysTick работает от тактов HCLK max/8. Реальное значение зависит от конкретного чипа и определяется в процессе производства, за подробностями обращайтесь к Product Reference Manual, секция SysTick Calibration Value. Когда HCLK запрограммирован на максимальную частоту, период таймера SysTick равен 1 мс. Если информация калибровки не известна, вычислите значение калибровки по частоте тактов процессора или внешней частоте тактов. Регистры SysTick в среде IAR, в режиме отладки можно посмотреть в группе регистров STK. Кто бы мог подумать?.. [Общие указания по использованию таймера SysTick] Счетчик SysTick работает от тактов процессора. Если тактовый сигнал остановлен в режиме пониженного энергопотребления, то счетчик SysTick прекращает счет. Убедитесь, что программа использует адрес, выровненный по слову для доступа к регистрам SysTick. После сброса текущее состояние счетчика и значение его перезагрузки не определены, и корректная последовательность инициализация для счетчика SysTick следующая: 1. Нужно запрограммировать значение для перезагрузки счетчика. Вектор прерывания таймера SysTick находится по адресу 0x000003C. RCC подает тактовую частоту на System Timer (SysTick) от частоты шины AHB (HCLK), поделенной на 8. SysTick может работать либо от этой частоты, либо от частоты тактов Cortex (HCLK), что конфигурируется через регистр статуса и управления SysTick. Более подробно о таймере SysTick можно прочитать в руководстве PM0214 компании ST (STM32F3 and STM32F4 Series Cortex®-M4 programming manual). Вместе с таймером SysTick написание хорошей функции задержки становится простой задачей. Просто заранее установите SysTick на удобную для Вас величину, чтобы получить в результате задержки наподобие 1 мс, 100 мкс, 10 мкс и даже 1 мкс. Выбранное значение должно соответствовать потребностям Вашего приложения; например, если требуемые задержки должны быть в диапазоне единиц и десятков миллисекунд, то подходящим значением для настройки SysTick будет интервал 100 мкс или 1 мс. В файле main.c примера [2] приведен код, который инициализирует SysTick следующим образом: /* SysTick закончит счет каждые 1 мс */ RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq(&RCC_Clocks); SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000); Здесь в поле HCLK_Frequency переменной RCC_ClocksTypeDef загружается значение текущей основной тактовой частоты, затем в функцию SysTick_Config передается количество тиков, которое должен сделать таймер SysTick до своего окончания счета. Чтобы отслеживать моменты срабатывания таймера SysTick в программе, для обработчика прерывания SysTick нужно определить функцию SysTick_Handler, примерно так: volatile uint32_t timestamp = 0; void SysTick_Handler (void) { //сюда попадаем каждую 1 миллисекунду timestamp++; } Для того, чтобы удостовериться в корректной работе SysTick, для бесконечного цикла main можно написать такой код (здесь не приведен код настройки, полностью код можно скачать по ссылке [2]): /* Бесконечный цикл мигания светодиодом STAT1 на макетной плате Olimex STM32-P407 */uint32_t delaystamp;while(1) { if (delaystamp < timestamp) { if (GPIO_ReadInputDataBit(GPIOF, GPIO_Pin_6)) GPIOF->BSRRH = GPIO_Pin_6; //STAT==0, светодиод погас else GPIOF->BSRRL = GPIO_Pin_6; //STAT==1, светодиод горит delaystamp = timestamp + 500; //полупериод мигания 0.5 сек. } } Принцип работы понятен: переменная timestamp постоянно увеличивает свое значение каждые 1 мс. Как только пройдет 500 мс, значение timestamp станет больше переменной delaystamp, и выполнится код, который переключит светодиод в противоположное состояние. Потом переменная delaystamp снова загружается на величину, на 500 превышающую timestamp, чем обеспечивается отслеживание очередного интервала задержки 500 мс. Исходный код проекта IAR 6.30, демонстрирующего использование SysTick, можно скачать по ссылке [2]. [Задержка на основе DWT] // Функции задержки взяты из статьи [3]
#include "delay.h"
#include "stm32f429xx.h"
#define DWT_CYCCNT *(volatile unsigned long *)0xE0001004
#define DWT_CONTROL *(volatile unsigned long *)0xE0001000
#define SCB_DEMCR *(volatile unsigned long *)0xE000EDFC
void delay_us(uint32_t us) { int32_t us_count_tick = us * (SystemCoreClock/1000000); //разрешаем использовать счётчик SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; //обнуляем значение счётного регистра DWT_CYCCNT = 0; //запускаем счётчик DWT_CONTROL |= DWT_CTRL_CYCCNTENA_Msk; while(DWT_CYCCNT < us_count_tick); //останавливаем счётчик DWT_CONTROL &= ~DWT_CTRL_CYCCNTENA_Msk; } void delay_ms(uint32_t ms) { int32_t ms_count_tick = ms * (SystemCoreClock/1000); //разрешаем использовать счётчик SCB_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; //обнуляем значение счётного регистра DWT_CYCCNT = 0; //запускаем счётчик DWT_CONTROL|= DWT_CTRL_CYCCNTENA_Msk; while(DWT_CYCCNT < ms_count_tick); //останавливаем счётчик DWT_CONTROL &= ~DWT_CTRL_CYCCNTENA_Msk; } [HAL_Delay] В библиотеках HAL есть функция HAL_Delay, реализованная на основе миллисекундного таймера. В качестве параметра она принимает значение задержки, указанное в миллисекундах. [osDelay] В среде FreeRTOS реализована функция osDelay, в качестве параметра она принимает значение задержки, указанное в тиках системы (по умолчанию длительность тика 1 мс). [Ссылки] 1. STM32F407 Delay with SysTick site:patrickleyman.be. |