Pulse Width Modulation就是通常所说的PWM,译为脉冲宽度调制,简称脉宽调制。脉冲宽度调制(PWM)是一种对模拟信号电平进行数字编码的方法,由于计算机不能输出模拟电压,只能输出0或5V的的数字电压值,我们就通过使用高分辨率计数器,利用方波的占空比被调制的方法来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么是5V(ON),要么是0V(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。 输出的电压值是通过通和断的时间进行计算的。
输出电压=(接通时间/脉冲时间)*最大电压值
PWM被用在许多地方,调光灯具、电机调速、声音的制作等等。
下面介绍一下PWM的三个基本参数:
1、脉冲宽度变化幅度(最小值/最大值)
2、脉冲周期(1秒内脉冲频率个数的倒数)
3、电压高度(例如:0V-5V)
今天我们要讲的就是STM32的定时器配置和应用。
首先打开我们的STM32Dduino的程序,找到main.c这个文件夹
然后找到main函数
这里看到红色方框中的俩个函数
TIM2_PWM_Init();//TIME2初始化
TIM5_PWM_Init();//TIME5初始化
这俩个就是定时器2和定时器5的是初始化函数。
我们选择TIM2_PWM_Init右击鼠标在弹出的对话框中选择Go to Definition
‘TIM2_PWM_Init’来找到TIM2_PWM_Init的原函数。
这样就来到了time_test.c文件的void TIM2_PWM_Init(void)函数
在这里又看到俩个函数,我们先选择第一个同样是右击鼠标
Go to Definition‘TIM2_GPIO_Config’看到整个TIM2_GPIO_Config
函数的原型
这个函数其实就是定时器2PWM信号的IO配置
先来看看第一句 GPIO_InitTypeDef GPIO_InitStructure;指的是结构体初始化
完了之后RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 使能定时器2的时钟,查询STM32F105的datasheet知道定时器2是挂载APB1总线上的。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 使能总线APB2上的A组GPIO时钟,我们用的就是A组上的GPIO。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;选通A组GPIO上的2通道和3通道,(这个是根据你硬件的设计来选通的)。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;把2、3引脚设置为推挽输出
STM32引脚的输出模式有8种之多,分别是
(1)GPIO_Mode_AIN 模拟输入
(2)GPIO_Mode_IN_FLOATING 浮空输入
(3)GPIO_Mode_IPD 下拉输入
(4)GPIO_Mode_IPU 上拉输入
(5)GPIO_Mode_Out_OD 开漏输出
(6)GPIO_Mode_Out_PP 推挽输出
(7)GPIO_Mode_AF_OD 复用开漏输出
(8)GPIO_Mode_AF_PP 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;选择速率(可选2M 10M 50M,这里设置为50)
GPIO_Init(GPIOA, &GPIO_InitStructure);使能A组GPIO
再看第二个函数static void TIM2_Mode_Config(void)
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure; 基本定时器TIM2的定时配置的结构体(包含定时器配置的所有元素例如:TIM_Period = 计数值)
TIM_TimeBaseStructure.TIM_Period = (20000-1);计数0~19999一共20000次
TIM_TimeBaseStructure.TIM_Prescaler = (72-1);设置分频系数,不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);输出比较&PWM通道
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;配置定时器为PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;开启OC*输出对应引脚
TIM_OCInitStructure.TIM_Pulse =1500;设置通道3的电平跳变值,输出另外一个占空比的PWM ,相当于说计数周期是20000,在计数还没到1500的时候输出高电平在计数1500-20000时是低电平
TIM_OC3Init(TIM2, &TIM_OCInitStructure);打开通道3的使能
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);开启OC1重装,计数器计数满后自动重装
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;开启OC*输出对应引脚
TIM_OCInitStructure.TIM_Pulse = 1500; //设置通道4的电平跳变值,输出另外一个占空比的PWM
TIM_OC4Init(TIM2, &TIM_OCInitStructure); //使能通道4
TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);开启OC1重装,计数器计数满后自动重装
TIM_Cmd(TIM2, ENABLE); 打开定时器2
上面各函数的功能讲完了,现在我们来讲讲定时器怎么计算时间,怎么配置才能是我们想要的定时时间呢
假设
系统时钟是72Mhz,TIM1 是由PCLK2 (72MHz)得到,TIM2-7是由 PCLK1 得到
关键是设定时钟预分频数,自动重装载寄存器周期的值
按照上面的设置
/*每20豪秒发生一次更新事件(进入中断服务程序)。
RCC_Configuration()的SystemInit()的
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2表明TIM2CLK为72MHz。
因此,每次进入中断服务程序间隔时间为
((1+TIM_Prescaler )/72M)*(1+TIM_Period )=((1+(72-1)/72M)*(1+(20000-1))=20毫秒 刚好是驱动舵机的一个周期
TIME2我们用来产生舵机PWM波,既然PWM波已经产生了,那我们怎么样才能去驱动舵机让他转任意 角度呢?
我们先来了解下180°舵机的特性,驱动舵机必须要用50HZ的PWM方波驱动,这里我们已经设定好了,不就是1/50=20ms的周期的方波吗
当给舵机一个0.5ms的脉宽(就是0.5ms的高电平)时舵机转到-90°,
当给舵机一个1ms的脉宽(就是1ms的高电平)时舵机转到-45°,
当给舵机一个1.5ms的脉宽(就是1.5ms的高电平)时舵机转到0°,
当给舵机一个2ms的脉宽(就是2ms的高电平)时舵机转到45°,
当给舵机一个2.5ms的脉宽(就是2.5ms的高电平)时舵机转到+90°,
好了,知道这些,当我们给定一个0.5ms~2.5ms之间的一个任意脉宽时必然会对应舵机一个-90~+90°之间的一个角度,我们只要把这个脉宽和角度对应起来,就能在程序里控制舵机的任意角度。
前面我们把计数设置为20000-1分频为72-1时周期是20ms
那么转0°时需要计1.5/20*20000=1500个数
再来看上面
我们设置的电平跳变值是1500,在定时器初始化后舵机就自然转到0°
要想控制任意舵机角度,只要给这个语句赋值不同的值就OK。
再来看void PWM_Channel3_ser1(u16 PWM_3)
void PWM_Channel4_ser2(u16 PWM_4) 这俩个是带参数的函数(内容和定时器2初始化中的一样)PWM3_1和PWM4_1,这就是给俩个舵机通道赋值俩个变量,那么这俩个带参数的函数在哪用到呢!
打开function.c这个文件找到Communication_Decode(void) 这个函数中可以看到
这一段,先来了解下小R科技wifi智能小车控制端控制指令协议下发格式
他是以FF开头,数据位1,数据位2,数据位3,FF结尾(例如前进控制指令是FF 00 01 00 FF)这样的格式。在下位机串口中断中解析(这里先不讲串口中断,有时间的朋友可以自己去看看)中
数据位1赋值给了buffer[0]
数据位2赋值给了buffer[1]
数据位3赋值给了buffer[2]
再开看上面的那段代码,当buffer[0]等于0x01时,程序判断这条指令是控制舵机的指令,在看数据位第二位buffer[1]等于0x07是程序判断这条指令是控制舵机7号的指令,自来看第三位buffer[2]这位就是我们的舵机角度值了0~180换算成16进制是0x00~0xb4,前面我们加了一句if(buffer[2]>160)return是为了防止舵机转到+90°会卡死。
case 0x07:se_timer[0]=buffer[2];PWM_Channel3_ser1((se_timer[0]*(2000/180)+500)); return;
这一句是关键,前面我们已经了解了控制舵机-90°度是0.5ms脉宽即计数0.5/20*20000=500,+90度是2.5ms脉宽即计数2.5/20*20000=2500,而舵机的角度值是-90到+90,所以要把500~2500和-90~90映射
PWM计数值=角度值*((2500-500)/((90-(-90)))+500
只要上位机给定buffer[2]一个角度值就能把角度值转换成pwm计数值,从而完成对舵机的控制。
到这里我们的定时器设置,PWM控制舵机就已经讲完啦
|