目录
PWM简介
PWM原理
PWM实验内容
PWM实验步骤
PWM实验代码
PWM解惑时刻
1、向上计数
2、刹车、死区
附录
初始化函数的完整代码
主函数的完整代码
都知道BMW,那么你们知道PWM是什么吗?BMW异父异母的亲兄弟?nonono!下面就让小蛋糕带你走进PWM吧!GOGOGO!
PWM简介
PWM是Pulse Width Modulation的缩写,中文为脉冲宽度调制,简称脉宽调制器,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的计数。简单一点,就是对脉冲宽度的控制。
PWM原理
PWM对脉冲宽度控制的原理其实很好理解。下面我们根据
在上图中,CCRx代表TIMx_CCPx即捕获/比较寄存器,ARR代表TIMx_ARR自动重装载寄存器。CCRx和ARR构成了整个系统的比较器。上图表达的工作原理便是:当信号小于CCRx时,输入捕获为低电平;当信号与CCRx进行比较,得出结果为大于CCRx时,输出高电平;并且在信号大于ARR时,发生更新,回到低电平,开始新一轮的信号捕获比较和高低电平变化。
PWM实验内容
下面,通过PWM输出实验带大家进一步了解PWM。本实验的实验平台依旧时STM32F103系列的MINI开发板实验平台。在本次实验中,我们主要通过TIM1_CH1输出的PWM来控制LED0的亮度。
PWM实验步骤
很惊喜,我们这个实验so easy,实验步骤可以只分成两部,惊不惊喜,意不意外!
- 第一步,编写定时器初始化函数
- 首先我们需要开启TIM1和PA8的时钟;
- 然后我们需要对GPIO进行初始化;
- 然后我们需要设置TIM1的一些参数进行初始化;
- 然后需要设置TIM1的比较参数;
- 由于我们使用了高级定时器,所以需要使能刹车和死区寄存器的MOE位;
- 然后开启寄存器的预装载功能;
- 然后使能TIM1在ARR上的预装载寄存器;
- 然后使能TIM1;
- 最后编写主函数即可;
PWM实验代码
首先由于我们需要设置TIM1的自动重装值和时钟预分频数,所以我们的定时器初始化函数是有参数的,初始化函数的框架如下行代码所示
void TIM_PWM_Init(u16 arr,u16 psc)
{
}
然后我们开启所使用的定时器和IO口的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);//开启定时器的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启复用IO口的时钟
接下来,我们进行GPIO的初始化
//定义初始化函数参数结构体
GPIO_InitTypeDef GPIO_Initstruct;
//为结构体成员变量赋值
GPIO_Initstruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Initstruct.GPIO_Pin=GPIO_Pin_8;
GPIO_Initstruct.GPIO_Speed=GPIO_Speed_50MHz;
//调用初始化函数
GPIO_Init(GPIOA,&GPIO_Initstruct);
然后我们根据指定的参数初始化所使用的定时器中的时间基数单位
//定义初始化参数结构体
TIM_TimeBaseInitTypeDef TIM_Initstruct;
//为结构体中的参数赋值
TIM_Initstruct.TIM_Period=arr;
TIM_Initstruct.TIM_Prescaler=psc;
TIM_Initstruct.TIM_ClockDivision=0;
TIM_Initstruct.TIM_CounterMode=TIM_CounterMode_Up;
//调用初始化函数
TIM_TimeBaseInit(TIM1,&TIM_Initstruct);
然后我们初始化比较参数
//定义初始化参数结构体
TIM_OCInitTypeDef TIM_OCInitstruct;
//为比较参数赋值
TIM_OCInitstruct.TIM_OCMode=TIM_OCMode_PWM2;
TIM_OCInitstruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitstruct.TIM_OCNPolarity=TIM_OCPolarity_High;
//调用函数
TIM_OC1Init(TIM1,&TIM_OCInitstruct);
然后我们编写初始化函数中的剩余内容
//使能刹车和死区寄存器的MOE位,以使能整个OCx输出
TIM_CtrlPWMOutputs(TIM1,ENABLE);
//开启寄存器的预装载功能
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
//使能定时器1在ARR上的预装载寄存器
TIM_ARRPreloadConfig(TIM1, ENABLE);
//使能定时器
TIM_Cmd(TIM1,ENABLE);
最后,我们来编写主函数:
在主函数中,我们在while循环(死循环)中控制LED0_PWM_VAL的值从0到300,然后从300到0,如此循环,因此LED的亮度也会从暗变亮,然后又从亮变暗。
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
delay_init();
TIM_PWM_Init(899,0);
while(1)
{
delay_ms(10);
if(dir)led0pwmval++;
else led0pwmval--;
if(led0pwmval>300)dir=0;
if(led0pwmval==0)dir=1;
TIM_SetCompare1(TIM1,led0pwmval);
}
}
至此,我们的函数就写完了。
PWM解惑时刻
可能我们编写完函数之后,对于某些地方还是存有疑惑,下面我针对几个地方向大家说明:
1、向上计数
STM32的通用定时器和高级定时器支持的计数模式包括:向上计数、向下计数和中心对齐计数模式。其中向上计数是最简单的一种技术过程:从0开始递增计数,直到ARR,发生溢出,计数器重装为0,开始下一轮计数。
2、刹车、死区
stm32的刹车功能:stm32有PWM输出,可以用来驱动电机,刹车就是关掉PWM紧急停止的意思。
STM32的高级定时器可以输出两路互补信号,并管理输出与接通瞬间,输出与接通之间的延迟时间便成为死区。
附录
初始化函数的完整代码
void TIM_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_Initstruct;
TIM_TimeBaseInitTypeDef TIM_Initstruct;
TIM_OCInitTypeDef TIM_OCInitstruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_Initstruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Initstruct.GPIO_Pin=GPIO_Pin_8;
GPIO_Initstruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstruct);
TIM_Initstruct.TIM_Period=arr;
TIM_Initstruct.TIM_Prescaler=psc;
TIM_Initstruct.TIM_ClockDivision=0;
TIM_Initstruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1,&TIM_Initstruct);
TIM_OCInitstruct.TIM_OCMode=TIM_OCMode_PWM2;
TIM_OCInitstruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitstruct.TIM_OCNPolarity=TIM_OCPolarity_High;
TIM_OC1Init(TIM1,&TIM_OCInitstruct);
TIM_CtrlPWMOutputs(TIM1,ENABLE);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1, ENABLE);
TIM_Cmd(TIM1,ENABLE);
}
主函数的完整代码
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
delay_init();
TIM_PWM_Init(899,0);
while(1)
{
delay_ms(10);
if(dir)led0pwmval++;
else led0pwmval--;
if(led0pwmval>300)dir=0;
if(led0pwmval==0)dir=1;
TIM_SetCompare1(TIM1,led0pwmval);
}
}