【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

版权声明:本文为博主原创文章,转载请附上原文出处链接。

文章目录


前言

本次讲解STC8A8K64S4A12系列PWM脉冲宽度调制的原理;8个PWM外设相关寄存器配置及程序设计。


一、硬件设计

1.PWM脉冲宽度调制介绍

PWM (全称是Pulse Width Modulation)脉冲宽度调制是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用于测量、通信到功率控制与变换的许多领域中。

下面以一个简单有趣的例子和大家分析PWM的作用。下图是使用9V电池来给一个电灯供电,这是个模拟电路,电路使用开关来控制电灯工作状态。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图1:PWM控制模拟电路示意图

■ 开关状态与电灯工作状态之间的关系:

根据常识我们知道开关按下,则电灯两端有压降,电灯会被点亮;如果开关松开,则电灯两端没有压降,形成不了回路,电灯就是灭的。那请分析下下面几种情况:

  • 1)假如开关按下去5秒、之后松开5秒,如此反复,则实验现象必然是灯亮5秒,然后熄灭5秒,不停闪烁下去。
  • 2)假如开关按下去1秒、之后松开1秒,如此反复,则实验现象也会是灯亮1秒,然后熄灭1秒,不停闪烁下去。
  • 3)假如开关按下去1毫秒、之后松开1毫秒,那实现现象会是灯亮1毫秒,然后熄灭1毫秒,如此反复闪烁下去吗?
  • 4)假如开关按下去0.1毫秒,之后松开0.9毫秒,那实现现象又会是什么?会和开关按下去0.9毫秒、之后松开0.1毫秒一样吗?

分析:

  • 1)当开关按下去和松开的时间比较长时,比如1秒或5秒,人眼是很容易分辨出灯被点亮或是熄灭了,所以看到的就是灯闪烁的现象。
  • 2)当开关按下去1毫秒、之后松开1毫秒,实际灯确实是被点亮1毫秒、然后熄灭1毫秒的,但人眼已经分辨不出来了。(一般来说,人眼对于每11毫秒闪烁一次约83赫兹基本感觉不到,每13毫秒闪烁一次约66赫兹轻微频闪。)
  • 3)既然人眼分辨不出,那开关按下去1毫秒、之后松开1毫秒的实验现象会是什么,答案是看到灯始终是亮的,只是这个亮度没有开关一直按下去那么亮。根据上图中的分析,易知此时是占空比为50%的情况,可等效为4.5V电池给电灯供电,那电灯的亮度自然会有所下降。
  • 4)同理,在开关按下去0.1毫秒,之后松开0.9毫秒时,人眼看到的还是灯一直亮的状态,只是此时占空比是10%,等效于0.9V电池给电灯供电,亮度会很暗。而当开关按下去0.9毫秒、之后松开0.1毫秒时,占空比为90%,可等效于8.1V电池给电灯供电,电灯亮度只略低于9V电池直接供电的亮度。
  • 5)细心的朋友会发现,开关按下去1秒、松开1秒,容易实现。那开关按下去1毫秒、松开1毫秒怎么实现?所以上面说的是假设,实际使用中,开关会是一个开关电路,触发信号是单片机提供的PWM信号,该PWM的周期和占空比,单片机都可以精确给出。

☆注:呼吸灯的原理,如果我们控制PWM输出频率达到83HZ以上,那么PWM引脚连接的指示灯是看不到闪烁的,是一种常亮的状态,但亮度又不像直接给个低电平那么亮,如果频率不变,不断控制占空比,那么被点亮的指示灯的亮度就是不断变化的,像“呼吸”一样。

■ 关于频率和占空比的理解:

