Эх сурвалжийг харах

настройка таймера SysTick

Vladimir N. Shilov 9 жил өмнө
parent
commit
8097511a47
1 өөрчлөгдсөн 467 нэмэгдсэн , 0 устгасан
  1. 467 0
      stm32/SysTick.txt

+ 467 - 0
stm32/SysTick.txt

@@ -0,0 +1,467 @@
+Сам таймер 24 разрядный. И тикает вниз от предзагруженного значения до 
+нуля, после чего перезагружается вновь и генерирует прерывание. 
+Источником тактирования является системная тактовая частота SYSCLK, либо 
+та же частота, но поделенная на 8 – SYSCLK/8.
+
+Управляется он четырьмя регистрами:
+0xE000E010	SYST_CSR	RW	SysTick control and status register
+0xE000E014	SYSCT_RVR	RW	SysTick reload value register
+0xE000E018	SYSCT_CVR	RW	SysTick current value register
+0xE000E01C	SYSCT_CALIB	RO	SysTick calibration value register
+
+
+*SYST_CSR* — SysTick Control and Status Register
+Регистр управления и статуса. Несмотря на то, что он 32 битный заняты там всего 4 бита :) 
+ * Бит 0 — ENABLE — бит разрешающий таймеру считать. 1 можно, 0 — нельзя. Когда
+   мы ставим ENABLE в 1 то таймер автоматически загружает свои счетные регистры
+   значением из регистра SYST_RVR и начинает тикать вниз.
+ * Бит 1 — TICKINT — разрешение прерывания. Когда этот бит 1, то на 
+   обнулении будет прерывание таймера.
+ * Бит 2 — CLKSOURCE — откуда брать тики. Варианта два 0 — внешнее 
+   тактирование эталонного генератора, 1 — с частоты процессора.
+ * Бит 16 — COUNTFLAG — флаг отсчета. Первый раз ставится в 1 после 
+   первого обнуления. При чтении, как я понял, автоматом обнуляется. 
+   Возвращает 1 если с последнего его чтения таймер перешел через ноль. 
+   Позволяет отследить были ли переполнения. Удобно.
+
+
+*SYST_RVR* — SysTick Reload Value Register
+Регистр предзагрузки. Именно отсюда берет таймер свое значение для 
+перезагрузки при обнулении. Фактически вот сюда и нужно грузить требуемое 
+число задержки. Регистр 32 разрядный, но используются только первые 24 
+бита.
+Класть туда можно любое число в диапазоне 0x00000001-0x00FFFFFF. Можно и 
+0 положить, но ничего не будет. Т.к. у таймера весь экшн происходит с 
+перехода из 0x00000001 в 0x00000000. А на ноль и получается ноль. Разве 
+что COUNTFLAG вскочит. Только и всего. Поэтому значение SYST_RVR должно 
+быть N-1 от желаемого количества тактов. Т.е. если надо получить 
+прерывание на каждые 10000 тактов, то кладем 9999.
+
+
+*SYST_CVR* — SysTick Current Value Register
+Это, собственно, и есть счетный регистр. Тут оно тикает! В первых трех 
+байтах. Есть у этого регистра особенность одна забавная. Из него можно 
+только читать. А вот запись любого значения сразу его обнуляет. С 
+обнулением флага COUNTFLAG.
+Т.е. если надо занулить таймер — пиши сюда :)
+
+
+*SYST_CALIB* — SysTick Calibration Value Register
+Это, как ясно из названия, регистр калибровки. Его можно только читать, и 
+возвращает он следующее:
+ * SKEW — Показывает что записано в TENMS 0 — там реальные калибровочные 
+   константы. 1 — какой то мусор который не имеет значения. Ну или ноль.
+ * NOREF — показывает есть ли у девайса эталонная тактовая частота. 0 — 
+   есть, 1 — нет. Это зависит от производителя, позаботился ли он о такой 
+   штуке :) Если эталонных часов нет, то бит CLKSOURCE из регистра SYST_CSR 
+   читается как 1 и его нельзя изменить.
+ * TENMS — калибровочная константа. Содержит значение для 10мс задержки. 
+   Как я понял, для эталонного генератора. Которого может и не быть.
+
+
+ *** Адреса и имена ***
+Осталось залезть в CMSIS и поискать там описание и дефайны SysTick. 
+Находятся быстро в core_cm3.h:
+*------------------ File Begin -------------------*
+typedef struct
+{
+  __IO uint32_t CTRL;                         /*!< Offset: 0x00  SysTick Control and Status Register */
+  __IO uint32_t LOAD;                         /*!< Offset: 0x04  SysTick Reload Value Register       */
+  __IO uint32_t VAL;                          /*!< Offset: 0x08  SysTick Current Value Register      */
+  __I  uint32_t CALIB;                        /*!< Offset: 0x0C  SysTick Calibration Register        */
+} SysTick_Type;
+ 
+/* SysTick Control / Status Register Definitions */
+#define SysTick_CTRL_COUNTFLAG_Pos         16                                             /*!< SysTick CTRL: COUNTFLAG Position */
+#define SysTick_CTRL_COUNTFLAG_Msk         (1ul << SysTick_CTRL_COUNTFLAG_Pos)            /*!< SysTick CTRL: COUNTFLAG Mask */
+ 
+#define SysTick_CTRL_CLKSOURCE_Pos          2                                             /*!< SysTick CTRL: CLKSOURCE Position */
+#define SysTick_CTRL_CLKSOURCE_Msk         (1ul << SysTick_CTRL_CLKSOURCE_Pos)            /*!< SysTick CTRL: CLKSOURCE Mask */
+ 
+#define SysTick_CTRL_TICKINT_Pos            1                                             /*!< SysTick CTRL: TICKINT Position */
+#define SysTick_CTRL_TICKINT_Msk           (1ul << SysTick_CTRL_TICKINT_Pos)              /*!< SysTick CTRL: TICKINT Mask */
+ 
+#define SysTick_CTRL_ENABLE_Pos             0                                             /*!< SysTick CTRL: ENABLE Position */
+#define SysTick_CTRL_ENABLE_Msk            (1ul << SysTick_CTRL_ENABLE_Pos)               /*!< SysTick CTRL: ENABLE Mask */
+ 
+/* SysTick Reload Register Definitions */
+#define SysTick_LOAD_RELOAD_Pos             0                                             /*!< SysTick LOAD: RELOAD Position */
+#define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos)        /*!< SysTick LOAD: RELOAD Mask */
+ 
+/* SysTick Current Register Definitions */
+#define SysTick_VAL_CURRENT_Pos             0                                             /*!< SysTick VAL: CURRENT Position */
+#define SysTick_VAL_CURRENT_Msk            (0xFFFFFFul << SysTick_VAL_CURRENT_Pos)        /*!< SysTick VAL: CURRENT Mask */
+ 
+/* SysTick Calibration Register Definitions */
+#define SysTick_CALIB_NOREF_Pos            31                                             /*!< SysTick CALIB: NOREF Position */
+#define SysTick_CALIB_NOREF_Msk            (1ul << SysTick_CALIB_NOREF_Pos)               /*!< SysTick CALIB: NOREF Mask */
+ 
+#define SysTick_CALIB_SKEW_Pos             30                                             /*!< SysTick CALIB: SKEW Position */
+#define SysTick_CALIB_SKEW_Msk             (1ul << SysTick_CALIB_SKEW_Pos)                /*!< SysTick CALIB: SKEW Mask */
+ 
+#define SysTick_CALIB_TENMS_Pos             0                                             /*!< SysTick CALIB: TENMS Position */
+#define SysTick_CALIB_TENMS_Msk            (0xFFFFFFul << SysTick_VAL_CURRENT_Pos)        /*!< SysTick CALIB: TENMS Mask */
+/*@}*/ /* end of group CMSIS_CM3_SysTick */
+*------------------ File End -------------------*
+
+
+Нам там нужны имена битов. Вот они:
+SysTick_CTRL_CLKSOURCE_Msk, SysTick_CTRL_TICKINT_Msk, SysTick_CTRL_ENABLE_Msk.
+
+
+Конфигурация таймера на тик в 1ms, таким образом, будет выглядеть примерно 
+так:
+*------------------ File Begin -------------------*
+#define F_CPU		72000000UL	// Тактовая у нас 72МГЦ
+#define TimerTick	F_CPU/1000-1	// Нам нужен килогерц
+ 
+SysTick->LOAD=TimerTick;	// Загрузка значения
+SysTick->VAL=TimerTick;		// Обнуляем таймеры и флаги. Записью, помните? 
+ 
+SysTick->CTRL=	SysTick_CTRL_CLKSOURCE_Msk |
+                SysTick_CTRL_TICKINT_Msk   |
+                SysTick_CTRL_ENABLE_Msk;
+*------------------ File End -------------------*
+
+
+ *** Обработчик прерывания SysTick ***
+Где взять имя обработчика? Да из таблицы прерываний. Поглядеть, если кто 
+не помнит, можно в STM32F10x.s
+
+Вот ее кусочек:
+*------------------ File Begin -------------------*
+; Vector Table Mapped to Address 0 at Reset
+ 
+                AREA    RESET, DATA, READONLY
+                EXPORT  __Vectors
+ 
+__Vectors       DCD     __initial_sp              ; Top of Stack
+                DCD     Reset_Handler             ; Reset Handler
+                DCD     NMI_Handler               ; NMI Handler
+                DCD     HardFault_Handler         ; Hard Fault Handler
+                DCD     MemManage_Handler         ; MPU Fault Handler
+                DCD     BusFault_Handler          ; Bus Fault Handler
+                DCD     UsageFault_Handler        ; Usage Fault Handler
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     SVC_Handler               ; SVCall Handler
+                DCD     DebugMon_Handler          ; Debug Monitor Handler
+                DCD     0                         ; Reserved
+                DCD     PendSV_Handler            ; PendSV Handler
+                DCD     SysTick_Handler           ; SysTick Handler
+*------------------ File End -------------------*
+ 
+
+Имя есть. Осталось организовать прерывание:
+*------------------ File Begin -------------------*
+//SysTick Interrupt
+void SysTick_Handler(void)
+{
+	// Тут будем делать нечто полезное. Например, ставить бит B.05
+	GPIOB->BSRR = GPIO_BSRR_BS5;	// Set bit
+}
+*------------------ File End -------------------*
+ 
+
+А сбрасывать его будет в главном цикле. В результате мы получим иголки с 
+частотой 1Кгц которые хорошо видно осциллографом :)
+
+
+Вот так выглядит полный код примера:
+*------------------ File Begin -------------------*
+#include "stm32f10x.h"
+#define F_CPU 		72000000UL
+#define TimerTick	F_CPU/1000-1
+ 
+void Delay(uint32_t Val);
+ 
+//SysTick Interrupt
+void SysTick_Handler(void)
+{
+	GPIOB->BSRR = GPIO_BSRR_BS5;		// Set bit
+}
+ 
+int main(void)
+{
+	SystemInit();
+ 
+	RCC->APB2ENR 	|= RCC_APB2ENR_IOPBEN;
+	// Config CRL
+	GPIOB->CRL	&= ~GPIO_CRL_CNF5;	// Clear CNF bit 5. Mode 00 - Push-Pull 
+	GPIOB->CRL 	|= GPIO_CRL_MODE5_0;	// Set bit MODE0 for pin 5. Mode 01 = Max Speed 10MHz
+ 
+ 
+	SysTick->LOAD=TimerTick;
+	SysTick->VAL=TimerTick;
+	SysTick->CTRL=	SysTick_CTRL_CLKSOURCE_Msk |
+	                SysTick_CTRL_TICKINT_Msk   |
+	                SysTick_CTRL_ENABLE_Msk;;
+	while(1)
+	{
+		GPIOB->BSRR = GPIO_BSRR_BR5;		// Clear bit
+	}
+}
+*------------------ File End -------------------*
+
+
+ *** Функции CMSIS ***
+
+У Таймера, раз он часть ядра, есть функция конфигурации в CMSIS. Описана 
+она в core_cm3.h и выглядит так:
+*------------------ File Begin -------------------*
+static __INLINE uint32_t SysTick_Config(uint32_t ticks)
+{ 
+  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            	/* Reload value impossible */
+ 
+  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      	/* set reload register */
+  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  	/* set Priority for Cortex-M0 System Interrupts */
+ 
+  SysTick->VAL   = 0;                                          	/* Load the SysTick Counter Value */
+  SysTick->CTRL  = 	SysTick_CTRL_CLKSOURCE_Msk | 
+                   	SysTick_CTRL_TICKINT_Msk   | 
+                   	SysTick_CTRL_ENABLE_Msk; 		/* Enable SysTick IRQ and SysTick Timer */
+  return (0);                                                   /* Function successful */
+}
+*------------------ File End -------------------*
+
+То же самое, что и мы сделали вручную. Только есть проверка на 
+корректность значения и обрезка лишних битов. Возвращает 1 если данные 
+некорректные. Ну и еще там приоритет ставится если ядро М0 (это кстати хз 
+зачем? У M0 же должна быть своя версия CMSIS?)
+
+
+Так что таймер можно загрузить и следующей фигней:
+	SysTick_Config(TimerTick);
+
+Для LPC все совершенно аналогично. До последней буковки можно сказать. 
+Т.к. это фича не STM32, а ядра и описана в CMSIS. Т.е. едина для всех.
+
+===============================================================================
+
+Для конфигурации таймера в файле core_cm3.h есть функция 
+SysTick_Config(uint32_t ticks), в качестве аргумента которой передается 
+коэффициент деления тактовой частоты для получения необходимой временной 
+задержки.
+~~~~~~~~~~~~~~~~~~~~
+static __INLINE uint32_t SysTick_Config(uint32_t ticks)
+{
+  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
+
+  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
+  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
+  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
+  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
+                   SysTick_CTRL_TICKINT_Msk   |
+                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
+  return (0);                                                  /* Function successful */
+}
+~~~~~~~~~~~~~~~~~~~~
+
+Это значение заносится в регистр перезагрузки. В самом начале выполняется 
+проверка на то, что данная величина не выходит за максимальный предел, 
+поскольку счетчик 24-разрядный, а передаваемый аргумент функции является 
+32-разрядным числом и необходимо об этом помнить.
+
+Далее в функции конфигурации SysTick_Config() задается уровень приоритета 
+прерывания, обнуляется счетный регистр, разрешается генерация прерывания, 
+задается источник тактирования и разрешается работа таймера – запускается 
+счет.
+
+По умолчанию тактовая частота этого таймера будет равна системной тактовой 
+частоте SYSCLK. Если же нужно задать частоту тактирования таймера как 
+SYSCLK/8, то уже после этой функции инициализации можно вызвать функцию
+
+	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8),
+
+которая находится в файле misc.c. Для этого к проекту необходимо 
+подключить файлы misc.c и misc.h.
+
+
+В данных примерах таймер настраивается на генерацию прерывания через 
+“базовый” промежуток времени, равный 1 миллисекунде. Затем, уже с помощью 
+программного цикла формируется задержка, равная 1 секунде. Через каждую 
+секунду происходит попеременное зажигание/гашение светодиодов платы, здесь 
+я не стал особо мудрить, поскольку основной задачей была настройка 
+системного таймера для генерации прерываний через определенный интервал.
+
+Для задания длительности между двумя последующими прерываниями от таймера, 
+в регистр перезагрузки необходимо записать значение, которое 
+рассчитывается по простой формуле:
+
+ * Reload Value = SysTick Counter Clock (Hz) x  Desired Time base (s), где
+ * Reload Value – величина перезагружаемого значения для таймера 
+ * SysTick Counter Clock (Hz) – тактовая частота таймера 
+ * Desired Time base (s) – желаемое время формируемой временной задержки между прерываниями 
+
+Системная тактовая частота задается при начальной инициализации микроконтроллера.
+
+На плате STM32VL-DISCOVERY микроконтроллер конфигурируется на работу с 
+внешним кварцем частотой 8 МГц, при этом его системная тактовая частота 
+после начальной инициализации будет равна 24 МГц.
+
+На плате STM32L-DISCOVERY внешнего кварца нет и после начальной 
+инициализации системная тактовая частота формируется внутренним источником 
+MSI и равна 2,097 МГц.
+
+Расчет перезагружаемых значений приведен ниже, в комментариях примеров программ для этих плат.
+
+
+Код программы для платы STM32VL-DISCOVERY (контроллер STM32F100)
+~~~~~~~~~~~~~~~~~~~~
+#include "stm32f10x.h"
+
+static __IO uint32_t TimingDelay;
+
+void Delay_ms(__IO uint32_t nTime);
+
+GPIO_InitTypeDef    GPIO_InitStruct;
+
+int main()
+{
+  /*Вызов функции конфигурации системного таймера SysTick.
+  Эта функция находится в файле core_cm3.h и:
+  --Задает источник тактирования системного таймера (по умолчанию это SYSCLK = 24 МГц,
+    другой возможный вариант  - SysTick тактируется от SYSCLK/8);
+  --Задает уровень приоритета прерывания;
+  --Сбрасывает флаг ожидания прерывания, если он выставлен;
+  --Заносит в нужный регистр перезагружаемое значение для декрементирующего счетчика,
+    которое вычисляется по формуле:
+        Reload Value = SysTick Counter Clock (Hz) x  Desired Time base (s),
+        для базовой задержки длительностью 1 мс получим величину
+        Reload Value = 24 000 000 Гц х 0,001 с = 24 000
+    (Необходимо самостоятельно посчитать эту величину и вставить в качестве
+    параметра при вызове функции);
+  --Обнуляет счетчик
+  --Запускает счет системного таймера*/
+  SysTick_Config(24000);
+
+  //Включаем тактирование порта GPIOC
+  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
+  //Конфигурируем выводы, к которым подключены светодиоды платы
+  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; //Выбираем выводы PC8, PC9
+  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //Максимальная скорость работы
+  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //Выход Push-Pull
+  GPIO_Init(GPIOC, &GPIO_InitStruct); //Заносим заданные настройки в регистры порта
+
+  while(1)
+  {
+    GPIO_ResetBits(GPIOC, GPIO_Pin_9); //Гасим зеленый LED
+    GPIO_SetBits(GPIOC, GPIO_Pin_8);   //Зажигаем синий LED
+    Delay_ms(1000); //Временная задержка на 1 с
+    GPIO_ResetBits(GPIOC, GPIO_Pin_8); //Гасим синий LED
+    GPIO_SetBits(GPIOC, GPIO_Pin_9);   //Зажигаем зеленый LED
+    Delay_ms(1000); //Временная задержка на 1 с
+  }
+}
+
+//Функция временной задержки
+void Delay_ms(__IO uint32_t nTime)
+{
+  TimingDelay = nTime;
+  while(TimingDelay != 0);
+}
+
+void TimingDelay_Decrement(void)
+{
+  if (TimingDelay != 0x00)
+  {
+    TimingDelay--;
+  }
+}
+
+//Обработчик прерывания системного таймера
+void SysTick_Handler(void)
+{
+  TimingDelay_Decrement();
+}
+~~~~~~~~~~~~~~~~~~~~
+
+
+Код программы для платы STM32L-DISCOVERY (контроллер STM32L152)
+~~~~~~~~~~~~~~~~~~~~
+#include "stm32l1xx.h"
+
+static __IO uint32_t TimingDelay;
+
+void Delay_ms(__IO uint32_t nTime);
+
+GPIO_InitTypeDef    GPIO_InitStruct;
+
+int main()
+{
+    /*Вызов функции конфигурации системного таймера SysTick.
+  Эта функция находится в файле core_cm3.h и:
+  --Задает источник тактирования системного таймера (по умолчанию это SYSCLK = 2.097 МГц,
+    другой возможный вариант  - SysTick тактируется от SYSCLK/8);
+  --Задает уровень приоритета прерывания;
+  --Сбрасывает флаг ожидания прерывания, если он выставлен;
+  --Заносит в нужный регистр перезагружаемое значение для декрементирующего счетчика,
+    которое вычисляется по формуле:
+        Reload Value = SysTick Counter Clock (Hz) x  Desired Time base (s),
+        для базовой задержки длительностью 1 мс получим величину
+        Reload Value = 2 097 000 Гц х 0,001 с = 2097
+    (Необходимо самостоятельно посчитать эту величину и вставить в качестве
+    параметра при вызове функции);
+  --Обнуляет счетчик
+  --Запускает счет системного таймера*/
+  SysTick_Config(2097);
+
+  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); //Включаем тактирование GPIOB
+
+  //Конфигурируем выводы, к которым подключены светодиоды платы
+  GPIO_InitStruct.GPIO_Pin = (GPIO_Pin_6 | GPIO_Pin_7); //Выбираем выводы PB6, PB7
+  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //Выводы порта в режиме выхода
+  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //Выход Push-Pull
+  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; //Выход без подтягивающих резисторов
+  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_40MHz; //Скорость порта максимальная
+  GPIO_Init(GPIOB, &GPIO_InitStruct); //Заданные настройки сохраняем в регистрах GPIOB
+
+  while(1)
+  {
+    GPIO_ResetBits(GPIOB, GPIO_Pin_7); //Гасим зеленый LED
+    GPIO_SetBits(GPIOB, GPIO_Pin_6);   //Зажигаем синий LED
+    Delay_ms(1000); //Временная задержка на 1 с
+    GPIO_ResetBits(GPIOB, GPIO_Pin_6); //Гасим синий LED
+    GPIO_SetBits(GPIOB, GPIO_Pin_7);   //Зажигаем зеленый LED
+    Delay_ms(1000); //Временная задержка на 1 с
+  }
+}
+
+//Функция временной задержки
+void Delay_ms(__IO uint32_t nTime)
+{
+  TimingDelay = nTime;
+  while(TimingDelay != 0);
+}
+
+void TimingDelay_Decrement(void)
+{
+  if (TimingDelay != 0x00)
+  {
+    TimingDelay--;
+  }
+}
+
+//Обработчик прерывания системного таймера
+void SysTick_Handler(void)
+{
+  TimingDelay_Decrement();
+}
+~~~~~~~~~~~~~~~~~~~~
+
+===============================================================================
+
+ *** Литература: ***
+
+Cortex-M3 Devices Generic User Guide
+Cortex-M3 Peripherals > System timer, SysTick
+4.4. System timer, SysTick
+http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Babieigh.html
+
+ARM. Учебный Курс. SysTick — Системный таймер
+http://easyelectronics.ru/arm-uchebnyj-kurs-systick-sistemnyj-tajmer.html
+
+STM32. Системный таймер SysTick.
+http://chipspace.ru/stm32-systick/