APP下载

基于STM32的多串口并行传输系统设计

2019-01-30,

计算机测量与控制 2019年1期
关键词:线程上位队列

,

(1.武汉纺织大学 数学与计算机学院,武汉 430200; 2.湖北大学 教育学院,武汉 430062)

0 引言

串口作为一种常用的串行通讯接口,由于其标准发布时间早、使用简单,使其在工控和测量设备以及部分通信设备中有着广泛的应用。很多模块设备提供一个串口来进行通讯,如导航定位模块、读卡器模块、打印模块和无线通信模块等。当一些应用需要同时接有多个串口模块时,如果系统串口有限,可以使用分时方式与各个模块进行通讯,但是在一些实时突发传输的场合,这种分时传输方式显然不能够满足使用要求,于是对系统的物理串口数目需求越来越多。在嵌入式应用系统中,很多嵌入式处理器的串口数目通常只有1-3个,如果有更多串口需求就需要使用软件或外部电路扩充串口。

串口扩充方法很多,对实时突发性没有要求时,可以通过模拟开关切换来分时使用串口[1];节约成本可以使用通用IO口来模拟串口[2];追求稳定性和控制简单可以使用一些专用的串口扩展芯片[3-4];高速和更灵活的应用可利用FPGA芯片来实现[5]。

一些嵌入式处理器厂商也意识到多串口的需求,在处理器中提供了多个串口功能,相对于其他串口扩充的方式,在处理器中提供多串口的方式具有可靠性高、传输速率快和实现容易的特点,且外围电路结构简单。如ST公司的STM32F427/429就是其中之一。该系列芯片基于ARM Cortex-M4内核,最高主频能达到180 Mhz,拥有256K SRAM和512 K以上的FLASH,最多能同时提供8个串口、一个USB接口和一个以太网MII/RMII接口[6]。

系统要求采集7路读卡信息,上位机不可用时可独立工作,读卡后对比已录入的卡号给出相应的提示和控制动作,并记录相应的信息。在有上位机时,可通过网络或串口将相关信息上传到上位机,并预留了USB接口。

1 系统硬件与软件结构

1.1 系统硬件结构

由于要求同时接收7路读卡信息,可选一个串口或网口与上位机通信,一共需要8个串口和一个网络接口。系统选用STM32F429VIT6芯片,由于传输距离有数十米左右,采用RS485接口传输,每个串口前面都接有RS485转换芯片,各由一个GPIO端口控制转换芯片的收发,RS485转换芯片可选max3485等芯片,需要隔离功能时可选ADM2582等芯片。STM32F429VIT6只有MII/RMII接口并没有PHY(物理层)接口,需要外接PHY网络接口芯片,可选LAN8742、DP83848等芯片,这里选用的是DP83848。USB接口作为备选上位机通信方案,使用全速模式,无需外接接口芯片。系统另外外接了一片大容量SPI FLASH,用于保存卡号和记录读卡等信息。系统结构和8个二线串口的引脚分配见图1所示。

图1 系统硬件结构图

1.2 系统软件结构

STM32F429各个模块的初始化配置代码使用图形化软件配置工具STM32CubeMX生成,该软件可用图形化向导自动生成初始化代码[7],库版本是STM32Cube_FW_F4_V1.16.0。网络功能选用了轻量级TCP/IP协议栈lwIP,由于系统需要多任务和消息队列等功能,使用了FREERTOS操作系统[8],编译环境为MDK-Arm 5.23。4个线程分配如下:网络通信用一个线程;7个串口接收使用DMA方式,共用一个线程;与PC通信串口收发各用一个线程。

2 软件设计

2.1 串口程序设计

STM32F429的串口在其波特率的16倍采样率下,最大波特率可达5.62 Mbit/s,在8倍采样率下最大波特率可达11.25 Mbit/s,而且串口没有硬件FIFO,每个串口只有一个接收和一个发送寄存器[9],中断接收方式已经不能保证数据的可靠接收。即使在低波特率下,微控制器每接收一个字符都要进入一次中断处理程序,会使微控制器的处理时间大量浪费,再加上8个串口可能同时接收,这时就需要用到微控制器的DMA串口接收功能。串口发送一个字时,会存入到发送数据寄存器TDR,然后由串口硬件逐位发送,此时跟控制器的处理时间没有关系;而串口发送字的字间间隔时间一般没有明显的时序要求,所以串口发送可以不采用DMA方式。