上面的实例中已经在使用频率和占空比的知识点,频率是相对于信号的周期而言,通过波形知道了周期就可以算出频率,频率是周期的倒数。而占空比是指高电平的时间占整个周期的比例。

  • 1)实现呼吸灯的效果,对输出的PWM信号频率有要求,该PWM频率越高,效果越好,完成一个调制周期的时间越短。但PWM的频率越高,对硬件的要求也越苛刻。
  • 2)PWM信号可以模拟产生也可以控制PWM外设寄存器实现,一般模拟产生会占用CPU较多资源,尤其高速PWM信号生成必须借助PWM外设来实现。
  • 3)脉冲宽度调制的宽,不是物体的宽度,而是高电平(有效电平)信号在一个调制周期中持续的时间长短,他可以用占空比去衡量,占空比越大,脉冲宽度越宽。

■ 使用PWM信号控制模拟电路的优势:

根据上面的实例,我们知道可以通过PWM信号的频率和占空比控制电灯的状态和亮度。那么使用PWM信号控制模拟电路的优势有哪些呢?下面分析:

  • 1)控制灯的亮度,使用纯模拟电路可以实现,比如改变电池的输出电压或者改变电阻的阻值。但这两个参数改动起来都比较困难,即使可以还有两个不能忽视的问题,一个是电池电压会随着电量减少而不断变化,一个是模拟电路容易随时间漂移而难以调节。
  • 2)从功耗上面考虑,一般模拟电路还会存在发热严重的问题,其功耗相对于工作元件两端电压与电流的乘积成正比。
  • 3)抗干扰性上,模拟电路还可能对噪声很敏感,任何扰动或噪声都会改变电流值的大小。而PWM信号是数字信号,外部噪声很难改变逻辑0或逻辑1的信息。

■ PWM脉冲宽度调制还有哪些应用呢?

  • 1)电机驱动:PWM被称为“开关驱动装置”,PWM信号的高低电平可控制电机是否通电,电机通电,就会加速;电机断电,就会减速甚至停止。只要PWM信号频率达到一定值,改变PWM占空比可实现对电机速度的控制。
  • 2)开关电源:PWM开关型稳压电路是在控制电路输出频率不变的情况下,通过电压反馈调整其占空比,从而达到稳定输出电压的目的。
  • 3)电池充电:在镍氢电池智能充电器中采用的脉宽PWM法,他是把所有脉冲宽度均相等的脉冲列作为PWM波形,通过改变脉冲列的周期来调频,改变脉冲的宽度或占空比来调压,采用适当控制方法即可使电压与频率协调变化,实现通过调整PWM的周期、PWM的占空比而达到控制充电电流的目的。
  • 4)D/A转换:PWM高频输出后加RC滤波电路,通过改变PWM信号的占空比实现输出不同电压值的目的。
  • 5)逆变电路:目前中小功率的逆变电路几乎都采用PWM技术,逆变电路是PWM控制技术极为重要的应用场合。

☆注:占空比的设置:理论上说,通过控制占空比(为0%或者100%)可以实现输出纯粹的低电平或者高电平,但实际使用要结合所选单片机特点进行设计。

2.STC8A8K64S4A12系列单片机PWM介绍

STC8A8K64S4A12系列单片机集成了一组增强型PWM波形发生器,该PWM波形发生器内部有一个15位的PWM计数器供8路相互独立的PWM使用,即PWM0、PWM1、PWM2、PWM3、PWM4、PWM5、PWM6和PWM7。另外,用户可设置每路PWM的初始电平,也可以通过对每路PWM的两个控制波形翻转的计数器的配置实现每路PWM高低电平宽度的调节。

由于8路PWM是各自独立的,且每路PWM的初始状态可以自行设定,所以用户可以将其中的任意两路配合起来使用,从而实现互补对称输出及死区控制等特殊用途。

STC8A8K64S4A12系列单片机PWM波形发生器还设计了对外部异常事件进行监控的功能,可用于紧急关闭PWM输出,避免出现严重后果。

STC8A8K64S4A12系列单片机每一路PWM都有3个IO引脚供选择使用,如下表。

表1:单片机PWM输出控制引脚分配

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。针对非独立GPIO使用时需特别注意。

