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

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

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

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

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

2 Таймеры.docx

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

                              // TIM capture/compare register 3,

  __IO uint32_t CCR3;         // offset: 0x3C

                              // TIM capture/compare register 4,

  __IO uint32_t CCR4;         // offset: 0x40

  uint32_t      RESERVED17;                    // Reserved, 0x44

  __IO uint16_t DCR;   // TIM DMA control register, offset: 0x48

  uint16_t      RESERVED18;                    // Reserved, 0x4A

                           // TIM DMA address for full transfer,

  __IO uint16_t DMAR;                            // offset: 0x4C

  uint16_t      RESERVED19;                    // Reserved, 0x4E

  __IO uint16_t OR;         // TIM option register, offset: 0x50

  uint16_t      RESERVED20;                    // Reserved, 0x52

} TIM_TypeDef;

В структурном типе TIM_TypeDef  поле CR1 (смещение 0x00) запускает работу счетчика серий. Это можно увидеть в макросе, например, TIM2_Start перед главной функцией main() проекта FT11.

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

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

#define  TIM_CR1_CEN    ((uint16_t)0x0001)

Остановить счетчик серий таймера TIM2 можно с помощью макроса TIM2_stop.

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

Поле CNT (смещение 0x24) является регистром счетчика серий. Его можно использовать для выработки прерываний таймера. Например, для таймера TIM2 – это счетчик, при сбросе или перезагрузке которого выполняется прерывание, обозначаемое в векторе прерываний меткой TIM2_IRQHandler (п. 2.1.1, 2.1.6). Значение счетчика увеличивается на единицу, когда прошли все такты одной серии системной частоты таймера. Если значение счетчика CNT совпадает с количеством серий в поле ARR (смещение 0x2C), то происходят два действия: 1) счетчик CNT сбрасывается в 0; 2) вырабатывается сигнал прерывания. Количество серий ARR задается программно во время инициализации таймера (п. 2.1.1) как поле reload. Для каждой серии работает внутренний счетчик масштабирования. Значение внутреннего счетчика увеличивается на единицу на каждом такте часов таймера. Когда значение внутреннего счетчика совпадает со значением поля PSC (смещение 0x28), обозначаемое как поле prescaler, выполняются два действия: 1) внутренний счетчик сбрасывается в 0; 2) значение счетчика серий CNT увеличивается на единицу. Количество тактов масштабирования PSC задается программно во время инициализации таймера (п. 2.1.1).

Когда атрибуты ARR и PSC таймера установлены, следует разрешить возможность прерывания, установив единицу в регистре DIER (смещение 0x0C) (п. 2.1.1, 2.1.3, 2.1.4).

Остальные поля в структуре TIM_TypeDef будут рассматриваться позже в соответствующих ситуациях.

 

2.1.3. Функция инициализации таймера Init_TIMx1.

Функция Init_TIMx1() позволяет инициализировать таймеры TIM2, TIM3, TIM4, TIM6, TIM7, подключенные к первой системной периферийной шине APB1. Инициализация заключается в настройке счетчиков таймера для создания прерываний через заданные интервалы времени.

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 );

}

Первый параметр TIM_TypeDef* TIMx содержит адрес регистровой обрасти таймера (п. 2.1.2). Например, для таймера TIM2 – это адрес 0x40000000. Второй параметр uint32_t TIMx_RCC задает маску бита, который включает часы таймера. В программе FT11 этот параметр для таймера TIM2 задается с помощью макроса TIM2_RCC.

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

Константа RCC_APB1ENR_TIM2EN определена в файле stm32l1xx.h.

#define RCC_APB1ENR_TIM2EN        ((uint32_t)0x00000001)

Третий параметр uint16_t reload задает количество серий для крайнего значения в счетчике CNT (п. 2.1.2). Четвертый параметр uint16_t prescaler определяет масштабируемость (scale – масштаб) или количество тактов в одной серии таймера (п. 2.1.2). Иногда prescaler трактуют как делитель или замедлитель по времени наступления прерывания, но это дело привычек программиста.

Пятый параметр uint8_t TIMx_IRQn задает битовое место для таймера в регистре автономного устройства приоритетов для регулирования очередности выполнения прерывания в зависимости от заданного приоритета (п. 2.1.4, 2.1.5). В программе FT11 – это место TIM2_IRQn.

Шестой параметр uint8_t preemptionPriority определяет преимущественный приоритет прерывания по таймеру. Чем меньше, тем главнее. Такое прерывание прерывает прерывание с большим приоритетом. Седьмой параметр uint8_t subPriority устанавливает приоритет среди прерываний с равными преимущественными приоритетами. Оба приоритета задают группы приоритетов и приоритеты внутри группы (п. 2.1.4).

