ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RT-Thread PWM 设备驱动详解:从占空比原理到 rt_pwm_set/Enable/Disable 实战开发

RT-Thread PWM 设备驱动详解:从占空比原理到 rt_pwm_set/Enable/Disable 实战开发 操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载PWM脉宽调制是嵌入式系统中应用最广泛的信号输出方式之一。本篇技术指南以 RT-Thread 官方设备驱动文档为骨架系统讲解 PWM 的工作原理、RT-Thread PWM 设备管理框架的四类核心接口查找设备、设置周期与脉宽、使能、关闭、FinSH 调试命令以及完整的 LED 呼吸灯实战示例并深入仓库源码PWM 框架实现、设备头文件 与 STM32 底层驱动揭示其底层调用链帮助读者在任意 RT-Thread BSP 上快速驱动 PWM 外设。PWM 基本原理与典型应用场景PWMPulse Width Modulation脉冲宽度调制是一种用数字方式对模拟信号电平进行编码的方法通过不同频率的脉冲序列来编码特定模拟信号的幅值输出端收到一系列等幅脉冲用这些脉冲去驱动负载从而在负载上等效出期望的波形。上图是一个简单的 PWM 原理示意图。假设定时器工作在向上计数模式当计数值小于阈值时输出一种电平状态例如高电平当计数值大于阈值时输出相反状态例如低电平当计数值到达最大值后计数器从 0 重新计数并回到原始电平状态。高电平持续时间脉宽与周期的比值即占空比取值范围为 0100%。上图中高电平恰好占整个周期的一半因此占空比为 50%。一个常见的 PWM 控制场景是调节灯或屏幕的亮度。亮度通过改变占空比来调整PWM 并不是连续地调整光强而是不断地让灯或屏幕“开—关”切换。当开关频率足够快时人眼会认为它始终是亮的。在开关切换过程中灯灭的时间越长肉眼看到的屏幕亮度越低反之灯亮的时间越长屏幕看起来越亮。RT-Thread PWM 设备访问接口总览应用程序通过 RT-Thread 提供的 PWM 设备管理接口访问 PWM 设备硬件相关接口如下函数描述rt_device_find()根据 PWM 设备名称查找设备句柄rt_pwm_set()设置 PWM 周期和脉冲宽度rt_pwm_enable()使能 PWM 设备rt_pwm_disable()关闭 PWM 设备除此之外PWM 设备头文件 还提供了rt_pwm_set_period()仅设置周期、rt_pwm_set_pulse()仅设置脉宽、rt_pwm_set_dead_time()设置死区时间、rt_pwm_set_phase()设置相位和rt_pwm_get()读取配置等扩展接口用于电机控制等高级场景。查找 PWM 设备应用程序根据 PWM 设备名称获取设备句柄进而操作 PWM 设备函数原型如下rt_device_t rt_device_find(const char* name);参数描述name设备名称返回——设备句柄找到对应设备时返回对应的设备句柄RT_NULL未找到设备通常注册到系统中的 PWM 设备名称为 pwm0、pwm1 等具体名称由各 BSP 的驱动定义例如 STM32 系列会按PWMx的命名规则注册。用法示例如下#define PWM_DEV_NAME pwm3 /* PWM 设备名称 */ struct rt_device_pwm *pwm_dev; /* PWM 设备句柄 */ /* 查找设备 */ pwm_dev (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);从源码看rt_device_find()返回的是通用rt_device_t句柄而 PWM 操作接口需要struct rt_device_pwm *类型因此示例中做了显式类型转换。struct rt_device_pwm在 dev_pwm.h 中定义包含struct rt_device parent继承自通用设备对象和const struct rt_pwm_ops *ops底层操作集两个成员。设置 PWM 周期和脉冲宽度通过以下函数设置 PWM 周期和占空比rt_err_t rt_pwm_set(struct rt_device_pwm *device, int channel, rt_uint32_t period, rt_uint32_t pulse);参数描述devicePWM 设备句柄channelPWM 通道periodPWM 周期nspulsePWM 脉冲宽度时间ns返回——RT_EOK成功-RT_EIO设备为空-RT_ENOSYS设备操作方法为空其他错误执行失败PWM 的输出频率由周期决定。例如一个周期时间为 0.5ms毫秒则周期值应填 500000ns纳秒输出频率为 2KHz占空比 pulse / period且 pulse 值不能超过 period。需要注意的是rt_pwm_set()只负责下发配置之后还需要调用rt_pwm_enable()才能真正输出波形。用法示例如下#define PWM_DEV_NAME pwm3 /* PWM 设备名称 */ #define PWM_DEV_CHANNEL 4 /* PWM 通道 */ struct rt_device_pwm *pwm_dev; /* PWM 设备句柄 */ rt_uint32_t period, pulse; period 500000; /* 周期为 0.5ms单位为纳秒 */ pulse 0; /* PWM 脉冲宽度值单位为纳秒 */ /* 查找设备 */ pwm_dev (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); /* 设置 PWM 周期和脉冲宽度 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);使能 PWM 设备设置完 PWM 周期和脉冲宽度后通过以下函数使能 PWM 设备rt_err_t rt_pwm_enable(struct rt_device_pwm *device, int channel);参数描述devicePWM 设备句柄channelPWM 通道返回——RT_EOK使能设备成功-RT_ENOSYS设备操作方法为空其他错误使能设备失败用法示例如下#define PWM_DEV_NAME pwm3 /* PWM 设备名称 */ #define PWM_DEV_CHANNEL 4 /* PWM 通道 */ struct rt_device_pwm *pwm_dev; /* PWM 设备句柄 */ rt_uint32_t period, pulse; period 500000; /* 周期为 0.5ms单位为纳秒 */ pulse 0; /* PWM 脉冲宽度值单位为纳秒 */ /* 查找设备 */ pwm_dev (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); /* 设置 PWM 周期和脉冲宽度 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); /* 使能设备 */ rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);关闭 PWM 设备通道使用以下函数关闭 PWM 设备对应通道rt_err_t rt_pwm_disable(struct rt_device_pwm *device, int channel);参数描述devicePWM 设备句柄channelPWM 通道返回——RT_EOK关闭设备成功-RT_EIO设备句柄为空其他错误关闭设备失败用法示例如下#define PWM_DEV_NAME pwm3 /* PWM 设备名称 */ #define PWM_DEV_CHANNEL 4 /* PWM 通道 */ struct rt_device_pwm *pwm_dev; /* PWM 设备句柄 */ rt_uint32_t period, pulse; period 500000; /* 周期为 0.5ms单位为纳秒 */ pulse 0; /* PWM 脉冲宽度值单位为纳秒 */ /* 查找设备 */ pwm_dev (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); /* 设置 PWM 周期和脉冲宽度 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); /* 使能设备 */ rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL); /* 关闭设备通道 */ rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL);框架层实现原理rt_pwm_set 的底层调用链rt_pwm_set()、rt_pwm_enable()、rt_pwm_disable()是 RT-Thread PWM 设备框架rt_drv_pwm.c对外提供的统一接口其内部并不直接操作寄存器而是把参数封装进struct rt_pwm_configuration再通过通用设备控制接口rt_device_control()下发到驱动层。配置结构体与命令字dev_pwm.h 中定义了核心配置结构体struct rt_pwm_configuration { rt_uint32_t channel; /* 0 ~ n 或 0 ~ -n取决于具体 MCU 需求 */ rt_uint32_t period; /* 单位:ns1ns~4.29s:1Ghz~0.23hz */ rt_uint32_t pulse; /* 单位:nspulseperiod */ rt_uint32_t dead_time; /* 单位:ns */ rt_uint32_t phase; /* 单位:度0~360PWM 输出相位 */ /* * RT_TRUE : PWM 通道为互补输出 * RT_FALSE : PWM 通道为普通输出 */ rt_bool_t complementary; };period的合法取值范围为 1ns~4.29s对应 1GHz~0.23Hzpulse必须小于等于period。dead_time、phase、complementary是 2022 年后新增的扩展字段用于电机控制中的死区插入、移相与互补输出。框架层用宏定义了 12 个控制命令字dev_pwm.hPWM_CMD_ENABLE、PWM_CMD_DISABLE、PWM_CMD_SET、PWM_CMD_GET、PWMN_CMD_ENABLE/DISABLE互补输出、PWM_CMD_SET_PERIOD、PWM_CMD_SET_PULSE、PWM_CMD_SET_DEAD_TIME、PWM_CMD_SET_PHASE、PWM_CMD_ENABLE_IRQ/DISABLE_IRQ。所有命令字均基于RT_DEVICE_CTRL_BASE(PWM)派生确保设备类型区分。通道正负数约定普通输出与互补输出查看 rt_pwm_enable() 实现 可以发现一个关键的通道约定通道为正数0~n表示使用普通输出引脚通道为负数0~-n表示使用互补输出引脚PWMN。rt_pwm_enable()会先根据 channel 的符号下发PWMN_CMD_DISABLE普通或PWMN_CMD_ENABLE互补再统一下发PWM_CMD_ENABLE。rt_pwm_set()则在 内部 将 channel 归一化为正数后填入配置结构体仅下发PWM_CMD_SET。在 FinSH 中启用互补通道的典型写法是pwm enable -1见下文命令说明与底层约定一一对应。底层驱动如何响应以 STM32 为例不同芯片的底层驱动只需实现一个control回调即可挂载到框架。以 STM32 PWM 驱动 为例static struct rt_pwm_ops drv_ops { drv_pwm_control };驱动初始化时在stm32_pwm_init()中根据BSP_USING_PWMx_CHy之类的宏配置启用对应通道然后逐个调用rt_device_pwm_register()注册设备rt_device_pwm_register(stm32_pwm_obj[i].pwm_device, stm32_pwm_obj[i].name, drv_ops, stm32_pwm_obj[i].tim_handle);drv_pwm_enable()内部把框架的通道号转换为 HAL 库通道号0x04 * (configuration-channel - 1)并根据complementary标志选择调用HAL_TIM_PWM_Start/Stop还是HAL_TIMEx_PWMN_Start/Stop。从源码结构可以推断只要实现struct rt_pwm_ops中的control回调任何平台的 PWM 定时器都可以接入 RT-Thread PWM 框架这也是rt_pwm_set()等接口在所有 BSP 上行为一致的原因。FinSH 调试命令RT-Thread 的 FinSH 控制台提供了 PWM 的在线调试命令方便在无代码改动的情况下验证硬件输出。框架层在 rt_drv_pwm.c 中以pwm为关键字导出了一组二级命令pwm probe/enable/disable/get/set/phase/dead_time使用前需先用pwm probe选定设备。设置周期与占空比设置 PWM 设备某个通道的周期和占空比使用命令pwm_set pwm1 1 500000 5000。各参数含义第一个参数是命令名第二个参数是 PWM 设备名称第三个参数是 PWM 通道第四个参数是 PWM 周期ns第五个参数是脉冲宽度ns。msh /pwm_set pwm1 1 500000 5000 msh /对应框架层的pwm set命令参数顺序为 channel、period、pulse例如pwm set 1 500000 5000。使能通道使能 PWM 设备某个通道使用命令pwm_enable pwm1 1。第一个参数是命令名第二个参数是 PWM 设备名称第三个参数是 PWM 通道。msh /pwm_enable pwm1 1 msh /对应框架层的pwm enable channel当 channel 为负数时如pwm enable -1表示使能互补输出通道。关闭通道关闭 PWM 设备某个通道使用命令pwm_disable pwm1 1。第一个参数是命令名第二个参数是 PWM 设备名称第三个参数是 PWM 通道。msh /pwm_disable pwm1 1 msh /框架层pwm命令还支持pwm get channel回读周期、脉宽并打印实时占空比以及pwm phase channel phase、pwm dead_time channel dead_time用于高级配置可直接在终端查看命令用法输入pwm回车即可看到 Usage 列表。完整示例PWM 驱动 LED 呼吸灯下面的示例代码是一个完整的 PWM 设备使用样例核心步骤为查找 PWM 设备获取设备句柄设置 PWM 周期和脉冲宽度使能 PWM 设备在 while 循环中每 50 毫秒修改一次脉冲宽度将 PWM 通道连接到 LED可以看到 LED 由暗逐渐变亮再由亮逐渐变暗。/* * 程序清单PWM 设备使用示例 * 该例程向控制终端导出 pwm_led_sample 命令 * 命令格式pwm_led_sample * 程序功能通过 PWM 设备控制 LED 灯的亮度可以看到 LED 由暗逐渐变亮再由亮逐渐变暗。 */ #include rtthread.h #include rtdevice.h #define PWM_DEV_NAME pwm3 /* PWM 设备名称 */ #define PWM_DEV_CHANNEL 4 /* PWM 通道 */ struct rt_device_pwm *pwm_dev; /* PWM 设备句柄 */ static int pwm_led_sample(int argc, char *argv[]) { rt_uint32_t period, pulse, dir; period 500000; /* 周期为 0.5ms单位为纳秒 */ dir 1; /* PWM 脉冲宽度值的增减方向 */ pulse 0; /* PWM 脉冲宽度值单位为纳秒 */ /* 查找设备 */ pwm_dev (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); if (pwm_dev RT_NULL) { rt_kprintf(pwm sample run failed! cant find %s device!\n, PWM_DEV_NAME); return -RT_ERROR; } /* 设置 PWM 周期和脉冲宽度默认值 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); /* 使能设备 */ rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL); while (1) { rt_thread_mdelay(50); if (dir) { pulse 5000; /* 从 0 值开始每次增加 5000ns */ } else { pulse - 5000; /* 从最大值开始每次减少 5000ns */ } if (pulse period) { dir 0; } if (0 pulse) { dir 1; } /* 设置 PWM 周期和脉冲宽度 */ rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); } } /* 导出到 msh 命令列表 */ MSH_CMD_EXPORT(pwm_led_sample, pwm sample);编译烧录后在 FinSH 控制台执行pwm_led_sample即可观察呼吸灯效果。该示例同样被收录在 PWM 设备头文件的 Doxygen 示例 中作为官方 API 文档的一部分。仓库中的其他 PWM 消费方与测试验证PWM 框架不仅服务于应用层也被 RT-Thread 多个组件复用可作为二次开发的参考实现LED 组件驱动 led-pwm.c通过 PWM 调节 LED 亮度背光驱动 backlight-pwm.c屏幕背光亮度控制温控风扇 thermal-cool-pwm-fan.c根据温度调节风扇转速稳压器 regulator-pwm.c电压调节。在测试验证方面PWM 单元测试用例 实现了“频率与占空比矩阵测试”将period 1e9 / frequency、pulse period * duty / 10000的换算逻辑固化为测试点支持固定输出、单次扫描、循环扫描三种输出模式并通过rt_pwm_get()按容差回读校验周期与脉宽覆盖rt_pwm_set、rt_pwm_enable、rt_pwm_disable的完整调用链。其默认配置pwm_tc_config.h中给出了从 500Hz 到 10KHz、占空比从 10% 到 75% 的波形点表可直接作为评估 PWM 驱动精度的测试依据。小结RT-Thread 的 PWM 设备框架通过“应用层标准接口 驱动层单一 control 回调”的分层设计屏蔽了不同芯片定时器差异。开发者只需掌握rt_device_find()rt_pwm_set()rt_pwm_enable()/rt_pwm_disable()四个接口配合 FinSH 的pwm_set、pwm_enable、pwm_disable命令即可完成从调试验证到应用落地的全流程。本文涉及的周期/脉宽换算周期 0.5ms ↔ 500000ns ↔ 2KHz占空比 pulse/period以及正数普通输出、负数互补输出的通道约定是移植与排障时最容易踩坑、也最需要牢记的关键点。赞分享操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载相关推荐RT-Thread ADC 设备驱动使用指南从模数转换原理到 rt_adc_read 实战采样RT Thread ADC 设备驱动使用指南从模数转换原理到 rt_adc_read 实战采样 导读 ADC模拟数字转换器是嵌入式系统中最常用的外设之一操作系统嵌入式物联网嵌入式OSRTOSRT-Thread 冷却设备Cooling Devices驱动详解PWM 风扇、GPIO 风扇与 DVFS 降温RT Thread 冷却设备Cooling Devices驱动详解PWM 风扇、GPIO 风扇与 DVFS 降温 本文以 RT Thread 仓库中 co操作系统嵌入式物联网嵌入式OSRTOSRT-Thread NUCLEO-F767ZI 开发板 BSP 详解从快速上手到外设驱动配置实战RT Thread NUCLEO F767ZI 开发板 BSP 详解从快速上手到外设驱动配置实战 导读 本文以 bsp/stm32/stm32f767 st操作系统嵌入式物联网嵌入式OSRTOS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表