STC8A8K64S4A12系列单片机PWM外设的理解主要是对其PWM波形发生器的内部结构框图的解析,下面给出该结构图。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图2:PWM波形发生器内部结构示意图

☆注:从该图不难看出,虽然8路PWM相互独立,但因为共用同一个计数器,所以8路PWM若多个同时使用则其频率都是一样的。

3.PWM配置步骤

针对STC8A8K64S4A12系列单片机有8路PWM外设,软件的配置过程如下:

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图3:PWM软件配置步骤

☆注:实验例程即是按照上述配置步骤操作寄存器的相关位,后有详述。

二、软件设计

1.PWM寄存器汇集

STC8A8K64S4A12系列单片机操作PWM时会用到57个寄存器,如下表所示:

表2:STC8A8K64S4A12系列PWM使用寄存器汇总

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

☆注:8路相互独立的PWM都有计数器T1、T2、控制寄存器和电平保持控制寄存器,只需把一路PWM对应的寄存器搞清楚就可以了。

2.寄存器解析

2.1.PWM配置寄存器PWMCFG

PWM配置寄存器PWMCFG的B6位为PWM计数与硬件自动触发ADC转换的关联控制位,该ETADC位应用于比较特殊的场合,一般配置为0即可。PWMCFG寄存器的B7位为PWM计数器归零中断标志位(硬件实现自动置位,不受ECBI位影响)。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图4:PWM配置寄存器

2.2.PWM控制寄存器PWMCR

PWM控制寄存器PWMCR的B6位为PWM计数器归零中断使能位,B7位为PWM波形发生器使能位,在对相应PWM口配置完成后需使能该位。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图5:PWM控制寄存器

2.3.PWM中断标志寄存器

PWM中断标志寄存器PWMIF的C0IF~C7IF位的操作: PWM相应通道的中断标志位。C0IF~C7IF位在当PWM发生翻转时,硬件自动置1,需软件清零。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图6:PWM中断标志寄存器

2.4.PWM外部异常控制寄存器

PWM外部异常控制寄存器PWMFDCR的ENFD位为PWM外部异常检测功能控制位,需要用到PWM外部异常检测功能时置1。PWMFDCR寄存器的EFDI位为PWM异常检测中断使能位。该寄存器其他位请参考下图。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图7:PWM外部异常控制寄存器

☆注:PWMFDCR寄存器是对PWM外部异常控制有需求时使用,一般应用可对相应位设置为0即可。

2.5.PWM时钟选择寄存器

PWM时钟选择寄存器PWMCKS的SELT2位为PWM时钟源选择位,该时钟源选择位为0时,即选择PWM时钟源为系统时钟提供,那么设置PS[3:0]才有意义。PWMCKS寄存器的PS[3:0]位用于对系统时钟分频后提供PWM时钟源。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图8:PWM时钟选择寄存器

☆注:PWMCKS是在扩展RAM区,这就要求在访问这些寄存器时需将P_SW2寄存器的EAXSFR位置1。

2.6.PWM触发ADC计数器寄存器

PWM触发ADC计数器寄存器分触发ADC计数值高8位寄存器TADCPH和触发ADC计数值低8位寄存器TADCPL,其中触发ADC计数值高8位寄存器TADCPH的B7位为保留位,这样{TADCPH, TADCPL}可组成一个15位的寄存器。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图9:PWM触发ADC计数器寄存器

☆注:在ETADC=1且ADC_POWER=1时,15位寄存器{TADCPH, TADCPL}值与PWM内部计数值相等时,硬件会自动触发AD转换。

2.7.PWM2翻转寄存器

PWM2有2个15位的翻转计数器PWM2T1和PWM2T2,因为是8位单片机,寄存器都是8位的,所以PWM2实际拥有4个用于翻转的寄存器,分别是PWM2T1H、PWM2T1L、PWM2T2H和PWM2T2L。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图10:PWM2翻转寄存器

2.8.PWMn控制寄存器

