打印

RTThread从底层AT组件到上层SAL之间的关系

[复制链接]
13764|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
keer_zu|  楼主 | 2021-7-22 17:09 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
RTThread嵌入式系统有着丰富的网络组件,虽然官方提供的文档也很详细,但是各个组件之间的耦关系并不是很明确,这就对于我这种初学者有了很大的难度。这两天我从开始学习设备驱动UART设备->基于UART设备的AT组件->基于AT组件的AT设备和AT Socket->netdev网卡层->SAL套接字抽象层。经过这几天不断的学习,终于简单的弄明白了,它们之间的耦合关系。好记性不烂笔头,我觉得为了防止我忘记,还是需要记录一下比较好。
本**所有内容都是基于正点原子的STM32L475开发板,因为RTThread对此开发板提供了大量的丰富的文档和例程,所以学起来也比较方面,网卡模块使用的是ESP8266,内置lwip协议,采用AT指令基于L475的UART2进行数据传输。

1.设备驱动UART2
1.RTThread驱动大体流程
RTThread提供了基于HAL库的驱动,所以,也不需要自己去写UART的驱动,是需要在env工具中打开使能UART2即可。我这个L475的打开路径如下所示:



使用特权

评论回复

相关帖子

沙发
keer_zu|  楼主 | 2021-7-22 17:12 | 只看该作者
打开以后,在rtconfig.h文件中有找到这个选项:


MDK工程中就会多drv_usart.c,serial.c文件。其中drv_usart.c文件是基于STM32HAL库编写的底层UART驱动文件,不同系列的芯片代码可能不一样,然后serial.c文件是RTThread的串口驱动的框架文件,向下给drv_usart.c提供统一UART操作函数结构体struct rt_uart_ops,然后drv_usart.c将此结构的具体内容实现,然后通过serial.c的rt_hw_serial_register把此结构体注册给serial.c文件使用,然后serial.c文件根据此结构体的不同内容进行封装成RTThread的device的格式通过rt_device_register注册给RTThread内核的设备管理,这样我们就不需要关系底层的具体硬件驱动代码实现,是需要RTThread提供的deviceAPI来访问不同的设备。
其中从硬件底层驱动代码到生成device的关系如下:


使用特权

评论回复
板凳
keer_zu|  楼主 | 2021-7-22 17:46 | 只看该作者

其中rt_hw_serial_register函数和struct rt_uart_ops结构体的内容是serial.c文件提供的,serial.c规定底层硬件驱动代码的功能和把底层功能进行按照rt_device的要求进行封装,然后将封装后的内容注册到内核中,统一管理使用。rt_device函数和struct rt_device结构体是device.c文件提供。

2.UART具体硬件驱动
UART的每一个硬件驱动定义在uart_config.h的文件中,路径libraries\HAL_Drivers\config\l4\uart_config.h。它定义了基于HAL库串口使用的串口基本东西,比如串口1的寄存器地址,串口1的中断服务函数名字,和注册到内核驱动的串口名字,通过此名字可以查找到具体的驱动。


使用特权

评论回复
地板
keer_zu|  楼主 | 2021-7-22 17:54 | 只看该作者


串口所使用的的IO引脚是在STMl4xx_hal_msp.c文件中定义的,让初始化串口是,这个里面针对串口IO的回调函数就会执行,然后就初始化了具体的引脚。路径board\CubeMX_Config\Src\stm32l4xx_hal_msp.c。这个文件是通过STM32CubeMX软件生成的,生成以后我们可以根据具体情况自己修改。


使用特权

评论回复
5
keer_zu|  楼主 | 2021-7-22 17:55 | 只看该作者



然后,串口API操作函数可以通过文件RTThread官网文档来查看。

2.AT组件1.AT基本框架

AT组件是RTThread针对采用AT命令的设备推出的框架,我们使用AT模块主要也就是使用AT框架的AT Client功能,在env工具中打开:



712560f9404f28445.png (548.21 KB )

712560f9404f28445.png

使用特权

评论回复
6
keer_zu|  楼主 | 2021-7-22 17:56 | 只看该作者


然后在构建工程就可以添加到MDK中了。
如果我们自己使用AT框架,只需要通过int at_client_init(const char *dev_name, rt_size_t recv_bufsz)此函数初始化AT框架就可以了,dev_name参数是AT组件与AT模块进行数据通信是所使用的的设备名字,比如我的ESP8266使用的是UART2,这个参数就是UART2传入即可,然后recv_bufsz是一次接受AT命令消息的大小,可以自由设置,小的话接受的AT消息可能不全。
初始化完成以后我们就可以通过调用at.h里面声明的不用的函数名来操作AT设备了,具体函数操作意思,可以通过点击RTThread官网文档来查看。

2.2AT设备框架
AT设备框架主要是struct at_device结构体,路径packages\at_device-v2.0.2\inc\at_device.h。它包含了这个设备所有的功能,包括设备操作函数,at_socket操作函数,这俩是在设备类中包含,所使用的AT组件结构体,注册成网卡的结构体,at_socket结构体。在具体的设备文件驱动中,需要根据设备的实际情况初始化响应的内容。其中的user_data变量存放这个此AT设备全部信息结构体的地址。
需要先使用at_device_class_register函数将此设备的操作函数注册到AT设备类链表中,然后再使用at_device_register函数注册到AT设备链表中。
在AT设备驱动文件中,需要先定义struct at_device_ops结构体变量,然后按照at_device_ops的要求实现具体内容,将其添加到定义的at_device_calss结构体变量中,然后注册到AT设备类链表中。最后AT设备驱动中需要定义struct at_device结构体变量,然后按照它的要求实现具体内容,然后注册到设备链表中。