2.1.1 DMA配置

STM32F429拥有2个DMA控制器,每个控制器可与8个数据流中的一个连接,每个数据流可达8个通道(请求),从其DMA请求映射表上可知8个串口的接收可以配置到2个DMA控制器的不同通道上,表1为8个串口接收功能的DMA配置。如果发送也要使用DMA方式,在表1配置的基础上,只有4个串口可以使用DMA发送功能,见表2。

表1 8个串口接收功能的DMA配置

表2 在表1基础上4个串口发送功能的DMA配置

每个串口接收DMA请求的配置如下:

DMA模式是循环方式,DMA源地址是串口接收数据寄存器RDR,属于外围设备,总是从该地址读,源地址不知递增,DMA目的地址是SRAM存储器,从RDR读取后依次存放,所以目的地址要递增。数据宽度根据串口设置选择,如果数据和校验位一起不超过8位则选一个字节,否则选半字长。

2.1.2 循环队列

在串口和DMA初始化之后,运行HAL_UART_Receive_DMA函数多次开启多个串口的DMA接收,其形参分别为各串口句柄、接收缓存区和待接收的数据长度。该函数非阻塞,运行后立即返回。随后开始DMA自动接收,接收的串口数据依次存放在接收缓存区中,超过待接收的长度后,循环覆盖接收缓存区。如果实现HAL_UART_RxHalfCpltCallback回调函数,会在收到待收数据的一半时调用,实现HAL_UART_RxCpltCallback回调函数,会在收到所有数据后调用。要获取其他接收到的数据长度时要使用__HAL_DMA_GET_COUNTER (__HANDLE__)宏,该宏返回DMA中剩余要传输的长度,因此接收到的长度等于待接收的数据长度减去该宏返回的值,当接收到所有待接收长度的数据后,该宏返回0,此时处理不当会丢失数据。

可以把串口接收DMA缓存看作一个循环队列,写该缓存由DMA控制,用户无法干预,写满后自动从头开始,同时更新剩余要传输的长度。如果用户无法在缓存循环覆盖前读取数据,数据就会丢失,可根据情况调整DMA缓存长度UART_SIZE,即使发生覆盖,最近的长度为UART_SIZE的数据是可读取的,这跟普通循环队列有所不同。一次DMA已传输的数据长度为len=UART_SIZE-_HAL_DMA_GET_COUNTER,用户维护已读数据长度read和已接收到的数据总长度total,初始都赋值为0,在UART_RxCpltCallback中更新接收到的数据总长度total+= UART_SIZE,而该回调函数只在DMA接收完数据后调用。实现ReadFromDMA函数从指定串口DMA缓存区读取指定长度数据,并返回实际读取的数据长度,该函数非阻塞。

图2为循环接收缓存示意,表3列举了UART_SIZE=8时DMA接收缓存处理实例。ReadFromDMA函数首先判断总长度total和已读长度read的大小,如read大于等于total,说明本次DMA接收数据长度还没到UART_SIZE,可读数据缓存区从read Mod UART_SIZE开始,长度为len-(read Mod UART_SIZE),如表3中第1和第2数据行。如果read小于total,要判断接收数据总的长度(total+len)-read与缓存长度的大小,如果前者大,说明上次读后发生循环覆盖,说明有数据没有及时读取,已经覆盖了部分数据,可读数据缓存区从len后面开始,可能的数据长度就是UART_SIZE,如表3中第4和第5数据行;如前者小于等于后者,可读数据缓存区从read Mod UART_SIZE开始,长度为UART_SIZE -(read Mod UART_SIZE)+ len,如表3中第3数据行。实际UART_SIZE可取128、256等,这样上述的Mod在C语言中的操作read%UART_SIZE可替换为read&(UART_SIZE-1)。

图2 DMA接收缓存

totalreadlen接收总长可读起始地址可读长度数据丢失024422否81161433否161111736否161172378是802058558是

