九齐单片机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. 在应用中动态更新占空比
相关推荐
东京老树根13 小时前
Arduino - 入门02 - Arduino编程基础 Arduino程序结构,实物与模拟器对照,Arduino常用函数
单片机·机器人
salipopl15 小时前
基于STM32平台的多旋翼无人机系统设计与实现
stm32·嵌入式硬件·无人机
iCxhust1 天前
8086/8088单板机VSCode集成自动下载功能(完善串口接收显示版)
ide·vscode·单片机·编辑器·微机原理·8088单板机·8086单板机
振南的单片机世界1 天前
从数码管到点阵屏:动态扫描加595,3个IO驱动256个LED
stm32·单片机·嵌入式硬件
listhi5201 天前
STC8 16通道模拟采集 + 4路串口 + 8路PWM 程序
stm32·单片机·嵌入式硬件
星夜夏空991 天前
STM32单片机学习(4)——嵌入式概述
stm32·单片机·学习
Deitymoon1 天前
STM32——OLED显示字符串
单片机·嵌入式硬件
Graceful_scenery1 天前
龙芯2k0300 - 走马观碑组按键驱动移植
单片机·嵌入式硬件
d111111111d1 天前
MQTT+STM32+ESP8266网络程序分层+韦老师
笔记·stm32·单片机·嵌入式硬件·学习·php
长安第一美人1 天前
RT-Thread 工业屏驱动开发实战:UART 串口屏协议解析 + 数据实时刷新 + 设备驱动框架完整实现
驱动开发·嵌入式硬件·rt-thread·工业控制·uart通信·串口屏驱动