PWMn控制寄存器PWMnCR的ENCnO位为PWMn的输出使能位,CnINI位为PWM输出端口的初始电平设置,Cn_S[1:0]位为输出引脚选择(每一路PWM都有3个IO引脚供选择,所以需要寄存器2位实现)。PWMnCR寄存器的ECnI位的操作,置1可保证CnIF被硬件置1时,程序进入相应中断入口。关于ECnT2SI和ECnT1SI位的操作,置1使能T1或T2翻转时中断,切记此处T1或T2不是单片机本身的定时器T1或T2,而是PWMn里面的翻转计数器。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图11:PWMn控制寄存器

2.9.PWMn电平保持控制寄存器

PWMn电平保持控制寄存器可实现PWM通道输出PWM信号时,通过对该寄存器相应位操作强制将输出信号置为高电平或低电平。这在有些场合很实用。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图12:PWMn电平保持控制寄存器

3.PWM6呼吸灯实验(P2.6)

3.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表3:实验需要用到的c文件
序号 文件名 后缀 功能描述
1 pwm .c 外部PWM有关的用户自定义函数。
2 delay .c 包含用户自定义延时函数。

3.2.头文件引用和路径设置

■ 需要引用的头文件

#include "delay.h"  
#include "pwm.h"  

■ 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表4:头文件包含路径
序号 路径 描述
1 …\ Source pwm.h和delay.h头文件在该路径,所以要包含。
2 …\User STC8.h头文件在该路径,所以要包含。

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图13:添加头文件包含路径

3.3.编写代码

首先,在pwm.c文件中编写PWM6的初始化函数PWM6_Configuration,代码如下。

程序清单:PWM6初始化函数

/************************************************************************* 
功能描述:对PWM6进行初始化 
入口参数:无 
返回值:无 
 *************************************************************************/  
void PWM6_Configuration(void)  
{  
    PWMCFG &= 0xBF;              //将ETADC位置0,即PWM计数不关联触发ADC转换  
    PWMCFG &= 0x7F;              //将CBIF位置0,PWM计数器归零中断标志位,需软件清零 
      
    P_SW2 |= 0x80;         //将EAXSFR位置1,以访问PWM在扩展RAM区的特殊功能寄存器  
    //对PWM6的初始化部分  
    PWM6CR &= 0xE7;            //将C6_S[1:0]位置00,选择PWM6的输出引脚是P2.6  
    PWM6CR |= 0x80;                //将ENC6O位置1,PWM6的端口为PWM输出口,受PWM波形发生器控制     
    PWM6CR &= 0xBF;           //将C6INI位置0,设置PWM6输出端口的初始电平为低电平  
    PWMIF &= 0xBF;               //将C6IF位置0,PWM6中断标志位,需软件清零  
    PWM6CR |= 0x04;            //将EC6I位置1,使能PWM6中断  
    PWM6CR &= 0xFD;            //将EC6T2SI位置0,关闭T2翻转时中断  
    PWM6CR &= 0xFE;            //将EC6T1SI位置0,关闭T1翻转时中断  
    //对PWM6翻转计数器赋初值  
    PWM6T1 =1;                 //赋值PWM6第一次翻转计数器值  
    PWM6T2 = 0x00FA;           //赋值PWM6第二次翻转计数器值  
      
  //对PWM波形发生器时钟源进行初始化  
    PWMCKS |= 0x10;              //将SELT2位置1,PWM时钟源为定时器2溢出脉冲  
    PWMC = 0x00FA;             //PWM计数器赋值(同时对PWMCH和PWMCL进行了赋值)  
    AUXR |= 0x04;                  //定时器2时钟为Fosc,即1T  
    T2L = 0xE0;                  //设定定时初值  
    T2H = 0xFE;                //设定定时初值  
    AUXR |= 0x10;              //启动定时器2  
    P_SW2 &= 0x7F;               //将EAXSFR位置0,恢复访问XRAM  
      
    //PWM外部异常控制寄存器的操作  
    PWMFDCR &= 0xDF;               //将ENFD位置0,关闭PWM外部异常检测功能  
    PWMFDCR &= 0xF7;               //将EFDI位置0,关闭PWM异常检测中断  
    PWMFDCR &= 0xFB;               //将FDCMP位置0,比较器与PWM无关  
    PWMFDCR &= 0xFD;               //将FDIO位置0,P3.5的状态与PWM无关  
    PWMFDCR &= 0xFE;             //将FDIF位置0,PWM异常检测中断标志位,需软件清零  
      
    IP2 |= 0x40;                   //将PPWM位置1,使能PWM中断为最高优先级中断  
    //使能PWM波形发生器  
    PWMCR |= 0x80;            //将ENPWM位置1,使能PWM波形发生器,PWM计数器开始计数 
    PWMCR &= 0xBF;               //将ECBI位置0,禁止PWM计数器归零中断  
} 

