From 4aaa84ba177657a5eaffa074164bd45e2cc79d36 Mon Sep 17 00:00:00 2001 From: usualtreee <1340399194@qq.com> Date: Fri, 13 Nov 2026 09:36:42 +0800 Subject: [bsp][k230] Fix PWM pulse width readback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正 PWM 占空比读回换算和周期上限的有符号移位,补充驱动回归用例。 --- bsp/k230/drivers/interdrv/pwm/drv_pwm.c | 17 ++-- bsp/k230/drivers/utest/test_pwm.c | 162 +++++++++++++++++++++++--------- 2 files changed, 131 insertions(+), 48 deletions(-) diff --git a/bsp/k230/drivers/interdrv/pwm/drv_pwm.c b/bsp/k230/drivers/interdrv/pwm/drv_pwm.c index 4ad6ee4454..4692eff890 100644 --- a/bsp/k230/drivers/interdrv/pwm/drv_pwm.c +++ b/bsp/k230/drivers/interdrv/pwm/drv_pwm.c @@ -92,6 +92,7 @@ static struct k230_pwm_dev pwm_devs[] = { #endif #if !defined(BSP_USING_PWM0) && !defined(BSP_USING_PWM1) +// cppcheck-suppress preprocessorErrorDirective #error "No pwm device defined!" #endif }; @@ -131,7 +132,7 @@ static rt_err_t kd_pwm_get(kd_pwm_t *reg, int channel, struct rt_pwm_configurati { int ret; uint64_t pulse, period; - uint32_t pwm_pclock, pwmscale; + uint32_t pwm_pclock, pwmscale, period_count, compare; ret = check_channel(channel); if (ret < 0) @@ -141,9 +142,11 @@ static rt_err_t kd_pwm_get(kd_pwm_t *reg, int channel, struct rt_pwm_configurati pwmscale = reg->pwmcfg & 0xf; pwm_pclock >>= pwmscale; - period = reg->pwmcmp0; - period = period * NSEC_PER_SEC / pwm_pclock; - pulse = *((®->pwmcmp1) + channel); + period_count = reg->pwmcmp0; + compare = *((®->pwmcmp1) + channel); + period = period_count * NSEC_PER_SEC / pwm_pclock; + /* A compare value at or beyond the period produces no high pulse. */ + pulse = compare < period_count ? period_count - compare : 0; pulse = pulse * NSEC_PER_SEC / pwm_pclock; configuration->period = period; @@ -171,8 +174,10 @@ static rt_err_t kd_pwm_set(kd_pwm_t *reg, int channel, struct rt_pwm_configurati /* Calculate duty cycle */ pwmcmpx_max = (1 << PWM_CMP_WIDTH) - 1; - if (period > ((1 << (PWM_SCALE_MAX_BITS + PWM_PERIOD_BITS)) - 1LL)) + if (period > ((1ULL << (PWM_SCALE_MAX_BITS + PWM_PERIOD_BITS)) - 1ULL)) + { return -RT_EINVAL; + } while ((period >> pwmscale) > pwmcmpx_max) pwmscale++; @@ -240,4 +245,4 @@ int rt_hw_pwm_init(void) } return RT_EOK; } -INIT_DEVICE_EXPORT(rt_hw_pwm_init); \ No newline at end of file +INIT_DEVICE_EXPORT(rt_hw_pwm_init); diff --git a/bsp/k230/drivers/utest/test_pwm.c b/bsp/k230/drivers/utest/test_pwm.c index 9b76fdd74a..69c2fab8fb 100644 --- a/bsp/k230/drivers/utest/test_pwm.c +++ b/bsp/k230/drivers/utest/test_pwm.c @@ -38,66 +38,144 @@ #include "utest.h" /* - * 测试 PWM 驱动对板载 LED 的亮度控制功能 - * - * 1. 查找名为 "pwm" 的 PWM 设备。 - * 2. 使用 pinctrl 将 GPIO52 映射为 PWM4 通道,并配置引脚属性: - * - 使能输出(OE); - * - 禁用输入(IE); - * - 禁用上下拉(PU/PD); - * - 设置中等驱动能力(驱动强度为 4)。 - * 3. 设置 PWM 输出周期为 100000ns(对应频率为 10kHz)。 - * 4. 通过逐步增加 PWM 的脉冲宽度(pulse),控制板载 LED 的亮度由暗到亮: - * - 从 10% 占空比(10000ns)开始; - * - 每次增加 10%,直到 100%(全亮); - * - 每次间隔 2 秒以便观察 LED 亮度变化。 - * 5. 最后禁用 PWM 输出,关闭 LED。 - * - * 硬件说明: - * - 本测试基于 K230-01studio 开发板; - * - GPIO52 已通过电路连接一颗板载 LED; - * - PWM4 通道控制该 LED 的亮灭和亮度; - * - 测试过程中,可肉眼观察 LED 明暗变化。 - * + * GPIO52 is connected to the blue LED on the 01Studio CanMV K230. + * PWM4 is logical channel 1 of pwm1. Check both the visible output and + * the period/pulse readback, including a period that requires scaling. */ -#define PWM_DEV_NAME "pwm1" /* PWM设备名称 */ -#define PWM_DEV_CHANNEL 1 /* PWM通道 */ +#include "sysctl_clk.h" + +#define PWM_DEV_NAME "pwm1" +#define PWM_DEV_CHANNEL 1 #define TEST_GPIO_LED 52 +#define PWM_CMP_MAX 0xFFFF + +struct pwm_test_point +{ + rt_uint32_t period; + rt_uint32_t pulse; +}; + +static rt_uint32_t pwm_readback_tolerance(rt_uint32_t period, rt_uint32_t *scale) +{ + rt_uint32_t clock = sysctl_clk_get_leaf_freq(SYSCTL_CLK_PWM_APB_GATE); + rt_uint64_t period_count = (rt_uint64_t)period * clock / NSEC_PER_SEC; + + *scale = 0; + while ((period_count >> *scale) > PWM_CMP_MAX) + { + (*scale)++; + } + + /* One scaled count, one unscaled truncation count, and rounding margin. */ + return NSEC_PER_SEC / (clock >> *scale) + NSEC_PER_SEC / clock + 2; +} + +static void pwm_check_readback(struct rt_device_pwm *pwm_dev, + const struct pwm_test_point *point) +{ + struct rt_pwm_configuration configuration = { 0 }; + rt_uint32_t period_error, pulse_error, scale, tolerance; + rt_err_t ret; + + configuration.channel = PWM_DEV_CHANNEL; + ret = rt_pwm_get(pwm_dev, &configuration); + uassert_int_equal(ret, RT_EOK); + if (ret != RT_EOK) + { + return; + } + + tolerance = pwm_readback_tolerance(point->period, &scale); + period_error = configuration.period > point->period ? configuration.period - point->period : point->period - configuration.period; + pulse_error = configuration.pulse > point->pulse ? configuration.pulse - point->pulse : point->pulse - configuration.pulse; + + rt_kprintf("PWM readback: scale=%u, requested=%u/%u ns, actual=%u/%u ns, tolerance=%u ns\n", + scale, point->period, point->pulse, + configuration.period, configuration.pulse, tolerance); + uassert_true(period_error <= tolerance); + uassert_true(pulse_error <= tolerance); +} + +static void pwm_check_cross_channel(struct rt_device_pwm *pwm_dev) +{ + struct rt_pwm_configuration configuration = { 0 }; + rt_err_t ret; + + ret = rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, 100000, 25000); + uassert_int_equal(ret, RT_EOK); + if (ret != RT_EOK) + { + return; + } + + /* This shorter shared period leaves channel 1's compare above pwmcmp0. */ + ret = rt_pwm_set(pwm_dev, 0, 50000, 12500); + uassert_int_equal(ret, RT_EOK); + if (ret != RT_EOK) + { + return; + } + + configuration.channel = PWM_DEV_CHANNEL; + ret = rt_pwm_get(pwm_dev, &configuration); + rt_kprintf("PWM cross-channel readback: ret=%d, actual=%u/%u ns\n", + ret, configuration.period, configuration.pulse); + uassert_int_equal(ret, RT_EOK); + if (ret != RT_EOK) + { + return; + } + + uassert_int_equal(configuration.period, 50000); + uassert_int_equal(configuration.pulse, 0); +} void pwm_demo(void) { - LOG_I("set gpio52 in PWM1 start.\n"); + static const struct pwm_test_point points[] = { + { 100000, 0 }, + { 100000, 25000 }, + { 100000, 75000 }, + { 100000, 100000 }, + { 10000000, 2500000 }, + }; + struct rt_device_pwm *pwm_dev; + rt_err_t ret; + rt_size_t i; + + rt_kprintf("Set GPIO52 to PWM4 (pwm1 channel 1).\n"); 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); - /* 设置驱动能力为4(中等强度) */ k230_pinctrl_set_drv(TEST_GPIO_LED, 4); - LOG_I("pwm test start.\n"); - struct rt_device_pwm *pwm_dev; /* PWM设备句柄 */ - rt_uint32_t period; /* 周期, 单位为纳秒ns */ - rt_uint32_t pulse; /* PWM脉冲宽度值, 单位为纳秒ns */ - rt_err_t ret; - /* 查找设备 */ + pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); uassert_not_null(pwm_dev); - /* 设置PWM周期和脉冲宽度 */ - period = 100000; /* PWM 周期:100000ns = 100us,即 10kHz */ - /* 逐步循环提高PWM脉冲宽度 */ - for(pulse = 10000; pulse <= 100000; pulse+=10000) + if (pwm_dev == RT_NULL) { - ret = rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); + return; + } + + for (i = 0; i < sizeof(points) / sizeof(points[0]); i++) + { + ret = rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, points[i].period, points[i].pulse); uassert_int_equal(ret, RT_EOK); + if (ret != RT_EOK) + { + continue; + } + + pwm_check_readback(pwm_dev, &points[i]); ret = rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL); uassert_int_equal(ret, RT_EOK); - sleep(2); + sleep(1); } + + pwm_check_cross_channel(pwm_dev); + ret = rt_pwm_disable(pwm_dev, PWM_DEV_CHANNEL); uassert_int_equal(ret, RT_EOK); } @@ -117,4 +195,4 @@ static rt_err_t utest_tc_cleanup(void) return RT_EOK; } -UTEST_TC_EXPORT(pwm_testcase, "bsp.k230.drivers.pwm", utest_tc_init, utest_tc_cleanup, 10); \ No newline at end of file +UTEST_TC_EXPORT(pwm_testcase, "bsp.k230.drivers.pwm", utest_tc_init, utest_tc_cleanup, 30); -- cgit v1.3.1