《嵌入式系统- 玩转中科蓝讯(AB32VG1)开发板(基于RT-Thread系统)》第2章 全彩LED实现魔幻色彩

开发环境:
RT-Thread版本:4.0.3
操作系统:Windows 10
RT-Thread Studio版本:2.0.0
开发板MCU:AB5301A

2.1全彩LED简介

大家常的LED都是单色的,控制一端的高低电平即可控制LED的亮灭。全彩LED就是可以通过RGB三种基本LED组合多种色彩的LED,当然还有一种RGBW 四基色的全彩LED。全彩LED分为共阳极和共阴极两种。蓝讯AB32VG1的板子是共阳的。

60BoGt.png

图1

全彩LED的控制有两种方式:一种是直接通过三个引脚直接控制,另外一种是通过SM16703、WS2811、TM1829等驱动芯片进行控制。本文采用直接控制的方式,但要想获得更多颜色,就需要通过PWM来控制。

2.2简单实现七彩色

2.2.1实验分析

先来看看电路图。

60BLqg.png

图2 LED电路图

这些 LED 灯的阴极都是连接到 AB5301A的 GPIO 引脚,只要我们控制 GPIO 引脚的电平输出状态,即可控制 LED 灯的亮灭。

2.2.2代码实现

关于工程创建,这里就不说了,不懂的上上一章吧。

开发环境搭与使用 ( RT-Thread Studio)

七色很简单,就通过控制三个引脚亮灭来组合其中颜色。直接看代码。

/**Includes*********************************************************************/
#include <rtthread.h>
#include "board.h"

/**define***********************************************************************/
#define LED_R "PE.1"
#define LED_G "PE.4"
#define LED_B "PA.2"

/** the macro definition to the led on or off
  * 1 - off
  *0 - on
  */
#define ON  PIN_LOW
#define OFF PIN_HIGH

/**
  * @brief  main
  * @param  None
  * @retval None
  */
int main(void)
{
    uint32_t cnt = 0;
    uint8_t pin_r = rt_pin_get(LED_R);
    uint8_t pin_g = rt_pin_get(LED_G);
    uint8_t pin_b = rt_pin_get(LED_B);

    rt_pin_mode(pin_r, PIN_MODE_OUTPUT);
    rt_pin_mode(pin_g, PIN_MODE_OUTPUT);
    rt_pin_mode(pin_b, PIN_MODE_OUTPUT);

    while (1)
    {
        //红
        rt_pin_write(pin_r, ON);
        rt_pin_write(pin_g, OFF);
        rt_pin_write(pin_b, OFF);
        rt_thread_mdelay(1000);

        //绿
        rt_pin_write(pin_r, OFF);
        rt_pin_write(pin_g, ON);
        rt_pin_write(pin_b, OFF);
        rt_thread_mdelay(1000);

        //蓝
        rt_pin_write(pin_r, OFF);
        rt_pin_write(pin_g, OFF);
        rt_pin_write(pin_b, ON);
        rt_thread_mdelay(1000);

        //黄(红+绿)
        rt_pin_write(pin_r, ON);
        rt_pin_write(pin_g, ON);
        rt_pin_write(pin_b, OFF);
        rt_thread_mdelay(1000);

        //紫(红+蓝)
        rt_pin_write(pin_r, ON);
        rt_pin_write(pin_g, OFF);
        rt_pin_write(pin_b, ON);
        rt_thread_mdelay(1000);

        //青(绿+蓝)
        rt_pin_write(pin_r, OFF);
        rt_pin_write(pin_g, ON);
        rt_pin_write(pin_b, ON);
        rt_thread_mdelay(1000);

        //白(红+绿+蓝)
        rt_pin_write(pin_r, ON);
        rt_pin_write(pin_g, ON);
        rt_pin_write(pin_b, ON);
        rt_thread_mdelay(1000);

        //黑(全部关闭)
        rt_pin_write(pin_r, OFF);
        rt_pin_write(pin_g, OFF);
        rt_pin_write(pin_b, OFF);
        rt_thread_mdelay(1000);
        cnt++;
    }

    return 0;
}

代码很简单,主要是通过rt_pin_write()函数来控制不同LED。

2.2.3实验现象

编译完成后,通过Downloader下载程序,即可看到不同七色光依次出现。

2.3 PWM 控制全彩LED

2.3.1实验分析

在正式讲解之前,先来说说什么是PWM。PWM(Pulse Width Modulation , 脉冲宽度调制) 是一种对模拟信号电平进行数字编码的方法,通过不同频率的脉冲使用方波的占空比用来对一个具体模拟信号的电平进行编码,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替所需要波形的设备