然后,编写PWM中断服务函数,一旦进入中断则软件清除相应中断标志位,代码如下。

程序清单:中断服务函数

/*************************************************************************** 
 * 描  述 : PWM中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void PWM(void) interrupt 22 using 1  
{  
    PWMCFG &= 0x7F;             //将CBIF位置0,PWM计数器归零中断标志位,需软件清零  
    PWMIF &= 0xBF;               //将C6IF位置0,PWM6中断标志位,需软件清零  
} 

最后,在主函数中调用PWM6初始化函数,开启总中断,在主循环中不断改变PWM6翻转计数器T1和T2值实现对PWM6输出信号占空比的调节。

代码清单:主函数

int main()  
{  
    uint8 flag=1;  
    uint16 ledpwmval=0;   
  
    PWM6_Configuration();       //初始化PWM6口  
    EA = 1;                       //允许总中断  
      
  while(1)  
  {  
        delay_ms(30);        //延迟每次指示灯亮度的时间,更方便观察实验现象   
          
        if(flag)             //如果标识符为1则递增变量ledpwmval  
            ledpwmval++;  
        else                 //如果标识符为0则递减变量ledpwmval  
            ledpwmval--;      
          
        if(ledpwmval>248)    //如果变量ledpwmval递增到一定值则控制标识符为0,以实现ledpwmval递减  
            flag=0;  
        if(ledpwmval==1)     //如果变量ledpwmval递减到一定值则控制标识符为1,以实现ledpwmval递增  
            flag=1;    
     
        P_SW2 |= 0x80;      //将EAXSFR位置1,以访问PWM在扩展RAM区的特殊功能寄存器  
        PWM6T1 =(uint16)ledpwmval;    //赋值PWM6第一次翻转计数器值(不断变化值)  
        PWM6T2 = 0x00FA;              //赋值PWM6第二次翻转计数器值(定值)  
        P_SW2 &= 0x7F;                  //将EAXSFR位置0,恢复访问XRAM         
    }  
}

3.4.硬件连接

本实验需要使用短路帽将J29端子的P26与D1短接,如下图所示。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图14:开发板连接图

4.PWM7呼吸灯实验(P2.7)

1.编写代码

首先,在pwm.c文件中编写PWM7的初始化函数PWM7_Configuration,代码如下。

程序清单:PWM7初始化函数

/************************************************************************* 
功能描述:对PWM7进行初始化 
入口参数:无 
返回值:无 
 **************************************************************************/  
