打印
[IOT技术]

【新定义MCU开发板测评】PWM使用与呼吸灯

[复制链接]
496|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 HonestQiao 于 2022-12-17 19:29 编辑

51单片机迷人的一点,就是能够通过寄存器直接进行系统功能控制,以及外设的控制。新定义MCU作为51系列单片机中的佼佼者,当然少不了这一点。

在官方提供的演示代码中,有关于PWM的演示,具体代码如下:

通过众多寄存器的联合设置,将PWM功能发挥出来。

首先看一下手册,了解一下系统提供的PWM功能:
【此处应配图】

系统总共提供了两类PWM功能,PWM0功能较为丰富,所以下面以PWM0为例说明。
PWM0一共提供了8路PWM,可以独立运行,也可以两两互补运行。

要使用PWM功能,我们首先需要确定一些前置数据:
  • 时钟频率:PWM运行的频率
  • 工作模式:如果为独立模式,则每一个PWM都单独设置;如果为互补模式,则两两波形相反。
  • PWM类型:边沿对齐或者中心对齐,简单来说,就是边沿从头开始计数,达到周期则直接从头开始,而中心对齐则是达到最高点后再递减直到0;后面使用边沿对齐模式
  • 是否反向:如果反向,则占空比表示低电平时间
  • PWM周期:在指定的时钟频率下,一次PWM完成的周期
  • 占空比:高电平时间占比

所谓的PWM类型,看下面的图,能够加深理解:边沿对齐:

中心对齐:


系统的运行频率为32MHz,所以PWM时钟频率,最高为32MHz,而其配置,则使用了两个bit位来控制分频,所以可用分频为2、4、8
结合手册和源码,可以得知PWMCON0中特定的位控制分频,具体如下:
【此处应配图】
因为是PWMCON0的bit4、bit5,所以对应的操作值如下:
  • 1分频:0x00 也就是不分频
  • 2分频:0x10 bit4为1,对应二进制 0b00 01 00 00
  • 4分频:0x20 bit5位1,对应二进制 0b00 10 00 00
  • 8分频:0x30 bit4、bit5均为1,对应二进制 0b00 11 00 00


因为是初次初始化PWM0,所以演示代码中,直接设置PWMCON0为0,也就是不分频了。

知道了系统频率,就可以求得周期值。
如果我们需要PWM的频率为1KHz,那么:T=1000/(1/32M)=0x7d00 其他以此类推。
演示代码使用的为0x0c80,为 0x7d00 / 0x0a,也就是10KHz了。
得到了周期值,就可以存放到下面的寄存器了:
【此处应配图】
注意是高位和地位分开存放的。

然后,是否反向,通过下面的寄存器设置:
【此处应配图】

下一步就是设置各路PWM是否输出了,使用下面的寄存器:
【此处应配图】

需要提醒的一点是,上述说明了位编号的,都是通过位操作来进行的,也就是实际使用时,要看其二进制编码状态。
样式代码使用了0x03,对应二进制0b11,表示启用PWM00、PWM01

再然后就是设置占空比了,占空比不能大于周期值。在运行过程中,可以进行改变,从而达到输出变化的结果。
演示代码中,使用如下的代码定义周期值的存储地址:
unsigned int xdata PWMREG[14] _at_ 0x2034;        //PWM占空比调节寄存器

为什么是0x2034,这通过手册可以获取:
【此处应配图】

占空比值使用两个8位寄存器来保存,所以上述地址的对应关系:
  • 0x2034、0x2035:对应PWM20,PWMREG[0]
  • 0x2036、0x2037:对应PWM21,PWMREG[1]
  • 0x2038、0x2039:对应PWM30,PWMREG[2]
  • 0x203a、0x203b:对应PWM31,PWMREG[3]
  • 0x203c、0x203d:对应PWM40,PWMREG[4]
  • 0x203e、0x203f:对应PWM41,PWMREG[5]
  • 0x2040、0x2041:对应PWM00,PWMREG[6]
  • 0x2042、0x2043:对应PWM01,PWMREG[7]
  • 0x2044、0x2045:对应PWM02,PWMREG[8]
  • 0x2046、0x2046:对应PWM03,PWMREG[9]
  • 0x2048、0x2047:对应PWM04,PWMREG[10]
  • 0x204a、0x204b:对应PWM05,PWMREG[11]
  • 0x204c、0x204d:对应PWM06,PWMREG[12]
  • 0x204e、0x204f:对应PWM07,PWMREG[13]
