RT-Thread 入门学习笔记:把LED抽象成rt_device来操作

前言

  • RT-Thread 的device框架,还是比较的实用的,可以用来对一些外设、模块进行【抽象】,这样底层驱动与上层应用可以降低耦合性。
  • 默认RT-Thread的BSP工程,大部分都是一个流水灯控制,一般流水灯是通过GPIO引脚直接控制的。
  • 这里使用rt_device的方法,把led注册成一个led device,通过device的API接口进行控制

学习rt_device

  • rt_device 提供了较为通用的接口,可以实现对设备的初始化、读写、控制,当然,如果外设过于复杂,可以增加额外的ops,也就是可以自己定义一个device,继承rt_device。
  • rt_device 主要的ops如下:
    /* common device interface */
    rt_err_t  (*init)   (rt_device_t dev);
    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
    rt_err_t  (*close)  (rt_device_t dev);
    rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
    rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
  • 可以通过自定义的cmd,充分利用device的control,来实现对设备的管理,如开关、配置等操作

LED改为Device设备

  • 验证平台:NUCLEO-L476RG 开发板(STM32L476RG)
  • LED : PA5引脚,高电平:亮 低电平:灭
  • 抽象在软件设计中很有用,使用rt_device框架后,你会发现,上层应用可以与底层解耦,也就是上层应用只需要通过一个设备名称来访问控制设备,通过标准的device api 来操作设备,不用关心底层的改变。如果直接控制某个引脚,引脚改变后,应用就要一起更改。
  • 使用框架的好处就是底层改变,上层不需要改变或只会很少的改变。
  • 注册设备的代码如下:
  • led_dev.c : led设备注册
#include "led_dev.h"
#include "board.h"

#define LED1_DEVICE_NAME        "led1"

/* defined the LED0 pin: PA5 */
#define LED0_PIN        GET_PIN(A, 5)

static struct rt_device _led_dev;

static rt_err_t  _led_init(rt_device_t dev)
{
    
    
    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);

    return RT_EOK;
}

static rt_err_t  _led_open(rt_device_t dev, rt_uint16_t oflag)
{
    
    
    if (dev == RT_NULL)
        return -RT_ERROR;

    return RT_EOK;
}

static rt_err_t _led_close(rt_device_t dev)
{
    
    
    if (dev == RT_NULL)
        return -RT_ERROR;

    return RT_EOK;
}

