关于PWM, 这真是一个复杂而实用的东西,因为利用它,可以很好的用数字电平去控制模拟电平信号;也可以增加数字电平的稳定性从而增强驱动能力。
比如使用PWM控制直流电机、多久、伺服电机等等。那么PWM到底是啥玩意呢?比较标准的解释如下:
PWM:
PWM也称为脉冲宽度调制,它是一种对模拟信号进行数字编码的方法。通过使用定时器/计数器,调制方波的占空比来对一个具体的模拟信号的电平进行编码。PWM是数字的电平,在给定的任何时刻,满幅值的直流供电要么为高电平,要么为低电平。
电压或电流源是以一种通(ON)或断(OFF)的重复脉冲系列被加载到模拟负载上去,通的时候是直流供电被加载到负载上的时候,断的时候就是供电断开的时候,所以不停的周期性的通断形成的方波就是PWM了(取自度娘)。
所以,说白了 ,PWM就是一堆方波!通过控制方波的高电平和低电平的频率(出现的时间),就可以调整驱动能力(即电平的稳定性)。
那么,PWM的存在是有条件的,如下:
1、提供调制方波的定时器/计数器的周期,决定PWM的频率。2、提供一个比较值,决定PWM占空比。
3、存在PWM的周期
满足以上三个条件,那输出PWM就不是事了!但是有必要解释一下:
定时器/计数器周期: 就是定时器/计数器一次数绵羊的时间。
占空比: 接通(高电平)时间与周期(PWM周期)之比就是占空比啦!
PWM周期:
就是一个时间段啦!着这个时间段内,通过调节占空比(实际上是调节比较值)来确定高电平的时间和低电平的时间,高电平的时间和低电平的时间之和就是占空比啦!还有就是PWM的周期的倒数就是PWM的调制频率啦!通常外部设备使用在1KHz到200KHz的频率就差不多啦!
OK!关于PWM的解释就是这样了!通过分析FireBLE的硬件底板原理图可知,QN9021的两路PWM分别连接再板载的LED1和蜂鸣器上了!如下图:
说到蜂鸣器!还有一些奇妙呢!
蜂鸣器分为有源蜂鸣器和无源蜂鸣器两种:
有源蜂鸣器内部带有振荡源,所以只要正极和负极有一定的电压,就会滴滴滴的叫。
无源蜂鸣器内部不带振荡源,所以使用直流信号是不能让它鸣叫的(因为声音的来源也是需要振动的)。必须需要使用方波去驱动无源蜂鸣器,才能使它鸣叫。并且无源蜂鸣器的声音是可控的,通过PWM可以让其发出“哆来咪法索拉西”基本音。基本音发出来了!奏乐不是有希望了?是的,就是这样。
根据FireBLE底板的原理图,可以使用两路PWM分别实现大众喜爱的呼吸灯和驱动蜂鸣器鸣叫(甚至让蜂鸣器奏乐)。但是在这里主要讲解PWM,所以,呼吸灯就不玩了!咱就玩蜂鸣器!嘿嘿!
无论怎么样,分析完了原理图,就得看看Datasheet,看看是否能得到一些有用的信息。如下图:
有这么几个信息:驱动蜂鸣器的PWM通道为QN9021的PWM1,并且是由定时器2输出。这一点要注意了! OK!分析完资源,就得码代码了!过程如下:
1、初始化PWM1(初始化蜂鸣器端口):
如上图可以看到,初始化太简单了,3句话解决,因为官方一个非常好的给我们封装了。还是老一套:配置复用P26口为PWM1通道、初始化PWM通道1,使能PWM1。就是这么简单!
简单只是外表的!作为驱动开发者,作为学习,在有时间有条件的情况下,还是有必要进去看看!那么有必要跟踪的就是pwm_init(PWM_CH1);函数了!原型如下:
主要看的就是与PWM1有关的!那么如上蓝色框的预定义,再进一步跟踪宏CONFIG_PWM1_ENABLE_INTERRUPT的值,如下图:
可以看到宏CONFIG_PWM1_ENABLE_INTERRUPT的值为假,即在我的这个工程中,PWM1是默认不打开中断的!
2、实现开关蜂鸣器
呵呵!还是很简单!一句话解决!其实就是设置PWM1的占空比啦!使用的是pwm_config(PWM_CH1,119,PWM_COUNT_US(1000,119),PWM_COUNT_US(500,
119));函数。
那么对于这个函数,它包含的信息可不少呢!
因为前面的PWM1初始化仅仅是配置选择了一下,然后打开功能!但是PWM的具体配置,比如定时器的计时周期,PWM的频率,PWM的比较值等等,还没发现在哪里设置!
所以,猜的没错的话,就是在这个函数当中玩了!通过代码的跟踪,基本上就可以确定,这些参数的确是在这个函数中被设置的!但是,由于官方未给出参考手册,所以光是看函数原型无法也是操作一堆寄存器!那么我们就从注释入手吧!如下图:
从原型函数名可以看到,产生有4个参数,含义如下:
Pwmch:PWM通道,取值为PWM_CH0, PWM_CH1。
Pscal:预分频数,取值为 0x0 ~ 0x3FF。
Periodcount:PWM的周期,取值为0x0 ~ 0xFF。
Pulsecount:脉冲数,什么意思呢?其实就是比较值啦!通过比较值就可以知道在一个周期内有多上时间是高电平和低电平。总的来说,其实就有占空比的意思了!这其实就是调节占空比。
从上图中可以看到,pulsecount的值必须小于periodcount的值,也就是比较值必须小于周期值啦!当两者等于或者比较值大于周期值时,就是永远都比较不成功或者刚好相对,那么就意味着在一个PWM周期能输出的全是一个电平,那么这还是PWM吗?这就不是PWM啦!所以,所以没所以了!!
在上图中还看到了,这个函数的使用示例,并且得知periodcount和pulsecount这两个值可以由宏PWM_COUNT_US(us,
pscl_div)获取,OK!那就简单了!那就的看看是怎么获取这个参数的了!如下图:
呵呵!看到上图,就知道这里包含的信息量可大了!那么在这里就拿我的调用来分析分析!
pwm_config(PWM_CH1, 119, PWM_COUNT_US(1000, 119), PWM_COUNT_US(500,
119));PWM_CH1:代表配置的是PWM1通道。
119:表示将PWM的定时器的时钟进行119+1=120分频
查看Datasheet如下图:
从时钟树可以得知PWM定时器的时钟来自于APB总线,再看初始化配置:
看到上面两个图!不用说了!定时器的时钟为8MHz,所以,要对其进行120分频,就得到66666Hz,约等于67KHz。那么计时周期就是1/66666 ≈
15us,就是这样,15微秒。那么就意味着,每15us,PWM的端口就可以变换电平一次。
PWM_COUNT_US(1000, 119):肯定是获取周期值啦!那么是怎么获取的呢?得解剖一下:
PWM_COUNT_US(1000,119)其实就等价于((1000) * PWM_CLK(TIMER_DIV, 119) /
1000000)也等价于((1000) * (8MHz / (119+1))/
1000000),呵呵!基本上就一般明白了!PWM周期为1000个定时器周期,而不是例程注释的1000us,这里得注意了。
PWM_COUNT_US(500,
119):解剖如上,代表的就是比较值为500个定时器周期,页面也就是所,在一个PWM周期中,有一半是高电平,一半是低电平。
嘿嘿!就这么愉快的完成了分析。
3、主函数调用
主函数的调用超简单!和点灯一样!嘿嘿!但是别忘了哦!关蜂鸣器我并不是将PWM1关闭,而是将比较值设为0哦!为毛呢?上面其实有解释了,这样就可以放PWM端口持续输出一个电平,就不存在方波啦,更不存在PWM了,那么蜂鸣器就不响啦啦啦!
4、调试蜂鸣器发出不同的声音
呵呵!本想找出几个基本乐音
“哆来咪法索拉西”,再找各种半音节的声音的占空比值,然后进行奏乐,但是,还是太高估自己了,蜂鸣器嗒嗒嗒的叫,耳朵已经麻木,分别不出音节了!所以还是放弃了!但是,为了让大家更深的理解!还是把测试程序贴一下!哈哈!其实也没啥!如下图:
没错!还是一句话!呵呵!再看调用:
就是这样了!反正听得我耳朵发麻!加上没音乐细胞!我奏乐的梦想就这样破灭了!
最后!前面讲过,默认情况下,定时器的中断是关闭的!那么打开定时器中断后,有神马后果呢?Why?嘿嘿!这个还真值得玩玩!!
总结:本文基本上详细的分析了PWM,并且此分析基本上能在各种情况下都实用!哈哈~!