void PWM7_Configuration(void)  
{  
    PWMCFG &= 0xBF;              //将ETADC位置0,即PWM计数不关联触发ADC转换  
    PWMCFG &= 0x7F;              //将CBIF位置0,PWM计数器归零中断标志位,需软件清零 
      
    P_SW2 |= 0x80;          //将EAXSFR位置1,以访问PWM在扩展RAM区的特殊功能寄存器  
    //对PWM7的初始化部分  
    PWM7CR &= 0xE7;            //将C7_S[1:0]位置00,选择PWM7的输出引脚是P2.7  
    PWM7CR |= 0x80;                //将ENC7O位置1,PWM7的端口为PWM输出口,受PWM波形发生器控制     
    PWM7CR &= 0xBF;          //将C7INI位置0,设置PWM7输出端口的初始电平为低电平  
    PWMIF &= 0x7F;               //将C7IF位置0,PWM7中断标志位,需软件清零  
    PWM7CR |= 0x04;            //将EC7I位置1,使能PWM7中断  
    PWM7CR &= 0xFD;            //将EC7T2SI位置0,关闭T2翻转时中断  
    PWM7CR &= 0xFE;            //将EC7T1SI位置0,关闭T1翻转时中断  
    //对PWM7翻转计数器赋初值  
    PWM7T1 =1;                 //赋值PWM7第一次翻转计数器值  
    PWM7T2 = 0x00FA;           //赋值PWM7第二次翻转计数器值  
      
  //对PWM波形发生器时钟源进行初始化  
    PWMCKS |= 0x10;              //将SELT2位置1,PWM时钟源为定时器2溢出脉冲  
    PWMC = 0x00FA;             //PWM计数器赋值(同时对PWMCH和PWMCL进行了赋值)  
    AUXR |= 0x04;                  //定时器2时钟为Fosc,即1T  
    T2L = 0xE0;                  //设定定时初值  
    T2H = 0xFE;                //设定定时初值  
    AUXR |= 0x10;              //启动定时器2  
    P_SW2 &= 0x7F;               //将EAXSFR位置0,恢复访问XRAM  
      
    //PWM外部异常控制寄存器的操作  
    PWMFDCR &= 0xDF;               //将ENFD位置0,关闭PWM外部异常检测功能  
    PWMFDCR &= 0xF7;               //将EFDI位置0,关闭PWM异常检测中断  
    PWMFDCR &= 0xFB;               //将FDCMP位置0,比较器与PWM无关  
    PWMFDCR &= 0xFD;               //将FDIO位置0,P3.5的状态与PWM无关  
    PWMFDCR &= 0xFE;             //将FDIF位置0,PWM异常检测中断标志位,需软件清零  
      
    IP2 |= 0x40;                   //将PPWM位置1,使能PWM中断为最高优先级中断  
    //使能PWM波形发生器  
    PWMCR |= 0x80;          //将ENPWM位置1,使能PWM波形发生器,PWM计数器开始计数  
    PWMCR &= 0xBF;               //将ECBI位置0,禁止PWM计数器归零中断  
} 

然后,编写PWM中断服务函数,一旦进入中断则软件清除相应中断标志位,代码如下。

程序清单:中断服务函数

/*************************************************************************** 
 * 描  述 : PWM中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void PWM(void) interrupt 22 using 1  
{  
    PWMCFG &= 0x7F;             //将CBIF位置0,PWM计数器归零中断标志位,需软件清零  
    PWMIF &= 0x7F;               //将C7IF位置0,PWM7中断标志位,需软件清零  
} 

最后,在主函数中调用PWM7初始化函数,开启总中断,在主循环中不断改变PWM7翻转计数器T1和T2值实现对PWM7输出信号占空比的调节。

代码清单:主函数

int main()  
{  
    uint8 flag=1;  
    uint16 ledpwmval=0;   
  
    PWM7_Configuration();       //初始化PWM7口  
    EA = 1;                       //允许总中断  
      
  while(1)  
  {  
        delay_ms(30);        //延迟每次指示灯亮度的时间,更方便观察实验现象   
          
        if(flag)             //如果标识符为1则递增变量ledpwmval  
            ledpwmval++;  
        else                 //如果标识符为0则递减变量ledpwmval  
            ledpwmval--;      
          
        if(ledpwmval>248)    //如果变量ledpwmval递增到一定值则控制标识符为0,以实现ledpwmval递减  
            flag=0;  
        if(ledpwmval==1)     //如果变量ledpwmval递减到一定值则控制标识符为1,以实现ledpwmval递增  
            flag=1;    
     
        P_SW2 |= 0x80;                  //将EAXSFR位置1,以访问PWM在扩展RAM区的特殊功能寄存器  
        PWM7T1 =(uint16)ledpwmval;    //赋值PWM7第一次翻转计数器值(不断变化值)  
        PWM7T2 = 0x00FA;              //赋值PWM7第二次翻转计数器值(定值)  
        P_SW2 &= 0x7F;                  //将EAXSFR位置0,恢复访问XRAM  
          
    }  
}  

2.硬件连接

本实验需要使用短路帽将J29端子的P27与D2短接,如下图所示。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图15:开发板连接图

5.PWM6和PWM7呼吸灯实验(P2.6和P2.7)

1.编写代码

首先,在pwm.c文件中编写PWM6和PWM7的初始化函数PWM6PWM7_Configuration,代码如下。

程序清单:PWM6和PWM7初始化函数

/*************************************************************************** 
功能描述:对PWM6和PWM7进行初始化 
入口参数:无 
返回值:无 
 **************************************************************************/  
