前言
- 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設備,操作前先:
init或open - 使用device的方式,發現代碼量變多了,有點復雜,看上去不如直接控制引腳來的方便,但是后期維護很容易
- 使用device的方式,可以很容易的擴展到其他外設的控制,如喇叭、電機等
- 如果只是控制一個led,確實有點浪費,通過修改可以用來控制一組LED
小結
- 學習掌握rt_device的框架的使用,了解使用的目的是為了上層應用于底層驅動的解耦,
- 了解抽象在軟體設計中的作用,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/413593.html
標籤:其他
