【STC8A8K64D4开发板】第2-15讲:脉宽调制PWM

第2-15讲:脉宽调制PWM

    1. 学习目的
  1. 掌握PWM的基本概念。
  2. 掌握PWM周期和占空比的计算。
  3. 掌握PWM的基本编程应用,编程实现PWM输出不同频率和占空比的波形。
  4. 掌握PWM实现呼吸灯。
  5. 掌握PWM驱动震动马达,通过不同的占空比控制震动马达的震动强度。
    1. PWM概述

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

所谓的脉冲宽度调制,也就是输出占空比可变的脉冲波形。下图是一个PWM控制模拟电路(电压是模拟量)的示例,当开关直接接通时,电灯供电电压为9V,当使用PWM脉冲控制开关的接通和闭合时,PWM占空比为50%的情况下,可等效为4.5V电池给电灯供电,电灯的亮度会有所下降。同理,占空比是10%,等效于0.9V电池给电灯供电,亮度会很暗。由此,我们可以看到,通过改变PWM信号的频率和占空比可以控制电灯的状态和亮度。

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

  1. PWM的重要概念
  1. 周期和频率

PWM周期是指PWM信号从一个上升沿执行到下一个上升沿所需要的时间,周期的倒数即为PWM频率。

  1. 占空比

一个PWM周期中,高电平保持的时间与该 PWM的周期的时间之比如一个PWM的周期是10ms高电平的时间是1ms,那么占空比就是10%。

  1. 边沿对齐、中心对齐、单斜率和双斜率
  • 单斜率:PWM计数器从0开始计数到最大值,达到最大值后清零重新从0开始计数,如此反复。单斜率和PWM比较值实现的是边沿对齐。
  • 双斜率:PWM计数器从0开始计数到最大值,再从最大值计数到0,如此反复。双斜率和PWM比较值实现的是中心对齐。
  1. 分辨率

占空比最小能达的数值,如8位的PWM,理论的分辨率就是1:255(单斜率),16位的PWM理论就是1:65535(单斜率)。

  1. PWM信号控制模拟电路的优点

PWM信号是数字信号,处理器到被控系统信号都是数字形式的,可将噪声影响降到最低,噪声只有在强到足以将逻辑1改变为逻辑0或将逻辑0改变为逻辑1时,也才能对数字信号产生影响。PWM对噪声抵抗能力的增强,因此,从模拟信号转向PWM可以极大地延长通信距离。

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

    1. STC8A8K64D4的PWM应用步骤

STC8A8K64D4单片机片内集成了1 组增强型的 PWM 波形发生器,可产生各自独立的8 路 PWM。

PWM 的时钟源可以通过PWM 时钟选择寄存器(PWMCKS)配置,但需要注意的是并不是8 路 PWM的时钟源可以独立配置,他们是共用一个时钟源的。

PWM 波形发生器内部有一个 15 位的 PWM 计数器供 8 路 PWM使用,用户可以设置每路 PWM 的初始电平。另外, PWM 波形发生器为每路 PWM 又设计了两个用于控制波形翻转的计数器 T1/T2,可以非常灵活的控制每路 PWM 的高低电平宽度,从而达到对 PWM 的占空比以及 PWM 的输出延迟进行控制的目的。

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

 增强型的 PWM 波形发生器还设计了对外部异常事件(包括外部端口 P3.5 电平异常、比较器比较结果异常)进行监控的功能,可用于紧急关闭 PWM 输出。PWM 波形发生器还可与 ADC 相关联,设置PWM 周期的任一时间点触发 ADC 转换事件。

图2:PWM应用步骤

      1. 使能PWM模块

配置PWM模块之前,必须先使能PWM模块,否则,配置无效。PWM模块是通过“增强型 PWM 全局配置寄存器(PWMSET)”中的ENPWM位使能的,如下图所示。

增强型 PWM 全局配置寄存器(PWMSET):

P_SW2寄存器中的I2C_S[1:0]为 I2C功能脚选择位,如下表所示。

  1. ENPWM:PWM 使能位(使能/关闭PWM通道 PWM0~PWM7)。
  1. 0:关闭 PWM。
  2. 1:使能 PWM。
      1. PWM通道配置

PWM通道配置包含PWM通道功能引脚配置、PWM通道初始电平以及PWM通道中断配置。PWM模块的8个通道各自具有一个独立的通道配置寄存器(PWMnCR,n=0~7),可以对各个通道单独配置,如下图所示。

每个PWM通道对应多个物理引脚,但使用时只能选择其中一个引脚作为该PWM通道的输出引脚。选择了PWM通道输出引脚后,还需要使能该引脚为PWM通道输出,并配置PWM通道的初始电平,初始电平会体现在PWM通道的输出引脚上。