2.1.3 超时处理

串口接收的数据帧可能是不定长或定长的,不定长的数据帧需要解析其协议,得到该帧数据长度,从而读取完整的一帧数据,定长的数据帧直接接收定长数据即可。不论定长或不定长的数据帧,当接收到一个不完整的数据帧时,如果没有帧的超时处理,会把后面一帧部分数据当作前面帧的一部分,会导致大量后续帧数据错误,甚至可能一直无法接收到一个正确的完整数据帧。

串口超时接收可以利用HAL_GetTick函数实现,该函数返回系统运行的ticks数(1毫秒),通过接收前后调用获取时间差以及待接收和实际接收的数据长度来判断是否超时。

2.1.4 帧协议处理

一般工程中的串口协议相对简单,但是在实际中经常会遇到一些问题。如首次收发失败,后续正常;收发失败后重试多次才能成功,甚至不可恢复;RS485需要多发一个字节等等,这些问题很大部分都是软件引起的。如串口初始化不当可能导致首次收发失败,对RS485收发芯片的收发引脚控制时机不当导致需多发数据。下面通过工程中遇到的两个典型帧协议说明接收的处理方法,这里只讨论数据的完整接收,并不涉及到数据的校验,校验信息可以包含在数据中。

当数据帧是定长时,例如如表4所示的一种读卡器的协议,可以直接一次接收18个字节,然后判断结束标记字符。不加超时功能时,一旦有非18个字节数据,例如只收到16字节,数据不足,会用接收的下一帧数据的前2字节补入,这样导致后续读到的数据都有误。增加接收18个字节的超时功能后,如只收到部分数据,超时时间到会放弃该次接收的数据,后一帧数据正常则能恢复正常接收。这种方法启动超时的时刻需要保证是在接收到第一个字节后,如果不是,例如启动接收就开始超时计时,数据可能在超时结束的时候才收到部分数据,另外一部分数据就丢失了,如图3所示。如果某种情况下收到的数据多于18字节,如果前18字节不是完整数据,也会导致数据丢失。这些异常一般情况下出现的机率不大,但在收发方调试的情况下经常出现。

表4 定长数据帧示例

图3 数据在超时即将结束时出现

系统采用的接收方法流程如图4所示,就是每接收到18个字节判断最后两个字节是否是0D 0A,是则为接收到完整一帧,否则丢弃前面一个字节,再接收一个字节,然后判断最后两个字节是否是0D 0A,如此循环。每接收一个字节后判断是否超时,超时后丢弃所有接收的内容。算法保存最近接收的18个字节内容,没有使用循环队列,所以当收到的18个字节不是完整帧时,丢弃前面一字节,然后后面字节依次左移一字节。可见表4这种帧标记在数据后面的协议并不方便接收处理。

图4 定长协议帧接收处理流程

当数据帧是不定长时,例如表5所示的协议,则处理流程如图5所示。比较定长和不定长数据帧的处理流程,可以发现并不是串口通信协议简单,处理就简单,一个好的串口协议才能简化程序的处理流程。

表5 不定长数据帧示例

图5 不定长协议帧接收处理流程

2.2 上位机通信串口

系统工作时,要求随时可以接收上位机的命令,所以单独使用一个线程接收上位机命令并处理。在接收到读卡信息后,有可能设置读卡信息实时上传,如果加上上位机的通信应答,可能有多条串口数据要发送,这些发送的动作可能在不同的线程中,而与上位机通信串口只有一个,由于线程的调度可能会导致一条数据发送了部分然后切换到另外一个线程发送数据,从而导致接收的数据混乱。这就要求串口发送必需保证一个数据帧完全发送完后才能发送另外一帧,这里采用一个独立的线程单独处理与向上位机发送数据,发送的数据通过消息队列排队,串口向上位机发送流程图如图6所示。

图6 串口向上位机发送流程图

虽然7个串口可能同时接收数据,然后与上位机通信,但是每个串口的接收间隔却较长(刷卡间隔一般为秒级),在此间隔内所有数据都可以上传到上位机,对于8个串口,消息队列长度不小于8即可。消息队列中带的是数据对象的地址,入队列时需要各个串口使用不同的发送缓存区,在此使用了动态内存分配,消息取出后再释放。

