首先从定时器的功能框图上介绍定时器的基本功能,然后介绍输出比较模式(输出PWM)和输入捕获模式。最后使用定时器的输入捕获功能测量一个脉冲的宽度。
通用定时器框图如下:( STM32中文参考手册_V10.pdf P254)
图1
1.输出模式
从图1可以看到通用定时器的基本框图。框图比较复杂,把定时器的功能单独拆分成下图,首先分析下定时器的输出比较模式用到的部分。
图2
(1)左上角第1部分,是定时器的时钟选择部分,默认使用内部时钟(CK_INT),就是使用来自APB1的时钟,频率是72M。注意一点,APB1是低速时钟,最高36M,但是定时器可以到72M。
(2)右上角第2部分,暂时不了解。
(3)第3部分,是定时器的基本配置,叫 时基。这一部分包括了PSC预分频寄存器、ARR自动重装寄存器和CNT计数寄存器。无论是使用定时器的哪个功能,这个时都要配置的,所以叫时基。时基部分决定了定时器的频率、周期。时基和第4部分决定输出PWM的占空比(输出模式下)。
(4)第4部分,有4个捕获比较寄存器。分别对应定时器的4个通道。这个寄存器在输出模式和输入模式下是公用的,输出模式下,可读可写,输入模式下只读。输出pwm时,使用CNT计数寄存器和这个寄存器进行比较,控制pwm的占空比。在第4、5部分之间有一个CCxI和OCxREF(x can be 1 2 3 4 ,对应4个通道),CCxI是产生比较中断(当CNT寄存器和捕获比较寄存器的值相等时),并反转电平,OCxREF是参考信号(高低电平)。
(5)第5部分,输出控制。低4部分的OCxREF的参考信号到达本部分的输出控制时,输出控制主要做两部分操作,1:确定有效和无效电平(OCxREF在输出时就确定了是否是有效电平) 2:确定有效时高电平还是低电平。
(6)第6部分,低5部分输出的高低电平通过OCx通道输出最终的信号。
1.1输出比较模式下的工作流程以及框图和寄存器对应关系
(1) 对应图2中 第1部分:72M内部时钟给定时器提供时钟,让定时器工作起来。
(2.1)对应图2中第3部分:PSC预分频器进行分频,定时器可能用不到72M,将72M分频。PSC是16位的,分频系数为1-2^16。需要注意的是,对写入该寄存器的值-1,比如分频100,写入99即可,防止此寄存器写入0。
(2.2)对应图2中第3部分:自动重装载寄存器(TIMx_ARR),和计数寄存器。当我们打开定时器时,计数器(TIMx_CNT)的值从0开始自增(向上计数模式),当自增的数值等于自动重装载寄存器(TIMx_ARR)中的值时,TIMx_CNT将清零并重新自增。PSC预分频只是确定了定时器的时钟频率,想要确定定时器一个脉冲的频率还要ARR的参与,计算公式如下:
比如 PSC=71+1,ARR=999+1 ,则F = 1KHz。知道了频率。则周期=1/F = 0.001s (周期*频率=1)
(3)对应图2中第4部分: 捕获/比较寄存器(TIMx_CCRx,x can be 1 2 3 4,对应4个通道)。通过上一步知道了一个脉冲的持续时间是0.001s。那这个脉冲中高电平持续的时间和第电平持续的时间怎么划分,(占空比怎么分),这时候就要捕获/比较寄存器1(TIMx_CCRx)的参与了。通过上一步,我们知道计数器(TIMx_CNT)的值从0开始自增(向上计数模式),当自增的数值等于自动重装载寄存器(TIMx_ARR)中的值时,TIMx_CNT将清零并重新自增。现在CNT寄存器的值每自增一次,就和捕获/比较寄存器(TIMx_CCRx)的值进行比较,当CNT的值<CCRx的值时OCxREF输出有效或者无效电平。当CNT的值>CCRx的值时OCxREF输出有效或者无效电平(可配置),并且会产生比较中断 CCxI,相应的标志位 CCxIF(SR 寄存器中)会置位。那么谁来决定什么时候是有效还是无效电平,对应寄存器如下:
(3.1)补充:pwm周期和占空比的关系如下:ARR=1000,CCRx=600,占空比=60%(高电平持续时间),想要改变占空比就修改CCR,想要修改周期(一个高电平+低电平的持续时间就修改ARR和PSC)
(4)图2中,第4部分输出的OCREFx电压(有效or无效)到第5部分的输出控制部分。输出控制部分决定有效电平是高电平or低电平。
(5)图2中,第5部分把最终的信号输出到通道CHx where x can be 1 2 3 4。
2.输入捕获
输入捕获框图
输入捕获模式和上面的输出pwm模式都是定时器的具体功能,所以在定时器功能框图上有些是共用的,比如途中的红色框图1属于定时器的基本部分,无论那种模式都是必须要配置的,这部分觉得定时器的频率和周期。剩下的2,3,4,5再来具体说一下。
(1)左边红色框图2部分是,用来作为输入捕获的引脚。定时器想要捕获外部的信号,需要有个通道,也就是对应的IO引脚。具体对应关系在数据手册的pin map and description中。
(2)左边红色框图3部分是,用来设置被捕获的信号的检测方式和滤波方式。当输入的信号存在高频干扰的时候,我们需要对输入信号进行滤波,即进行重新采样,根据采样定律,采样的频率必须大于等于两倍的输入信号。比如输入的信号为 1M,又存在高频的信号干扰,那么此时就很有必要进行滤波,我们可以设置采样频率为 2M,这样可以在保证采样到有效信号的基础上把高于 2M 的高频干扰信号过滤掉。
14.4.1 控制寄存器 1(TIMx_CR1) 位9:8
14.4.7 捕获/比较模式寄存器1(TIMx_CCMR1) 位7:4
疑惑:图中第3.4部分中间的TF1FP1 TF1FP2是什么意思,举个例子:输入捕获通道1叫TI1,从这个信号捕捉到的信号经过通道1的滤波和边沿检测器之后,输出2个完全相同的信号,分别叫TF1FP1、 TF1FP2。TF1FP1输入到第4部分的通道1(IC1),TF1FP2输入到第4部分的通道2(IC2)。这个是用来作为定时器的捕获PWM的功能。详细介绍https://blog.csdn.net/guosir_/article/details/78407063
(3)第4部分。输入捕获的通道ICx。ICx通道和TI通道的区别:TI通道是直接输入的信号,TI通道直接输入的信号进行滤波后到达ICx通道。最终ICx通道的信号到达定时器内部。在第4部分可以进行预分频设置。ICx 的输出信号会经过一个预分频器,用于决定发生多少个事件时进行一次捕获。具体的由寄存器 CCMRx 的位 ICxPSC 配置,如果希望捕获信号的每一个边沿,则不分频。
14.4.7 捕获/比较模式寄存器1(TIMx_CCMR1) 位3:2
(4)第4部分进行分频之后就会得到最终的信号,叫ICxPS,如果使能了捕获中断则会进入中断。
14.4.4 DMA/中断使能寄存器(TIMx_DIER)
(5)经过预分频器的信号 ICxPS 是最终被捕获的信号,当发生捕获时(第一次),计数器CNT 的值会被锁存到捕获寄存器 CCR 中,还会产生 CCxI 中断,相应的中断位 CCxIF(在SR 寄存器中)会被置位,通过软件或者读取 CCR 中的值可以将 CCxIF 清 0。如果发生第
二次捕获(即重复捕获: CCR 寄存器中已捕获到计数器值且 CCxIF 标志已置 1),则捕获溢出标志位 CCxOF(在 SR 寄存器中)会被置位, CCxOF 只能通过软件清零。
第5部分的CCRx寄存器是输出模式和输入捕获模式共用的。
3.输入捕获测量一个脉冲的宽度。
3.1输出占空比为60%的PWM
使用定时器输出一个10KHz,占空比为60%的PWM
定时器2的ch2对应PA1
1 TIM2_PWM_Init(1000-1,71);//频率1khz 周期0.001s->1ms 占空比60%,高电平0.6ms,低电平0.4ms
函数实现如下:
1 void TIM2_PWM_Init(u16 arr,u16 psc) 2 { 3 GPIO_InitTypeDef GPIO_InitStructure; 4 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 5 TIM_OCInitTypeDef TIM_OCInitStructure; 6 7 8 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能定时器3时钟 9 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟 10 11 //GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5 12 13 //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5 14 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM_CH2 15 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 16 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 17 18 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO 19 20 21 //初始化TIM3 22 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 23 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 24 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim 25 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 26 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 27 28 //初始化TIM3 Channel2 PWM模式 29 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2 30 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 31 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 32 TIM_OC2Init(TIM2, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2 33 //TIM_OC1Init(TIM5, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2 34 35 TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器 36 37 TIM_SetCompare2(TIM2,600-1); 38 39 TIM_Cmd(TIM2, ENABLE); //使能TIM3 40 41 42 }
结果:
先输出高电平还是先输出低电平受到这三个参数的影响
TIM_CounterMode_Up
TIM_OCMode_PWM1
TIM_OCPolarity_High
这里遇到了一个问题,先记录一下,stm32f103zet6芯片,测试TIM2 TIM3 TIM4 都可以输出4路PWM,但是TIM5始终没有输出。 我以为是TIM5的引脚连接了其他硬件导致的。
但是TIM2 ch2和TIM5的ch2是同一个引脚,查看数据手册,这几个定时器都在APB2上,时钟没有问题。TIM5的更新中断可CC中断都可以进,就是引脚没有pwm输出。先记录下吧。后来测试TIM5 ch4 pa3的输入捕获可以正常使用。。。再后来测试,使用HAL库,TIM5正常工作。
3.2捕获脉冲时间
在开始捕获时,清空CNT计数器。在捕获结束时,记录CCRx寄存器的数值,计数器CNT中间可能会多次溢出,要计算一下溢出的次数*每次溢出的最大值,再加上当前CCRx寄存器的数值。这么多数值,再*每次计数需要的时间(1/定时器频率),以下代码取自野火资料。
定时器更新中断和捕获中断处理函数:
1 void GENERAL_TIM_INT_FUN(void) 2 { 3 // 当要被捕获的信号的周期大于定时器的最长定时时,定时器就会溢出,产生更新中断 4 // 这个时候我们需要把这个最长的定时周期加到捕获信号的时间里面去 5 if ( TIM_GetITStatus ( GENERAL_TIM, TIM_IT_Update) != RESET ) 6 { 7 TIM_ICUserValueStructure.Capture_Period ++; 8 TIM_ClearITPendingBit ( GENERAL_TIM, TIM_FLAG_Update ); 9 } 10 11 // 上升沿捕获中断 12 if ( TIM_GetITStatus (GENERAL_TIM, GENERAL_TIM_IT_CCx ) != RESET) 13 { 14 // 第一次捕获 15 if ( TIM_ICUserValueStructure.Capture_StartFlag == 0 ) 16 { 17 // 计数器清0 18 TIM_SetCounter ( GENERAL_TIM, 0 ); 19 // 自动重装载寄存器更新标志清0 20 TIM_ICUserValueStructure.Capture_Period = 0; 21 // 存捕获比较寄存器的值的变量的值清0 22 TIM_ICUserValueStructure.Capture_CcrValue = 0; 23 24 // 当第一次捕获到上升沿之后,就把捕获边沿配置为下降沿 25 GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Falling); 26 // 开始捕获标准置1 27 TIM_ICUserValueStructure.Capture_StartFlag = 1; 28 } 29 // 下降沿捕获中断 30 else // 第二次捕获 31 { 32 // 获取捕获比较寄存器的值,这个值就是捕获到的高电平的时间的值 33 TIM_ICUserValueStructure.Capture_CcrValue = GENERAL_TIM_GetCapturex_FUN (GENERAL_TIM); 34 35 // 当第二次捕获到下降沿之后,就把捕获边沿配置为上升沿,好开启新的一轮捕获 36 GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Rising); 37 // 开始捕获标志清0 38 TIM_ICUserValueStructure.Capture_StartFlag = 0; 39 // 捕获完成标志置1 40 TIM_ICUserValueStructure.Capture_FinishFlag = 1; 41 } 42 43 TIM_ClearITPendingBit (GENERAL_TIM,GENERAL_TIM_IT_CCx); 44 } 45 }
1 // 定时器输入捕获用户自定义变量结构体声明 2 typedef struct 3 { 4 uint8_t Capture_FinishFlag; // 捕获结束标志位 5 uint8_t Capture_StartFlag; // 捕获开始标志位 6 uint16_t Capture_CcrValue; // 捕获寄存器的值 7 uint16_t Capture_Period; // 自动重装载寄存器更新标志 8 }TIM_ICUserValueTypeDef;
再测试时发现,tim5的捕获功能可用。可以排除不是 时钟,引脚,通道错误导致tim5 pwm不能用的问题。先记录下吧。
参考资料