定时器本质上是一个以单位时间为准的计数器,计数值可从0开始累加,也可从一个设定数值递减。每隔一个固定时间计时器的值加1或减1,当加到(或减到)到头时,会产生一个溢出信号,并将计时值清0重新计时。
利用定时器溢出可产生总周期,利用PWM可以调制该周期的占空比。
STM32F103xx增强型产品中,内置了多达3个可同步运行的标准定时器(TIM2、TIM3、TIM4)。每个定时器都有一个16位的自动加载递加/递减计数器、一个16位的预分频器和4个独立的通道,每个通道都可用于输入捕获、输出比较、PWM和单脉冲模式输出,在大的封装配置中可提供多12个输入捕获、输出比较或PWM通道。它们还能通过定时器链接功能与高级控制定时器共同工作,提供同步或事件链接功能。在调试模式下,计数器可以被冻结。任一标准定时器都能用于产生PWM输出。每个定时器都有独立的DMA请求机制。这些定时器还能够处理增量编码器的信号,也能处理1至3个霍尔传感器的数字输出。
高级控制定时器(TIM1)可以被看成是分配到6个通道的三相PWM发生器,它具有带死区插入的互补PWM输出,还可以被当成完整的通用定时器。四个独立的通道可以用于:
配置为16位标准定时器时,它与TIMx定时器具有相同的功能。
配置为16位PWM发生器时,它具有全调制能力(0~100%)。
在调试模式下,计数器可以被冻结,同时PWM输出被禁止,从而切断由这些输出所控制的开关。
很多功能都与标准的TIM定时器相同,内部结构也相同,
因此高级控制定时器可以通过定时器链接功能与TIM定时器协同操作,提供同步或事件链接功能。
本次我们具体了解定时器产生PWM的功能
PWM脉宽调制
脉冲宽度调制(PWM),是英文“Pulse Width Modulation’ 的缩写,简称脉宽调制。脉宽调制也叫占空比。
它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
在我看来就是单片机通过相应定时器的设置,将某个IO端口设置成可以产生一定周期、一定占空比的方波。
利用定时器产生PWM的原理图如下:
通过设置计数值ARR的值,我们用来产生一定周期的波形(因为每过一个机器周期,计数值加1),通过设定CCRx的值,当计数值在CCRx之前的值,将IO端口设置成低电平,在CCRx和ARR值之间设置成高电平 ,如此即完成了相应的占空比。
STM32F103对应的定时器的通道与IO端口复用,同时也可以通过重映射,将对应的通道映射到其他的IO端口,只需要使用固件库函数即可。
由于本例我们利用了TIM3定时器产生PWM波,因此特将TIM3的重映射表列下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO时钟(定时器3通道3需要重映射到BP5引脚)
又因为我们使用了48引脚封装,因此不可使用重映射。
TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up;
//计数器向上溢出
TIM_OCInitStrue.TIM_OCMode=TIM_OCMode_PWM1;
// PWM模式1:CNT < CCR时输出有效电平
可以对相应的字符串修改改变方式(字符串被重定义为相应功能的代码)
TIM_OCInitStrue.TIM_OCPolarity=TIM_OCPolarity_High;// 设置极性-有效电平为:高电平
通过该函数稳定定时期:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO时钟(定时器3通道3需要重映射到BP5引脚)
void TIM3_PWM_Init(u16 arr,u16 psc){ //TIM3 PWM初始化 arr重装载值 psc预分频系数
GPIO_InitTypeDef GPIO_InitStrue;
TIM_OCInitTypeDef TIM_OCInitStrue;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3和相关GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB时钟(LED在PB0引脚)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO时钟(定时器3通道3需要重映射到BP5引脚)
GPIO_InitStrue.GPIO_Pin=GPIO_Pin_0; // TIM_CH3
GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽
GPIO_InitStrue.GPIO_Speed=GPIO_Speed_50MHz; //设置最大输出速度
GPIO_Init(GPIOB,&GPIO_InitStrue); //GPIO端口初始化设置
// GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //映射,重映射只用于64、100、144脚单片机
//当没有重映射时,TIM3的四个通道CH1,CH2,CH3,CH4分别对应PA6,PA7,PB0,PB1
//当部分重映射时,TIM3的四个通道CH1,CH2,CH3,CH4分别对应PB4,PB5,PB0,PB1 (GPIO_PartialRemap_TIM3)
//当完全重映射时,TIM3的四个通道CH1,CH2,CH3,CH4分别对应PC6,PC7,PC8,PC9 (GPIO_FullRemap_TIM3)
TIM_TimeBaseInitStrue.TIM_Period=arr; //设置自动重装载值
TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数
TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数器向上溢出
TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //时钟的分频因子,起到了一点点的延时作用,一般设为TIM_CKD_DIV1
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStrue); //TIM3初始化设置(设置PWM的周期)
TIM_OCInitStrue.TIM_OCMode=TIM_OCMode_PWM1; // PWM模式1:CNT < CCR时输出有效电平
TIM_OCInitStrue.TIM_OCPolarity=TIM_OCPolarity_High;// 设置极性-有效电平为:高电平
TIM_OCInitStrue.TIM_OutputState=TIM_OutputState_Enable;// 输出使能
TIM_OC3Init(TIM3,&TIM_OCInitStrue); //TIM3的通道3 PWM 模式设置
TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能预装载寄存器
TIM_Cmd(TIM3,ENABLE); //使能TIM3
}
使用实例如下:
TIM3_PWM_Init(59999,23); //设置频率为50Hz,公式为:溢出时间Tout(单位秒)=(arr+1)(psc+1)/Tclk 20MS = (59999+1)*(23+1)/72000000
//Tclk为通用定时器的时钟,如果APB1没有分频,则就为系统时钟,72MHZ
//PWM时钟频率=72000000/(59999+1)*(23+1) = 50HZ (20ms),设置自动装载值60000,预分频系数24
需要注意的是,由于之前我们利用PB0产生PWM波,而TIM3的通道3和PB0复用,因此此时一定要设置TIM3的通道3。即下面函数:
TIM_OC3Init(TIM3,&TIM_OCInitStrue); //TIM3的通道3 PWM 模式设置
TIM_SetCompare3(TIM3,3000);
其中的第一个值3是设置定时器的通道3的值,因为前面我们用的是TIM3的通道3,因此这里要写3,一定要注意对应关系。
第二个参数是设置对应的定时器号。
第三个值是设定前面图的CCR的值,CCR值小于自动重装载值,当计数值到达CCR时,将产生高低电平的切换,结合前面PWM的方式设置,以及有效电平的设置,便可完全控制实现PWM占空比的设置。
由于20ms是60000计数值,推算出0.5ms的计数值便是1500,2.5ms的计数值是7500。因此将CCR设置成1500,便达到了转动0°,设置成7500,便达到了转动180°,设置成3000,便达到了转动3000×180/6000=90°的要求。