static rt_err_t  _led_control(rt_device_t dev, int cmd, void *args)
{
    
    
    if (dev == RT_NULL)
        return -RT_ERROR;

    switch (cmd)
    {
    
    
        case LED_CTRL_CMD_POWER_ON:
            rt_pin_write(LED0_PIN, PIN_HIGH);
            break;
        case LED_CTRL_CMD_POWER_OFF:
            rt_pin_write(LED0_PIN, PIN_LOW);
            break;
        default:
            break;
    }

    return RT_EOK;
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops led_dev_ops =
{
    
    
    _led_init,
    _led_open,
    _led_close,
    RT_NULL,
    RT_NULL,
    _led_control
};
#endif

static int led1_device_register(const char *name, void *user_data)
{
    
    
    _led_dev.type         = RT_Device_Class_Miscellaneous;
    _led_dev.rx_indicate  = RT_NULL;
    _led_dev.tx_complete  = RT_NULL;

#ifdef RT_USING_DEVICE_OPS
    _led_dev.ops          = &led_dev_ops;
#else
    _led_dev.init         = _led_init;
    _led_dev.open         = _led_open;
    _led_dev.close        = _led_close;
    _led_dev.read         = RT_NULL;
    _led_dev.write        = RT_NULL;
    _led_dev.control      = _led_control;
#endif

    _led_dev.user_data    = user_data;

    /* register a character device */
    rt_device_register(&_led_dev, name, RT_DEVICE_FLAG_RDWR);

    return 0;
}

int led1_device_init(void)
{
    
    
    return led1_device_register(LED1_DEVICE_NAME, RT_NULL);
}
  • led_dev.h
#ifndef __LED_DEV_H__
#define __LED_DEV_H__

#include <rtthread.h>
#include <rtdevice.h>

#define LED_CTRL_CMD_POWER_OFF          0x00
#define LED_CTRL_CMD_POWER_ON           0x01

int led1_device_init(void);

#endif
  • led1_app.c : led 设备应用接口
#include "led1_app.h"

#ifndef LED1_DEVICE_NAME
#define LED1_DEVICE_NAME            "led1"
#endif

static rt_device_t led1_dev = RT_NULL;

static rt_device_t get_led1_dev(void)
{
    
    
    if (led1_dev != RT_NULL)
        return led1_dev;

    led1_dev = rt_device_find(LED1_DEVICE_NAME);

    return led1_dev;
}

static rt_err_t led1_open(void)
{
    
    
    rt_device_t dev = get_led1_dev();
    if (dev == RT_NULL)
        return -RT_ERROR;

    return rt_device_open(dev, RT_DEVICE_FLAG_RDWR);
}

rt_err_t led_grn_init(void)
{
    
    
    led1_device_init();
    return led1_open();
}

rt_err_t led_grn_power_on(void)
{
    
    
    rt_device_t dev = get_led1_dev();

    if (dev == RT_NULL)
        return -RT_ERROR;

    return rt_device_control(dev, LED_CTRL_CMD_POWER_ON, RT_NULL);
}

rt_err_t led_grn_power_off(void)
{
    
    
    rt_device_t dev = get_led1_dev();

    if (dev == RT_NULL)
        return -RT_ERROR;

    return rt_device_control(dev, LED_CTRL_CMD_POWER_OFF, RT_NULL);
}
  • led1_app.h
#ifndef __LED1_APP_H__
#define __LED1_APP_H__

#include "led_dev.h"

rt_err_t led_grn_init(void);
rt_err_t led_grn_power_on(void);
rt_err_t led_grn_power_off(void);

#endif
  • main.c 初始化与功能验证
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include "drv_gpio.h"
#include "key.h"
#include "pms.h"
#include "led1_app.h"


int main(void)
{
    
    
    key_gpio_init();
    led_grn_init();
    pms_task_init();
    rt_thread_mdelay(1000);

#ifdef RT_USING_PM
    rt_pm_module_delay_sleep(PM_POWER_ID, 10000);
    //rt_pm_module_release(PM_POWER_ID, PM_SLEEP_MODE_NONE);
#endif

    while (1)
    {
    
    
        led_grn_power_on(); /* LED ON */
        rt_thread_mdelay(500);
        led_grn_power_off(); /* LED OFF */
        rt_thread_mdelay(500);
    }
}

运行效果

msh >
 \ | /
- RT -     Thread Operating System
 / | \     4.1.0 build Jan 16 2022 18:19:43
 2006 - 2021 Copyright by rt-thread team
[D/key] key_gpio_init.

[D/key] PIN_KEY0=45

msh >
msh >
msh >
msh >list_device
device           type         ref count
-------- -------------------- ----------
led1     Miscellaneous Device 1         /* 注册的设备 */
uart2    Character Device     2       
pin      Miscellaneous Device 0       
msh >

心得体会

  • 注意rt_device设备,操作前先: initopen
  • 使用device的方式,发现代码量变多了,有点复杂,看上去不如直接控制引脚来的方便,但是后期维护很容易
  • 使用device的方式,可以很容易的扩展到其他外设的控制,如喇叭、电机等
  • 如果只是控制一个led,确实有点浪费,通过修改可以用来控制一组LED

小结

  • 学习掌握rt_device的框架的使用,了解使用的目的是为了上层应用于底层驱动的解耦。
  • 了解抽象在软件设计中的作用。

猜你喜欢

转载自blog.csdn.net/tcjy1000/article/details/122530896
今日推荐