PWM 通道控制寄存器(PWMnCR,n=0~7

  1. ENO: PWMn输出使能位。( n= 0~7)
  1. 0: PWM 的 通道n相应端口为 GPIO。
  2. 1: PWM 的通道n相应端口为 PWM 输出口,受 PWM 波形发生器控制。
  1. INI: 设置 PWMn 输出端口的初始电平。( n= 0~7)
  1. 0: PWM 的 通道n初始电平为低电平。
  2. 1: PWM 的 通道n初始电平为高电平。
  1. Cn_S[1:0](n=0~7): PWM 通道输出脚选择位。

PWM模块的每个通道均有多个引脚与之对应,同一时刻,只能选择其中的一个引脚作为PWM输出引脚使用, STC8A8K64D4单片机PWM的各个通道的引脚分配如下表所示。

表1:STC8A8K64D4单片机PWM引脚分配

PWM 通道

信号编号

对应的IO

Cn_S[1:0]位(n=0~7)

PWM 通道0

PWM0

P2.0

C0_S[1:0] = 00

PWM0_2

P1.0

C0_S[1:0] = 01

PWM0_3

P6.0

C0_S[1:0] = 10

PWM 通道1

PWM1

P2.1

C1_S[1:0] = 00

PWM1_2

P1.1

C1_S[1:0] = 01

PWM1_3

P6.1

C1_S[1:0] = 10

PWM 通道2

PWM2

P2.2

C2_S[1:0] = 00

PWM2_2

P1.2

C2_S[1:0] = 01

PWM2_3

P6.2

C2_S[1:0] = 10

PWM 通道3

PWM3

P2.3

C3_S[1:0] = 00

PWM3_2

P1.3

C3_S[1:0] = 01

PWM3_3

P6.3

C3_S[1:0] = 10

PWM 通道4

PWM4

P2.4

C4_S[1:0] = 00

PWM4_2

P1.4

C4_S[1:0] = 01

PWM4_3

P6.4

C4_S[1:0] = 10

PWM 通道5

PWM5

P2.5

C5_S[1:0] = 00

PWM5_2

P1.5

C5_S[1:0] = 01

PWM5_3

P6.5

C5_S[1:0] = 10

PWM 通道6

PWM6

P2.6

C6_S[1:0] = 00

PWM6_2

P1.6

C6_S[1:0] = 01

PWM6_3

P6.6

C6_S[1:0] = 10

PWM 通道7

PWM7

P2.7

C7_S[1:0] = 00

PWM7_2

P1.7

C7_S[1:0] = 01

PWM7_3

P6.7

C7_S[1:0] = 10

  1. ENI: PWM 的通道n中断使能控制位。(n= 0~7)
  1. 0: 关闭 PWM 通道n的 PWM 中断。
  2. 1: 使能 PWM 通道n的 PWM 中断。
  1. ENT2I: PWM 的 i 通道在第 2 个触发点中断使能控制位。( n= 0~7)
  1. 0: 关闭 PWM 通道n在第 2 个触发点中断。
  2. 1: 使能 PWM 通道n在第 2 个触发点中断。
  1. ENT1I: PWM 的 i 通道在第 1 个触发点中断使能控制位。( n= 0~7)
  1. 0: 关闭 PWM 通道n在第 1 个触发点中断。
  2. 1: 使能 PWM 通道n在第 1 个触发点中断。

  • 说明:PWM通道中断在后面的章节中和归零中断一起讲解。

      1. 周期和占空比

对于一个PWM输出波形来说,最直观的参数就是波形的频率和占空比,其中频率反映了一个PWM周期占用的时间,占空比反映了一个周期内高电平的时间和PWM的周期的时间之比。接下来,我们就从这两个方面讲解PWM波形的输出。

        1. PWM输出波形的周期、频率

PWM输出波形的周期和PWM计数器的时钟、PWM 计数器寄存器(PWMC)的值以及计数模式有关。PWM计数器所用的时钟决定了一个计数所用的时间,PWMC的值决定了计数器的匹配值,这两点很好理解,那么计数模式是如何影响PWM周期的?

PWM计数模式通常有三种:向上计数、向下计数和向上/向下计数,其中向上计数和向下计数又称为边沿对齐模式,向上/向下计数又称为中心对齐模式。注意,STC8A8K64D4单片机的PWM只有向上计数模式。为了能更好的理解PWM波形的产生,我们有必要了解一下这三种模式。

  1. 向上计数模式

向上计数模式中,计数器从 0 计数到PWMC寄存器的值(该值由用户写入),然后重新从0 开始计数,如此反复执行,如下图所示。

图3:向上计数

  1. 向下计数模式

向下计数模式中,计数器从PWMC 寄存器的值(该值由用户写入)向下计数到0,然后重新从PWMC 寄存器的值向下计数,如此反复执行,如下图所示。

图4:向上计数

  1. 向上/向下计数模式

向上/向下计数模式中,从 0 开始计数到PWMC 寄存器的值(该值由用户写入),接着从PWMC 寄存器的值向下计数到0,然后再从 0 开始重新计数,如此反复执行,如下图所示。

图5:向上/向下计数

由上面三种计数模式的示意图可以看出,在设置了同样的计数比较值后,向上/向下计数模式的周期是向上计数模式和向下计数模式的2倍。

  1. PWM计数器的时钟源

PWM计数器的时钟源是通过“PWM 时钟选择寄存器(PWMCKS)”中的“时钟选择位(SELT2)”配置的,有2个可选择项:系统时钟和定时器2的溢出脉冲。如果选择使用系统时钟,可通过“PWM_PS[3:0]”设置系统时钟的预分频,如下图所示。

  1. SELT2: PWM 时钟源选择。
  1. 0: PWM 时钟源为系统时钟经分频器分频之后的时钟
  2. 1: PWM 时钟源为定时器 2 的溢出脉冲
  1. PWM_PS[3:0]:系统时钟预分频参数
  2. PWM 计数器寄存器(PWMC)

SELT2

PWM_PS[3:0] 

PWM 输入时钟源频率

1

xxxx

定时器 2 的溢出脉冲

0

0000

SYSclk/1

0

0001

SYSclk/2

0

0010

SYSclk/3

...

...

...

0

x

SYSclk/(x+1)

...

...

...

0

1111

SYSclk/16

由于PWM只有向上计数模式,因此,PWMC设置的值即为PWM一个周期所包含的计数个数,PWMC是一个 15 位的寄存器, 其中:

  1. PWMCH: PWM 计数器周期值的高 7 位。
  2. PWMCL: PWM 计数器周期值的低 8 位。

可设定 1~32767 之间的任意值作为 PWM 的周期,PWM 波形发生器内部的计数器从 0 开始计数,每个 PWM 时钟周期递增 1,当内部计数器的计数值达到[PWMCH,PWMCL]所设定的 PWM 周期时,PWM 波形发生器内部的计数器将会从 0 重新开始开始计数。

  1. 计数模式

STC8A8K64D4单片机的PWM只有向上计数模式。

综上所述,我们即可得到PWM的频率计算公式,如下表所示。

        1. PWM输出波形的占空比

上一节中我们知道了如何配置PWM输出波形的周期,周期确定后,接下来需要设置的是在一个周期中高电平的占比,即占空比。

PWM模块的8个通道均有两个可配置的触发点,他们都是由两个寄存器组合而成的 15 位寄存器:

  1. 第1触发点:PWMiT1H的低7位和PWMiT1L组成15位寄存器,i=0~7。
  2. 第2触发点:PWMiT2H的低7位和PWMiT2L组成15位寄存器,i=0~7。

当PWM计数器的计数值到达第1触发点设置的数值时,PWM输出低电平,当PWM计数器的计数值到达第2触发点设置的数值时,PWM输出高电平。由此,通过设置第1触发点和第2触发点的数值,即可得到我们需要的占空比。

为了便于读者理解,我们用图形的方式来说明如何通过两个触发点来配置占空比。下面讨论了两种方式,读者对比一下这两种方式,即可清晰理解两个触发点的作用。

  1. 示例:PWM时钟设置为2M,PWM的周期设置为1ms(即PWMC的值设置为1999),在此周期中配置PWM占空比为40%。

方法1:第1触发点的数值设置为0,第2触发点的数值设置为1200

图6:第1触发点的值设置为0时PWM输出波形

当计数器的值归零后,因为第1触发点的值设置的是0,所以PWM输出低电平。计数值递增到1200,即计数值等于第2触发点的值时,PWM输出高电平。当计数器的值继续递增至2000(PWMC)后,计数值归零,PWM输出再次回到低电平,如此反复。即输出一定占空比的波形。

方法2:第1触发点的数值设置为400,第2触发点的数值设置为1600(当然,用其他的数值也可以的,只要第2触发点的数值减去第1触发点的数值占周期计数值的60%即可)。

图7:第1触发点的值设置不为0时PWM输出波形

由上面的示例可以看到,通过第1触发点和第2触发点可以很灵活的配置PWM占空比,读者在开发过程中,可以根据实际应用情况灵活使用。

      1. PWM中断

PWM中断包含通道中断、归零中断和异常中断。

  1. 通道中断

PWM 通道控制寄存器(PWMnCR)中的ENT1I和ENT2I位分别用于使能/关闭第1触发点中断和第2触发点中断,ENI位用于使能/关闭通道中断。通道中断是针对PWM模块的通道的,也就是说PWM模块的8个通道均可单独配置通道中断。

  1. 归零中断

PWM 配置寄存器(PWMCFG)中的EPWMCBI位用于使能/关闭计数器的归零中断。归零中断不是针对PWM通道,而是针对PWM计数器的,归零中断使能后,计数器计数到PWMC的数值后,计数值归零并产生中断,中断标志位需要软件清零。

  1. 异常检测中断

异常检测中断由PWM 异常检测控制寄存器(PWMnFDCR)配置,当发生 PWM 异常时,硬件自动产生中断跳转到相应中断入口执行中断服务程序,PWM 异常检测中断标志位需软件清零。

  • 注意:开启PWM中断的情况下,还需要开启总中断“EA=1”,PWM中断才能起作用。

  • 注:PWMnFDCR的详细解释可阅读《STC8A8K64D4 系列单片机技术参考手册》的19.2.4。

      1. 启动PWM

PWM配置完成后,还需要通过增强型 PWM 配置寄存器(PWMCFG)中的PWMCEN位启动计数,PWM 才能输出波形。

  1. PWMCEN: PWM 波形发生器开始计数。
  1. 0: PWM 停止计数
  2. 1: PWM 计数器开始计数

PWMCEN 一旦被使能后,内部的 PWM 计数器会立即开始计数,并与 T1/T2 的值进行比较。所以 PWMCEN 必须在其他所有的 PWM 设置(包括 T1/T2 的设置、初始电平的设置、 PWM 异常检测的设置以及 PWM 中断设置)都完成后,最后才能使能 PWMCEN 位。

在 PWM 计数器计数的过程中, PWMCEN 控制位被关闭时,PWM 计数会立即停止,当再次使能 PWMCEN 控制位时,PWM 的计数会从 0 开始重新计数,而不会记忆 PWM 停止计数前的计数值。

  • 特别注意:当 PWMCEN 由 0 变为 1 时,内部的 PWM 计数器是从之前的不确定值归零后重新开始计数,所以此时会产生立即产生一个归零中断,当用户需要使用 PWM 的归零中断时,需特别注意这一点,即第一个归零中断并不是真正的 PWM 周期记满后归零所产生的。

    1. 软件设计
      1. PWM输出波形
  • 注:本节的实验是在“实验2-1-3:流水灯(自编驱动文件方式)”的基础上修改,本节对应的实验源码是:“实验2-15-1:PWM输出波形”。
        1. 实验内容

本例中使用PWM通道6的P2.6引脚输出频率为1KHz,占空比为60%的波形。P2.6在开发板上连接到了指示灯D1,当我们改变占空比时,可以观察到D1亮度的变化,本例的功能需求如下表所示。

表2:功能需求表

使用的PWM通道

PWM 通道6。

输出引脚

P2.6。

PWM时钟

系统时钟的12分频,系统时钟使用的是24MHz,所以,PWM时钟是2MHz。

PWM频率

1KHz(周期1ms)。

由上面的PWM频率计算公式可计算出PWMC的值应设置为1999,对应的16进制为0x07CF,即PWMCH=0x07,PWMCL=0xCF。

占空比

60%。第1触发点设置为0,第2触发点设置为800。

初始电平

高电平。

中断配置

不使用中断。

  • 说明:读者可以通过修改PWM时钟和PWMC的值得到自己需要的PWM周期,通过修改触发点的值得到自己需要的占空比。

        1. 代码编写
  1. 新建一个名称为“pwm.c”的文件及其头文件“pwm.h”保存到工程的“Source”文件夹,并将“pwm.c”加入到Keil工程中的“SOURCE”组。该文件用于存放I2C硬件操作相关的函数。
  2. 引用头文件

因为在“main.c”文件中使用了“pwm.c”文件中的函数,所以需要引用下面的头文件“pwm.h”。

代码清单:引用头文件

  1. //引用PWM的头文件  
  2. #include    "pwm.h"  

  1. 配置PWM

按照功能需求表配置PWM,代码清单如下。

代码清单:配置PWM

  1. /********************************************************************************** 
  2. 功能描述:初始化PWM模块。 
  3.         :使能比较器上升沿和下降沿中断 
  4.         :使用通道6,选择P2.6为通道6的输出引脚,频率:1KHz,占空比60% 
  5. 参    数:无 
  6. 返 回 值:无 
  7. ***********************************************************************************/  
  8. void pwm_config(void)  
  9. {  
  10.     PWMSET |= 0x01;              //使能PWM模块,必须先使能后面的配置才能生效  
  11.       
  12.     /*---------------------PWMCFG----------------------------------- 
  13.     位7  位6  位5  位4    位3      位2      位1     位0    
  14.     --   --   --   --   PWMCBIF  EPWMCBI  EPWMTA  PWMCEN 
  15.     x    x    x     x      0        0        0       0 
  16.     PWMCBIF:PWM计数器归零中断标志位,需软件清零 
  17.     PEPWMCBI=0:使能PWM计数器归零中断 
  18.     EPWMTA = 0:PWM与ADC不关联 
  19.     PWMCEN = 0:PWM停止计数 
  20.     ---------------------------------------------------------------*/  
  21.     PWMCFG = 0x00;                 
  22.   
  23.     P_SW2 |= 0x80;               //将EAXFR位置1,允许访问扩展RAM区特殊功能寄存器(XFR)  
  24.     /*---------------------PWMCKS----------------------------------- 
  25.     位7  位6  位5   位4     位3~0    
  26.     --    --    --   SELT2   PWM_PS[3:0] 
  27.      1     0     x     0      1011 
  28.  
  29.     SELT2=0:PWM 时钟源为系统时钟经分频器分频之后的时钟  
  30.     PWM_PS[3:0]=1011:系统时钟的1/12,(24/12)MHz = 2MHz 
  31.     ---------------------------------------------------------------*/  
  32.     PWMCKS = 0x0B;        
  33.       
  34.     /*---------------------PWM6CR----------------------------------- 
  35.     位7  位6  位5  位4~3     位2   位1    位0 
  36.     ENO  INI  --  C0_S[1:0]  ENI  ENT2I  ENT1I 
  37.      1    0    x     00       1     0      0 
  38.      
  39.     ENO = 1:PWM的通道6相应PWM端口为 PWM 输出口 
  40.     INI = 0:PWM的通道6初始电平为低电平 
  41.     C0_S[1:0] =00:选择P2.6为PWM通道6输出脚 
  42.     ENI = 1:使能PWM通道6的PWM中断 
  43.     ENT2I = 0:关闭PWM的通道6在第2个触发点中断 
  44.     ENT1I = 0:关闭PWM的通道6在第1个触发点中断 
  45.     ---------------------------------------------------------------*/  
  46.     PWM6CR = 0xC0;    
  47.       
  48.     PWMC   = 1999;          //设置 PWM 周期为1ms
  49.     PWM6T1 = 0;             //第1触发点设置为0  
  50.     PWM6T2 = 800;           //第2触发点设置为800  
  51.     P_SW2 &= 0x7F;          //将EAXFR位置0,禁止访问XFR  
  52.       
  53.     PWMCFG |= 0x01;         //将PWMCEN位置1,使能PWM波形发生器,PWM计数器开始计数  
  1. 主函数

主函数中将P2.6配置为准双向口,之后调用pwm_config()函数配置PWM即可,代码清单如下。

代码清单:主函数

  1. /********************************************************************************** 
  2. 功能描述:主函数 
  3. 参    数:无 
  4. 返 回 值:int类型 
  5. ***********************************************************************************/  
  6. int main(void)  
  7. {  
  8.    P2M1 &= 0xBF;   P2M0 &= 0xBF;     //设置P2.6为准双向口(指示灯D1)  
  9.   
  10.    pwm_config();                     //初始化PWM  
  11.    while(1)  
  12.    {  
  13.       ;     
  14.    }  
  15. }  
        1. 硬件连接

本实验需要使用LED指示灯D1,按照下图所示短接跳线帽。

图8:跳线帽短接

        1. 实验步骤
  1. 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-15-1:PWM输出波形”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
  2. 双击“…\pwm\project”目录下的工程文件“pwm.uvproj”。
  3. 点击编译按钮编译工程,编译成功后生成的HEX文件“pwm.hex”位于工程的“…\pwm\Project\Object”目录下。
  4. 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
  5. 电脑上打开串口调试助手,选择开发板对应的串口号,将波特率设置为9600bps。
  6. 程序运行后,可观察到指示灯D1点亮,用示波器测量D1驱动引脚P2.6,可以观察到频率为1KHz(周期为1ms),占空比为60%的PWM波行。
  7. 读者可以尝试修改PWM周期和占空比,并用示波器观察波形,直观地观察修改后波形的变化。
      1. PWM实现呼吸灯
  • 注:本节的实验是在“实验2-15-1:PWM输出波形”的基础上修改,本节对应的实验源码是:“实验2-15-2:PWM实现呼吸灯”。
        1. 实验内容

本例中使用PWM驱动指示灯D1、D2实现呼吸灯。在PWM周期中,高电平时指示灯熄灭,低电平时指示灯点亮,因此,改变占空比即可改变指示灯点亮的时间,从而达到改变指示灯亮度的目的。程序中以步进值2递减/递增占空比,程序运行后可以连续地观察到指示灯光亮度逐渐增加,之后逐渐降低,即呼吸灯效果,本例的功能需求如下表所示。

表3:功能需求表

使用的PWM通道

PWM 通道6和PWM 通道7。

输出引脚

P2.6(PWM 通道6)、P2.7(PWM 通道7)。

PWM时钟

系统时钟的12分频,系统时钟使用的是24MHz,所以,PWM时钟是2MHz。

PWM频率

1KHz(周期1ms)。

由上面的PWM频率计算公式可计算出PWMC的值应设置为1999,对应的16进制为0x07CF,即PWMCH=0x07,PWMCL=0xCF。

占空比

第1触发点设置为0,第2触发点的值从1开始以步进2递增,递增到周期计数值后以步进2递减,递减到到1后,再次以步进2递增,如此反复。

初始电平

高电平。

中断配置

使用PWM归零中断,中断服务函数中修改第2触发点的值(以步进2递增/递减)。

        1. 代码编写
  1. 初始化配置PWM

按照功能需求表配置PWM,代码清单如下。

代码清单:配置PWM 

  1. /********************************************************************************** 
  2. 功能描述:初始化PWM模块。 
  3.         :使用通道6(选择P2.6为通道6的输出引脚)和通道7(选择P2.7为通道7的输出引脚) 
  4.         :PWM频率:1KHz 
  5. 参    数:无 
  6. 返 回 值:无 
  7. ***********************************************************************************/  
  8. void pwm_config(void)  
  9. {  
  10.    PWMSET |= 0x01;              //使能PWM模块,必须先使能后面的配置才能生效  
  11.       
  12.    /*------------------- PWMCFG-------------------------------------
  13.    位7  位6  位5  位4    位3      位2      位1     位0    
  14.    --   --   --   --   PWMCBIF  EPWMCBI  EPWMTA  PWMCEN 
  15.    x    x    x     x      0        1        0       0 
  16.  
  17.    PWMCBIF:PWM计数器归零中断标志位,需软件清零 
  18.    PEPWMCBI=1:使能PWM计数器归零中断 
  19.    EPWMTA = 0:PWM与ADC不关联 
  20.    PWMCEN = 0:PWM停止计数 
  21.    ---------------------------------------------------------------*/  
  22.    PWMCFG = 0x04;                 
  23.   
  24.    P_SW2 |= 0x80;               //将EAXFR位置1,允许访问扩展RAM区特殊功能寄存器(XFR)  
  25.    /*--------------------- PWMCKS---------------------------------- 
  26.    位7  位6  位5   位4     位3~0    
  27.    --   --   --   SELT2  PWM_PS[3:0] 
  28.    1    0    x     0        0 
  29.  
  30.    SELT2=0:PWM 时钟源为系统时钟经分频器分频之后的时钟  
  31.    PWM_PS[3:0]=1111:系统时钟的1/16 
  32.    ---------------------------------------------------------------*/  
  33.    PWMCKS = 0x0B;    
  34.       
  35.    /*-------------------PWM6CR------------------------------------- 
  36.    位7  位6  位5  位4~3     位2   位1    位0 
  37.    ENO  INI  --  C0_S[1:0]  ENI  ENT2I  ENT1I 
  38.    1    1    x     00       1     0      0 
  39.      
  40.    ENO = 1:PWM的通道6相应PWM端口为 PWM 输出口 
  41.    INI = 1:PWM的通道6初始电平为高电平 
  42.    C0_S[1:0] =00:选择P2.6为PWM通道6输出脚 
  43.    ENI = 0:关闭PWM通道6的PWM中断 
  44.    ENT2I = 0:关闭PWM的通道6在第2个触发点中断 
  45.    ENT1I = 0:关闭PWM的通道6在第1个触发点中断 
  46.    ---------------------------------------------------------------*/  
  47.    PWM6CR = 0xC0;  
  48.    PWM7CR = 0xC0;             //通道7配置和通道6一样  
  49.    PWMC   = CYCLE_VALUE;      //设置 PWM 周期为1ms  
  50.    PWM6T1 = 0;                //通道6第1触发点设置为0  
  51.    PWM6T2 = 0x0001;           //通道6第2触发点设置为1  
  52.    PWM7T1 = 0;                //通道7第1触发点设置为0  
  53.    PWM7T2 = 0x0001;           //通道7第2触发点设置为1  
  54.   
  55.    P_SW2 &= 0x7F;             //将EAXFR位置0,禁止访问XFR  
  56.    PWMCFG |= 0x01;            //将PWMCEN位置1,使能PWM波形发生器,PWM计数器开始计数  
  57. }
  1. PWM中断服务程序

PWM中断服务程序中修改第2触发点的值,也就是修改占空比。这里用了一个变量“dir”来标志递增和递减。第2触发点的值从1开始以步进2递增,递增到周期计数值后以步进2递减,递减到到1后,再次以步进2递增,代码清单如下。

代码清单:I2C初始化

  1. /*************************************************************************** 
  2.  * 描  述 : PWM中断服务函数 
  3.  * 入  参 : 无 
  4.  * 返回值 : 无 
  5.  **************************************************************************/  
  6. void pwm_isr() interrupt 22 using 1  
  7. {  
  8.    static bit dir = 1;  
  9.    static u16 t2_val = 0;  
  10.       
  11.    if(PWMCFG & 0x08)  
  12.    {  
  13.       PWMCFG &= ~0x08; //清归零中断标志  
  14.       if (dir)         //第2触发点的值以步进值2递增  
  15.       {  
  16.          t2_val += DUTY_CYCLE_STEP;  
  17.          if (t2_val >= CYCLE_VALUE) dir = 0;  
  18.       }  
  19.       else            //第2触发点的值以步进值2递减  
  20.       {  
  21.          t2_val -= DUTY_CYCLE_STEP;  
  22.          if (t2_val <= DUTY_CYCLE_STEP) dir = 1;  
  23.       }  
  24.       P_SW2 |= 0x80;       //将EAXSFR位置1,以访问PWM在扩展RAM区的特殊功能寄存器  
  25.       PWM6T2 = t2_val;      //更新PWM通道6第2触发点的值  
  26.       PWM7T2 = t2_val;      //更新PWM通道7第2触发点的值  
  27.       P_SW2 &= 0x7F;         //将EAXSFR位置0,恢复访问XRAM  
  28.    }  
  29.  
  1. 主函数

主函数中将P2.6、P2.7配置为准双向口,之后调用pwm_config()函数配置PWM即可,代码清单如下。

代码清单:I2C初始化

  1. /********************************************************************************** 
  2. 功能描述:主函数 
  3. 参    数:无 
  4. 返 回 值:int类型 
  5. ***********************************************************************************/  
  6. int main(void)  
  7. {  
  8.    P2M1 &= 0x3F; P2M0 &= 0x3F;   //设置P2.6、P2.7为准双向口(指示灯D1和D2)  
  9.    pwm_config();               //初始化PWM  
  10.    EA = 1;                     //使能总中断  
  11.    while(1)  
  12.    {  
  13.       ;  
  14.    }  
  15. }  
        1. 硬件连接

本实验需要使用LED指示灯D1和D2,按照下图所示短接跳线帽。

图9:跳线帽短接

        1. 实验步骤
  1. 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-15-2:PWM实现呼吸灯”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
  2. 双击“…\pwm_led\project”目录下的工程文件“pwm_led.uvproj”。
  3. 点击编译按钮编译工程,编译成功后生成的HEX文件“pwm_led.hex”位于工程的“…\pwm_led\Project\Object”目录下。
  4. 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
  5. 电脑上打开串口调试助手,选择开发板对应的串口号,将波特率设置为9600bps。
  6. 程序运行后,可连续观察到指示灯D1、D2的亮度逐步增加和降低,即呼吸灯效果。
      1. PWM驱动震动马达
  • 注:本节的实验是在“实验2-15-1:PWM输出波形”的基础上修改,本节对应的实验源码是:“实验2-15-3:PWM驱动震动马达”。
        1. 实验内容

本例中通过PWM输出不同占空比的波形控制震动马达的震动强度。例中使用PWM模块的通道6,选择P1.6为其输出引脚,用于驱动震动马达。

P1.6以3秒的间隔轮流输出40%占空比和90%占空比的PWM波形,以便于观察不同占空比时马达的震动强度,本例的功能需求如下表所示。

表4:功能需求表

使用的PWM通道

PWM 通道6。

输出引脚

P1.6(PWM 通道6)。

PWM时钟

系统时钟的12分频,系统时钟使用的是24MHz,所以,PWM时钟是2MHz。

PWM频率

1KHz(周期1ms)。

由上面的PWM频率计算公式可计算出PWMC的值应设置为1999,对应的16进制为0x07CF,即PWMCH=0x07,PWMCL=0xCF。

占空比

轮流间隔3秒使用40%占空比和90%占空比的PWM波形分别驱动震动马达,以观察不同占空比的波形对马达震动强度的影响。

初始电平

低电平,因为振动马达是高电平驱动,因此引脚初始电平输出低电平。

中断配置

不使用中断。

  • 注:本节的实验需要使用艾克姆科技的震动马达模块。

        1. 震动马达模块

艾克姆震动马达模块使用的是1027震动马达,其中10指的是马达的直径,27指的是马达的厚度,单位是mm。模块上板载了MOS管驱动电路,可直接通过单片机GPIO驱动。模块规格如下图所示。

图10:震动马达模块规格

震动马达模块的引脚如下图所示,IN脚输入高电平,马达震动,IN脚输入低电平,马达停止震动。因此,可以通过PWM输出不同的占空比的信号给IN脚,控制IN脚高电平的占比,从而控制马达的震动强度。

图11:震动马达引脚

        1. 代码编写
  1. 初始化配置PWM

按照功能需求表配置PWM,代码清单如下。

代码清单:配置PWM 

  1. /********************************************************************************** 
  2. 功能描述:初始化PWM模块。 
  3.         :使用通道6,选择P1.6为通道6的输出引脚,频率:1KHz,占空比90% 
  4. 参    数:无 
  5. 返 回 值:无 
  6. ***********************************************************************************/  
  7. void pwm_config(void)  
  8. {  
  9.    PWMSET |= 0x01;              //使能PWM模块,必须先使能后面的配置才能生效  
  10.       
  11.    /*---------------------PWMCFG----------------------------------- 
  12.    位7  位6  位5  位4    位3      位2      位1     位0    
  13.    --   --   --   --   PWMCBIF  EPWMCBI  EPWMTA  PWMCEN 
  14.    x    x    x     x      0        0        0       0 
  15.    PWMCBIF:PWM计数器归零中断标志位,需软件清零 
  16.    PEPWMCBI=0:使能PWM计数器归零中断 
  17.    EPWMTA = 0:PWM与ADC不关联 
  18.    PWMCEN = 0:PWM停止计数 
  19.    ---------------------------------------------------------------*/  
  20.    PWMCFG = 0x00;                 
  21.   
  22.    P_SW2 |= 0x80;               //将EAXFR位置1,允许访问扩展RAM区特殊功能寄存器(XFR)  
  23.    /*---------------------PWMCKS----------------------------------- 
  24.    位7  位6  位5   位4     位3~0    
  25.    --   --   --   SELT2  PWM_PS[3:0] 
  26.    1    0    x     0        1011 
  27.  
  28.    SELT2=0:PWM 时钟源为系统时钟经分频器分频之后的时钟  
  29.    PWM_PS[3:0]=1011:系统时钟的1/12,(24/12)MHz = 2MHz 
  30.    ---------------------------------------------------------------*/  
  31.    PWMCKS = 0x0B;        
  32.       
  33.    /*---------------------PWM6CR----------------------------------- 
  34.    位7  位6  位5  位4~3     位2   位1    位0 
  35.    ENO  INI  --  C0_S[1:0]  ENI  ENT2I  ENT1I 
  36.     1    0    x     01       0     0      0 
  37.      
  38.    ENO = 1:PWM的通道6相应PWM端口为 PWM 输出口 
  39.    INI = 0:PWM的通道6初始电平为低电平 
  40.    C0_S[1:0] =10:选择P1.6为PWM通道6输出脚 
  41.    ENI = 0:使能PWM通道6的PWM中断 
  42.    ENT2I = 0:关闭PWM的通道6在第2个触发点中断 
  43.    ENT1I = 0:关闭PWM的通道6在第1个触发点中断 
  44.    ---------------------------------------------------------------*/  
  45.    PWM6CR = 0x88;    
  46.       
  47.    PWMC   = 1999;          //设置 PWM 周期为 1ms  
  48.    PWM6T1 = 0;             //第1触发点设置为0  
  49.    PWM6T2 = 200;           //第2触发点设置为200,此时,占空比为90%  
  50.    P_SW2 &= 0x7F;          //将EAXFR位置0,禁止访问XFR  
  51.    PWMCFG |= 0x01;         //将PWMCEN位置1,使能PWM波形发生器,PWM计数器开始计数  
  1. 主函数

主函数中将P1.6配置为准双向口,并输出低电平(震动马达是高电平有效,初始化时关闭其震动),之后调用pwm_config()函数初始化配置PWM。主循环中以3秒的间隔轮流输出40%占空比和90%占空比的PWM波形,代码清单如下。

代码清单:主函数

  1. /********************************************************************************** 
  2. 功能描述:主函数 
  3. 参    数:无 
  4. 返 回 值:int类型 
  5. ***********************************************************************************/  
  6. int main(void)  
  7. {  
  8.    P1M1 &= 0xBF;   P1M0 &= 0xBF;     //设置P1.6为准双向口  
  9.    P16 = 0;                          //因为震动马达是高电平驱动,P16输出低电平  
  10.   
  11.    pwm_config();                     //初始化PWM  
  12.    while(1)  
  13.    {  
  14.       delay_ms(3000);        //延时3秒  
  15.       P_SW2 |= 0x80;         //将EAXFR位置1,允许访问扩展RAM区特殊功能寄存器(XFR)  
  16.       PWM6T2 = 1200;         //第2触发点设置为1200,此时,占空比为40%  
  17.       P_SW2 &= 0x7F;         //将EAXFR位置0,禁止访问XFR  
  18.       delay_ms(3000);  
  19.       P_SW2 |= 0x80;         //将EAXFR位置1,允许访问扩展RAM区特殊功能寄存器(XFR)  
  20.       PWM6T2 = 200;          //第2触发点设置为200,此时,占空比为90%  
  21.       P_SW2 &= 0x7F;         //将EAXFR位置0,禁止访问XFR  
  22.    }  
  23. }  
        1. 硬件连接

本实验需要用到艾克姆科技的振动马达模块,模块和开发板之间的连接如下图所示。

  • 注意:P1.6和矩阵按键共用,因此,震动马达模块的IN脚连接到P1.6后,不能使用矩阵按键。

图12:震动马达模块安装

        1. 实验步骤
  1. 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-15-3:PWM驱动震动马达”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
  2. 双击“…\pwm_motor\project”目录下的工程文件“pwm_motor.uvproj”。
  3. 点击编译按钮编译工程,编译成功后生成的HEX文件“pwm_motor.hex”位于工程的“…\pwm_motor\Project\Object”目录下。
  4. 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
  5. 电脑上打开串口调试助手,选择开发板对应的串口号,将波特率设置为9600bps。
  6. 程序运行后,可观察到震动马达连续3秒震动强度较大(90%占空比),接着连续3秒震动强度较小(40%占空比),如此反复。

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,以下是STC8A8K64D4单片机与JDY-31蓝牙模块进行通信的基本代码: ```c #include <STC8.H> #include <intrins.h> #define FOSC 24000000L //系统时钟频率 #define BAUD 9600 //串口波特率 #define MD0 P3_0 //定义STC8A8K64D4 P3.0引脚作为蓝牙模块的模式选择引脚 bit busy; //串口忙标志位 unsigned char RcvBuff; //串口接收缓冲区 void InitUART() //初始化串口 { SCON = 0x50; //8位数据,可变波特率 TMOD &= 0x0F; //清除原来的定时器设置 TMOD |= 0x20; //设定定时器1为8位自动重装方式 TH1 = TL1 = -(FOSC/12/32/BAUD);//设定波特率 TR1 = 1; //启动定时器1 ES = 1; //串口中断允许 EA = 1; //总中断允许 } void UART_SendByte(unsigned char dat) //串口发送一个字符 { while(busy); //等待前面的数据发送完成 busy = 1; SBUF = dat; //写数据到UART数据寄存器 } void main() { InitUART(); //初始化串口 while(1) { MD0 = 0; //将P3.0引脚置为低电平,使蓝牙模块进入AT指令模式 UART_SendByte('A'); //向蓝牙模块发送AT指令 MD0 = 1; //将P3.0引脚置为高电平,使蓝牙模块退出AT指令模式 } } void UART_Isr() interrupt 4 using 1 //串口中断服务程序 { if(RI) { RI = 0; RcvBuff = SBUF; } if(TI) { TI = 0; busy = 0; } } ``` 以上代码实现了串口初始化、发送一个字符的函数以及主函数中对蓝牙模块的AT指令操作。其中,P3.0引脚作为蓝牙模块的模式选择引脚,当该引脚为低电平时,蓝牙模块进入AT指令模式;当该引脚为高电平时,蓝牙模块退出AT指令模式。此外,还需要注意将STC8A8K64D4单片机的串口接口连接到JDY-31蓝牙模块的TX和RX引脚上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电子友人张

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值