STM32输出PWM波形进行比较
PWM配置
Introduction
STM32定时器输出比较功能,主要用来输出PWM波形,应用在包含有电机的项目中,如智能车、机器人等;
OC(Output Compare)输出比较,可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形,每个高级定时器和通用定时器都拥有4个输出比较通道,高级定时器的前3个通道额外拥有死区生成和互补输出的功能。
在右下部分,CNT是时基单元里面的计数器;CCR是捕获/比较寄存器,它是输入捕获和输出比较共用的,当使用输入捕获时,它就是捕获寄存器,当使用输出比较时,它就是比较寄存器。
红框里的电路会比较CNT和CCR的值,CNT计数自增,CCR是我们给定的一个值,当CNT>CCR、CNT<CCR、CNT=CCR时,输出端TIMx_CH1就会对应置1、置0、置1、置0,这样就可以输出一个电平不断跳变的PWM波形了。
PWM波形是一个数字输出信号,由高低电平组成,通过不断调整PWM波形可以等效地实现一个模拟信号的输出。比如可以使用PWM波形来实现呼吸灯的效果,通过让LED不断点亮、熄灭、点亮达到一定频率时,LED就不会闪烁而是呈现出一个中等亮度,调整点亮和熄灭的时间比例就能让LED呈现出不同的亮度。
占空比50%就是高低电平时间相等的方波,占空比20%就是高电平占20%低电平占80%。占空比决定了PWM等效出来的模拟电压大小,占空比越大等效的模拟电压就越趋近于高电平,占空比越小等效的模拟电压就越趋近于低电平,等效关系一般是线性的,比如高电平是5V,低电平是0V,那50%占空比等效于中间电压,就是2.5V。
分辨率它等于占空比变化步距,如占空比按1%、2%、3%这样的变化就是分辨率为1%。
START
Step1. 开启外设时钟
把要使用的外设时钟打开,RCC开启TIM和GPIO外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
Step2. 配置GPIO
使用PA0输出PWM波形
GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode=GPIO_Mode_AF_PP;//配置为复用推挽输出
GPIO_Initstructure.GPIO_Pin=GPIO_Pin_0;
GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructure);
Step3. 配置时基单元
配置时基单元,包括这前面的时钟源选择
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period=100-1;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
Step4. 配置输出比较单元
配置输出比较单元,CCR的值、输出比较模式、极性选择、输出使能
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋初始值函数
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较模式
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//设置输出比较的极性
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStructure.TIM_Pulse=50//用来设置CCR的
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
PWM参数计算
假如要产生一个频率为1Khz,占空比为50%,分辨率为1%的PWM波形
由公式可得ARR+1=100,CCR=50,PSC+1=720,CK_PSC为固定72MHz。
Step5. 开启计数器
运行控制,启动计数器
TIM_Cmd(TIM2,ENABLE);
END
检查
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);//单独更改通道1CCR的值
}
调用上面函数可以改变CCR的值进而影响PWM波形占空比,由于使用A0口输出PWM波形,所以就使用A0口实现呼吸灯。
记得在main函数中初始化PWM,在while循环里面调用下面代码即可。
for(i=0;i <=100;i++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for(i=0;i <=100;i++)
{
PWM_SetCompare1(100-i);
Delay_ms(10);
}
Parameter selection
在stm32f10x_tim.h文件
TIM_OCInitTypeDef
TIM输出比较初始化结构体定义
typedef struct
{
uint16_t TIM_OCMode; //输出比较模式
uint16_t TIM_OutputState;
uint16_t TIM_OutputNState;
uint16_t TIM_Pulse;
uint16_t TIM_OCPolarity;
uint16_t TIM_OCNPolarity;
uint16_t TIM_OCIdleState;
uint16_t TIM_OCNIdleState;
} c
TIM_OCMode
解释 & 说明
#define TIM_OCMode_Timing //冻结模式
#define TIM_OCMode_Active //相等时置有效电平
#define TIM_OCMode_Inactive //相等时置无效电平
#define TIM_OCMode_Toggle //相等时电平翻转
#define TIM_OCMode_PWM1 //PWM模式1
#define TIM_OCMode_PWM2 //PWM模式2
输出模式控制器的输入是CNT和CCR的大小关系,输出是REF的高低电平,输出模式可以根据下面的TIMx_CCMR1寄存器进行配置。
冻结模式,不再管CNT和CCR,直接REF保持不变,维持上一个状态,比如正在输出PWM波,突然想暂停一会儿输出,就可以设置成冻结模式,该模式下输出的高低电平也保持在暂停时刻状态不变化;
匹配时置有效电平、无效电平、电平翻转为高级定时器里说法,可以认为匹配时置有效电平就是置高电平,无效就是低电平。匹配时电平翻转可以方便地输出一个频率可调,占空比始终为50%的PWM波形。
PWM模式1可以输出频率和占空比都可调的PWM波形
**原因是:**CNT不断计数运行,CCR是自己设定,同时他们两个会不断比较,CCR后面是输出模式控制器。观察右上图,蓝色线是CNT的值,黄色线是ARR的值,蓝色线从0开始自增,一直增到ARR到99,之后清零继续自增,红色线是CCR,假如CCR为30,CNT<30时下面绿色为输出,它会输出高电平,CNT>30就会输出低电平。所以通过改变CCR就可以改变PWM占空比
TIM_OCPolarity
#define TIM_OCPolarity_High ((uint16_t)0x0000)
#define TIM_OCPolarity_Low ((uint16_t)0x0002)
#define IS_TIM_OC_POLARITY(POLARITY) (((POLARITY) == TIM_OCPolarity_High) || \
((POLARITY) == TIM_OCPolarity_Low))
TIM_OCPolarity_High
高极性,就是极性不翻转,REF波形直接输出或者说是有效电平是高电平,REF有效时,输出高电平;
TIM_OCPolarity_Low
低极性,就是REF电平取反,或者说是有效电平为低电平;
TIM_OutputState
输出状态使能或者失能
#define TIM_OutputState_Disable ((uint16_t)0x0000)
#define TIM_OutputState_Enable ((uint16_t)0x0001)
#define IS_TIM_OUTPUT_STATE(STATE) (((STATE) == TIM_OutputState_Disable) || \
((STATE) == TIM_OutputState_Enable))
TIM_Pulse
pulse 脉冲,用来设置CCR寄存器值的
参数可以为0~FFFF之间的一个值,就是16位的范围
引脚重映射
在引脚表中可以可以看到TIM2的CH1可以从PA0挪到PA15引脚上
引脚重映射需要开启AFIO时钟,以及引脚重映射配置函数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
没有重映射,PA0、PA1、PA2、PA3还是他们;
部分重映射,PA0->PA15,PA1->PB3,其他不变;
部分重映射,PA2->PB10,PA3->PB11,其他不变;
全部重映射,全部都变。
在stm32f10x_gpio.c里面可以看到TIM2有部分重映射1、部分重映射2和完全重映射
选择部分重映射1
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
还需注意PA15端口它是调试端口JTDI,如果想让PA15作为普通GPIO或者复用定时器的通道,应该先关闭调试端口复用。
第一个是解除NJTRST,PB4当作GPIO口使用;
第二个是解除JTAG调试端口复用,PA15、PB3、PB4变回GPIO;
第三个是解除SWD和JTAG的调试端口,PA15,14,13、PB3、PB4变回GPIO;
第三个使用有风险,使用后STLINK就无法下载程序了,这个时候只能使用串口下载程序了。
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
总结
如果想把PA15、PB3、PB4当作GPIO使用,需要下面第一行和第三行;
如果想重映射定时器或者其他外设的复用引脚,需要下面第一行和第二行;
如果上面都想,就使用下面三行代码;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
注意调用上面程序后,GPIO初始化需要把PA0改成PA15。
同一个定时器不同通道输出PWM的特点
对于同一个定时器的不同通道输出的PWM,它们的频率,因为不同的通道是共用应该计数器的,使用他们的频率是一样的。他们的占空比由各自的CCR决定,所以占空比可以各自设定,还有他们的相位,由于计数器更新,所有PWM会同时跳变,因此他们的相位是同步的。
如果驱动多个舵机或者直流电机,使用一个定时器不同通道的PWM,就完全可以。
作者:qq_52658815