1、定时器
STM32F1 的通用定时器是一个通过可编程预分频器(PSC)驱动的 16 位自动装载计数器
(CNT)构成。 STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产
生输出波形(输出比较和 PWM)等。 使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长
度和波形周期可以在几个微秒到几个毫秒间调整。 STM32 的每个通用定时器都是完全独立的,
没有互相共享的任何资源。
STM3F1 的通用 TIMx (TIM2、 TIM3、 TIM4 和 TIM5)定时器功能包括:
1)16 位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。
2)16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~
65535 之间的任意数值。
3) 4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
A.输入捕获
B.输出比较
C. PWM 生成(边缘或中间对齐模式)
D.单脉冲模式输出
4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外
一个定时器)的同步电路。
5)如下事件发生时产生中断/DMA:
A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
C.输入捕获
D.输出比较
E.支持针对定位的增量(正交)编码器和霍尔传感器电路
F.触发输入作为外部时钟或者按周期的电流管
本次程序主要实现led1-led8的依次点亮(间隔1s)并且将led信息显示在lcd上
步骤如下:
①、添加库文件
②、主函数
#include "stm32f10x.h"
#include <stdio.h>
#include "lcd.h"
u32 TimeDelay=0;
extern u8 leds;
void Delay_Ms(u32 nTime);
void Tim_Config(void);
void NVIC_Configuration(void);
void LED_Config(void);
int main(void)
{
u8 string[20];
SysTick_Config(SystemCoreClock/1000);
Tim_Config();
LED_Config();
STM3210B_LCD_Init();
LCD_Clear(White);
LCD_SetTextColor(White);
LCD_SetBackColor(Blue);
LCD_ClearLine(Line0);
LCD_ClearLine(Line1);
LCD_ClearLine(Line2);
LCD_ClearLine(Line3);
LCD_ClearLine(Line4);
LCD_DisplayStringLine(Line1," TIMER DEMO ");
LCD_DisplayStringLine(Line3," See The LEDs!!! ");
LCD_SetTextColor(Blue);
LCD_SetBackColor(White);
while (1)
{
GPIO_Write(GPIOC,~(1<<(leds+7)));
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
sprintf(string,"%s%d","LED ON:LD",leds);
LCD_DisplayStringLine(Line5,string);
}
}
void Tim_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
NVIC_Configuration();
TIM_TimeBaseStructure.TIM_Period = 9999; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 7199; //预分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分频因子
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数方式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM IT enable */
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //允许更新中断
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM2 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void LED_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void Delay_Ms(u32 nTime)
{
TimeDelay=nTime;
while(TimeDelay!=0) ;
}
这里需要注意的是溢出时间Tout= ((arr+1)*(psc+1))/Tclk;
其中:
Tclk: TIMx的输入时钟频率(单位为 Mhz)
arr:TIMx自动重装值
psc:TIMx时钟预分频数
中断服务函数
void TIM2_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update) == SET){
//清除标志位
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
if(leds==8)
leds=1;
else
leds++;
}
}
固件库中清除中断标志位的函数是:
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
该函数的作用是,清除定时器 TIMx 的中断 TIM_IT 标志位。 使用起来非常简单,比如我们在
TIM3 的溢出中断发生后,我们要清除中断标志位,方法是:
TIM_ClearITPendingBit(TIM3, TIM_IT_Update );
这里需要说明一下,固件库还提供了两个函数用来判断定时器状态以及清除定时器状态标
志位的函数 TIM_GetFlagStatus 和 TIM_ClearFlag,他们的作用和前面两个函数的作用类似。 只
是在 TIM_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而
TIM_GetFlagStatus 直接用来判断状态标志位。
2、PWM输出
本次实验的主要目的是为了输出固定占空比的PWM波,即利用TIM2_CH2和TIM2_CH3对应引脚PA1和PA2
①、库文件
上同
②、主函数
#include "stm32f10x.h"
#include <stdio.h>
#include "lcd.h"
u32 TimeDelay=0;
void Delay_Ms(u32 nTime);
void Tim_Config(uint16_t CCR2Val,uint16_t CCR3Val);
void PWM_IO_Config(void);
int main(void)
{
SysTick_Config(SystemCoreClock/1000);
PWM_IO_Config();
Tim_Config(9999/2,9999*7/10);
STM3210B_LCD_Init();
LCD_Clear(White);
LCD_SetTextColor(White);
LCD_SetBackColor(Blue);
LCD_ClearLine(Line0);
LCD_ClearLine(Line1);
LCD_ClearLine(Line2);
LCD_ClearLine(Line3);
LCD_ClearLine(Line4);
LCD_DisplayStringLine(Line1," TIMER DEMO ");
LCD_DisplayStringLine(Line3," TIM2 PWM MODE ");
LCD_SetTextColor(Blue);
LCD_SetBackColor(White);
LCD_DisplayStringLine(Line6,"PA1-PWMVALUE:50% ");
LCD_DisplayStringLine(Line8,"PA2-PWMVALUE:70% ");
while(1){
;
}
}
void Tim_Config(uint16_t CCR2Val,uint16_t CCR3Val)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 9999; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 7199; //预分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分频因子
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数方式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //pwm的模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 比较输出使能
TIM_OCInitStructure.TIM_Pulse = CCR2Val; // 设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高
TIM_OC2Init(TIM2, &TIM_OCInitStructure); //根据指定的参数初始化外设 TIMx
TIM_OCInitStructure.TIM_Pulse = CCR3Val;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
/* Prescaler configuration */
// TIM_PrescalerConfig(TIM2, 71, TIM_PSCReloadMode_Immediate);
/* TIM IT enable */
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //允许更新中断
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
//使能TIM2 PWM输出模式
TIM_CtrlPWMOutputs(TIM2, ENABLE);
}
void PWM_IO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //引脚复用模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void Delay_Ms(u32 nTime)
{
TimeDelay=nTime;
while(TimeDelay!=0) ;
}
这里提一下PWM的两种模式(假设占空比30%)PWM1指高电平(凸起)占一个完整波形的30%,而PWM2指低电平(凹下)占完整波形的30%;
因为简单我们这里直接初始化是PA1占空比50%,PA2占空比30%,模式为PWM1;
再来解释下为何占空比是50%我们设置自动重装载值为9999,而再初始化 设置待装入捕获比较寄存器的脉冲值使时期为9999/2相当于到预装载值的一半开始改变输出电平;
如果在程序中需要改变占空比可以调用函数
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
这里不再赘述。
3、输入捕获
输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32 的定时器,除了 TIM6 和 TIM7,
其他定时器都有输入捕获功能。 STM32 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的
边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)
存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。同时还可以配置
捕获时是否触发中断/DMA 等
①、库函数与上同
②、主函数
#include "stm32f10x.h"
#include <lcd.h>
#include <stdio.h>
uint32_t timingdelay = 0;
u8 TIM4CH2_CAPTURE_STA; //输入状态
u16 TIM4CH2_CAPTURE_VAL; //捕获值
uint8_t string[20];
void Delay_Ms(uint32_t nTime);
void TIM2_PWM_Init(void);
void TIM4_PWMINPUT_INIT(void);
/**
* @brief Main program.
* @param None
* @retval None
*/
int main(void)
{
SysTick_Config(SystemCoreClock / 1000); //1ms 中断一次
STM3210B_LCD_Init();
LCD_Clear(White);
LCD_SetTextColor(White);
LCD_SetBackColor(Blue);
LCD_ClearLine(Line0);
LCD_ClearLine(Line1);
LCD_ClearLine(Line2);
LCD_ClearLine(Line3);
LCD_ClearLine(Line4);
LCD_DisplayStringLine(Line1," TIMER DEMO ");
LCD_DisplayStringLine(Line3," See The LEDs! ");
LCD_SetTextColor(Blue);
LCD_SetBackColor(White);
TIM2_PWM_Init(); // 1分频 周期 1/72000 频率72Khz
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
TIM4_PWMINPUT_INIT(); //1分频 以72M 频率捕获
while (1)
{
Delay_Ms(200);
if(TIM4CH2_CAPTURE_STA&0X80)
{
sprintf((char*)string,"%s%.3f","duty =",(double)TIM4CH2_CAPTURE_VAL);
LCD_DisplayStringLine(Line8,string);
TIM4CH2_CAPTURE_STA=0;
}
}
}
/*功能名称TIM2_PWM_Init(u16 arr,u16 psc)
描述 TIM2产生四路PWM
*/
void TIM2_PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定时器结构体声明
TIM_OCInitTypeDef TIM_OCInitStructure;//通道输出初始化结构
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟设置:启动TIM2
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; //初始化GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 999; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =0; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_PrescalerConfig(TIM2,71, TIM_PSCReloadMode_Immediate);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OCInitStructure.TIM_Pulse = 700;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OCInitStructure.TIM_Pulse = 900;
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE); //使能 TIMx 在 ARR 上的预装载寄存器
TIM_Cmd(TIM2, ENABLE);
}
/*功能名称IM4_PWMINPUT_INIT(u16 arr,u16 psc)
描述 PWM输入初始化*/
void TIM4_PWMINPUT_INIT(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_ICInitTypeDef TIM4_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //Open TIM4 clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //open gpioB clock
//PB7
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIO??
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 0XFFFF; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =71; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM4_ICInitStructure.TIM_Channel = TIM_Channel_2; //选择输入端 IC1 映射到 通道2 上
TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI上
TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,这里选择不分频
TIM4_ICInitStructure.TIM_ICFilter = 0x0; //IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM4, &TIM4_ICInitStructure);
/*配置中断优先级*/
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM4, TIM_IT_CC2|TIM_IT_Update, ENABLE); //中断配置 允许更新中断捕获中断
TIM_Cmd(TIM4, ENABLE);
}
void TIM4_IRQHandler(void)
{
if((TIM4CH2_CAPTURE_STA&0X80)==0)// )//还未成功捕获
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
if(TIM4CH2_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM4CH2_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM4CH2_CAPTURE_STA|=0X80; //标记成功捕获了一次
TIM4CH2_CAPTURE_VAL=0XFFFF;
}
else TIM4CH2_CAPTURE_STA++;
}
}
if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET)//捕获 2 发生捕获事件
{
if(TIM4CH2_CAPTURE_STA&0X40) //
{
TIM4CH2_CAPTURE_STA|=0X80; //标记成功捕获到一次下降沿
TIM4CH2_CAPTURE_VAL=TIM_GetCapture2(TIM4);
TIM_OC2PolarityConfig(TIM4,TIM_ICPolarity_Rising); //设置为上升沿捕获
}
else //还未开始,第一次捕获上升沿
{
TIM4CH2_CAPTURE_STA=0; //清空
TIM4CH2_CAPTURE_VAL=0;
TIM_SetCounter(TIM4,0);
TIM4CH2_CAPTURE_STA|=0X40; //标记捕获到了上升沿
TIM_OC2PolarityConfig(TIM4,TIM_ICPolarity_Falling); //设置为下降沿捕获
}
}
}
TIM_ClearITPendingBit(TIM4, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
}
void Delay_Ms(uint32_t nTime)
{
timingdelay = nTime;
while(timingdelay !=0);
}
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
大部分上面都有提到,这里着重讲一下输入捕获的设置,我们配置PA1,PA2,PA3三路输出PWM配置PB7为输入捕获。