详细介绍如何在九齐单片机上实现2路PWM控制输出,包括硬件配置、寄存器设置和代码实现。
一、九齐单片机PWM模块概述
九齐单片机(如NY8A/B系列)通常内置1-2个PWM模块,具有以下特点:
- 8位或10位分辨率
- 可编程频率和占空比
- 可配置输出引脚
- 支持互补输出和死区控制(部分型号)
- 与定时器模块共享资源
二、硬件配置
1. 典型应用电路
c
+Vcc (3.3V/5V)
│
├─[电阻]─┬─ PWM0输出引脚(如PA0)
│ └─ 负载(LED/电机)
│
├─[电阻]─┬─ PWM1输出引脚(如PA1)
│ └─ 负载(LED/电机)
│
└─ 九齐单片机
├─ Vss(地)
├─ XTAL(晶振)
└─ 其他外围电路
2. 引脚分配示例(NY8A051D)
| PWM通道 | 默认引脚 | 可重映射引脚 |
|---|---|---|
| PWM0 | PA0 | PA2, PB0 |
| PWM1 | PA1 | PA3, PB1 |
三、寄存器配置
1. 关键寄存器
| 寄存器名 | 功能描述 | 地址(示例) |
|---|---|---|
| PWM0CON | PWM0控制寄存器 | 0x0B |
| PWM0DUTY | PWM0占空比寄存器 | 0x0C |
| PWM0PRD | PWM0周期寄存器 | 0x0D |
| PWM1CON | PWM1控制寄存器 | 0x0E |
| PWM1DUTY | PWM1占空比寄存器 | 0x0F |
| PWM1PRD | PWM1周期寄存器 | 0x10 |
| PIOA/B | 端口A/B数据寄存器 | 0x00/0x02 |
| PIOACON/B | 端口A/B控制寄存器 | 0x08/0x09 |
2. PWM控制寄存器位定义(PWMxCON)
| 位 | 名称 | 功能描述 |
|---|---|---|
| 7 | EN | PWM使能位(1=使能, 0=禁用) |
| 6 | OE | 输出使能(1=输出, 0=高阻) |
| 5 | POL | 极性选择(1=反相, 0=同相) |
| 4-0 | CKS | 时钟预分频选择(见下表) |
3. 时钟预分频选择(CKS)
| CKS[4:0] | 分频系数 | 说明 |
|---|---|---|
| 00000 | 1 | 不分频 |
| 00001 | 2 | ÷2 |
| 00010 | 4 | ÷4 |
| 00011 | 8 | ÷8 |
| 00100 | 16 | ÷16 |
| ... | ... | ... |
| 11111 | 64 | ÷64 |
四、代码实现
1. 完整代码示例(NY8A051D, 2路PWM)
c
#include <ny8.h>
#include "ny8_constant.h"
// 系统时钟定义(Hz)
#define SYSCLK 4000000L
// PWM周期计算(单位:系统时钟周期)
// 目标频率1kHz: 周期 = SYSCLK / 1000 = 4000
#define PWM_PERIOD 4000
// 函数声明
void SystemInit(void);
void PWM0_Init(void);
void PWM1_Init(void);
void Update_PWM_Duty(uint8_t ch, uint16_t duty);
void main(void)
{
SystemInit();
PWM0_Init();
PWM1_Init();
// 初始占空比
Update_PWM_Duty(0, 1000); // PWM0: 25% (1000/4000)
Update_PWM_Duty(1, 2000); // PWM1: 50% (2000/4000)
while(1)
{
// 主循环中可以动态改变占空比
// 例如: 呼吸灯效果
for(uint16_t i=0; i<PWM_PERIOD; i++)
{
Update_PWM_Duty(0, i);
Update_PWM_Duty(1, PWM_PERIOD - i);
_delay_ms(1);
}
for(uint16_t i=PWM_PERIOD; i>0; i--)
{
Update_PWM_Duty(0, i);
Update_PWM_Duty(1, PWM_PERIOD - i);
_delay_ms(1);
}
}
}
// 系统初始化
void SystemInit(void)
{
// 设置系统时钟(4MHz)
DISI();
CLKMD = 0x00; // 内部RC振荡器
CLKMDbits.IE = 0;
INTE = 0;
PCON = 0;
DISI();
// 设置I/O为输出
IOSTA = 0x03; // PA0(0), PA1(1)为输出
PORTA = 0x00; // 初始低电平
}
// PWM0初始化
void PWM0_Init(void)
{
// 设置PWM0引脚(PA0)为输出
IOSTA |= 0x01; // PA0输出
// 配置PWM0
PWM0PRD = PWM_PERIOD - 1; // 周期值(0-based)
PWM0DUTY = 0; // 初始占空比0
// 控制寄存器: 使能PWM, 输出使能, 同相, 不分频
PWM0CON = (1<<7) | (1<<6) | (0<<5) | 0x00;
}
// PWM1初始化
void PWM1_Init(void)
{
// 设置PWM1引脚(PA1)为输出
IOSTA |= 0x02; // PA1输出
// 配置PWM1
PWM1PRD = PWM_PERIOD - 1; // 周期值(0-based)
PWM1DUTY = 0; // 初始占空比0
// 控制寄存器: 使能PWM, 输出使能, 同相, 不分频
PWM1CON = (1<<7) | (1<<6) | (0<<5) | 0x00;
}
// 更新PWM占空比
// ch: 0=PWM0, 1=PWM1
// duty: 0-PWM_PERIOD之间的占空比值
void Update_PWM_Duty(uint8_t ch, uint16_t duty)
{
if(duty > PWM_PERIOD) duty = PWM_PERIOD;
if(ch == 0)
{
PWM0DUTY = duty;
}
else if(ch == 1)
{
PWM1DUTY = duty;
}
}
2. 关键函数说明
PWM初始化函数
c
void PWM0_Init(void)
{
IOSTA |= 0x01; // 设置PA0为输出
PWM0PRD = 3999; // 周期=4000 (0-3999)
PWM0DUTY = 0; // 初始占空比0
PWM0CON = 0b11000000; // 使能+输出使能+同相+不分频
}
占空比更新函数
c
void Set_PWM_Duty(uint8_t channel, uint16_t duty)
{
if(duty > PWM_PERIOD) duty = PWM_PERIOD;
switch(channel)
{
case 0: PWM0DUTY = duty; break;
case 1: PWM1DUTY = duty; break;
}
}
呼吸灯效果实现
c
void Breathing_LED_Effect(void)
{
for(int i=0; i<PWM_PERIOD; i++)
{
Set_PWM_Duty(0, i); // PWM0渐亮
Set_PWM_Duty(1, PWM_PERIOD-i); // PWM1渐暗
Delay_ms(2);
}
for(int i=PWM_PERIOD; i>0; i--)
{
Set_PWM_Duty(0, i); // PWM0渐暗
Set_PWM_Duty(1, PWM_PERIOD-i); // PWM1渐亮
Delay_ms(2);
}
}
五、应用场景
1. LED调光控制
c
// 设置LED亮度(0-100%)
void Set_LED_Brightness(uint8_t percent)
{
uint16_t duty = (uint16_t)((PWM_PERIOD * percent) / 100);
Set_PWM_Duty(0, duty);
}
2. 直流电机控制
c
// 控制电机速度和方向
void Motor_Control(int8_t speed)
{
if(speed > 0) {
// 正转
Set_PWM_Duty(0, speed * 40); // 0-100%映射到0-4000
Set_PWM_Duty(1, 0);
}
else if(speed < 0) {
// 反转
Set_PWM_Duty(0, 0);
Set_PWM_Duty(1, (-speed) * 40);
}
else {
// 停止
Set_PWM_Duty(0, 0);
Set_PWM_Duty(1, 0);
}
}
3. 蜂鸣器音调控制
c
// 播放指定频率声音
void Buzzer_Beep(uint16_t freq, uint16_t duration_ms)
{
// 计算周期值: SYSCLK / (freq * 2)
uint16_t period = SYSCLK / (freq * 2);
PWM0PRD = period - 1;
Set_PWM_Duty(0, period / 2); // 50%占空比
Delay_ms(duration_ms);
// 关闭蜂鸣器
Set_PWM_Duty(0, 0);
}
参考代码 九齐单片机2路PWM控制输出 www.youwenfan.com/contentcss/183127.html
六、调试技巧
-
示波器检查
- 使用示波器检查PWM输出波形
- 验证频率和占空比是否符合预期
- 检查上升/下降时间
-
常见问题解决
问题现象 可能原因 解决方案 无输出 引脚配置错误 检查IOSTA和PIOACON设置 PWM未使能 确认PWMxCON.EN=1 输出被禁用 确认PWMxCON.OE=1 频率偏差 时钟源错误 检查CLKMD设置 分频系数错误 验证CKS设置 占空比异常 寄存器值超限 确保duty ≤ PRD 数据类型错误 使用uint16_t存储duty值 -
低功耗优化
c// 进入空闲模式 void Enter_Idle_Mode(void) { PCONbits.IDLE = 1; // 进入空闲模式 NOP(); NOP(); } // 在PWM配置中使用低功耗模式 PWM0CON |= (1<<3); // 启用低功耗模式(如果支持)
七、高级应用
1. 互补输出与死区控制
c
// 配置互补PWM输出(需要支持此功能的型号)
void Complementary_PWM_Init(void)
{
// 设置PWM0和PWM1为互补输出
PWM0CON |= (1<<4); // 使能互补模式
PWM0CON |= (1<<2); // 使能死区
PWM0DBC = 20; // 死区时间(时钟周期数)
}
2. PWM捕获功能
c
// 配置PWM输入捕获
void PWM_Capture_Init(void)
{
// 设置PA2为PWM输入捕获
IOSTA &= ~0x04; // PA2输入
IC0CON = 0b10000100; // 上升沿捕获, 使能捕获
// 使能捕获中断
INTE |= 0x01; // 使能IC0中断
_emi = 1; // 全局中断使能
}
// 捕获中断服务函数
void ISR_IC0(void) interrupt
{
static uint16_t rise_time, fall_time;
if(IC0CONbits.EDGE) {
// 上升沿捕获
rise_time = IC0BUF;
} else {
// 下降沿捕获
fall_time = IC0BUF;
uint16_t pulse_width = fall_time - rise_time;
// 处理脉冲宽度...
}
IC0CONbits.EDGE ^= 1; // 切换边沿
IC0CONbits.IF = 0; // 清除中断标志
}
八、不同型号配置差异
| 型号 | PWM通道 | 分辨率 | 特殊功能 | 推荐应用 |
|---|---|---|---|---|
| NY8A050D | 1路 | 8位 | 基本PWM | LED调光 |
| NY8A051D | 2路 | 8位 | 独立控制 | 双电机控制 |
| NY8B062A | 2路 | 10位 | 互补输出+死区 | 半桥驱动 |
| NY8B072E | 3路 | 10位 | 中心对齐模式 | 三相电机控制 |
九、总结
九齐单片机的2路PWM控制实现相对简单,主要步骤包括:
- 配置I/O引脚为PWM输出
- 设置PWM周期寄存器(PRD)
- 设置PWM占空比寄存器(DUTY)
- 配置控制寄存器(CON)使能PWM
- 在应用中动态更新占空比