1707|1

92

帖子

0

TA的资源

一粒金砂(高级)

楼主
 

读《RT-Thread设备驱动开发指南》---第一篇 [复制链接]

 
本帖最后由 xiaolinen 于 2023-8-20 17:34 编辑

读《RT-Thread设备驱动开发指南》---  I/O设备框架

前言:

        首先,作为一个正在工作中学习和使用RT-Thread的开发者,非常感谢论坛和RT-Thread提供这么一个机会,让自己拿到这本书,从而进行进一步的学习,也祝平台和RT-Thread的发展更上一层楼!!!此外,帖子只是自己的学习笔记,不足之处,请多多指教。

 

第一部分:I/O设备模型认知

           I/O设备框架是什么:设备框架就是针对某一类外设,抽象出来一套统一的操作方法以及接入标准。注:所说的I/O设备指的是输入/输出设备,不是GPIO(不要问为什么在这里写这么一句,因为我有一个朋友曾经就有这样的疑惑)。

 

        I/O设备框架有什么:I/O设备框架位于硬件和应用层之间,总共分为三层,从上到下分别是I/O设备管理层,设备驱动框架层,设备驱动层。具体如下如所示:

        图中提到的应用层:也就是我们自己的业务逻辑程序,比如:众所周知的main.c文件。在这里,我们通过调用I/O设备管理层的函数接口,实现我们想要的功能,比如:点个灯。
        图中提到的I/O设备管理层:在这一层,RT-Thread实现对设备驱动程序的封装,对应着工程中的device.c文件。实现对上为应用层提供统一的函数接口,使得设备驱动程序升级时,不会对应用层产生影响;对下通过设备驱动框架层操作硬件的动作。此外,在这一层包含rt_device_find,open,read,write,close,register等管理接口!
        图中提到的设备驱动框架:在这一层进行对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现,也就是求同存异,最大力度的统一格式再使用!!!比如:serial.c,adc.c等文件。
        图中提到出的设备驱动层:在这一层通过程序,实现对硬件设备的访问功能。负责I/O设备的创建和注册功能;比如:drv_gpio.c,drv_adc.c,drv_usart.c等文件。
        这部分的注册功能有两种实现方式:
        1)使用I/O设备管理接口直接注册,通过rt_device_register()接口实现;流程如下图所示:

 

        2)通过设备驱动框架层提供的注册函数进行注册,注册函数名一般为rt_hw_xxx_register(),其后,设备驱动框架层的注册函数调用rt_device_register()接口,实现注册功能;流程如下图所示:

 

        图中提到出的硬件:比如:单片机自带的外设,外接的传感器,FLASH芯片等等。
 
        I/O设备框架的优势是什么:
        一开始自己也认为在裸机程序中,直接调用驱动程序,多么直接,多么干脆,为什么要费劲巴拉的使用实时系统呢?为什么还要了解这个I/O设备框架呢?但是,经历了这几年的换芯潮之后,现在已经转变了自己的想法,不得不说有一个统一的框架确实香!RT-Thread实时系统降低了代码的耦合性,复杂性,提高了系统的可靠性,易维护性,在这其中呢,因为有I/O设备框架的存在,不管使用哪一款MCU,只要应用层调用的对设备操作的函数保持不变,那么开发人员就只需要修改驱动代码,从而节省一部分时间,去做其他有意义的事情
 

第二部分:I/O设备模型使用准备

        I/O设备管理接口:

        1)创建设备:

rt_device_t rt_device_create(int type, int attach_size);

        2)销毁设备:

 

void rt_device_destroy(rt_device_t device);

        3)注册设备:

 

rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags);

        4)注销设备:

 

rt_err_t rt_device_unregister(rt_device_t dev);

        5)查找设备:

 

rt_device_t rt_device_find(const char* name);

        6)初始化设备:

 

rt_err_t rt_device_init(rt_device_t dev);

        7)打开设备:

 

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);

        8)关闭设备:

 

rt_err_t rt_device_close(rt_device_t dev);

        9)控制设备:

 

rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);

        10)读设备:

 

rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos,void* buffer, rt_size_t size);

        11)写设备:

 

rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos,const void* buffer, rt_size_t size);

        12)数据发送回调函数:

 

rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));

        13)数据接收回调函数:

 

rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));

        注:上面只是记录了函数接口,参数和详细说明,请移步RT-Thread官网:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/device

 

第三部分:I/O设备模型实操(UART)

        使用流程:注册-》查找-》打开-》读取或发送

        1)注册串口设备:

        注:串口的参数配置保存在uart_obj数组中;INIT_BOARD_EXPORT(rt_hw_usart_init)为自动初始化(关于RT-Thread自动初始化机制更多了解,请移步:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/basic/basic?id=rt-thread-%E8%87%AA%E5%8A%A8%E5%88%9D%E5%A7%8B%E5%8C%96%E6%9C%BA%E5%88%B6);

