单片机/MCU论坛
直播中

jf_16874806

1年用户 16经验值
擅长:可编程逻辑 嵌入式技术
私信 关注
[文章]

【嘉楠堪智K230开发板试用体验】基于RT-THREAD上的PWM驱动开发

K230 平台 PWM 驱动移植与实现详解

一、K230 上的 PWM 控制器资源概览

在 01studio K230 平台上,PWM 控制器被设计为灵活且高性能的外设模块。根据官方手册描述,k230上有两个pwm控制器, 每个 PWM 控制器包含 4 个比较器通道(pwmcmp0 ~ pwmcmp3) ,它们的功能如下:

  • pwmcmp0:仅用于设定 PWM 的周期(Period),不直接输出 PWM 信号
  • pwmcmp1 ~ pwmcmp3:分别用于控制 PWM 通道的占空比(Duty),是实际的 PWM 输出通道

因此,虽然每个控制器有 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);

*((&reg->pwmcmp1) + channel) =
reg->pwmcmp0 - (pulse >> pwmscale);

这段写法中,(&reg->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 = *((&reg->pwmcmp1) + channel)

* NSEC_PER_SEC / pwm_pclock;

configuration->period = period;

configuration->pulse = pulse;

四、代码中的设计细节

  • 通道合法性判断 :check_channel() 防止非法通道访问
  • 寄存器结构封装清晰 :使用 kd_pwm_t 映射寄存器,提升可读性和可维护性
  • 逻辑通道映射简洁 :实际通道为 pwmcmp13,但逻辑编号为 channel 02,使用 (&reg->pwmcmp1) + channel 简洁访问
  • 兼容多实例 :支持多组 PWM 控制器注册,扩展性良好

五、驱动测试:基于 PWM 控制LED 亮度

为了验证驱动功能是否正常,我进行了一个简单的测试:K230通过GPIO52连接了一个板载LED灯,使用
PWM1的第二通道复用到GPIO52上,配置PWM1的第二通道,观察其亮度随占空比变化而变化。

测试思路

  • 将 GPIO52 配置为 PWM 功能(PWM1 第二通道);
  • 设置 PWM 周期为 100μs(10kHz);
  • 逐步增大占空比,从 10% 到 100%;
  • 每次变化间隔 2 秒,用肉眼观察 LED 的明暗变化;
  • 最后关闭 PWM 输出,熄灭 LED。

测试代码

#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 的输出控制能力。

更多回帖

发帖
×
20
完善资料,
赚取积分