void PWM6PWM7_Configuration(void)  
{  
    PWMCFG &= 0xBF;              //将ETADC位置0,即PWM计数不关联触发ADC转换  
    PWMCFG &= 0x7F;              //将CBIF位置0,PWM计数器归零中断标志位,需软件清零  
      
    P_SW2 |= 0x80;          //将EAXSFR位置1,以访问PWM在扩展RAM区的特殊功能寄存器  
    //对PWM6的初始化部分  
    PWM6CR &= 0xE7;            //将C6_S[1:0]位置00,选择PWM6的输出引脚是P2.6  
    PWM6CR |= 0x80;                //将ENC6O位置1,PWM6的端口为PWM输出口,受PWM波形发生器控制     
    PWM6CR &= 0xBF;             //将C6INI位置0,设置PWM6输出端口的初始电平为低电平  
    PWMIF &= 0xBF;               //将C6IF位置0,PWM6中断标志位,需软件清零  
    PWM6CR |= 0x04;            //将EC6I位置1,使能PWM6中断  
    PWM6CR &= 0xFD;            //将EC6T2SI位置0,关闭T2翻转时中断  
    PWM6CR &= 0xFE;            //将EC6T1SI位置0,关闭T1翻转时中断  
    //对PWM7的初始化部分  
    PWM7CR &= 0xE7;            //将C7_S[1:0]位置00,选择PWM7的输出引脚是P2.7  
    PWM7CR |= 0x80;                //将ENC7O位置1,PWM7的端口为PWM输出口,受PWM波形发生器控制     
    PWM7CR &= 0xBF;          //将C7INI位置0,设置PWM7输出端口的初始电平为低电平  
    PWMIF &= 0x7F;               //将C7IF位置0,PWM7中断标志位,需软件清零  
    PWM7CR |= 0x04;            //将EC7I位置1,使能PWM7中断  
    PWM7CR &= 0xFD;            //将EC7T2SI位置0,关闭T2翻转时中断  
    PWM7CR &= 0xFE;            //将EC7T1SI位置0,关闭T1翻转时中断  
    //对PWM6和PWM7翻转计数器赋初值  
    PWM6T1 =1;                 //赋值PWM6第一次翻转计数器值  
    PWM6T2 = 0x00FA;           //赋值PWM6第二次翻转计数器值  
    PWM7T1 =1;                 //赋值PWM7第一次翻转计数器值  
    PWM7T2 = 0x00FA;           //赋值PWM7第二次翻转计数器值  
      
  //对PWM波形发生器时钟源进行初始化  
    PWMCKS |= 0x10;              //将SELT2位置1,PWM时钟源为定时器2溢出脉冲  
    PWMC = 0x00FA;             //PWM计数器赋值(同时对PWMCH和PWMCL进行了赋值)  
    AUXR |= 0x04;                  //定时器2时钟为Fosc,即1T  
    T2L = 0xE0;                  //设定定时初值  
    T2H = 0xFE;                //设定定时初值  
    AUXR |= 0x10;              //启动定时器2  
    P_SW2 &= 0x7F;               //将EAXSFR位置0,恢复访问XRAM  
      
    //PWM外部异常控制寄存器的操作  
    PWMFDCR &= 0xDF;               //将ENFD位置0,关闭PWM外部异常检测功能  
    PWMFDCR &= 0xF7;               //将EFDI位置0,关闭PWM异常检测中断  
    PWMFDCR &= 0xFB;               //将FDCMP位置0,比较器与PWM无关  
    PWMFDCR &= 0xFD;               //将FDIO位置0,P3.5的状态与PWM无关  
    PWMFDCR &= 0xFE;               //将FDIF位置0,PWM异常检测中断标志位,需软件清零  
      
    IP2 |= 0x40;                   //将PPWM位置1,使能PWM中断为最高优先级中断  
    //使能PWM波形发生器  
    PWMCR |= 0x80;         //将ENPWM位置1,使能PWM波形发生器,PWM计数器开始计数  
    PWMCR &= 0xBF;               //将ECBI位置0,禁止PWM计数器归零中断  
}

