STC8 简单 PWM 输出使用指南
- 更新时间:20250213
本指南以 STC8H1K16 为例,介绍 PWM(脉宽调制)功能的基本使用方法,适合新手快速上手 PWM 配置。
1. STC8 的 PWM 基本概念
STC8 系列单片机内部集成了 8 通道 16 位高级 PWM 定时器,分成两组周期不同的 PWM:
- PWMA
- PWMB
每组 PWM 都可以独立配置不同的周期和占空比。
2. PWM 基本代码示例
以下示例展示了如何配置 PWMA 进行 PWM 输出,PWM 频率基于 11.0592MHz 主频。
#include "stc8h.h"
#include "intrins.h"
void main()
{
P_SW2 |= 0x80; // 使能访问 XFR
// 设置所有 GPIO 口为准双向模式(避免引脚受限)
P0M0 = P0M1 = 0x00;
P1M0 = P1M1 = 0x00;
P2M0 = P2M1 = 0x00;
P3M0 = P3M1 = 0x00;
P4M0 = P4M1 = 0x00;
P5M0 = P5M1 = 0x00;
PWMA_CCER1 = 0x00; // 关闭通道,写 CCMRx 前必须清零 CCERx
PWMA_CCMR1 = 0x60; // 设置 CC1 为 PWMA 输出模式
PWMA_CCER1 = 0x01; // 使能 CC1 通道
PWMA_CCR1 = 100; // 设置占空比时间
PWMA_ARR = 500; // 设置 PWM 周期时间
PWMA_ENO = 0x01; // 使能 PWM1P 端口输出
PWMA_BKR = 0x80; // 使能主输出
PWMA_CR1 = 0x01; // 启动 PWM 计时
while (1);
}
关键说明
- P_SW2 |= 0x80; —— 必须使能 XFR 访问,否则 PWM 配置无效,且程序不会报错。
- CCR/ARR = 有效电平的占空比
- ARR(自动重装寄存器) 是两个 8 位寄存器合成的 16 位寄存器,设置时需避免溢出。
3. PWM 频率计算公式
PWM 计数器的频率计算如下:
[
f_{CK_CNT} = \frac{f_{CK_PSC}}{PSCR[15:0] + 1}
]
其中:
- fCK_PSC:预分频时钟
- PSCR[15:0]:预分频系数
可选时钟源
- 内部时钟(fMASTER)
- 外部时钟模式 1(TIx 作为输入)
- 外部时钟模式 2(外部触发输入 ETR)
- 内部触发输入(ITRx)(使用一个 PWM 的 TRGO 作为另一个 PWM 的预分频时钟)
一般情况下,使用 内部时钟,若不进行预分频,则 PWM 频率同步于内部时钟。
4. PWM 极性调整
PWMB_CCER2 寄存器可调整 PWM 输出极性:
- CCxP = 0:高电平有效
- CCxP = 1:低电平有效
计算有效占空比
[
\text{有效电平占空比} = \frac{CCR}{ARR}
]
例如:
- 计算结果为 20%:
- 若 CCxP = 0,则 高电平占空比 = 20%
- 若 CCxP = 1,则 低电平占空比 = 20%
5. 实时更改 PWM 占空比
在 PWM 输出过程中,可以直接修改 CCR 寄存器的值来 实时调整 PWM 占空比,无需其他额外操作。
6. PWM 端口复用
PWMA_PS 寄存器用于 PWM 输出端口选择,请参考芯片手册对应的复用表。
7. 实战示例:按键控制 PWM 占空比和极性
- 该示例通过按键控制 PWM 占空比,并可切换 PWM 极性。
- 运行在20MHz频率。
代码
#include "STC8H.h"
// PWM 相关配置
#define PWM_MIN_DUTY 20 // 最小占空比 20%
#define PWM_MAX_DUTY 100 // 最大占空比 100%
#define PWM_INIT_DUTY 100 // 初始占空比 100%
#define PWM_ARR 10000 // PWM 频率相关参数
int pwm_duty = PWM_INIT_DUTY; // 当前 PWM 占空比
bit pwm_inverted = 0; // PWM 反相标志
// 延时函数
void Delay_ms(unsigned int ms)
{
unsigned int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 120; j++);
}
// 初始化 PWM
void PWM_Init()
{
PWMB_CCER2 = 0x00; // 关闭通道
PWMB_CCMR3 = 0x68; // 设置 PWM 输出模式
PWMB_CCER2 = 0x01; // 使能 CC7 通道
PWMB_CCR7 = PWM_ARR; // 设置占空比时间
PWMB_ARR = PWM_ARR; // 设置周期时间
PWMB_ENO = 0x10; // 使能 PWM 输出
PWMB_PS = 0x20; // PWM 通道7输出到 P0.2
PWMB_BKR = 0x80; // 使能主输出
PWMB_CR1 = 0x01; // 允许计数器
}
// 更新 PWM 占空比
void Update_PWM()
{
unsigned long temp = (unsigned long)pwm_duty * PWM_ARR; // 防止计算时溢出
PWMB_CCR7 = temp / 100; // 更新 PWM 输出
PWMB_CCER2 = pwm_inverted ? 0x03 : 0x01; // 设置 PWM 反相
}
// 主循环
void main()
{
PWM_Init(); // 初始化 PWM
while (1)
{
if (P10 == 0) // 占空比增加
{
while (P10 == 0);
if (pwm_duty < PWM_MAX_DUTY)
{
pwm_duty += 10;
if (pwm_duty > PWM_MAX_DUTY) pwm_duty = PWM_MAX_DUTY;
Update_PWM();
}
}
if (P11 == 0) // 占空比减少
{
while (P11 == 0);
if (pwm_duty > PWM_MIN_DUTY)
{
pwm_duty -= 10;
if (pwm_duty < PWM_MIN_DUTY) pwm_duty = PWM_MIN_DUTY;
Update_PWM();
}
}
if (P12 == 0) // 反相 PWM
{
while (P12 == 0);
pwm_inverted = !pwm_inverted;
Update_PWM();
}
Delay_ms(20); // 按键去抖
}
}
if (P12 == 0) // 反相 PWM
{
while (P12 == 0);
pwm_inverted = !pwm_inverted;
Update_PWM();
}
Delay_ms(20); // 按键去抖
}
}