一、PWM简介

PWM(脉宽调制,Pulse Width Modulation) 是一种通过调节数字脉冲信号的占空比来等效模拟输出电平的技术。它在数字系统(如单片机、树莓派)中广泛用于高效控制模拟电路,例如调节电机速度、LED亮度或电源电压等。

  • 占空比(Duty Cycle): 指在一个PWM信号周期内,高电平持续时间占整个周期时间的百分比。

    • 公式: 占空比 = (高电平时间 / 周期时间) × 100%

    • 示例: 50%占空比表示高电平和低电平持续时间各占周期的一半。

二、树莓派PWM输出实践

1. 基础PWM输出

  1. 创建程序文件:
    使用 nano 编辑器创建C语言源代码文件:

    nano pwm_basic.c
  2. 编写程序代码 (pwm_basic.c):

    #include <stdio.h>
    #include <stdlib.h> // 添加 exit 函数声明
    #include <wiringPi.h>
    #include <softPwm.h>
    
    #define PWM_PIN 29 // 使用 WiringPi 引脚编号 29 (对应 BCM GPIO 21)
    
    int main()
    {
        printf("树莓派 PWM 输出基础实验\n");
    
        // 初始化 wiringPi 库
        if (wiringPiSetup() == -1) {
            printf("初始化 wiringPi 失败!\n");
            exit(1); // 退出程序
        }
    
        // 创建软件 PWM
        // 参数: 引脚, 初始值, PWM范围 (Range)
        // 频率 = 100Hz (默认) / Range
        // 当前设置: Range=100 -> 频率 = 100Hz
        softPwmCreate(PWM_PIN, 0, 100);
    
        // 设置 PWM 输出占空比
        // 占空比 = value / Range * 100%
        // value=50, Range=100 -> 占空比 = 50%
        softPwmWrite(PWM_PIN, 50);
    
        printf("PWM 输出已启动 (引脚 %d, 50%% 占空比)...\n", PWM_PIN);
        printf("按 Ctrl+C 退出程序。\n");
    
        // 主循环,保持程序运行
        while (1) {
            delay(1000); // 延时1秒,避免CPU空转占用过高
        }
    
        return 0; // 实际不会执行到这里
    }

    代码说明:

    • 使用 softPwmCreate(PWM_PIN, 0, 100) 初始化引脚为软件PWM输出。100 是 PWM 范围 (pwmRange)。

    • PWM 频率计算: 软件PWM的默认基础频率约为100Hz。实际输出频率 = 100Hz / pwmRange

      • pwmRange=100 -> 频率 ≈ 100Hz / 100 = 1Hz

      • pwmRange=50 -> 频率 ≈ 100Hz / 50 = 2Hz

      • pwmRange=2 -> 频率 ≈ 100Hz / 2 = 50Hz

    • softPwmWrite(PWM_PIN, 50) 设置输出值。占空比 = value / pwmRange * 100%。这里 value=50pwmRange=100, 占空比为50%。

    • 添加了 delay(1000); 在 while(1) 循环中,避免程序空转消耗过多CPU资源。

    • 添加了退出提示信息。

  3. 保存文件:

    • 在 nano 中,按 Ctrl + O 写入文件,回车确认文件名。

    • 按 Ctrl + X 退出 nano

  4. 编译程序:
    打开终端,进入程序文件所在目录,使用以下命令编译:

    gcc pwm_basic.c -o pwm_basic -lwiringPi
    • gcc: C语言编译器

    • pwm_basic.c: 源代码文件

    • -o pwm_basic: 指定输出可执行文件名为 pwm_basic

    • -lwiringPi: 链接 wiringPi 库

  5. 运行程序:

    sudo ./pwm_basic  # 通常需要 root 权限访问 GPIO
  6. 测试验证:

    • 示波器: 最直接的方法是使用示波器探头连接到树莓派的 GPIO 21 (对应 WiringPi 29) 引脚和 GND 引脚,观察输出的PWM波形(周期应接近1s,占空比50%)。

    • LED: 将一个LED(带合适限流电阻,如220Ω-1kΩ)正极接GPIO 21,负极接GND。LED应呈现约50%的亮度(肉眼感知可能接近全亮,但用手机慢动作拍摄可观察到闪烁)。改变 softPwmWrite 的值(0-100)可调节亮度。

    • 舵机验证: 见下一节。

2. 应用:舵机控制 (SG90)

