知不足而奋进 望远山而前行
前言
学习pwm(脉宽调制)是探索数字信号转模拟信号的重要一步,尤其在控制电路输出方面具有广泛的应用。本次学习将聚焦于了解pwm的基础概念、掌握在stc8h芯片上配置pwma的方法以及应用实践。通过深入学习pwm技术,您将能够控制各种设备的亮度、速度等参数,从而在工程和电子领域中做出更多创新。
目标
- 了解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
- 导入头文件,初始化宏及全局变量
#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) - 1) // 周期
pwmx_duty dutya;
- 配置gpio
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; //周期时间, 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函数
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);
}
}
pwm配置详解
周期
系统主频:1秒钟计数多少次。
代码中的pwm周期(pwm period),指的是按n等份切分1秒钟,每个等份的计数值。
例如上图,我们按照8等份切分1秒钟的总计数值main_fosc
(主频),每个pwm周期的计数值为:
pwm_period = main_fosc / 8 = 24m / 8 = 3m = 3 000 000
单位为次。
即如果将这个3m
作为period
参数,可以得到pwm方波每个周期的时长为:
1 / 8 = 0.125s
代码中的配置:
#define period (main_fosc / freq) // 周期
pwmx_initstructure.pwm_period = period - 1;
配置的是周期中的计数值。
我们的理解策略:通常我们不关心计数值,关心的是1秒钟执行多少次(即频率hz),也就是一秒钟多少个周期。
因此在代码main_fosc / 1000
中的1000
表示的是1秒钟多少个周期(即频率hz)。
main_fosc / 1000
表示的是每个周期的计数值。那为什么要-1
呢?因为计数器是从0开始计数的。
占空比
在一个pwm的周期计数中,高电平的计数时长百分比。
模式
- 冻结: 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)》
- 9.2.8 《扩展 sfr 使能寄存器 eaxfr 的使用说明》
总结
通过本次学习,您已经了解了pwm的基本概念和工作原理,掌握了在stc8h上配置pwma的步骤以及pwm的各种配置参数。此外,您还学会了如何利用pwm控制led的亮度,并掌握了调试pwm的方法
发表评论