然后,编写PWM中断服务函数,一旦进入中断则软件清除相应中断标志位,代码如下。

程序清单:中断服务函数

/*************************************************************************** 
 * 描  述 : PWM中断服务函数 
 * 入  参 : 无 
 * 返回值 : 无 
 **************************************************************************/  
void PWM(void) interrupt 22 using 1  
{  
    PWMCFG &= 0x7F;          //将CBIF位置0,PWM计数器归零中断标志位,需软件清零  
    PWMIF &= 0xBF;               //将C6IF位置0,PWM6中断标志位,需软件清零  
    PWMIF &= 0x7F;               //将C7IF位置0,PWM7中断标志位,需软件清零  
} 

最后,在主函数中调用PWM6和PWM7初始化函数,开启总中断,在主循环中不断改变PWM6和PWM7翻转计数器T1和T2值实现对PWM6和PWM7输出信号占空比的调节。

代码清单:主函数

int main()  
{  
    uint8 flag=1;  
    uint16 ledpwmval=0;   
  
    PWM6PWM7_Configuration();      //初始化PWM6和PWM7口  
    EA = 1;                          //允许总中断  
      
  while(1)  
  {  
        delay_ms(30);        //延迟每次指示灯亮度的时间,更方便观察实验现象   
          
        if(flag)             //如果标识符为1则递增变量ledpwmval  
            ledpwmval++;  
        else                 //如果标识符为0则递减变量ledpwmval  
            ledpwmval--;      
          
        if(ledpwmval>248)    //如果变量ledpwmval递增到一定值则控制标识符为0,以实现ledpwmval递减  
            flag=0;  
        if(ledpwmval==1)     //如果变量ledpwmval递减到一定值则控制标识符为1,以实现ledpwmval递增  
            flag=1;    
     
        P_SW2 |= 0x80;      //将EAXSFR位置1,以访问PWM在扩展RAM区的特殊功能寄存器  
        PWM6T1 =(uint16)ledpwmval;    //赋值PWM6第一次翻转计数器值(不断变化值)  
        PWM6T2 = 0x00FA;              //赋值PWM6第二次翻转计数器值(定值)  
        PWM7T1 =(uint16)ledpwmval;    //赋值PWM7第一次翻转计数器值(不断变化值)  
        PWM7T2 = 0x00FA;              //赋值PWM7第二次翻转计数器值(定值)  
        P_SW2 &= 0x7F;                  //将EAXSFR位置0,恢复访问XRAM  
          
    }  
} 

2.硬件连接

本实验需要使用短路帽将J29端子的P26与D1短接、P27与D2短接,如下图所示。

【STC8A8K64S4A12开发板】—学习PWM脉冲宽度调制

图16:开发板连接图

总结

以上就是今天要讲的内容,希望对大家有所帮助!
上一篇:红外通讯之红外遥控器让你的舵机转圈圈


下一篇:go语言读文件