一、板载LED
NUCLEO-U5A5ZJ-Q开发板自身提供了3个可以被用户控制的LED:
默认的引脚定义如下:
上述三个LED,可以在用户程序中进行控制。
二、PWM控制LED源码
在Zephyr的源码中,提供了一个 samples/drivers/led_pwm 可以使用PWM驱动LED:
/*
* Copyright (c) 2020 Seagate Technology LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <errno.h>
#include <zephyr/drivers/led.h>
#include <zephyr/sys/util.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL);
#define LED_PWM_NODE_ID DT_COMPAT_GET_ANY_STATUS_OKAY(pwm_leds)
const char *led_label[] = {
DT_FOREACH_CHILD_SEP_VARGS(LED_PWM_NODE_ID, DT_PROP_OR, (,), label, NULL)
};
const int num_leds = ARRAY_SIZE(led_label);
#define MAX_BRIGHTNESS 100
#define FADE_DELAY_MS 10
#define FADE_DELAY K_MSEC(FADE_DELAY_MS)
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] Run tests on a single LED using the LED API syscalls.
*
* @param led_pwm LED PWM device.
* @param led Number of the LED to test.
*/
static void run_led_test(const struct device *led_pwm, uint8_t led)
{
int err;
uint16_t level;
LOG_INF("Testing LED %d - %s", led, led_label[led] ? : "no label");
/* Turn LED on. */
err = led_on(led_pwm, led);
if (err < 0) {
LOG_ERR("err=%d", err);
return;
}
LOG_INF(" Turned on");
k_sleep(K_MSEC(1000));
/* Turn LED off. */
err = led_off(led_pwm, led);
if (err < 0) {
LOG_ERR("err=%d", err);
return;
}
LOG_INF(" Turned off");
k_sleep(K_MSEC(1000));
/* Increase LED brightness gradually up to the maximum level. */
LOG_INF(" Increasing brightness gradually");
for (level = 0; level <= MAX_BRIGHTNESS; level++) {
err = led_set_brightness(led_pwm, led, level);
if (err < 0) {
LOG_ERR("err=%d brightness=%d\n", err, level);
return;
}
k_sleep(FADE_DELAY);
}
k_sleep(K_MSEC(1000));
/* Set LED blinking (on: 0.1 sec, off: 0.1 sec) */
err = led_blink(led_pwm, led, 100, 100);
if (err < 0) {
LOG_ERR("err=%d", err);
return;
}
LOG_INF(" Blinking on: 0.1 sec, off: 0.1 sec");
k_sleep(K_MSEC(5000));
/* Enable LED blinking (on: 1 sec, off: 1 sec) */
err = led_blink(led_pwm, led, 1000, 1000);
if (err < 0) {
LOG_ERR("err=%d", err);
LOG_INF(" Cycle period not supported - on: 1 sec, off: 1 sec");
} else {
LOG_INF(" Blinking on: 1 sec, off: 1 sec");
}
k_sleep(K_MSEC(5000));
/* Turn LED off. */
err = led_off(led_pwm, led);
if (err < 0) {
LOG_ERR("err=%d", err);
return;
}
LOG_INF(" Turned off, loop end");
}
int main(void)
{
const struct device *led_pwm;
uint8_t led;
led_pwm = DEVICE_DT_GET(LED_PWM_NODE_ID);
if (!device_is_ready(led_pwm)) {
LOG_ERR("Device %s is not ready", led_pwm->name);
return 0;
}
if (!num_leds) {
LOG_ERR("No LEDs found for %s", led_pwm->name);
return 0;
}
do {
for (led = 0; led < num_leds; led++) {
run_led_test(led_pwm, led);
}
} while (true);
return 0;
}
在上述代码中,首先通过宏定义,找到设备树中pwm_leds对应的节点:
#define LED_PWM_NODE_ID DT_COMPAT_GET_ANY_STATUS_OKAY(pwm_leds)
然后,通过宏定义,获取pwm_leds中,各个led设备的名称(label):
const char *led_label[] = {
DT_FOREACH_CHILD_SEP_VARGS(LED_PWM_NODE_ID, DT_PROP_OR, (,), label, NULL)
};
再根据获得的名称数量,得到有多少个可被PWM控制的LED:
const int num_leds = ARRAY_SIZE(led_label);
另外,代码中还定义了 PWM控制LED的亮度,以及设置每个亮度后的延时时间:
#define MAX_BRIGHTNESS 100
#define FADE_DELAY_MS 10
#define FADE_DELAY K_MSEC(FADE_DELAY_MS)
在Zephyr中,用 K_MSEC 宏定义来获取具体ms对应的值,然后使用k_sleep()来进行延时。
在main()主逻辑中,先检查设备是否初始化完成:
led_pwm = DEVICE_DT_GET(LED_PWM_NODE_ID);
if (!device_is_ready(led_pwm)) {
LOG_ERR("Device %s is not ready", led_pwm->name);
return 0;
}
再循环对设置的LED进行测试:
do {
for (led = 0; led < num_leds; led++) {
run_led_test(led_pwm, led);
}
} while (true);
run_led_test()调用中,几个关键的led控制调用如下:
- led_on(led_pwm, led); 点亮对应的led
- led_off(led_pwm, led); 熄灭对应的led
- led_blink(led_pwm, led, 100, 100); 交替点亮100ms,熄灭100ms
- led_set_brightness(led_pwm, led, level); 设置对应led的亮度(0-100)
PWM控制LED亮度部分代码:
for (level = 0; level <= MAX_BRIGHTNESS; level++) {
err = led_set_brightness(led_pwm, led, level);
if (err < 0) {
LOG_ERR("err=%d brightness=%d\n", err, level);
return;
}
k_sleep(FADE_DELAY);
}
明白了以上代码,就能开始编译源码进行测试了。
不过,在Zephyr中,NUCLEO-U5A5ZJ-Q对应的LED默认作为普通GPIO LED控制,所以使用下面的命令编译,会出错:
west build -b nucleo_u5a5zj_q samples/drivers/led_pwm
在编译之前,需要先处理设备树定义。
对应的设备树定义文件为:
- ./boards/arm/nucleo_u5a5zj_q/nucleo_u5a5zj_q.dts
- ./boards/arm/nucleo_u5a5zj_q/nucleo_u5a5zj_q-common.dtsi
在nucleo_u5a5zj_q-common.dtsi中, 首先禁用leds设置:
leds: leds {
compatible = "gpio-leds";
status = "disabled";
green_led_1: led_1 {
gpios = <&gpioc 7 GPIO_ACTIVE_HIGH>;
label = "User LD1";
};
blue_led_1: led_2 {
gpios = <&gpiob 7 GPIO_ACTIVE_HIGH>;
label = "User LD2";
};
red_led_1: led_3 {
gpios = <&gpiog 2 GPIO_ACTIVE_HIGH>;
label = "User LD3";
};
};
设置 status = "disabled"; 即可禁用。
然后,启用pwm_leds的设置:
pwmleds: pwmleds {
compatible = "pwm-leds";
status = "okay";
pwm_led_1: green_led_1 {
pwms = <&pwm3 2 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
label = "green led";
};
pwm_led_2: blue_led_1 {
pwms = <&pwm4 2 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
label = "blue led";
};
};
设置 status = "okey"; 即可启用。
从上述的配置中,可以看到,只有绿灯和蓝灯,设置了PWM驱动。
红灯对应的是PG2,没有看到对应的PWM调用。
要确定哪些引脚可被PWM控制,可以查看 ./modules/hal/stm32/dts/st/u5/stm32u5a5zjtxq-pinctrl.dtsi:
上面的绿灯和蓝灯的pwm配置:
&timers3 {
st,prescaler = <10000>;
status = "okay";
pwm3: pwm {
pinctrl-0 = <&tim3_ch2_pc7>;
pinctrl-names = "default";
status = "okay";
};
};
&timers4 {
st,prescaler = <10000>;
status = "okay";
pwm4: pwm {
pinctrl-0 = <&tim4_ch2_pb7>;
pinctrl-names = "default";
status = "okay";
};
};
分别对应tim3_ch2_pc7、tim4_ch2_pb7,pinctrl定义中,具体如下:
/omit-if-no-ref/ tim3_ch2_pc7: tim3_ch2_pc7 {
pinmux = <STM32_PINMUX('C', 7, AF2)>;
};
/omit-if-no-ref/ tim4_ch2_pb7: tim4_ch2_pb7 {
pinmux = <STM32_PINMUX('B', 7, AF2)>;
};
做好上述对应的修改后,再次编译源码,成功通过:
west build -b nucleo_u5a5zj_q samples/drivers/led_pwm
再将生成的固件烧录到开发板:
pyocd flash --erase chip --target stm32u5a5zjtxq ./build/zephyr/zephyr.hex
使用串口监听工具进行监听,就可以查看到实际的运行输出结果:
开发板上的绿色和蓝色LED,也会依次进行:点亮、熄灭、渐亮、0.1秒交替闪烁、1秒交替闪烁
三、添加自定义引脚使用PWM控制输出
在上面的演示代码中,使用PWM驱动了绿色和蓝色LED。
红色LED连接到了PG2,在 pinctrl 的定义中,没有定时器关联到PG2的设置。
在开发板上,有Arduino兼容接口:
在CN10上,提供了专门的定时器控制的引脚:
在pinctrl中,有PA0对应TIM2_CH1的配置:
在 nucleo_u5a5zj_q-common.dtsi 添加对应的配置:
&timers2 {
st,prescaler = <10000>;
status = "okay";
pwm2: pwm {
pinctrl-0 = <&tim2_ch1_pa0>;
pinctrl-names = "default";
status = "okay";
};
};
然后添加对应的pwd_leds设置:
pwmleds: pwmleds {
compatible = "pwm-leds";
status = "okay";
pwm_led_1: green_led_1 {
pwms = <&pwm3 2 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
label = "green led";
};
pwm_led_2: blue_led_1 {
pwms = <&pwm4 2 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
label = "blue led";
};
pwm_led_3: extra_led_1 {
pwms = <&pwm2 1 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
label = "extra led";
};
};
然后重新编译源码并烧录固件,开发板运行后,串口输出如下:
从上述输出可以看到,已经识别到了三个PWM LED,并进行了各项控制操作。
要确定我们自己添加的LED是否有效被PWM控制,可以自己在PA0上接一个LED,也可以用逻辑分析仪进行测试。
把逻辑分析仪连接上PA0和GND:
在 Testing LED 2 - extra led 的时候采集数据:
采样到的数据,具体部分如下:
- 对应点亮
- 对应熄灭
- 对应亮度控制
- 对应0.1秒闪烁
- 对应1.0秒闪烁
放大 亮度控制 部分的数据:
可以看到,随着设置的亮度变化,PWM占空比也在对应变化:
四、总结
通过上面的演示,了解了PWM控制LED,如果感兴趣,还可以查看./samples/basic/blinky_pwm 、./samples/basic/servo_motor,进一步了解PWM的控制,实现更多的功能。
---------------------
作者:HonestQiao
链接:https://bbs.21ic.com/icview-3345576-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。