[STM32U5]【NUCLEO-U5A5ZJ-Q测评】pwm驱动板载LED和自定义引脚PWM输出控制LED

一、板载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所有,任何人未经允许禁止转载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值