2.3 网络传输设计

网络协议栈使用STM32CubeMX自带的轻量级TCP/IP协议栈lwIP,是一个小型的开源协议栈,实现的重点是保持TCP主要功能的基础上减小对系统资源的占用,它需要的RAM和ROM在几十K以下[10]。lwIP有3种编程接口,分别是RAW、NETCONN和SOCKET。RAW使用回调机制,编程接口不需要操作系统的支持,但其编程接口稍复杂;NETCONN接口相对SOCKET接口来说更底层,SOCKET接口被广泛熟知,使用更方便,这两种编程接口都需要有操作系统的支持。

这里使用SOCKET方式编程,采用UDP协议传输,网络通信线程流程如图7所示。网络通信速度相比串口要快很多,只用了一个线程来处理数据收发,多个串口读卡信息同样通过消息队列排队,再经过网络接口发送至上位机。

图7 网络通信线程流程图

3 系统测试与分析

网络测试中发现如上电时不插网线,系统启动后再插入网线,网络功能无法使用。从现象看可能是网络状态改变时协议栈没有得到通知,通过下面步骤解决了这个问题。实现网络状态发生改变时的回调函数ethernetif_notify_conn_ changed,在该函数中判断连接标记,如果不是UP状态,首先调用netif_set_link_up函数,再更改连接标记。

解决上述问题后发现在运行中插拔网线后,网络也无法正常工作。当插拔网线的时候,PHY芯片会产生外部中断,在这个中断中重新初始化网络,外部中断要手工增加。首先将DP83848的第7脚PWR_DOWN/INT配置为中断输出,当网络状态发生改变时,该引脚电平会被拉低,配置微控制器对应的引脚为下降沿触发中断,在中断处理函数中后读DP83848的中断状态寄存器,确定是网络状态发生改变时再调用ETH_MACDMAConfig和HAL_ETH_Init函数,重新初始化网络。

本系统接收特点之一是7路串口数据可能同时到达,在这种情况下,DMA接收方式可以及时读取并保存每个串口RDR中的数据。另一个特点是读卡都有一个秒级的间隔时间,每路读卡数据经波特率为115200 b/s的串口上传至上位机需要2毫秒不到的时间,另外由于发送队列的存在,不论多路读卡信息何时到,读卡间隔时间也足以满足数据上传。

读卡器波特率为9600 b/s,系统7路串口也设为该速率。测试方案一为7路同时读卡,手工刷卡很难做到这一点,采用模拟读卡的方式,直接向读卡串口同时发送卡信息,间隔2秒,测试中7路读卡接收无数据丢失。测试方案二为等间隔读卡,依次轮流向7路串口发送卡信息,每次发送间隔280 ms,上传到上位机的数据无丢失。测试方案三为随机多次模拟和人工刷卡,模拟读卡要设置好每路发送间隔时间大于2秒,多次测试7路读卡串口无数据丢失,上传到上位机的数据无丢失。

4 结束语

利用STM32F429片内的多串口和网络接口功能,设计了多串口并发通信系统,接收多路同时到达的数据,相对于其他串口扩充的方法,无外扩电路,速度高且各串口可工作于不同的速率,测试表明多并发串口传输无数据帧丢失。受限于了读卡器波特率和要求一路串口上传至上位机,并没有测试更高的传输速率,STM32F429的DMA模块每通道有一个16字节的FIFO,更高的串口传输速率可以考虑使用。该设计可供相似应用参考,也可作为网络多串口服务器和USB多串口采集系统参考。

猜你喜欢

线程上位队列
5G终端模拟系统随机接入过程的设计与实现
实时操作系统mbedOS 互斥量调度机制剖析
浅析体育赛事售票系统错票问题的对策研究
队列队形体育教案
队列里的小秘密
基于多队列切换的SDN拥塞控制*
在队列里
特斯拉 风云之老阿姨上位
基于ZigBee和VC上位机的教室智能监测管理系统
基于VC的PLC数据采集管理系统