В теле функции Init_TIMx1() инструкция RCC->APB1ENR |= TIMx_RCC включает часы таймера на первой системной периферийной шине APB1. Следующая инструкция TIMx->ARR = reload устанавливает в регистре ARR таймера (п. 2.1.2) количество серий межу прерываниями. Инструкция TIMx->PSC = prescaler устанавливает в таймере количество тактов в одной серии. Произведение ARR * PSC – это количество тактов на шине APB1 между прерываниями. Момент прерывания определяется количеством тактов, которое фиксируется в счетчике CNT таймера. Когда CNT = ARR, тогда вырабатывается сигнал прерывания, а значение счетчика сбрасывается CNT = 0.

Инструкция TIMx->DIER |= TIM_DIER_UIE разрешает таймеры производить прерывания, когда сбрасывается счетчик. Константа TIM_DIER_UIE определена в файле stm32l1xx.h.

#define  TIM_DIER_UIE              ((uint16_t)0x0001)

Приоритет таймерного прерывания задается с помощью оператора функции Timer_NVIC( TIMx_IRQn, preemptionPriority, subPriority ). Параметр TIMx_IRQn битовое место таймера в регистре приоритетов. Подробности приоритетов обсуждаются в следующем разделе.

 

2.1.4. Контроллер приоритетов прерываний NVIC.

Краткое обозначение NVIC составлено по начальным буквам автономной части микроконтроллера Nested Vectored Interrupt Controller (контроллер вложенных векторных прерываний). Часто вложенное прерывание называют отложенным прерыванием, если оно происходит в тот момент, когда ядро CORTEX – 3M уже занято обслуживанием прерывания с более высоким приоритетом или когда обслуживание само запрещает, чтобы его прерывали. Тогда выполнение вновь поступившего прерывания откладывается, а информация о нем помещается в очередь прерываний. Когда обслуживание текущего прерывания завершается, следующее прерывание выбирается из очереди отложенных прерываний, но согласно их приоритетам. Такой дисциплиной взаимодействия с прерываниями руководит автономный контроллер прерываний NVIC.

Микроконтроллер с ядром CORTEX – 3M имеет несколько уровней преимущественных приоритетов  preemption priority. Каждый из этих уровней имеет подуровни приоритетов subpriority. Для регистров контроллера прерываний в адресном пространстве микроконтроллера отведена область NVIC_BASE. Eё адрес задается в системном файле core_3m.h.

#define SCS_BASE            (0xE000E000UL)

#define NVIC_BASE           (SCS_BASE +  0x0100UL)

Фрагментация области NVIC_BASE определяется с помощью структурного типа NVIC_Type, использование которого позволяет определить структурный объект NVIC для регистровой области контроллера прерываний в системном файле core_3m.h.

#define NVIC   ((NVIC_Type *) NVIC_BASE)

В том же системном файле core_3m.h структурный тип NVIC_Type имеет следующее определение.

 

 

#define     __IO    volatile

typedef struct

{

                         // (R/W)  Interrupt Set Enable Register

   __IO uint32_t ISER[8];                      //< Offset: 0x000

   uint32_t RESERVED0[24];

                       // (R/W)  Interrupt Clear Enable Register

  __IO uint32_t ICER[8];                       // Offset: 0x080                         

   uint32_t RSERVED1[24];

                        // (R/W)  Interrupt Set Pending Register

  __IO uint32_t ISPR[8];                       // Offset: 0x100                      

   uint32_t RESERVED2[24];

                      // (R/W)  Interrupt Clear Pending Register

  __IO uint32_t ICPR[8];                      // Offset: 0x180

   uint32_t RESERVED3[24];

                         // (R/W)  Interrupt Active bit Register

  __IO uint32_t IABR[8];                       // Offset: 0x200

   uint32_t RESERVED4[56];

               // (R/W)  Interrupt Priority Register (8Bit wide)

  __IO uint8_t  IP[240];                       // Offset: 0x300

   uint32_t RESERVED5[644];

                   // ( /W)  Software Trigger Interrupt Register

  __O  uint32_t STIR;                          // Offset: 0xE00

}  NVIC_Type;

В структурном типе NVIC_Type массив ISER[8] (смещение 0x00) отмечает возможность применения прерывания. Например, для таймера TIM2 номер элемента в массиве ISER[8] задается как TIM2_IRQn >> 5. Значение TIM2_IRQn определено в файле stm32l1xx.h как TIM2_IRQn = 28 в перечислении typedef enum IRQn. Следовательно, TIM2_IRQn >> 5 определяет число 28 = 10*1010+1000 = 10100 + 1000 = 11100 сдвинутое на 5 бит вправо, что в результате соответствует 0. Таким образом, разрешение на выполнение прерывания для TIM2 записывается в элемент ISER[0]. Подробности сделанных вычислений рассматриваются в следующем разделе (п. 2.1.5). Хотя это можно также узнать из определения в файле stm32l1xx.h константы NVIC_ISER_SETENA_28.

