Таймеры в микроконтроллерах

Автор работы: Пользователь скрыл имя, 09 Сентября 2014 в 09:42, лекция

Краткое описание

В микроконтроллерах семейства STM32L1xx можно организовать отсчет времени по тактам системных часов. По умолчанию в момент запуска системные часы ядра CORTEX – 3M устанавливаются на тактовую частоту f=2000000 гц, обеспечивая длительность одного такта τ=1/f=1/2000000 с =0.0000005 с =0.5 млс. Измерители времени или таймеры способны автономно подсчитывать системные такты до заданной величины.

Прикрепленные файлы: 1 файл

2 Таймеры.docx

— 113.41 Кб (Скачать документ)

2. Таймеры.

В микроконтроллерах семейства STM32L1xx можно организовать отсчет времени по тактам системных часов. По умолчанию в момент запуска системные часы ядра CORTEX – 3M устанавливаются на тактовую частоту гц, обеспечивая длительность одного такта с с млс. Измерители времени или таймеры способны автономно подсчитывать системные такты до заданной величины. Например, 100 тактов таймер подсчитывает за . После выполнения заданного количества отсчетов таймер перезагружается (reload – перезагрузка) на значение 0 и начинает заново подсчитывать такты часов. Количество этих тактов N  задается с помощью двух двухбайтных величин, которые обозначим u и v. Все три величины связаны следующим соотношением:

.

Величина u определяет количество серий между перезагрузками таймера. Величина v содержит количество тактов внутри каждой серии. Величина N – это общее количество тактов между сигналами перезагрузки и прерываний таймера. Хотя сами сигналы вырабатываются по счетчику серий до заданной величины. Например, если задано , то пары u и v можно определить различными способами:

;

;

;

.

Количество серий u задается как элемент reload (перезагрузка). Количество тактов в серии v задается как элемент prescaler (масштабирование), который часто называют делитель. В микроконтроллере определены свободные таймеры TIM2, TIM3, TIM4, TIM6, TIM7, TIM9, TIM10, TIM11. Эти таймеры независимы друг от друга. По умолчанию их частота составляет 2 Мгц. Каждый таймер может быть функционально связан со своей особой дополнительной деятельностью обслуживания микроконтроллера, например, с широтно-импульсной модуляцией или цифроаналоговым преобразованием.

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

 

 

 

2.1. Мигающий свет в проекте FT11.

Ниже представлен проект FT11, в котором таймер TIM2 используется для организации мигания зеленого светодиода с частотой гц. Чтобы обеспечить мигание, время между очередными включениями зеленого света разбито на два интервала по c. Разбиение пополам принято здесь условно для простоты программирования и соответствует скважности 0.5. Задание произвольной скважности обсуждается позже (п. 4. x).

Итак, переключение светодиода происходит с частотой гц. При системной частоте по умолчанию Мгц за одну секунду пройдет системных тактов. За время с включенного или выключенного состояния зеленого светодиода происходит тактов.

Это количество тактов следует разбить на количество серий u и количество тактов внутри серии v. Необходимо учесть, что переменные u и v являются двухбайтными с максимальными значениями не более . Например, можно положить , что обеспечит заданную частоту мигания при общем числе тактов таймера . Перезагрузка счетчика таймера происходит по числу серий .

 

2.1.1. Главная функция main.

Формула расчета частоты прерываний реализована в представленном ниже  проекте FT11 для мигания зеленого света с частотой 1 гц. При скважности 0.5 первую половину интервала времени можно использовать на свечение, а вторую половину интервала оставить без света. В программе присутствует дисплей, только чтобы показать, что он не мешает таймеру.

// Project FT11

// Мигание зеленого света с  частотой 1 hz по таймеру 2

//                                 // Из учебного курса А. Деона

#include "stm32l1xx.h"

#include "stm32l_discovery_lcd.h"

#include "Port_Init.h"

#include "Light_Lib.h"

#include "Display_Init.h"

#include "Display_Lib.h"

//--------------------------------------------------------------