60Bjaj.png

图3

如上图所示,就是PWM 原理示意图,假定定时器工作模式为向上计数,当计数值小于阈值时,则输出一种电平状态,比如高电平,当计数值大于阈值时则输出相反的电平状态,比如低电平。当计数值达到最大值是,计数器从0开始重新计数,又回到最初的电平状态。高电平持续时间(脉冲宽度)和周期时间的比值就是占空比,范围为0~100%。上图高电平的持续时间刚好是周期时间的一半,所以占空比为50%。

上一节讲了控制三基色的亮灭实现了七色,这一节通过使用 PWM对R、G、B三个灯进行颜色控制,通过改变三个通道的占空比,三个灯进行颜色的组合,就可以混合成多种不同的颜色。

那么要怎么做呢?其实很简单,也就通过改变电平的占空比使RGB分别显示出不同的色值,然后通过组合显示出想要的颜色。通过PWM和直接控制三基色有啥区别呢,其实没区别,通过PWM控制的颗粒度更细,因此能得到更多的色彩。

值得注意的是,这里要调整LED_R的GPIO,将LED_R接到PA1上,便于后面的控制。

表1LED寄存器表
Name Type Function
PE4(LED_G) I/O SPI0DI-G2
SPI1DI-G6
LPWM0-G1
IISMCLK-G2PE4
PA1(LED_R) I/O SPDIF1
SPI1CLK-G1
TX0-G5
HSTRX-G5
LPWM1-G3
IISDO-G1
PA1
PA2(LED_B) I/O SPI1DI-G1
LPWM2-G3
IISSCLK-G1
PA2

2.2.3代码实现

首先我们需要配置PWM驱动,双击RT-Thread Settings文件,打开 RT-Thread 项目配置界面,勾选PWM驱动,然后保存即可。

6rGBrj.png

图4添加PWM驱动

接下来需要使能3个LED的PWM。

6rGhM4.png

图5

还可以通过env工具来配置。

60DPMT.png

图6使能PWM

也可直接在rtconfig.h中直接添加以下宏定义即可。

60DFLF.png

图7

如果以上添加成功,编译烧写到开发板,查看设别,可以看到控制3个LED的PWM设备。

60DAZ4.png

图8

接下来就是下应用代码,在applications文件夹下新建文件,添加一个线程任务,然后在任务中操作3个LED。代码如下:


/**
  ******************************************************************************
  * @file                task.c
  * @author              BruceOu
  * @version             V1.0
  * @date                2021-03-12
  * @blog                https://blog.bruceou.cn/
  * @Official Accounts   嵌入式实验楼
  * @brief               RTT任务
  ******************************************************************************
  */
/*Includes**********************************************************************/
#include "task.h"

#define THREAD_PRIORITY         7
#define THREAD_STACK_SIZE       512
#define THREAD_TIMESLICE        3

#define PWM_DEV_NAME_R        "lpwm1"  /* PWM设备名称 */
#define PWM_DEV_CHANNEL_R     3       /* PWM通道 */

#define PWM_DEV_NAME_G        "lpwm0"  /* PWM设备名称 */
#define PWM_DEV_CHANNEL_G     1       /* PWM通道 */

#define PWM_DEV_NAME_B        "lpwm2"  /* PWM设备名称 */
#define PWM_DEV_CHANNEL_B     3       /* PWM通道 */

struct rt_device_pwm *pwm_dev_r;      /* PWM设备句柄 */
struct rt_device_pwm *pwm_dev_g;      /* PWM设备句柄 */
struct rt_device_pwm *pwm_dev_b;      /* PWM设备句柄 */

static rt_thread_t pwm_led_tid = RT_NULL;

/* 线程 pwm_led_thread_entry 的入口函数 */
/**
  * @brief  pwm_led_thread_entry
  * @param  parameter
  * @retval None
  */
