扑克王官方下载做主营不锈钢机箱机柜钣金外壳加工机箱机柜厂家多年生产经验

服务热线

15303174000
网站导航
产品中心
当前位置:首页 > 产品中心

基于GD32F305的多串口扩展模块设计

来源:扑克王官方下载    发布时间:2025-05-06 14:42:26 点击次数:1 次

  船用很多电子设备是通过RS-422 串口传输数据,比如导航仪通过RS-422 串口传输NMEA - 0183数据,这一些数据包括定位信息,导航信息,船艏信息,雷达信息等。这就要求船上的显示终端需要有很多的串口来接收和发送数据。导航仪主板上的SoC 芯片原生串口数量有限,有时不能使用户得到满足需要,这就需要外接模块来扩展串口。总线连接简单,信号只需要一对差分数据线传输,全速传输模式下带宽可达12 Mbps;常见的船舶电子设备,其中RS-422最高传输需求波特率为115 200 bps,

  本文探讨的扩展模块基于F305设计,采用USB 总线是兆易创新公司出品的一个单片机系列,该系列单片机有一路USB总线 路串口,CPU 核心采用Cortex-M4,可以运行在120 MHz,功能和性能均可满足设计要求。

  方案。图1 是主板信号连接图,为重点说明扩展方案,信号只保留USB 和串口部分。

  RK3128 是瑞芯微出品的ARM Cortex-A7 4 核处理器,RK3128有3 个原生串口,其中串口2 和SD接口复用,实际可用的原生串口只有2 个, 不够连接外部设备,因此采用本文所述方案扩展串口。如上图RK3128 有1 个USB OTG, 和1 个USB HOST 接口, 其中USB OTG用于其它通用外设( 如U 盘,鼠标) 和引导镜像烧写,USB HOST 接口连接

  兆易创新提供了GD32F305 的固件库,其中包含工程模板,启动程序,丰富的外设调用程序及范例程序,并且还有用于简化USB 固件程序设计的USB 程序框架。为加速开发过程,本方案充分的利用了固件库,并参照其中的USB CDC 范例代码, 以USB 程序框架为基础设计了USB 通信程序。单片机程序包括串口收发程序,USB 收发程序,数据转发及命令处理程序,图2 是整个单片机程序的概要图。

  uart_init() 为串口初始化函数,用于初始化所有用到的串口,最重要的包含收发缓冲初始化,串口引脚功能初始化,功能寄存器初始化,中断初始化。

  uart_read() 为串口接收函数,chn 为串口编号, dat 为接收数据缓冲指针,size 为数据缓冲的字节数,返回值为实际读取到的字节数。

  uart_write() 为串口发送函数,chn 为串口编号,dat为要发送的数据指针,size 为要发送的数据字节数,返回实际写入串口发送缓冲的字节数。

  uart_ioctl() 用于响应控制命令,chn 为串口编号,cmd 为命令编号,args 为命令参数,返回值根据不同命令定义。uart_ioctl() 主要处理串口波特率设置,回应当前对应串口发送缓冲字节数这两个功能。

  uart_read() 和 uart_write() 都是非阻塞设计,都是对相应的串口收发缓冲操作,实际数据收发是在中断函数中处理。串口的中断处理函数uart_irq_handle() 定义如下:

  如上代码, 串口中断处理函数uart_irq_handle()调用了固件库串口函数usart_interrupt_flag_get() 来判断当前串口是否触发了接收和发送中断,usart_data_receive() 用于从当前串口接收寄存器读取接收到的数据,usart_data_transmit() 用于将1 个字节的数据写入当前串口的发送寄存器发送数据。bfifo_in_byte() 和bfifo_out_byte() 是一种环形缓冲bfifo 的操作函数,bfifo_in_byte() 用于将1 个字节数据写入缓冲,bfifo_out_byte() 用于从缓冲读取1 个字节数据,成功读取返回true,如果缓冲无数据则返回false。

  串口一次收发字节数不固定,环形缓冲很适合这种中断处理随机字节数据流的收发。环形缓冲是一种有固定存储空间的数据结构,有读、写两个指针,读取缓冲时只操作读指针,不会修改写指针;往缓冲写入数据时只操作写指针而不会修改读指针,环形缓冲的这种指针操作机制使得操作指针时不需要对指针做中断互斥保护,因此不需要在收发数据时关闭开启中断。

  环形缓冲的操作,要将指针的操作限定在环形缓冲大小之内,通常能采用取模运算,比如以f-ptr_out为环形缓冲的读指针,f-size 为环形缓冲的字节大小,当读取完一个字节,读指针前进为例,代码如下:

  本方案采用的bfifo 参照linux kernel 的kfifo, 在上述基础上优化了指针的操作,将环形缓冲的大小限定为2 的n 次方,n 为整数,将取模操作用与运算替代以加速计算过程。同上述例子一样的操作,设f-mask=f-size-1,代码如下:

  由于串口接收到数据后,中断处理函数将数据保存到了环形接收缓冲中,uart_read() 函数只需要从环形接收缓冲将数据读出保存到形参;uart_write() 则将形参指向的数据写入到相应的环形发送缓冲中,并判断当前串口发送中断是否关闭,如果发送中断关闭则重新打开,单片机将触发发送中断,发送环形发送缓冲的数据。

  usb_init() 为初始化函数,主要初始化USB 端口,USB 程序框架用到的定时器,USB 中断,各种USB 描述符等。

  usb_write() 为USB 数据送函数,负责将数据通过bulk 端点发往主机,dat 为要发送的数据指针,size 为要发送的数据字节数,返回实际发送的字节数。

  usb_read() 是USB 数据读取函数,负责读取从主机发送往bulk 端点的数据,dat 为数据接收缓冲指针,size 为缓冲字节数,返回值为实际读取到的字节数。

  USB 数据收发程序相比串口数据收发程序复杂很多,因此本方案借助兆易创新的USB 程序框架来简化设计。USB 程序框架实现了基本的USB 传输,调用固件库提供的USB 设备初始化函数,设置好相应的回调程序指针和USB 描述符,可快速实现基本的USB 数据传输。

  其中参数 udev 为 USB 驱动句柄指针,usbd_init 将初始化其数据结构,之后程序操作 USB 设备将用到该句柄。

  参数 core 为 USB 设备驱动核心枚举。USB 固件库支持 USB 全速和 USB 高速设备,core 用来指示这两种类型设备的其中 1 种。(全速设备带宽为 12 Mbps,可满足设计,本方案实现的是全速设备;高速设备的带宽为480 Mbps,实现高速USB设备,需要外加ULPI芯片。)

  参数desc 为USB 描述符指针,desc 定义了设备描述符、配置描述符、接口描述符等。这些描述符用来描述USB 设备的属性和用途。主机会在枚举设备时获取以确定设备是怎样的设备,需要的总线资源,通讯方式等。

  参数class_core 为USB 类结构体,该结构体定义了USB 类的初始化、反初始化、类请求、数据收发等函数指针,程序在初始化时设置好这些指针,这些指针将在USB 程序框架中被调用。其定义如下:

  其中参数udev 为USB 驱动句柄指针,usbd_init 将初始化其数据结构,之后程序操作USB 设备将用到该句柄。

  参数core 为USB 设备驱动核心枚举。USB 固件库支持USB 全速和USB 高速设备,core 用来指示这两种类型设备的其中1 种。( 全速设备带宽为12 Mbps,可满足设计,本方案实现的是全速设备;高速设备的带宽为480 Mbps,实现高速USB设备,需要外加ULPI 芯片。)

  其中init 为初始化函数指针,当USB 连接时该指针指向的函数被调用,程序可在初始化函数中分配端点,初始化收发缓冲等;deinit 为反初始化函数指针,USB 连接断开时被调用,程序要在这里释放资源;req_proc 为设备请求函数指针,用于处理端点0 控制传输,当主机通过端点0 请求传输时,该指针指向的函数被调用,本方案在这里响应类请求,处理串口波特率设置和串口缓冲大小获取;data_in 是处理data in 传输的函数指针,当主机向USB 设备请求数据时,该指针指向的函数被调用,程序在这里准备好要发往主机的数据;data_out是处理data out 传输的函数指针,当主机往USB 设备发送数据时,该指针指向的函数被调用,程序在这里接收主机下发的数据。

  分析USB 程序框架,USB 数据传输采用DMA,1次可能传输多个字节数据;data_in 和data_out 都是在中断处理程序中被调用,因此本文案设计一种环形缓冲加双缓冲的方案来提高数据传输效率。环形缓冲用于避

  当程序往双缓冲写数据时,先获取写缓冲的地址,写缓冲的地址为buffer+index*buf_len,再将数据写入写缓冲的末尾,地址为buffer+index*buf_len+len,之后再依据数据大小累加len。

  当程序要读取双缓冲数据时,程序先读取当前写缓冲的字节数,获取当前写缓冲的内存地址,再对双缓冲做一次数据缓冲翻转,将原来的读写缓冲互换。双缓冲的翻转,重点是对index 进行反运算,index = !index。当DMA 完成一次传输时,程序能快速翻转双缓冲,将读写缓冲地址交给DMA控制器进行下一次数据传输。如此可达到减少DMA 控制器等待时间的目的,以提高数据传输效率。

  关于往bulk 端点发送数据,本方案定义了一个前文所述的环形缓冲fifo_bulk_in 和双缓冲dbuf_bulk_in 来缓存数据,程序通过调用usb_write() 函数完成。usb_write() 主要负责将形参数据写入fifo_bulk_in,并检测当前USB 框架是否正在传输数据,这个状态由变量is_bulk_in_busy 表示, 如果还未启动数据传输,则取出环形缓冲fifo_bulk_in 的数据转存至dbuf_bulk_in,翻转dbuf_bulk_in,并调用固件库函数usbd_ep_send() 启动一次DMA 传输。当单片机完成一次传输,USB 框架会调用回调函数data_in(),此时根据data_in() 传入的端点号,判断端点号为bulk 端点准备bulk 数据发送。检测fifo_bulk_in 是否有数据和上次传输的字节数是否为空,函数根据以下几种情况处理:

  当主机向单片机请求类的控制传输时,USB 程序框架将调用回调函数req_proc,请求的内容从req_proc 的参数req 获得,req 的类型usb_req 定义如下:

  程序接收到类控制传输请求时,根据req-bmRequestType 判断当前数据传输方向s,如果是IN 类型的传输,则调用前文所述usb_set_class_callback() 设置的回调函数,传递req 的其它参数,如果req-wLength不为0,从回调函数读取数据到全局变量ctlbuf 准备将数据回传给主机。将ctlbuf 和req-wLength 传递给USB程序框架,USB程序框架将发送数据和状态给主机。当数据发送完成时,USB 程序框架调用前文所述data_in 通知程序,程序设置调用API 通知USB 程序框架无剩余数据,完成本次控制传输请求。

  如果当前数据传输类型是OUT 传输时,则先判断req-wLength 是否为0,如果req-wLength 为0 时,直接调用前文所述usb_set_class_callback() 设置的回调函数即可。当req-wLength 不为0 时,表示此次控制传输附带数据,此时程序先用全局变量last_req 暂存req 值,然后调用API 通知USB 程序框架将把此次传输的数据保存到ctlbuf。当USB 程序框架接收完此次传输的数据,将调用前文所述的data_out 通知程序,这时程

  数据转发程序负责将所有串口的数据通过USB 端口转发到主机,同时通过USB 端口从主机读取数据发送给指定的串口。中间的数据传输采用特定的数据格式

  其中sync 为同步头,固定为两个’$’字符,用于解析时找到数据包的起始位置;chn 为串口编号,对应收发数据的串口;len 为数据字节数,用于表示后面dat 的实际大小;dat 为实际收发的数据,此处定义的数组大小不作为实际数据大小。

  本方案定义了函数mux_pack_data() 用于封装串口数据,其声明如下:

  其中dst 为数据缓冲地址,用于存放封装好的数据包;chn 为串口编号;dat 为要传输的数据;len 为上述dat的数据字节数;返回封装后的数据字节数。

  数据转发程序的串口接收部分主要操作为,逐一读取各个串口的数据,调用mux_pack_data() 将数据封装成一个个数据包存储至临时缓冲out_buf,最后调用前文所述USB 收发程序的发送函数usb_write() 将out_buf 的数据发给主机。

  串口发送部分操作为,调用usb_read() 函数从主机读取数据并解析,根据解析的数据包调用uart_write()往对应的串口发送数据。解析函数为mux_parse_data(),其声明如下:

  其中src 和len 为原始数据缓冲指针及数据大小;callback为回调函数;回调函数的参数chn 表示串口编号,dat为数据缓冲指针,len 为数据字节数。这里将从usb_read() 读取到的数据传入参数src 和len,当mux_parse_data() 解析到数据包,将通过callback 通知,此时程序将调用uart_write() 将数据发往指定串口。

  命令处理程序主要负责响应主机设置串口波特率,获取串口写缓冲的请求。这些请求通过USB 控制传输的类请求来处理,程序通过上文所述usb_set_class_callback() 设置类请求回调函数。类请求回调函数声明如下:

  bRequest 用于表示请求的命令,wIndex 表示串口编号,wValue 根据bRequest 不同用于表示设置的值,dat和wLength 用于当前请求需要补充的数据。

  用宏定义表示请求的命令,REQ_SET_BAUD,REQ_ALL_UART_WRITE_ROOM 分别表示设置串口波特率,请求所有串口的剩余写缓冲空间。

  当主机请求所有串口写缓冲时,bRequest 值为REQ_ALL_UART_WRITE_ROOM, 程序调用uart_ioctl() 逐一获取每个串口的剩余写缓冲空间,写至dat参数。传递至dat 的数据结构如下:

  其中chn_num 为串口数量,room 为各个串口的剩余写缓冲字节数,每个串口的剩余写缓冲字节数用2 个字节的类型uint16_t 表示。

  本方案USB 数据传输采用libusb 编写测试程序在LINUX 系统下验证。libusb 是一个在应用层调用的跨平台USB 库,包含了USB 传输所需要的API。相比在编写内核驱动来验证本方案的数据传输,采用libusb 更快捷,更方便调试。

  本方案验证传输采用了libusb 中较为方便调试的同步I/O API,声明如下:

  其中libusb_control_transfer() 用于发起控制传输,参数dev_handle 为USB 设备句柄,data 为补充数据的指针,timeout 为超时毫秒数,其它参数对应控制传输USB 的标准定义。函数返回传输状态,成功传输返回枚举值LIBUSB_SUCCESS。

  4)电脑测试程序通过libusb_bulk_transfer() 从单片机USB 口读取数据,通过转发程序定义的协议解析数据包,根据串口号不同将数据分别存储到不同的文件

  2)电脑测试程序加载测试文件,通过转发程序定义的协议将文件数据按各个串口封装数据包,通过libusb_bulk_transfer() 往单片机USB 口发送数据

  4)对比发送和接收到的文件由于USB 传输速度远高于本方案串口,为避免缓冲溢出导致数据丢失,主机发送数据时应该要依据串口最大发送缓冲大小和设计的最高波特率定时通过libusb_control_transfer() 获取各个串口的剩余发送缓冲空间,测试程序根据单片机串口剩余发送缓冲空间确定当时能发送的字节数。本方案设计单片机串口最大发送缓冲大小为1 024 个字节,最高波特率为115 200,根据最高波特率串口最高发送速度为115 200/10等于11 520 字节/s,根据以上参数可知最大缓冲填满时间为1 024/11 520 ≈ 88 ms,因此本方案轮询单片机串口剩余发送缓冲时间间隔为80 ms 即可。

  mux-test 为测试程序,-b 选项为波特率,这里设置为 115200;-f 选项为测试数据文件名,这里为origin.txt,origin.txt 为64kB 的文本文件;-s 为电脑的串口 tty 设备名,这里/dev/ttyUSB0 - 4 表示加载 /dev/ttyUSB0、/dev/ttyUSB1、…… /dev/ttyUSB4。

  程序运行后,将从电脑串口读取到的数据存储到./tty 目录下,文件根据串口编号命名,为0.txt、1.txt、……4.txt;从USB 读取到的数据存储到./usb 目录下,文件根据单片机串口编号命名,为0.txt、1.txt、…… 4.txt。当程序运行完,运行以下命令比较文件:

  本文首先分析了船舶导航仪对多串口的需求,并提出USB 扩展多串口的方案,分析该方案的可行性及便利性,并提出兆易创新的GD32F305 单片机来实现这一方案。接着以RK3128 主板为例介绍该扩展方案的硬件连接,然后探讨了单片机程序的具体实现,最后介绍用libusb 进行数据传输验证。





上一篇:保藏请紧记永不失联_保藏请紧记永不失联V
下一篇:全球与中国人脸识别旋转门市场战略规划及投资风险展望报告2025版

Copyright © 2002-2020 扑克王官方下载 版权所有  备案号:冀ICP备20015168号-1

地址:河北省沧州市青县马厂镇杨官店 电话:15303174000 主营产品:钣金外壳加工机箱机柜厂家

关注我们