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