#define TIM2_RCC   RCC_APB1ENR_TIM2EN      // включить часы TIM2

#define TIM2_Start TIM2->CR1 |= TIM_CR1_CEN    // старт счетчика

#define TIM2_Stop  TIM2->CR1 &= ~TIM_CR1_CEN    // стоп счетчика

 // разрешить использование этого прерывания другими источниками

#define TIM2_EnPending    TIM2->SR |= TIM_IT_Update

 // запретить использование этого прерывания другими источниками

#define TIM2_DisPending   TIM2->SR &= ~TIM_IT_Update

//--------------------------------------------------------------

void Init_TIMx1( TIM_TypeDef* TIMx, uint32_t TIMx_RCC,

                 uint16_t reload, uint16_t prescaler,

                 uint8_t TIMx_IRQn,

                 uint8_t preemptionPriority,

                 uint8_t subPriority );

void Timer_NVIC( uint8_t channel,

                 uint8_t preemptionPriority,

                 uint8_t subPriority );

//--------------------------------------------------------------

uint16_t DisplayArray[6];             // строка символов дисплея

uint32_t n = 0;                                   // целое число

//--------------------------------------------------------------

int main(void)

{

  Init_PortABC();                // инициализация портов A, B, C

  Init_BlueGreenLight();// инициализация синего и зеленого света

  Init_Display_PortABC();     // альтернативные функции дисплея

  Init_LCD();                           // инициализация дисплея

  DisplayUint32Convertion( DisplayArray, n );   // строка для n

  LCD_GLASS_DisplayStrDeci( DisplayArray );    // показать число

  LIGHT_ON( LIGHT_PORT, BLUE_LIGHT );  // включить синий свет

                            // T = 1 sec.; f = 1 / T = 1 hz

                       // fs = 2000000 hz

             // N = fs * T / 2 = 1000000

                                 // N = u * v = 1000000

                       // reload = u = 1000

                                 // prescaler = v = 1000

                  // инициализация таймера 2 с прерываниями

  Init_TIMx1( TIM2, TIM2_RCC, 1000, 1000, TIM2_IRQn, 0, 0 );

  TIM2_Start;                       // включить счетчик таймера

  while(1);               // цикл продолжения в реальном времени

}

//--------------------------------------------------------------

void Init_TIMx1( TIM_TypeDef* TIMx, uint32_t TIMx_RCC,

                 uint16_t reload, uint16_t prescaler,

                 uint8_t TIMx_IRQn,

                 uint8_t preemptionPriority,

                 uint8_t subPriority )

{

   RCC->APB1ENR |= TIMx_RCC;              // включить часы TIMx

   TIMx->ARR = reload;            // число серий на прерывание

  TIMx->PSC = prescaler;               // число тактов в серии

   TIMx->DIER |= TIM_DIER_UIE;//разрешить прерывания от таймера

                     // установить приоритеты канала прерываний

   Timer_NVIC( TIMx_IRQn, preemptionPriority, subPriority );

}

//--------------------------------------------------------------

                               // включить канал и его приоритет 

void Timer_NVIC( uint8_t channel,

                 uint8_t preemptionPriority,

                 uint8_t subPriority )

{

  uint8_t tmp = 0x00, tmppre = 0x00, tmpsub = 0x0F;

 

  assert_param(IS_NVIC_PREEMPTION_PRIORITY(preemptionPriority)); 

  assert_param(IS_NVIC_SUB_PRIORITY( subPriority ));

                                 // соответствующий IRQ Priority

  tmp = ( 0x700 - ( (SCB->AIRCR) & (uint32_t)0x700 ) ) >> 0x08;

  tmppre = ( 0x4 - tmp );

  tmpsub = tmpsub >> tmp;

 

  tmp = (uint32_t)preemptionPriority << tmppre;

  tmp |=  (uint8_t)( subPriority & tmpsub );

  tmp = tmp << 0x04;

  NVIC->IP[channel] = tmp;

                                           // включить IRQ канал

  NVIC->ISER[ channel >> 0x05 ] =

                    (uint32_t)0x01 << (channel & (uint8_t)0x1F);

}