#define NVIC_ISER_SETENA_28 ((uint32_t)0x10000000)

Тогда запись разрешения прерывания для TIM2 будет выглядеть следующим образом: NVIC->ISER[0] |= NVIC_ISER_SETENA_28.

В структуре NVIC_Type массив регистров __IO uint8_t  IP[240] хранит абсолютный приоритет прерывания, вычисленный как комбинация преимущественного приоритета PreemtionPriority и подприоритета SubPriority. Алгоритм комбинирования приоритетов рассматривается в следующем разделе (п. 2.1.5).

Остальные поля в структуре NVIC_Type будут рассматриваться позже по мере необходимости.

 

2.1.5. Функция Timer_NVIC для задания приоритета таймера.

Функция Timer_NVIC() помогает правильно установить приоритет канала прерывания. Понятие канал подразумевает, что приоритет можно назначить различным средствам. С позиции приоритета источник любого прерывания является каналом. Например, в программе FT11 каналом является таймер TIM2. В следующей программе FT12 каналом будет таймер TIM10. А в программе FB31 каналом будет внешнее прерывание для иголки PA0 порта A.

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);

}

В функции Timer_NVIC первый параметр uint8_t channel задает системный номер канала как номер источника прерывания. Например, для таймера TIM2 номер источника для канала задается в фале stm32l1xx.h как TIM2_IRQn = 28 с помощью перечислении typedef enum IRQn. Второй параметр uint8_t preemptionPriority определяет преимущественный приоритет прерывания по таймеру. Чем менбше, тем главнее. Такое прерывание прерывает прерывание с большим приоритетом. Третий параметр uint8_t subPriority устанавливает приоритет среди прерываний с равными преимущественными приоритетами. Оба приоритета относятся к группе приоритетов с приоритетами внутри группы.

В теле функции Timer_NVIC инструкция tmp = ( 0x700 - ( (SCB->AIRCR) & (uint32_t)0x700 ) ) >> 0x08 вычисляет временный стандартный приоритет, который будет использоваться для вычисления реального абсолютного приоритета любого канала. Структурный объект SCB является конфигурационной областью внешних устройств микроконтроллера. Адрес этой области определен в файле core_cm3.h.

#define SCS_BASE    (0xE000E000UL)

#define SCB_BASE    (SCS_BASE +  0x0D00UL)

#define SCB         ((SCB_Type *) SCB_BASE)

Фрагментация области SCB задается с помощью структурного типа SCB_Type в файле stm32l1xx.h.

#ifdef __cplusplus

  #define   __I     volatile  // Defines 'read only' permissions

#else

  #define  __I volatile const // Defines 'read only' permissions

#endif

#define    __IO    volatile

typedef struct

{

  __I  uint32_t CPUID; // (R/ )CPUID Base Register Offset: 0x000

                    (R/W)  Interrupt Control and State Register

  __IO uint32_t ICSR;                           // Offset: 0x004

                          // (R/W)  Vector Table Offset Register

  __IO uint32_t VTOR;                           // Offset: 0x008

      // (R/W)  Application Interrupt and Reset Control Register

  __IO uint32_t AIRCR;                          // Offset: 0x00C

                               // (R/W)  System Control Register

  __IO uint32_t SCR;                            // Offset: 0x010

                        // (R/W)  Configuration Control Register

  __IO uint32_t CCR;                            // Offset: 0x014

 // (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15)

  __IO uint8_t  SHP[12];                       // Offset: 0x018

             // (R/W)  System Handler Control and State Register

  __IO uint32_t SHCSR;                          // Offset: 0x024

                    // (R/W)  Configurable Fault Status Register

  __IO uint32_t CFSR;                           // Offset: 0x028

                             // (R/W)  HardFault Status Register

  __IO uint32_t HFSR;                           // Offset: 0x02C

                           // (R/W)  Debug Fault Status Register

  __IO uint32_t DFSR;                           // Offset: 0x030

                      // (R/W)  MemManage Fault Address Register

  __IO uint32_t MMFAR;                          // Offset: 0x034

                             // (R/W)  BusFault Address Register

  __IO uint32_t BFAR;                           // Offset: 0x038

                       // (R/W)  Auxiliary Fault Status Register

  __IO uint32_t AFSR;                           // Offset: 0x03C

                            // (R/ )  Processor Feature Register

  __I  uint32_t PFR[2];                         // Offset: 0x040

                               // (R/ )  Debug Feature Register

  __I  uint32_t DFR;                           // Offset: 0x048

                           // (R/ )  Auxiliary Feature Register

  __I  uint32_t ADR;                           // Offset: 0x04C

                        // (R/ )  Memory Model Feature Register

  __I  uint32_t MMFR[4];                       // Offset: 0x050

                  // (R/ )  Instruction Set Attributes Register

  __I  uint32_t ISAR[5];                       // Offset: 0x060

  uint32_t RESERVED0[5];

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