一、硬件了解
STM32C031C6的MCU系统架构中,提供了多个定时器:
这些定时器的具体异同点如下:
本篇分享使用定时器PWM输出控制RGB LED,选用了TIM3定时器来进行控制。
TIM3的具体介绍如下:
该定时器框图如下:
要单独控制RGB LED的R、G、B,需要3个通道。而TIM3有4个独立通道,所以用来控制没有问题。
二、RGB LED了解
我使用的RGB LED为5050灯珠的三色全彩LED:
注意这个上面的灯珠和WS2812B的灯珠看起来一样,但是实际使用不同。
WS2812B的自带IC,一个灯带上的每一颗都可以单独控制。而普通5050灯带需要统一行动。
三、zephyr对定时器的支持
从zephyr/../modules/hal/stm32/dts/st/c0/stm32c031c(4-6)tx-pinctrl.dtsi中,可以了解到tim3定时器关联的pinctrl信息:
因为PA5已经被 zephyr/boards/arm/nucleo_c031c6/nucleo_c031c6.dts 配置中默认的TIM1对应的PWM占用了,
所以我选择了TIM3中所对应的PA6、PA7、PB0通道来做为RGB的控制,对应开发板上的如下引脚:
不过,zephyr/boards/arm/nucleo_c031c6/nucleo_c031c6.dts中没有TIM3的配置,所以需要自己添加一个配置文件:nucleo_c031c6.overlay
/ {
mypwmleds {
compatible = "pwm-leds";
my_red_pwm_led: my_led_pwm_0 {
pwms = <&pwm3 1 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
label = "Red PWM LED";
};
my_green_pwm_led: my_led_pwm_1 {
pwms = <&pwm3 2 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
label = "Green PWM LED";
};
my_blue_pwm_led: my_led_pwm_2 {
pwms = <&pwm3 3 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
label = "Blue PWM LED";
};
};
aliases {
my-pwm-led0 = &my_green_pwm_led;
my-pwm-led1 = &my_blue_pwm_led;
my-pwm-led2 = &my_red_pwm_led;
my-green-pwm-led = &my_green_pwm_led;
my-blue-pwm-led = &my_blue_pwm_led;
my-red-pwm-led = &my_red_pwm_led;
};
};
&timers3 {
st,prescaler = <10000>;
status = "okay";
pwm3: pwm {
pinctrl-0 = <&tim3_ch1_pa6 &tim3_ch2_pa7 &tim3_ch3_pb0>;
pinctrl-names = "default";
status = "okay";
};
};
四、代码编写
在 zephyr/samples/basic/rgb_led,有单个RGB LED的控制代码:
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* [url=home.php?mod=space&uid=1307177]@File[/url] Sample app to demonstrate PWM-based RGB LED control
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/device.h>
#include <zephyr/drivers/pwm.h>
static const struct pwm_dt_spec red_pwm_led =
PWM_DT_SPEC_GET(DT_ALIAS(red_pwm_led));
static const struct pwm_dt_spec green_pwm_led =
PWM_DT_SPEC_GET(DT_ALIAS(green_pwm_led));
static const struct pwm_dt_spec blue_pwm_led =
PWM_DT_SPEC_GET(DT_ALIAS(blue_pwm_led));
#define STEP_SIZE PWM_USEC(2000)
int main(void)
{
uint32_t pulse_red, pulse_green, pulse_blue; /* pulse widths */
int ret;
printk("PWM-based RGB LED control\n");
if (!pwm_is_ready_dt(&red_pwm_led) ||
!pwm_is_ready_dt(&green_pwm_led) ||
!pwm_is_ready_dt(&blue_pwm_led)) {
printk("Error: one or more PWM devices not ready\n");
return 0;
}
while (1) {
for (pulse_red = 0U; pulse_red <= red_pwm_led.period;
pulse_red += STEP_SIZE) {
ret = pwm_set_pulse_dt(&red_pwm_led, pulse_red);
if (ret != 0) {
printk("Error %d: red write failed\n", ret);
return 0;
}
for (pulse_green = 0U;
pulse_green <= green_pwm_led.period;
pulse_green += STEP_SIZE) {
ret = pwm_set_pulse_dt(&green_pwm_led,
pulse_green);
if (ret != 0) {
printk("Error %d: green write failed\n",
ret);
return 0;
}
for (pulse_blue = 0U;
pulse_blue <= blue_pwm_led.period;
pulse_blue += STEP_SIZE) {
ret = pwm_set_pulse_dt(&blue_pwm_led,
pulse_blue);
if (ret != 0) {
printk("Error %d: "
"blue write failed\n",
ret);
return 0;
}
k_sleep(K_SECONDS(1));
}
}
}
}
return 0;
}
参考上述代码,编写控制3个LED的代码:
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/device.h>
#include <zephyr/drivers/pwm.h>
static const struct pwm_dt_spec red_pwm_led =
PWM_DT_SPEC_GET(DT_ALIAS(my_red_pwm_led));
static const struct pwm_dt_spec green_pwm_led =
PWM_DT_SPEC_GET(DT_ALIAS(my_green_pwm_led));
static const struct pwm_dt_spec blue_pwm_led =
PWM_DT_SPEC_GET(DT_ALIAS(my_blue_pwm_led));
// 延时时间
#define DELAY_TIME_MS 500U
// pulse基础值
#define PULSE_BASE 0x80000
// 亮度最大值
#define BRIGHTNESS_MAX 32
int main(void)
{
uint32_t pulse_red, pulse_green, pulse_blue; /* pulse widths */
int ret;
printk("PWM-based RGB LED control\n");
if (!pwm_is_ready_dt(&red_pwm_led) ||
!pwm_is_ready_dt(&green_pwm_led) ||
!pwm_is_ready_dt(&blue_pwm_led)) {
printk("Error: one or more PWM devices not ready\n");
return 0;
}
printk("period(r,g,b)=(%d,%d,%d)\n", red_pwm_led.period, green_pwm_led.period, blue_pwm_led.period);
uint32_t color_pulse_list[8][3] = {
{0, 0, 0},
{1, 0, 0},
{0, 1, 0},
{0, 0, 1},
{1, 1, 0},
{0, 1, 1},
{1, 0, 1},
{1, 1, 1}
};
uint32_t i = 0;
uint32_t brightness = 1;
while (1) {
pulse_red = color_pulse_list[i][0] * PULSE_BASE * brightness;
pulse_green = color_pulse_list[i][1] * PULSE_BASE * brightness;
pulse_blue = color_pulse_list[i][2] * PULSE_BASE * brightness;
ret = pwm_set_pulse_dt(&red_pwm_led, pulse_red);
if (ret != 0) {
printk("Error %d: red write failed\n", ret);
return 0;
}
ret = pwm_set_pulse_dt(&green_pwm_led,
pulse_green);
if (ret != 0) {
printk("Error %d: green write failed\n",
ret);
return 0;
}
ret = pwm_set_pulse_dt(&blue_pwm_led,
pulse_blue);
if (ret != 0) {
printk("Error %d: "
"blue write failed\n",
ret);
return 0;
}
printk("pulse(r,g,b)=(%04X,%04X,%04X), brightness=%d\n", pulse_red, pulse_green, pulse_blue, brightness);
k_sleep(K_MSEC(DELAY_TIME_MS));
i++;
if (i == 8) {
i = 0;
brightness++;
if(brightness>BRIGHTNESS_MAX) {
brightness = 1;
}
}
#endif
}
return 0;
}
上述代码中,使用 my_red_pwm_led、my_green_pwm_led、my_blue_pwm_led来对应dts定义的R、G、B控制设备。
然后定义了 color_pulse_list ,用于设置R、G、B的点亮情况:
uint32_t color_pulse_list[8][3] = {
{0, 0, 0},
{1, 0, 0},
{0, 1, 0},
{0, 0, 1},
{1, 1, 0},
{0, 1, 1},
{1, 0, 1},
{1, 1, 1}
};
然后在循环中,依次使用上述配置,点亮R、G、B中的1个或者多个,并使用brightness来控制亮度。
五、输出结果
将之前的nucleo_c031c6.overlay保存到 zephyr/samples/basic/rgb_led/boards/下,然后编译烧录到开发板,就能输出结果:
RGB LED也会随之跟着变换颜色:
4621_1708266556
六、参考资料