Автор работы: Пользователь скрыл имя, 09 Сентября 2014 в 09:42, лекция
В микроконтроллерах семейства STM32L1xx можно организовать отсчет времени по тактам системных часов. По умолчанию в момент запуска системные часы ядра CORTEX – 3M устанавливаются на тактовую частоту f=2000000 гц, обеспечивая длительность одного такта τ=1/f=1/2000000 с =0.0000005 с =0.5 млс. Измерители времени или таймеры способны автономно подсчитывать системные такты до заданной величины.
// 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() позволяет инициализировать таймеры 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_
assert_param(IS_NVIC_SUB_
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;
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;
// (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];