static void pwm_led_thread_entry(void *parameter)
{
    rt_uint32_t period, pulse_r,pulse_g,pulse_b, dir_r,dir_g,dir_b;

    period = 655360;    /* 周期为0.5ms,单位为纳秒ns */
    dir_r = 1;           /* PWM脉冲宽度值的增减方向 */
    dir_g = 1;
    dir_b = 1;
    pulse_r = 0;          /* PWM脉冲宽度值,单位为纳秒ns */
    pulse_g = 0;
    pulse_b = 0;

    rt_uint16_t r,g,b;

    /* 查找设备 */
    pwm_dev_r = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME_R);
    if (pwm_dev_r == RT_NULL)
    {
        rt_kprintf("pwm led r run failed! can't find %s device!\n", PWM_DEV_NAME_G);
    }

    pwm_dev_g = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME_G);
    if (pwm_dev_g == RT_NULL)
    {
        rt_kprintf("pwm led g run failed! can't find %s device!\n", PWM_DEV_NAME_G);
    }

    pwm_dev_b = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME_B);
    if (pwm_dev_b == RT_NULL)
    {
        rt_kprintf("pwm led b run failed! can't find %s device!\n", PWM_DEV_NAME_B);
    }

    /* 设置PWM周期和脉冲宽度默认值 */
    rt_pwm_set(pwm_dev_r, PWM_DEV_CHANNEL_R, period, pulse_r);
    rt_pwm_set(pwm_dev_g, PWM_DEV_CHANNEL_G, period, pulse_g);
    rt_pwm_set(pwm_dev_b, PWM_DEV_CHANNEL_B, period, pulse_b);

    /* 使能设备 */
    rt_pwm_enable(pwm_dev_r, PWM_DEV_CHANNEL_R);
    rt_pwm_enable(pwm_dev_g, PWM_DEV_CHANNEL_G);
    rt_pwm_enable(pwm_dev_b, PWM_DEV_CHANNEL_B);

    while (1)
    {
        for (r =0 ; r < 8; r++)
        {
            if (dir_r)
            {
                pulse_r += 81920;      /* 从0值开始每次增加81920ns */
            }
            else
            {
                pulse_r -= 81920;      /* 从0值开始每次减少81920ns */
            }
            if ((pulse_r) >= period)
            {
                dir_r = 0;
            }
            if (81920 > pulse_r)
            {
                dir_r = 1;
            }
            /* 设置PWM周期和脉冲宽度 */
            rt_pwm_set(pwm_dev_r, PWM_DEV_CHANNEL_R, period, pulse_r);
            for(g = 0; g < 8; g++)
            {
                if (dir_g)
                {
                    pulse_g += 81920;      /* 从0值开始每次增加81920ns */
                }
                else
                {
                    pulse_g -= 81920;      /* 从0值开始每次减少81920ns */
                }
                if ((pulse_g) >= period)
                {
                    dir_g = 0;
                }
                if (81920 > pulse_g)
                {
                    dir_g = 1;
                }
                rt_pwm_set(pwm_dev_g, PWM_DEV_CHANNEL_G, period, pulse_g);
                for(b = 0; b < 8; b++)
                {
                    rt_thread_mdelay(10);
                    if (dir_b)
                    {
                        pulse_b += 81920;      /* 从0值开始每次增加81920ns */
                    }
                    else
                    {
                        pulse_b -= 81920;      /* 从0值开始每次减少81920ns */
                    }
                    if ((pulse_b) >= period)
                    {
                        dir_b = 0;
                    }
                    if (81920 > pulse_b)
                    {
                        dir_b = 1;
                    }

                    rt_pwm_set(pwm_dev_b, PWM_DEV_CHANNEL_B, period, pulse_b);
                }
            }

        }
    }
}

/* 线程初始化*/
int thread_init(void)
{
    /* 创建线程,名称是 pwm_led_thread,入口是 pwm_led_thread*/
    pwm_led_tid = rt_thread_create("pwm_led_thread",
                                   pwm_led_thread_entry,
                                   RT_NULL,
                                   THREAD_STACK_SIZE,
                                   THREAD_PRIORITY,
                                   THREAD_TIMESLICE);

    /* 如果获得线程控制块,启动这个线程 */
    if (pwm_led_tid != RT_NULL)
        rt_thread_startup(pwm_led_tid);

    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(thread_init, thread init);

当然啦以上代码还可以优化,有兴趣自行去修改吧。控制PWM主要用到了以下三个函数:

rt_device_find()    //根据 PWM 设备名称查找设备获取设备句柄
rt_pwm_set()    //设置 PWM 周期和脉冲宽度
rt_pwm_enable()   //使能 PWM 设备

代码也很简单,就是通过PWM来控制三个LED的脉宽,从而得到不同的颜色。

PWM参考地址

2.3.3实验现象

烧写成功后复位,在终端输 thread_init

60rZnS.png

即可看到LED闪烁出不同的颜色。

在这里插入图片描述

当然啦,由于拍摄原因,呈现的效果不是很好。


Related posts

One Thought to “《嵌入式系统- 玩转中科蓝讯(AB32VG1)开发板(基于RT-Thread系统)》第2章 全彩LED实现魔幻色彩”

  1. xzx

    我看到文档说3路LPWM互斥,楼主怎么还可以使用3路LPWM点亮LED呢

Leave a Comment