//--------------------------------------------------------------

void TIM2_IRQHandler(void)

{

   // запретить использование  этого прерывания другим источникам

TIM2_DisPending;       // запретить прерывание этой функции

LIGHT_PORT->ODR ^= GREEN_LIGHT; // переключить зеленый свет

}

В подключаемом файле #include "Port_Init.h" (п. 1.7) находятся функции инициализации портов A, B, C. Подключаемый файл #include "Light_Lib.h" (п. 1.8) обеспечивает доступ к светодиодам. В подключаемом файле #include "Display_Init.h" (п. 1.12) собраны функции настройки портов A, B, C на альтернативные функции дисплея. Подключаемый файл #include " Display_Lib.h " (п. 1.15) обеспечивает преобразование информации для вывода на дисплей.

В главной функции main() выполняется инициализация портов A, B, C для дисплея и зажигается синий свет работоспособности устройства. Эта технология использовалась ранее в проекте FE21_Uint (п. 1.11).

Оператор функции Init_TIMx1( TIM2, TIM2_RCC, 1000, 1000, TIM2_IRQn, 0, 0 ) обеспечивает инициализацию таймера. Первый параметр TIM2 задает структурный объект таймера 2. Особенности этого объекта рассмотрим в следующем разделе (п. 2.1.2). Второй параметр TIM2_RCC определяет бит присутствия таймера 2 на первой периферийной шине. Значение TIM2_RCC определено вначале программы через макрос

#define TIM2_RCC   RCC_APB1ENR_TIM2EN.

В свою очередь значение RCC_APB1ENR_TIM2EN задано в системном файле stm32l1xx.h следующим образом:

#define RCC_APB1ENR_TIM2EN   ((uint32_t)0x00000001).

Третий и четвертый параметры 1000, 1000 задают количество серий reload и масштабируемость каждой серии prescaler. Их произведение 1000000 равно количеству машинных тактов, которые происходят между прерывания. Пятый параметр TIM2_IRQn определяет таймер 2 в регистрах приоритета прерываний (п. 2.1.4, 2.1.5). Шестой и седьмой параметры 0, 0 соответствуют приоритету и подприоритету канала прерывания (п. 2.1.4). Сейчас на этом канале задан таймер TIM2 с максимальным приоритетом 0, 0 в группе таймеров. В общем случае на приоритетном канале может находиться любое регистрируемое событие, например – изменение напряжения на какой-либо иголке какого-нибудь порта.

После инициализации таймера в функции Init_TIMx1() в теле главной функции main() запускается счетчик таймера в инструкции TIM2_Start. Определение макроса TIM2_Start находится в начале главного файла.

#define TIM2_Start TIM2->CR1 |= TIM_CR1_CEN    // старт счетчика

Значение TIM_CR1_CEN определено в системном файле stm32l1xx.h.

#define  TIM_CR1_CEN      ((uint16_t)0x0001)

Когда счетчик таймера достигнет значения 1000 серий, таймерный блок микроконтроллера выработает сигнал прерывания и сбросит значение счетчика в 0. Через следующие 1000 серий будет создано очередное прерывание. По сигналу прерывания вектор прерываний (п. 2.1.6) приостановит в главной функции main() выполнение вечного цикла реального времени while( 1 ) и направит процессор ядра CORTEX – 3M на выполнение функции TIM2_IRQHandler(). По окончании выполнения функции TIM2_IRQHandler() процессор продолжит работу пустого цикла реального времени while( 1 ). Таким образом, пустой цикл реального времени предназначен только для реальной поддержки работоспособности процессора микроконтроллера. Обслуживание возникающих ситуаций производится с помощью механизма прерываний, который переключает процессор на выполнение соответствующих функций. Например, в проекте FT11 ситуации прерываний создает таймер TIM2 через каждые 0.5 с. В одних случаях зеленый свет включается, в других – выключается. В целом свет зажигается один раз в секунду на время 0.5 с, что соответствует миганию с частотой 1 гц.

