K230 平台 PWM 驱动移植与实现详解
一、K230 上的 PWM 控制器资源概览
在 01studio K230 平台上,PWM 控制器被设计为灵活且高性能的外设模块。根据官方手册描述,k230上有两个pwm控制器, 每个 PWM 控制器包含 4 个比较器通道(pwmcmp0 ~ pwmcmp3) ,它们的功能如下:
因此,虽然每个控制器有 4 个比较器,但 实际可以输出 3 路独立的 PWM 波形 。
K230 通常包含两个 PWM 控制器(比如 PWM0 和 PWM1),每个控制器最多可支持 3 个 PWM 输出通道 ,共 6
路输出, 适用于电机控制、舵机、LED 灯调光等场景 。
这些 PWM 控制器通过 APB 总线连接,并受系统时钟控制模块(SYSCTL)管理。其基本地址如下:
#define PWM0_BASE_ADDR PWM_BASE_ADDR
#define PWM1_BASE_ADDR PWM_BASE_ADDR + 0x40 // 第二组 PWM 控制器偏移
二、PWM 驱动的整体结构设计
我们基于 RT-Thread 的设备驱动模型,实现了对 PWM 控制器的完整封装。每个控制器对应一个 struct k230_pwm_dev 结构体,包含设备名称、寄存器基地址和设备句柄:
struct k230_pwm_dev
{
struct rt_device_pwm device;
const char *name;
rt_ubase_t base;
};
在驱动初始化阶段,设备会通过 rt_device_pwm_register 注册为 RT-Thread 的标准 PWM 设备。
三、驱动核心功能模块讲解
1. 驱动注册与地址映射
我们在 rt_hw_pwm_init() 函数中完成了多个 PWM 控制器的注册,并通过 ioremap 映射物理地址:
dev->base = (rt_ubase_t)rt_ioremap((void
*)(dev->base), sizeof(kd_pwm_t));
ret =
rt_device_pwm_register(&dev->device, dev->name, &drv_ops, (void
*)dev->base);
注册后,用户可以通过 pwm_set, pwm_enable 等标准 API 使用这些设备。
2. PWM 输出参数设置逻辑(PWM_CMD_SET)
我们允许用户以纳秒为单位设置周期和占空比。在 kd_pwm_set() 函数中,我们首先将这两个时间值转换为 具体的 PWM 计数值 :
pwm_pclock =
sysctl_clk_get_leaf_freq(SYSCTL_CLK_PWM_APB_GATE);
pulse = (uint64_t)configuration->pulse *
pwm_pclock / NSEC_PER_SEC;
period = (uint64_t)configuration->period
* pwm_pclock / NSEC_PER_SEC;
由于 PWM 寄存器为 16 位宽,我们需要根据实际周期值, 自动选择一个合适的 scale 缩放因子 ,确保不会溢出:
while ((period >> pwmscale) > ((1
<< PWM_CMP_WIDTH) - 1))
pwmscale++;
最后将周期和占空比写入寄存器:
reg->pwmcmp0 = (period >>
pwmscale);
*((®->pwmcmp1) + channel) =
reg->pwmcmp0 - (pulse >> pwmscale);
这段写法中,(®->pwmcmp1) + channel 的偏移技巧很巧妙地实现了对 pwmcmp1/2/3 三个通道的选择。
3. PWM 启动与停止(PWM_CMD_ENABLE
/ DISABLE)
我们通过控制 PWM_CFG 寄存器中的 INVERT 位来控制输出:
reg->pwmcfg |= PWM_CFG_BIT_INVERT; // 启动 PWM
reg->pwmcfg &=
~PWM_CFG_BIT_INVERT; // 停止 PWM
同时默认开启 DEGLITCH(去毛刺)以确保波形稳定:
reg->pwmcfg |= PWM_CFG_DEGLITCH;
4. PWM 配置获取(PWM_CMD_GET)
该函数负责将当前的周期/占空比状态返回给用户,便于调试或状态同步:
period = reg->pwmcmp0 * NSEC_PER_SEC /
pwm_pclock;
pulse = *((®->pwmcmp1) + channel)
* NSEC_PER_SEC / pwm_pclock;
configuration->period = period;
configuration->pulse = pulse;
四、代码中的设计细节
五、驱动测试:基于 PWM 控制LED 亮度
为了验证驱动功能是否正常,我进行了一个简单的测试:K230通过GPIO52连接了一个板载LED灯,使用
PWM1的第二通道复用到GPIO52上,配置PWM1的第二通道,观察其亮度随占空比变化而变化。
测试思路
测试代码
#define PWM_DEV_NAME "pwm1"
#define PWM_DEV_CHANNEL 1
#define TEST_GPIO_LED 52
void pwm_demo(void)
{
/* 配置 GPIO52 为 PWM 功能 */
k230_pinctrl_set_function(TEST_GPIO_LED, IOMUX_FUNC3);
k230_pinctrl_set_oe(TEST_GPIO_LED, 1);
k230_pinctrl_set_ie(TEST_GPIO_LED, 0);
k230_pinctrl_set_pu(TEST_GPIO_LED, 0);
k230_pinctrl_set_pd(TEST_GPIO_LED, 0);
k230_pinctrl_set_drv(TEST_GPIO_LED, 4);
/* 查找 PWM 设备 */
struct rt_device_pwm *pwm_dev = (struct rt_device_pwm
*)rt_device_find(PWM_DEV_NAME);
uassert_not_null(pwm_dev);
rt_uint32_t period = 100000; //
100us = 10kHz
/* 逐步增加占空比:10% → 100% */
for(rt_uint32_t pulse = 10000; pulse <= 100000; pulse += 10000)
{
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
sleep(2); // 等待 2 秒观察亮度变化
}
/* 停止 PWM 输出 */
rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL);
}
该测试通过不断增加脉冲宽度(pulse),模拟 PWM 占空比变化,从而实现 LED 明暗变化的控制。测试运行效果符合预期,说明驱动实现功能完整,控制精度良好。
六,总结
本次在 K230 平台上成功移植了
PWM 驱动,并通过简单的 LED 控制测试验证了驱动的基本功能。从驱动层来看,核心工作在于正确配置寄存器和对接 RT-Thread 的设备框架;而在测试中,则通过 GPIO 映射和占空比调整,验证了 PWM 的输出控制能力。
更多回帖