前言
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如下:
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"
# 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;
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) ;
}
# 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
# 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) ;
}
# 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
# 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 ) ;
# endif
while ( 1 )
{
led_grn_power_on ( ) ;
rt_thread_mdelay ( 500 ) ;
led_grn_power_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设备,操作前先: init
或 open
使用device的方式,发现代码量变多了,有点复杂,看上去不如直接控制引脚来的方便,但是后期维护很容易
使用device的方式,可以很容易的扩展到其他外设的控制,如喇叭、电机等
如果只是控制一个led,确实有点浪费,通过修改可以用来控制一组LED
小结
学习掌握rt_device的框架的使用,了解使用的目的是为了上层应用于底层驱动的解耦。
了解抽象在软件设计中的作用。