3.AT_SOCKET
at_socket其实算是at_device的一部分,开启at_device的时候就默认使用了at_socket功能,它简化了我们使用AT设备进行网络开发的步骤,可以直接使用BSD套接字的方法直接在AT设备上进行网络编程。
在AT设备SOCKET驱动文件中,也需要定义struct at_socket_ops变量,然后根据AT基本组件按照at_socket的要求实现其内容,将其添加到定义的at_device_class结构体变量中,最后将at_device_class注册到AT设备类链表中。

3.netdev网卡
netdev网卡是管理每一个网络接口设备,统一提供给上层SAL组件所使用的框架,其具体内容可以参照RTThread netdev官方文档。每一个网络接口设备都使用一个struct netdev结构体变量来管理,包含了设备名字,设备IP、网关等一系列的信息。一般在网卡设备初始化中将其注册为网卡。net_dev的sal_user_data变量保存了该网卡进行BSD编程的操作函数信息。
在AT设备驱动文件中,一部分是根据at_device结构体实现AT设备基本操作函数,另一部分就是根据AT基础组件操作函数AT设备将其网卡相关的功能进行封装,然后将其注册成为网卡设备。先定义一个struct netdev_ops结构体变量,然后根据其要求实现其内容,在使用netdev_register函数将其注册。

4.SAL套接字抽象层
SAL套接字抽象层是根据不同的网络协议栈封装成统一的BSD操作函数,提供使用,包括了LWIP、AT、WIZNET,其具体信息根据RTThread SAL官方文档查看。
其中SAL.h文件里面包含了不同协议栈适配SAL抽象层所需要注册的内容,AT协议栈是在af_net_at.c中进行适配,然后只需要在AT设备文件中在注册netdev前调用sal_at_netdev_set_pf_info()函数就可以将AT协议栈适配到SAL抽象层了,就可以通过sal_socket.h文件进行网络编程了。


使用特权

评论回复
7
keer_zu|  楼主 | 2021-7-22 17:57 | 只看该作者
5.程序调用过程5.1注册过程
INIT_DEVICE_EXPORT(esp8266_device_class_register);RTThread自动初始化组件注册at_device_class
        => class = (struct at_device_class *) rt_calloc(1, sizeof(struct at_device_class)); 分配内存
        => esp8266_socket_class_register(class);
                => class->socket_num = AT_DEVICE_ESP8266_SOCKETS_NUM; ESP8266最大支持的socket数量
                => class->socket_ops = &esp8266_socket_ops保存at_socket_ops到class
        => class->device_ops = &esp8266_device_ops; 保存at_device_ops到class
        => at_device_class_register(class, AT_DEVICE_CLASS_ESP8266); 注册到AT设备类链表
at_device_register()运行设备前注册设备
        => device->sockets = (struct at_socket *) rt_calloc(class->socket_num, sizeof(struct at_socket));根据支持的最大
socket数据分配响应的空间。
        => device->class = class;保存AT设备类首地址到at_device结构体中
        => device->user_data = user_data; 保存用户数据
        => rt_slist_append(&at_device_list, &(device->list)); 保存当前设备结构体到AT设备链表
        => class->device_ops->init(device); 初始化当前设备
                => at_client_init(esp8266->client_name, esp8266->recv_line_num);初始化AT基本组件
                => device->client = at_client_get(esp8266->client_name);保存AT基本组件信息到at_device中
                => at_obj_set_urc_table();设置URC回调函数 针对配置的URC
                => esp8266_socket_init(device);初始化esp8266 socket
                        => at_obj_set_urc_table()也是设备URC回调函数,针对收发消息的URC
                => device->netdev = esp8266_netdev_add(esp8266->device_name); 将esp8266注册成网卡
                        => netdev = (struct netdev *) rt_calloc(1, sizeof(struct netdev));分配内存
                        => netdev->ops = &esp8266_netdev_ops;设备网卡操作函数
                        => sal_at_netdev_set_pf_info(netdev);将AT协议栈操作函数适配到SAL抽象层
                        => netdev_register(netdev, netdev_name, RT_NULL)注册网卡
                => esp8266_netdev_set_up(device->netdev);打开网卡
                        => device = at_device_get_by_name(AT_DEVICE_NAMETYPE_NETDEV, netdev->name);根据名字获取at_device
                        => esp8266_net_init(device);初始化at_device
                                => esp8266_init_thread_entry(device);根据env设置的是否是线程初始化esp8266,当前不是线程初始化
                                        => AT+RST ATE0 等ESP基本功能初始化
                        => netdev_low_level_set_status(netdev, RT_TRUE);设置网卡状态




使用特权

评论回复
8
Fillmore| | 2021-7-27 11:59 | 只看该作者
太强了

使用特权

评论回复
9
keer_zu|  楼主 | 2021-7-27 16:00 | 只看该作者

也在stm32上用rtthread?

使用特权

评论回复
10
cooldog123pp| | 2021-7-31 16:56 | 只看该作者
楼主讲的很详细,非常感谢楼主的讲解,mark一下,收藏学习,受教受教。

使用特权

评论回复
11
keer_zu|  楼主 | 2021-7-31 17:04 | 只看该作者
cooldog123pp 发表于 2021-7-31 16:56
楼主讲的很详细,非常感谢楼主的讲解,mark一下,收藏学习,受教受教。

我在尝试用rtt

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1351

主题

12431

帖子

53

粉丝