一个有14个,所以使用了一个 unsigned int xdata PWMREG[14] 来定义,方便操作。

PWM00和PWM01则对应使用PWMREG[6]、PWMREG[7],其取值均为0x640,实则位周期值0x0c80的一半,也就是说占空比位50%

通过上面一通的寄存器设置,PWM0做好准备,就等开工了。
现在,要使能PWM0,需要通过下面的寄存器使能:
【此处应配图】
启用时,对应的操作值为0x80。
在main.c中启用PWM_Test(),然后将代码编译烧录后,通过示波器或者逻辑分析仪可以分析输出情况。示波器更好,逻辑分析仪只能简单辅助。
要进行测量,需要先连接开发板的PWM输出引脚,通过原理图可以得到:
【此处应配图】

我手头没有示波器,所以用逻辑分析仪简单分析了一下,具体结果如下:
【此处应配图】


输出的信号,确实如设置所期望的:PWM频率为10KHz,占空比位0%,而PWM00和PWM01刚好是相反的,因为设置了PWM01输出反向。

然后,就可以做一些简单的处理,点亮LED,并使用呼吸灯的模式。

首先进行一些预先的计算:
  • 1. PWM运行频率使用1KHz,则其周期值对应之前计算的0x7d00

其他需要计算的就没有了。

然后,我们需要动态的改变占空比,让他从最小值,变化到最大值。这可以通过PWM0中断回调来进行处理,也就是每次达到计数器最大值,自动中断并进行一次回调:
【此处应配图】
演示代码中,使用如下的寄存器进行控制:
【此处应配图】
演示代码设置的为0x02,对应二进制位0b10,刚好通过上述寄存器控制。

在演示代码的最后,有一个PWM0_Interrup(),这个就是终端回调的函数,我们在其中编写自己的代码即可。
具体代码如下:
unsigned int step = 0;

void PWM0_Interrup() interrupt 8
{
  if(PWMCON0 & 0x40)
  {
    PWMCON0 &= ~0x40;  //清除中断标志位
    P04 = ~P04;
    if(step==0) {
        if(PWMREG[6]<=0x0c80-0x0a) {
            PWMREG[6] += 0x0a;
        } else {
            step = 1;
        }
    } else {
        if(PWMREG[6]>=0x0a) {
            PWMREG[6] -= 0x0a;
        } else {
            step = 0;
        }        
    }
  }
}


因为PWM00的占空比使用PWMREG[6]来控制,所以在每次回调中,修改其值,那么下一个周期中就会生效了。
上述代码中,用status表示要增大还是减小,循环往复。

将P50引脚连接上 LED:




然后编译烧录代码进行测试,效果如下:





写了半天,突然提示文章内容过长,不得不删除大量的图片。
发烧中,人有点迷糊,看东西有点重影,所以等好了再补足图片,另外理解内容和计算也可能不准确,敬请见谅。



使用特权

评论回复
沙发
backlugin| | 2023-1-5 11:49 | 只看该作者
支持多少路的PWM?              

使用特权

评论回复
板凳
loutin| | 2023-1-9 11:20 | 只看该作者
这个pwm支持多少路?              

使用特权

评论回复
地板
wwppd| | 2023-1-9 12:11 | 只看该作者
pwm的呼吸灯不出。              

使用特权

评论回复
5
abotomson| | 2023-1-9 14:22 | 只看该作者
这个芯片的功能还真是强大呢。              

使用特权

评论回复
6
modesty3jonah| | 2023-1-9 17:04 | 只看该作者
可以应用在舵机的孔子上吗?              

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

33

主题

85

帖子

2

粉丝