Переключение света выполняется в функции прерывания TIM2_IRQHandler(). Тело функции начинается с того, что запрещается TIM2_DisPending прерывать выполнение этой функции. В проекте FT11 такие ситуации не возникают, но, в общем, такое вложенное прерывание допустимо. Для надежного программирования ситуаций рекомендуется выполнять запрет прерывания функции, отправляя непредвиденные прерывания в очередь отложенных прерываний. Макрос TIM2_DisPending определен в начале проекта FT11.

define TIM2_DisPending   TIM2->SR &= ~TIM_IT_Update

Системная константа TIM_IT_Update определена в файле stm32l1xx_tim.h.

#define TIM_IT_Update        ((uint16_t)0x0001)

Переключение света производится, когда на иголке PB7 устанавливается или сбрасывается выходное напряжение с помощью инструкции LIGHT_PORT ->ODR ^= GREEN_LIGHT. Определения для LIGHT_PORT и GREEN_LIGHT находятся в подключаемом файле #include "Light_Lib.h" (п. 1.8).

 

2.1.2. Структурный тип TIM_TypeDef для параметров таймера.

Для каждого таймера в адресном пространстве памяти отведены своя область. В этой области находятся регистры таймера. Например, в общем пространстве адресов микроконтроллера начальный адрес 0x40000000 определяет регистры таймера TIM2 и задается как адрес области TIM2_BASE с помощью системного файла stm32l1xx.h.

#define PERIPH_BASE           ((uint32_t)0x40000000)

#define APB1PERIPH_BASE       PERIPH_BASE

#define TIM2_BASE             (APB1PERIPH_BASE + 0x0000)

#define TIM2                  ((TIM_TypeDef *) TIM2_BASE)

Фрагментация области TIM2_BASE определяется с помощью структурного типа TIM_TypeDef. Как обычно, для обозначений регистров используется обозначение volatile языка Си, закрепленное в системном файле core_cm3.h.

#define     __IO    volatile

typedef struct

{

  __IO uint16_t CR1;     // TIM control register 1, offset: 0x00

  uint16_t      RESERVED0;                     // Reserved, 0x02

  __IO uint16_t CR2;     // TIM control register 2, offset: 0x04

  uint16_t      RESERVED1;                     // Reserved, 0x06

  __IO uint16_t SMCR;   // TIM slave mode register, offset: 0x08

  uint16_t      RESERVED2;                     // Reserved, 0x0A

                           // TIM DMA/interrupt enable register,

  __IO uint16_t DIER;      // offset: 0x0C

  uint16_t      RESERVED3;                     // Reserved, 0x0E

  __IO uint16_t SR;         // TIM status register, offset: 0x10

  uint16_t      RESERVED4;                     // Reserved, 0x12

  __IO uint16_t EGR;//TIM event generation register,offset: 0x14

  uint16_t      RESERVED5;                     // Reserved, 0x16

                         // TIM capture/compare mode register 1,

  __IO uint16_t CCMR1;   // offset: 0x18

  uint16_t      RESERVED6;                     // Reserved, 0x1A

                         // TIM capture/compare mode register 2,

  __IO uint16_t CCMR2;   // offset: 0x1C

  uint16_t      RESERVED7;                     // Reserved, 0x1E

                         // TIM capture/compare enable register,

  __IO uint16_t CCER;    // offset: 0x20

  uint16_t      RESERVED8;                     // Reserved, 0x22

  __IO uint32_t CNT;       // TIM counter register, offset: 0x24

  __IO uint16_t PSC;              // TIM prescaler, offset: 0x28

  uint16_t      RESERVED10;                    // Reserved, 0x2A

  __IO uint32_t ARR;   // TIM auto-reload register, offset: 0x2C

  uint32_t      RESERVED12;                    // Reserved, 0x30

                              // TIM capture/compare register 1,

  __IO uint32_t CCR1;         // offset: 0x34

                              // TIM capture/compare register 2,

  __IO uint32_t CCR2;         // offset: 0x38

Информация о работе Таймеры в микроконтроллерах