九齐单片机2路PWM控制输出实现指南

详细介绍如何在九齐单片机上实现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

六、调试技巧

  1. 示波器检查

    • 使用示波器检查PWM输出波形
    • 验证频率和占空比是否符合预期
    • 检查上升/下降时间
  2. 常见问题解决

    问题现象 可能原因 解决方案
    无输出 引脚配置错误 检查IOSTA和PIOACON设置
    PWM未使能 确认PWMxCON.EN=1
    输出被禁用 确认PWMxCON.OE=1
    频率偏差 时钟源错误 检查CLKMD设置
    分频系数错误 验证CKS设置
    占空比异常 寄存器值超限 确保duty ≤ PRD
    数据类型错误 使用uint16_t存储duty值
  3. 低功耗优化

    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控制实现相对简单,主要步骤包括:

  1. 配置I/O引脚为PWM输出
  2. 设置PWM周期寄存器(PRD)
  3. 设置PWM占空比寄存器(DUTY)
  4. 配置控制寄存器(CON)使能PWM
  5. 在应用中动态更新占空比
相关推荐
电气_空空2 小时前
基于 LabVIEW 串口通信的研究
单片机·嵌入式硬件·毕业设计·labview
电气_空空3 小时前
基于 LabVIEW 的 PID 控制系统设计与实现
嵌入式硬件·毕业设计·labview
weixin_456808383 小时前
【沁恒蓝牙开发】关闭独立看门狗
c语言·单片机·嵌入式硬件
weixin_456808383 小时前
【沁恒蓝牙开发】拓展广播 Code PHY-从机
c语言·嵌入式硬件
LCG元3 小时前
STM32实战:基于DHT11的智能温湿度监测与OLED显示
stm32·单片机·嵌入式硬件
qq_411262423 小时前
ESP32-C3 内置 USB Serial/JTAG 在 Windows下,不同板子不同端口
stm32·单片机·嵌入式硬件
最好有梦想~3 小时前
嵌入式Linux Lua使用ZeroBrane远程调试
linux·嵌入式硬件·lua
aini_lovee4 小时前
基于STM32的光电感烟火灾报警器设计
stm32·单片机·嵌入式硬件
拒朽4 小时前
51单片机学习(六)模块化编程和LCD调试工具
嵌入式硬件·学习·51单片机