舵机(如常见的SG90 180°型号)通常使用周期为 20ms (50Hz)、高电平宽度在 0.5ms 到 2.5ms 之间的PWM信号来控制角度。

  • 角度与控制脉冲宽度关系:

    • 0.5ms -> 0°

    • 1.0ms -> 45°

    • 1.5ms -> 90°

    • 2.0ms -> 135°

    • 2.5ms -> 180°

  1. 硬件连接:

    • 将舵机的 信号线(通常是橙色或黄色) 连接到树莓派的 GPIO 21 (WiringPi 29) 引脚。

    • 将舵机的 电源线(红色) 连接到 5V 引脚。

    • 将舵机的 地线(棕色或黑色) 连接到 GND 引脚。

    注意: 如果控制多个舵机或大功率舵机,建议使用外部电源供电,并将外部电源地与树莓派GND相连。

  2. 修改程序 (pwm_servo.c):

    #include <stdio.h>
    #include <wiringPi.h>
    #include <softPwm.h>
    
    #define PWM_PIN 29 // 使用 WiringPi 引脚编号 29 (对应 BCM GPIO 21)
    
    int main() {
        printf("树莓派 PWM 舵机控制实验\n");
    
        // 初始化 wiringPi 库
        if (wiringPiSetup() == -1) {
            printf("初始化 wiringPi 失败!\n");
            return 1;
        }
    
        // 创建软件 PWM 用于舵机
        // 目标周期: 20ms (50Hz)
        // 计算 PWM Range: Range ≈ 100Hz(基础) / 目标频率(50Hz) = 2
        // 设置 Range = 200 可以获得更精细的角度控制
        softPwmCreate(PWM_PIN, 0, 200); // Range=200, 频率≈100Hz/200=0.5Hz? 需要修正理解
    
        // 修正: 软件PWM频率计算公式更正
        // 实际 softPwm 底层使用 100μs 的时间片。
        // 周期时间 (us) = Range * 100μs
        // 目标周期 20000us (20ms) = Range * 100us
        // => Range = 20000us / 100us = 200
        // 因此: softPwmCreate(PWM_PIN, 0, 200); 设置后,周期=20ms (50Hz) 正确。
        // 此时 value 范围 0-200, 对应高电平时间 = value * 100μs
    
        int angle = 0; // 初始角度设为 0°
    
        while (1) {
            // 将角度 (0°-180°) 转换为 PWM 值 (控制高电平时间)
            // 高电平时间范围: 500us (0°) - 2500us (180°)
            // 高电平时间 (us) = 500 + (angle * 2000 / 180) = 500 + (angle * 100 / 9)
            // 因为 value 代表 100μs 的个数: value = 高电平时间(us) / 100
            // => value = (500 + (angle * 100 / 9)) / 100 = 5 + (angle / 9)
            // 更直观的写法:
            int pulseWidthUs = 500 + angle * (2000 / 180); // 计算所需高电平时间(us)
            int value = pulseWidthUs / 100;                // 计算对应的PWM value (100us为单位)
    
            // 或者直接使用等效公式: value = 5 + (angle * 20) / 180; // 即 5 + angle/9
            value = 5 + (angle * 20) / 180; // 结果相同
    
            // 输出 PWM 信号控制舵机
            softPwmWrite(PWM_PIN, value);
    
            printf("当前角度: %d° (Value=%d)\n", angle, value); // 打印角度和对应值
    
            angle += 10;       // 角度增加 10°
            if (angle > 180) {
                angle = 0;     // 到达 180° 后归零
            }
    
            delay(1000);      // 延时 1 秒
        }
    
        return 0;
    }

    代码说明:

    • 核心公式: 控制舵机角度的关键在于生成对应宽度的高电平脉冲。

      • 所需高电平时间 (pulseWidthUs) = 500 + angle * (2000 / 180) 微秒 (μs)。简化计算:500 + angle * 100 / 9

      • 由于 softPwm 底层以 100μs 为单位,value = pulseWidthUs / 100

      • 合并公式:value = 5 + angle / 9。代码中使用了整数运算友好的 value = 5 + (angle * 20) / 180 (等价于 5 + angle/9)。

    • PWM设置 (softPwmCreate):

      • 设置 Range = 200。这是为了匹配舵机需要的 20ms (20000μs) 周期

      • 底层机制:softPwm 使用 100μs 时间片。周期 = Range * 100μs

      • 目标周期 20000μs = Range * 100μs => Range = 200。设置正确。

      • value 范围 0-200,对应高电平时间 0-20000μs。舵机只识别 500-2500μs 部分。

    • 角度变化步长改为 10° 并每秒移动一次,演示效果更明显。

    • 输出信息包含当前角度和计算出的 value 值,便于调试。

  3. 编译与运行:

    gcc pwm_servo.c -o pwm_servo -lwiringPi
    sudo ./pwm_servo
  4. 观察现象:
    舵机会从 0° 开始,每秒钟转动 10°,到达 180° 后跳回 0° 重新开始转动。

三、总结

本文详细介绍了如何在树莓派4B上利用 wiringPi 库的 softPwm 功能实现 PWM 信号输出。从 PWM 的基础原理讲起,通过基础 LED 亮度控制示例演示了 PWM 的生成方法,并重点讲解了如何使用 PWM 精确控制 SG90 舵机的角度。关键点在于理解 PWM 周期(20ms) 和 高电平脉冲宽度(0.5ms-2.5ms) 与舵机角度的对应关系,并通过计算将其转换为 softPwmWrite 函数所需的 value 值。

提供的示例代码可以直接用于基础实验和舵机控制,读者可以根据需要修改引脚、角度范围和运动模式。掌握 PWM 技术是树莓派进行电机控制、LED调光等交互应用的重要基础。

温馨提示:

  • 操作 GPIO 通常需要 sudo 权限。

  • 连接舵机时务必注意电源匹配,避免树莓派 5V 电源过载。

  • softPwm 是软件模拟实现,其精度和实时性有一定限制。对精度和实时性要求极高的应用(如多路精确同步)可考虑使用树莓派硬件 PWM 引脚(BCM GPIO 12, 13, 18, 19),但这需要不同的库和配置。

希望这篇博客能帮助你顺利在树莓派上实现 PWM 控制!如有任何疑问或发现文中疏漏,欢迎在评论区留言讨论。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