学习目标
- 了解PWM基础概念和工作原理
- 学习如何在STC8H上配置PWMA
- 掌握PWMA的各个配置
- 学习如何使用PWMA控制LED亮度
- 掌握调试PWM的方法
学习内容
PWM基础概念
PWM全称是脉宽调制(Pulse Width Modulation),是一种通过改变信号的脉冲宽度来控制电路输出的技术。PWM技术在工业自动化、电机控制、LED调光等领域广泛应用。
PWM是一种将数字信号转换为模拟信号的技术,它通过改变信号的占空比来控制输出的电平。在STC8H中,PWM输出的频率和占空比可以由程序控制,因此可以用来控制各种电机、灯光和其他设备的亮度、速度等参数。STC8H芯片
STC8H 系列的单片机内部集成了8 通道 16 位高级PWM 定时器,分成两周期可不同的 PWM,分别命名为 PWMA 和PWMB ,可分别单独设置。
第一组 PWMA 可配置成4 组互补/对称/死区控制的PWM 或捕捉外部信号。
第二组 PWMB 可配置成4 路PWM 输出或捕捉外部信号。
两组 PWM 的时钟频率可分别独立设置。
PWM与引脚对应关系如下图:
PWM | PWM通道 | 对应引脚 | |
---|---|---|---|
PWMxP | PWMxN | ||
PWMA | PWM1P & PWM1N | P1.0 | P1.1 |
P2.0 | P2.1 | ||
PWM2P & PWM2N | P5.4 | P1.3 | |
P2.2 | P2.3 | ||
PWM3P & PWM3N | P1.4 | P1.5 | |
P2.4 | P2.5 | ||
PWM4P & PWM4N | P1.6 | P1.7 | |
P2.6 | P2.7 | ||
P3.4 | P3.3 | ||
PWMB | PWM5 | P0.0 | |
P1.7 | |||
P2.0 | |||
PWM6 | P0.1 | ||
P2.1 | |||
P5.4 | |||
PWM7 | P0.2 | ||
P2.2 | |||
P3.3 | |||
PWM8 | P0.3 | ||
P2.3 | |||
P3.4 |
PWMA应用
控制引脚P2.7实现LED灯1的呼吸效果。
- 拷贝所需库文件(其他必备库请自行准备)
STC8H_PWM.c``STC8H_PWM.h
NVIC.c``NVIC.h
Switch.h
- 导入头文件,初始化宏及全局变量
```c
include “Config.h”
include “GPIO.h”
include “Delay.h”
include “NVIC.h”
include “Switch.h”
include “STC8H_PWM.h”
define LED_SW P45
define LED1 P27
define LED2 P26
define LED3 P15
define FREQ 1000
define PERIOD (MAIN_Fosc / FREQ) // 周期
PWMx_Duty dutyA;
3. 配置GPIO
```c
void GPIO_config(void) {
GPIO_InitTypeDef GPIO_InitStructure; //结构定义
// LED_SW
GPIO_InitStructure.Pin = GPIO_Pin_5; //指定要初始化的IO,
GPIO_InitStructure.Mode = GPIO_OUT_PP; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(GPIO_P4, &GPIO_InitStructure);//初始化
// P2
GPIO_InitStructure.Pin = GPIO_Pin_6 | GPIO_Pin_7; //指定要初始化的IO,
GPIO_InitStructure.Mode = GPIO_PullUp; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(GPIO_P2, &GPIO_InitStructure);//初始化
}
配置PWM
void PWM_config(void)
{
PWMx_InitDefine PWMx_InitStructure;
// 配置PWM4
PWMx_InitStructure.PWM_Mode = CCMRn_PWM_MODE2; //模式, CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2
PWMx_InitStructure.PWM_Duty = 0; //PWM占空比时间, 0~Period
PWMx_InitStructure.PWM_EnoSelect = ENO4P | ENO4N; //输出通道选择, ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8P
PWM_Configuration(PWM4, &PWMx_InitStructure);
// 配置PWMA
PWMx_InitStructure.PWM_Period = PERIOD - 1; //周期时间, 0~65535
PWMx_InitStructure.PWM_DeadTime = 0; //死区发生器设置, 0~255
PWMx_InitStructure.PWM_MainOutEnable= ENABLE; //主输出使能, ENABLE,DISABLE
PWMx_InitStructure.PWM_CEN_Enable = ENABLE; //使能计数器, ENABLE,DISABLE
PWM_Configuration(PWMA, &PWMx_InitStructure); //初始化PWM通用寄存器, PWMA,PWMB
// 切换PWM4选择PWM4_SW_P26_P27
PWM4_SW(PWM4_SW_P26_P27); //PWM4_SW_P16_P17,PWM4_SW_P26_P27,PWM4_SW_P66_P67,PWM4_SW_P34_P33
// 初始化PWMA的中断
NVIC_PWM_Init(PWMA,DISABLE,Priority_0);
}
编写Main函数 ```c
void main() { char direction = 1; u8 duty_percent = 0;// 0 -> 100
EAXSFR(); /* 扩展寄存器访问使能, 必写! */
GPIO_config();
PWM_config();
EA = 1;
// 总开关
LED_SW = 0;
LED1 = 0; // P2.7 PWM4
LED2 = 0;
LED3 = 0;
// 循环之前,设置一次pwm(可选)
dutyA.PWM4_Duty = PERIOD * duty_percent / 100;
UpdatePwm(PWM4, &dutyA);
// 0 -> 100
while(1) {
duty_percent += direction;
// 让duty_percent一直在0-100来回往返
if(duty_percent >= 100) {
duty_percent = 100;
direction = -1;
} else if(duty_percent <= 0) {
duty_percent = 0;
direction = 1;
}
// 修改PWM4的duty
dutyA.PWM4_Duty = PERIOD * duty_percent / 100;
UpdatePwm(PWM4, &dutyA);
delay_ms(10);
}
}
<a name="SF5Zj"></a>
### PWM配置详解
<a name="XxFAe"></a>
#### 周期
系统主频:1秒钟计数多少次。<br />代码中的PWM周期(PWM Period),指的是按N等份切分1秒钟,每个等份的计数值。<br />![55.png](https://cdn.nlark.com/yuque/0/2023/png/21441195/1679389225162-842d3b09-27f5-474b-a07b-3a4d4310461a.png#averageHue=%23f7f4f4&clientId=u604ca304-275c-4&from=ui&id=u48a40661&originHeight=471&originWidth=2396&originalType=binary&ratio=1&rotation=0&showTitle=false&size=44177&status=done&style=none&taskId=u4d054ca3-0a2e-4c65-89fa-121e61cb967&title=)<br />例如上图,我们按照8等份切分1秒钟的总计数值`MAIN_Fosc`(主频),每个PWM周期的计数值为:<br />`PWM_Period = MAIN_Fosc / 8 = 24M / 8 = 3M = 3 000 000` 单位为次。<br />即如果将这个`3M`作为`Period`参数,可以得到PWM方波每个周期的时长为:<br />`1 / 8 = 0.125s`
代码中的配置:
```c
#define PERIOD (MAIN_Fosc / FREQ) // 周期
PWMx_InitStructure.PWM_Period = PERIOD - 1;
配置的是周期中的计数值。
我们的理解策略:通常我们不关心计数值,关心的是1秒钟执行多少次(即频率Hz),也就是一秒钟多少个周期。
因此在代码MAIN_Fosc / 1000
中的1000
表示的是1秒钟多少个周期(即频率Hz)。MAIN_Fosc / 1000
表示的是每个周期的计数值。那为什么要-1
呢?因为计数器是从0开始计数的。
占空比
模式
- 冻结: CCMRn_FREEZE
- 匹配时设置通道 n 的输出为有效电平: CCMRn_MATCH_VALID
- 匹配时设置通道 n 的输出为无效电平: CCMRn_MATCH_INVALID
- 翻转: CCMRn_ROLLOVER
- 强制为无效电平: CCMRn_FORCE_INVALID
- 强制为有效电平: CCMRn_FORCE_VALID
- PWM 模式 1: CCMRn_PWM_MODE1
- PWM 模式 2: CCMRn_PWM_MODE2
常用的为PWM 模式 1``PWM 模式 2
PWM 模式 1和PWM 模式 2是反向的,一个占空比越大越亮,一个是越小越亮。
使能PWM
PWMx_InitStructure.PWM_MainOutEnable= ENABLE; //主输出使能, ENABLE,DISABLE
PWMx_InitStructure.PWM_CEN_Enable = ENABLE; //使能计数器, ENABLE,DISABLE
PWM_Configuration(PWMA, &PWMx_InitStructure); //初始化PWM通用寄存器, PWMA,PWMB
引脚配置
PWM4_SW(PWM4_SW_P26_P27);
使能配置成功后,pwm才能工作。
如果运行中pwm想停止掉,也可以通过配置使能来停止。
EAXSFR扩展寄存器
由于PWM的配置相关特殊功能寄存器位于扩展RAM区域,访问这些寄存器,需先将P_SW2的BIT7设置为1,才可正常读写。
EAXSFR(); /* 扩展寄存器访问使能 */
详细可参见STC8手册:
- 3.1.2 外设端口切换控制寄存器 2(P_SW2)
- 8.2.8 扩展 SFR 使能寄存器 EAXFR 的使用说明
练习题
- 使用PWMA实现8路呼吸灯
- 串口控制灯的明暗程度