/*
功能:设置所需串口的参数,注册串口
*/
int rt_hw_usart_init(void)
{
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    rt_err_t result = 0;

    stm32_uart_get_dma_config();

    for (rt_size_t i = 0; i < sizeof(uart_obj) / sizeof(struct stm32_uart); i++)
    {
        /* init UART object */
        uart_obj[i].config = &uart_config[i];
        uart_obj[i].serial.ops    = &stm32_uart_ops;
        uart_obj[i].serial.config = config;

        /* register UART device */
        result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
                                       RT_DEVICE_FLAG_RDWR
                                       | RT_DEVICE_FLAG_INT_RX
                                       | RT_DEVICE_FLAG_INT_TX
                                       | uart_obj[i].uart_dma_flag
                                       , NULL);
        RT_ASSERT(result == RT_EOK);
    }
    return result;
}

INIT_BOARD_EXPORT(rt_hw_usart_init);

        2)查找设备:

#define PRO_SENSOR_UART	"uart1"

/*
功能:查找传感器串口
*/
rt_device_t device = rt_device_find(PRO_SENSOR_UART);
if (!device){
    LOG_E("find %s failed!\n", PRO_SENSOR_UART);
    return RT_ERROR;
}

        3)打开设备

/*
功能:以RT_DEVICE_FLAG_DMA_RX方式打开串口
*/
if (rt_device_open(device, RT_DEVICE_FLAG_DMA_RX) != RT_EOK){
    LOG_E(" %s device open error,please check ...", PRO_SENSOR_UART);
}

        4)设置数据接收回调函数:

/*
功能:传感器串口的接受回调函数
*/
static rt_err_t sensor_uart_recv_callfunc(rt_device_t dev, rt_size_t size)
{
    if(size > 0){
       rt_sem_release(&rx_sem);
    }
    return RT_EOK;
}

/*
功能:设置串口的回调函数
*/
if(rt_device_set_rx_indicate(device, sensor_uart_recv_callfunc) != RT_EOK){
    LOG_E("uart_recv callfunc set error ,please check ...");
    return false;
}

        5)接收数据解析函数:

        注:因为传感器部分使用到了软件包Agile Modbus,所以串口接收到数据后,调用了下面的函数进行解析:

bool pro_sensormcu_mosbus_prase(rt_uint8_t *inbuf,rt_uint16_t length) 
{
    rt_uint16_t hold_register[HOLD_REGISTER_NUM];
	
    ctx->read_bufsz = length;
	rt_memcpy(ctx->read_buf,inbuf,ctx->read_bufsz);

    int rc = agile_modbus_deserialize_read_registers(ctx, ctx->read_bufsz, hold_register);
    if (rc < 0) {
        LOG_W("Receive failed.");

        if (rc != -1)
            LOG_W("Error code:%d", -128 - rc);	 
            LOG_I("Hold Registers:");
        
        for (int i = 0; i < HOLD_REGISTER_NUM; i++){
		    LOG_I("Register [%d]: 0x%04X", i, hold_register[i]);
	    }
        return false;  
    }

    LOG_I("Receive success Hold Registers:");
    for (int i = 0; i < HOLD_REGISTER_NUM; i++){
		LOG_I("Register [%d]: 0x%04X", i, hold_register[i]);
	}
	return true;
}

        6)采集到的数据记录:

ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x026C
ph_sensortwomcu1: Register [1]: 0x011A

ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x026B
ph_sensortwomcu1: Register [1]: 0x0119

ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x0268
ph_sensortwomcu1: Register [1]: 0x0118

ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x0249
ph_sensortwomcu1: Register [1]: 0x0118

ph_sensortwomcu1: Receive success Hold Registers:
ph_sensortwomcu1: Register [0]: 0x024A
ph_sensortwomcu1: Register [1]: 0x0117

        结束语:

        这是阅读《RT_Thread设备驱动开发指南》的第一篇笔记,结合外接的温湿度传感器,进行了一个小实验,不足之处,还请大家多多指正!

 

 

 

 

最新回复

本帖最后由 damiaa 于 2023-8-31 10:19 编辑 移步  官方文档 好 https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/device   详情 回复 发表于 2023-8-31 10:16
点赞(1) 关注
 

回复
举报

6107

帖子

4

TA的资源

版主

沙发
 
本帖最后由 damiaa 于 2023-8-31 10:19 编辑

移步  官方文档 好

https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/device

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表