1. 松瀚SN8F5703比较器PWM开发实战
最近接手一个消费电子项目,需要用到松瀚SN8F5703这款51内核单片机生成精确的PWM信号。虽然这款MCU已面市十余年,但在小家电、玩具等低成本领域仍有广泛应用。实际开发时发现官方例程缺少T2比较器输出PWM的参考代码,网上资料也寥寥无几。经过两天调试,终于实现了10kHz频率、0-100%可调占空比的PWM输出,现将完整实现过程分享给大家。
特别说明:本文所有代码基于Keil C51 V9.52开发环境验证,使用更高版本可能遇到头文件冲突问题。建议开发前先检查C51工具链的INT文件是否唯一。
2. 硬件基础与时钟配置
2.1 MCU时钟树解析
SN8F5703采用32MHz主频,通过CLKSEL寄存器可进行分频配置。我们需要先理解其时钟架构:
- 主时钟路径:32MHz → 预分频器 → CPU时钟
- 定时器时钟:CPU时钟 → 定时器分频器 → 定时器时钟源
- 外设时钟:部分外设可独立选择时钟源
关键寄存器配置如下:
c复制CLKSEL = 0x06; // Fcpu = 32M/2 = 16M
CLKCMD = 0x69; // 时钟控制字
CKCON = 0x10; // 时钟源选择
2.2 定时器时钟计算
T2定时器支持1/12或1/24分频,我们选择后者以获得更精细的PWM分辨率:
code复制T2时钟 = Fcpu / 24 = 16MHz / 24 ≈ 666.67kHz
单周期时间 = 1/666.67kHz ≈ 1.5μs
3. PWM输出实现详解
3.1 硬件引脚配置
使用P06作为PWM输出引脚,P25作为调试信号:
c复制P0M = 0x40; // P06推挽输出
P2M = 0x20; // P25推挽输出
3.2 定时器2核心配置
定时器2的比较器模式是PWM生成的关键,主要涉及三个寄存器组:
-
T2CON寄存器(0xD1):
- Bit7=1:选择1/24分频
- Bit6=1:使能CRC匹配中断
- Bit4-3=10:自动重装载模式
-
CCEN寄存器(0x80):
- Bit7-6=10:COM3比较器输出模式
-
中断使能寄存器:
c复制IEN1 |= 0xA0; // 使能T2重载和COM3中断 IEN0 |= 0xA0; // 开启总中断和T2中断
3.3 关键参数计算
实现10kHz PWM需要计算以下参数:
-
周期计算:
code复制周期 = 1/10kHz = 100μs 所需计数 = 100μs / 1.5μs ≈ 66.67 → 取整66 -
重载值计算:
code复制重载值 = 0xFFFF - 66 + 1 = 0xFFBE CRCH = 0xFF, CRCL = 0xBE -
占空比转换公式:
c复制compare_offset = (duty / 100.0f) * PERIOD_COUNTS; target_compare = RELOAD_VALUE + compare_offset;
4. 完整代码实现
4.1 初始化函数
c复制void InitT2(void) {
TH2 = 0x00; TL2 = 0x00;
CRCH = 0xFF; CRCL = 0xBE;
CCH3 = 0xFF; CCL3 = 0xFF;
T2CON = 0xD1; // 定时器配置
CCEN = 0x80; // 比较器输出使能
IEN1 |= 0xA0; // 中断使能
IEN0 |= 0xA0;
}
4.2 占空比设置函数
c复制void T2C3PWMSet(float duty) {
if(PWMResetFlag) {
PWMResetFlag = 0;
duty = (duty < 0.0f) ? 0.0f : (duty > 100.0f) ? 100.0f : duty;
uint16_t target = RELOAD_VALUE + (uint16_t)((duty/100)*66 +0.5);
CCL3 = target & 0xFF;
CCH3 = target >> 8;
PWMResetFlag = 1;
}
}
4.3 中断服务程序
c复制void T2COM3Interrupt(void) interrupt ISRCom4 {
P25 = ~P25; // 调试信号翻转
}
5. 调试技巧与问题排查
5.1 常见问题解决方案
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 无PWM输出 | 引脚模式配置错误 | 检查P0M寄存器配置 |
| 频率偏差大 | 时钟分频计算错误 | 重新校验CLKSEL配置 |
| 占空比不准 | 数值溢出 | 检查target_compare范围限制 |
5.2 示波器调试要点
- 建议先验证P25调试信号,确保基础定时正确
- 测量P06波形时注意硬件电路可能存在的反向问题
- 改变占空比后等待至少3个周期再测量
5.3 性能优化建议
- 若要提高PWM频率,可减小PERIOD_COUNTS值
- 需要更高分辨率时,可降低T2分频系数(改为1/12)
- 关键代码段禁用中断以保证时序精度
6. 扩展应用思路
6.1 多路PWM输出
通过配置CCEN寄存器的其他位,可同时启用COM0-COM2实现多路PWM:
c复制CCEN = 0xE0; // 同时启用COM3和COM2
6.2 动态频率调整
修改重载值可实现运行时频率调整:
c复制void SetPWMFreq(uint16_t freq) {
uint16_t counts = (uint16_t)(1.0/(freq*1.5e-6));
CRCH = (0xFFFF - counts + 1) >> 8;
CRCL = (0xFFFF - counts + 1) & 0xFF;
}
经过实际验证,这套方案在电机控制、LED调光等场景下工作稳定。特别提醒:松瀚单片机的IO驱动能力有限,驱动大电流负载时建议增加外部驱动电路。