MCU通信协议FIFO实现的方法.docx
MCU通信利用FIFO可以避免因数据量大而去包的问题.今天通过一种自定义通讯办议格式,给大家讲述,下实现F1.Fo的方法.1概述在此之前,先来列举一下传统串口数据收发的不足之处:每接收一个字节数据.产生一次接收中断“不能有效的利用串口便件F1.FO减少中断次数。应答数据采用等待发送的方法由于中行数据传输的时间远远跟不上CPU的处埋时间,等恃串口发送完当希字节再发送下一字节会造成CPU资源浪也,不利于系统整体响应(在1200bps下.发送一字节大约需要IOms.如果一次发送几十个字节数据,CPU会长时间处于等恃状态),应答数据采用中断发送增加个中断源增加系统的中断次数.这会影响系统整体稳定性(从可常性角度考虑,中断'K件应越少也好),计对上述的不足之处,构结合一个常用自定义通讯协议,提供一个完整的解决方案.2,1.FIFO11FIFO可以理解为率口专用的缓存,该缓存果用先进先出方式.数据接收FIFO和数据发送FIFO通常是独立的两个硬件.小口接收的数据,先放入接也FIFO'I*,当FIFO中的数据达到触发值(通常触发值为1、2,4、8、14字节或者FIFo中的数据虽然没有达到设定值但是一段时间(通常为3.5个字符传辎时间)没有再接收到数据,则通知CPU产生接收中断:发送的数据要先写入发送FIFO.只要发送FIFO未空,硬件会自动发送FIFO中的数据。写入发送FIFO的字节个数受FIFOW大深度影响,通常一次写入最多允许16字节.上述列举的数据跟具体的硬件有关,CPU类型不同,特性也不尽相同,使用前应参考相应的数据手册.3、数据接收与打包FIFO可以援存申口接收到的数据,因此我们可以利用FIFO来减少中断次她以NXP的IpCI778芯片为例,接收F1.Fo的触发级别可以设置为1、2、4,8,M字节,推荐使用8字节或者14字节,这也是PC申口接收F1.Fo的默认伯。这样,当接收到大Ift数据时每8个字节或者14个字节才会产生一次中断(最后一次接收除外),相比接收一个字节即产生一个中断,这种方法串口接收中断次数大大犍少.将接攻FIFO设置为8或者14字节也十分简单,还是以Ipc1.778为例,只需要设置UARTFIFO控制寄存器UnFCR即可.接收的数据要符合通讯协议现定,数据与协议是密不可分的.通常我们需要将接收到的数据根据孙议打包成帧,然后交由上层处理。下面介绍一个自定义的协议帧格式,并给出一个通用打包成帧的方法.自定义协议格式如图3-1所示.幡IW命令长校葡.,1«验帧首;通常是35个OXFF或者OXEE地址号:要进行通讯的设爵的地址编号,1字节命令号;对应不同的功能,1字节长度:数据区域的字节个数.1字节数据:与具体的命令号有关数据区长度可以为整个帧的长度不应超过256字节校裟:弁或和校验(1字节)或/CRCI6校验(2字节),本例使用CRC1.6校验下面介绍如何将接收到的数据按照图3-1所示的格式打包成一帧.3.1 定义数据结构typedefstructUint8_t”dst-buf;指向接收缓存u1.nt8jsfd;帕首标忐,为OxFF或者OxEEuint8-tsfd-f1.ag;找到帧首,一般是35个FF或EEu1.nt8jsfd-count;帧首的个数,僦3T个Uint8_treceived_1.en;已经接收的字节数Uint8find_fram_f1.ag;找到完整帧后,置1uit8-tframe_1.en;本帧数据总长度,这个区域是可选的f1.nd.frame.struct;3.2 初始化数化结构,般放在小11初始化中/* brief初始化寻找帧的数据结构,Paramp_fine_frame:指向打包帧数据结构体变琐 paramdsJbuf:指向帧缓冲区 Paramsfd:帧首标志一般为OxFF或者OxEE /voidin1.t_find_frame_struct(find_frame_structp_find_frame,uint8_t4dst-buf,unt8-tsfd)p.find-frame->dst-,buf三dstbuf;p_find_frame->sfd三sfd;p-find-frame->find-fram-f1.ag=O;p_find_frame->frameJen=IO;p-find-frame->receivedje=;p_find_frame->sfd_count=0;pind-frame->sfd-f1.ag=O;)3.3数据打包程序* brief寻找一帧数据返回处理的数据个数* Paramp_find_frame:指向打包帧数据结构体变优* Param$rc_buf:指向中口接收的原始数据“paramdata_1.en:src_buf本次HHI接收到的原始数据个数* paramSiJm_1.en:帧缓存的火长度* return本次处理的数据个数* /uint32tfindoneframe(findframestruct*Pfindframe,nstuint8t,srcbuf,uint32tdataJen,uint32-tsumJen)U1.nt32srcJen=O;whi1e(data-1.en-)if(p_find_frame->sfd-f1.ag三三O)没有找到起始帧首if(src_bufsrc_1.en»+J=p_find_frame->sfd)p_find_frame->dst_buf(p_find_frame->received_1.en*j=p_find_frameif(+p_find_frame->sfd-count三三S)(p_find_frame->sfd-f1.ag三1.;p_find_frame->sfd-count=0;p_find_frame->framejen=1.;)e1.se(p_fmd_frame->$fd_count=0;p_find_frame->receivedjen=;)e1.se是否是-长度-字节?->获取这帧的数据长度1.f(7=p_find_frame->rece<vedjen)(p_find_frame->frame_1.en=src_buf1.src_1.enj+5+1.+1+1+2;首+地址号命令号数据长度校验if(p-find-frame>frame-1.en>=sum-1.en)(这里处理方法根据具体应用不一定相同MY-DEBUGHS1.AVE_OEBUGr数据长度邮出缓fr1.n");p_find_frame->frame_1.en=sumjen;)p_find_frame->dst_buf(p_find_frame->received_1.en«-4=src_bufsrc_1.en-»*;1.f(p_find_frame->rece1.ved_1.en=p_f1.nd_frame->frame-1.en)(p_find_frame->receivedjen=;一帧完成p_find_frame->sfd-f1.ag=O;p_find_frame->find_fram_f1.ag=1.;returnsrc_1.en;p_find_frame->find_fram_f1.ag=0;returnsrc_1.en;)使用例子:定义数据结构体变hfind_frame_structs1.ave_find_frame_srt;定义接收数据慑冲区:WdefineS1.AVE_REC_DATA_1.EN128u1.nt8_ts1.ave_rec_bufS1.AVE_REC_DATA_1.EN;在串1初始化中诩用结构体变量初始化函数:init_find_frame_struct(&s1.ave_find_frame_$rt,$1.ave_rec_buf,OxEE);在串口接收中断中调用数据打包函数:find_one_frame(&s1.ave_fmd_frame_SrtJmP_rec_buf,data_1.en,S1.AVjREJDATA_IEN);其中,rejbuf是即【I接收临时线冲区,dataJen是本次接校的数据长度"4、数据发送前文提到,传统的等待发送方式会浪费CPU资源,而中断发送方式虽然不会造成CPU资源浪费,但又增加了一个中断源.在我们的使用中发现,定时器中断是几乎每个应用都会使用的,我们可以利用定时器中断以及硬件FIFo来进行数据发送,通过合理设计后,这样的发送方法即不会造成CPU资源浪费,也不会多增加中断源和中断事件,需要提前说明的是,这个方法并不足财所有应用都合适,时于那些没有开定时器中断的应用本方法当然是不支持的,另外如果定时器中断间隔较长而通讯波特率乂特别高的话,本方法也不太适用.公司目前使用的通讯波特率一般比较小(1200bps、2400bps),在这线波特率下.定时渊间隔为IOmS以下(含IOmS)就能满足.如果定时器间隔为Ims以下(含1ms),是可以使用11520ObPS的,本方法主要思想是:定时器中断触发后,判断是否有数据要发送,如果有数据要发送并且满足发送条件,则将数据放入发送FIFO中,对于1.p<1778来说,一次以第可以放16字节数据.之后硬件会自动启动发送.无儒CPU参与.下面介绍如何使用定时潺发送数据,使件载体为RS485因为发送需要操作中口寄存器以及RS485方向控制引脚,需跟硬件密切相关.以下代码使用的硬件为IPCI778.但思以下通用的.4.1 定义数据结构门帧发送结构体/WPedefstruct要发送的帧数据长度当前已经发送的数扭;长度是否发送标忐指向要发送的数据缓冲区(uint1.6_t$end_sum_1.en;uint8_t$end_cur_1.en;u1.nt8_t$end_f1.ag;uint8_t,send_data;uart_send_struct;4.2 定时处理函数/- brief定时发送函数,在定时器中断中调用,不使用发送中断的情况下减少发送等 ParamUARTX:指向硬件Hm寄存器基地址 Paramp:指向电口帧发送结构体变量7*defineFARME_$END_FA1.GOxSAMefineSEND_DATA_NUM12staticvoiduart_send_com(1.PC_UART_TypeDefUARTx,Uart_Send_StrUCt*p)(Uint321i;uint32-ttmp32;if(UART×->1.SR&(0x01«6)J发送为空if(p->sendj1.ag三FARME_SEND_FA1.G)RS485C1.rDE;/过485为发送状态tmp32=p->send_sum_1.en-p->send_cur_1.en;if(tmp32>SEND_DATA_NUM)向发送FIFO埴充字节数据for(i=0;i<send_data_num;i*)UARTx->THR=p->send-data(p->se11d-curJen*>e1.sefor(i=0;i<tmp32;i+)UARTx->THR=p->send_datap->send_curJen+J;)p->send_f1.ag=O;)e1.seRS485SetDE;)</tmp32;i>4)<send-datanum;i+÷)X,1.,.RS485QrDE为宏定义,设置RS485为发送模式:RS485SetDE也为宏定义,设捏RS485为接收模式。使用例子:定义数据结构体变早:uart_send_structuartO_send_str;定义发送缓冲区:uint8_tuartO_send_buf(UARTO_SEND_1.EN;根据使用的硬件申口,对定时处理函数做:次时装:voiduartO_send_datavoid)uart_send_com(1.PC_UARTO,&uartO_send_str);将封装函数UartO_Sen1.data();放入定时器中断处理函数中:在需要发送数据的地方,设而申口帧发送结构体变M:uartO_send_str.send_sum_1.en=data_1.en;/data_1.en为要发送的数据长度uartO_send_$tr.$end_cur_1.en=O;固定为0uartO_send_str.send_data=uartO_send_buf;绑定发送援冲区uartt)_send_str.send_f1.ag=FARME_SEND_FA1.G;设置发送标志