一、FIFOFIFO 是FirstInputFirstOutput的缩写,先入先出队列。 使用的场景:一般是在不同的时钟域之间的数据传输(简单理解即:一个(读写)快,另外的一个(读写)慢的场景中。) 本质操作:就是将收的数据存储的一个线性的数组中,通过指针指向该数组的自加1(偏移)来遍历数据,达到读取或者写入的目的。 作用:起到缓冲环节,可防止数据的丢失。 对数据量大的进行存储,避免频繁的总线操作。并且可满足dma的操作。 在fifo中需要理解连个重要成员: 宽度:指一次写读操作的数据位数。 深度:存储多少个宽度的数据。(如:存储8个16位宽的数据)。第一类、FIFO处理机制如下: FIFO信息的定义:该结构体定义成员有缓存区,长度,输出,输入的计数。typedefstructfifot{uint8tbuf;uint32tsize;uint32tin;uint32tout;}fifostr;definemin(x,y)((x)(y)?(x):(y))12345678910111213141、初始化FIFOfifostrfifostr;intFifoInit(uint8tfifoaddr,uint32tfifosize)初始化fifo{fifostrpfifostr;if(fifoaddrNULLfifosize0)判断数据是否为空return1;memset((char)p,0,sizeof(fifostr));初始化结构体pbuffifoaddr;对应宽度pin0;输入计数pout0;输出计数psizefifosize;对应深度return0;}123456789101112131415162、数据的长度获取数据的实际使用数据空间长度intFifoDataLen(void){fifostrpfifostr;return(pinpout);输入计数输出计数}剩余数据空间长度intFifoSpaceLen(void){fifostrpfifostr;return(psize(pinpout));定义长度(实际长度)}123456789101112133、FIFO的进和出处理获取fifo数据数据的内容缓存区,要读的长度intFifoRead(uint8tbuf,uint32tlen){uint32ti0,j0;fifostrpfifostr;j(poutpsize);获取剩余空间长度未读量lenmin(len,pinpout);防止长度为超出实际拥有的数据长度,即让读取的长度在(0设定len定义的缓存区长度len)这间的实际长度imin(len,psizej);获取实际内容的长度,的数据长度memcpy(buf,pbufj,i);将数据通道里的数据拷贝给缓存区memcpy(bufi,pbuf,leni);将未有数据的区域存入,对应为写入数据的区域数据(即,有数据的填数据,没数据的地方补0)poutlen;已读的数据量returnlen;实际读到的数据长度}对fifo写入数据intFifoWrite(uint8tbuf,uint32tlen){uint32ti0,j0;fifostrpfifostr;jpinpsize;获取要写入的剩余空间长度lenmin(len,psizepinpout);得到实际写入的长度imin(len,psizej);实际写入数据的长度memcpy(pbufj,buf,i);将写入的数据的内容拷贝值数据中。memcpy(pbuf,bufi,leni);补充多余空间的内容pinlen;记录实际写入数据的数量returnlen;返回写入的长度}1234567891011121314151617181920212223242526272829304、置位记录量清空fifo中的记录量voidFifoClear(void){fifostrpfifostr;pin0;pout0;}12345675、应用处理机制defineLEN2048uint8tpdata〔LEN〕{0};FifoInit(pdata,LEN);初始化FIFOuint8tbuf〔32〕{0};inttxlen0;uint8ttxbuf〔100〕{0};HALUARTReceiveIT(huart1,buf,ITLEN);串口的接收中断开启while(1){txlenFifoDataLen();获取数据长度if(txlen0){txlen(txlen100)?100:txlen;判读数据长度是否越界FifoRead(txbuf,txlen);读取在中断中写入FIFO缓存的数据HALUARTTransmit(huart1,txbuf,txlen,1000);将读到的数据通过串口发送出来}}接收回调函数中的处理HALUARTRxCpltCallback(UARTHandleTypeDefhuart){if(FifoSpaceLen()串口记录的接收数据长度)判断写入FIFO空间的数据量是否大于接收的数据量{FifoWrite(huartpRxBuffPtr,huartRxXferCount);想FIFO中写入数据}}123456789101112131415161718192021222324252627 该FIFO的处理机制中用的记录是通过uint32t类型进行记录的,可能在遇到超出其数据极限的情况,导致数据通信异常。(该类型的数据极限较大,为特殊情况可能出现的情况)。第二类、FIFO处理机制如下:定义串口波特率和FIFO缓冲区大小,分为发送缓冲区和接收缓冲区ifUART1FIFOEN1defineUART1BAUD115200defineUART1TXBUFSIZE11024defineUART1RXBUFSIZE11024endif串口设备结构体设置发送、接收缓存区(长度),并设置两个变量,一个是指针,一个是计数typedefstruct{USARTTypeDefuart;STM32内部串口设备指针uint8tpTxBuf;发送缓冲区uint8tpRxBuf;接收缓冲区uint16tusTxBufSize;发送缓冲区大小uint16tusRxBufSize;接收缓冲区大小IOuint16tusTxWrite;发送缓冲区写指针IOuint16tusTxRead;发送缓冲区读指针IOuint16tusTxCount;等待发送的数据个数IOuint16tusRxWrite;接收缓冲区写指针IOuint16tusRxRead;接收缓冲区读指针IOuint16tusRxCount;还未读取的新数据个数void(SendBefor)(void);开始发送之前的回调函数指针(主要用于RS485切换到发送模式)void(SendOver)(void);发送完毕的回调函数指针(主要用于RS485将发送模式切换为接收模式)void(ReciveNew)(uint8tbyte);串口收到数据的回调函数指针uint8tSending;正在发送中}UARTT;定义每个串口结构体变量ifUART1FIFOEN1staticUARTTgtUart1;staticuint8tgTxBuf1〔UART1TXBUFSIZE〕;发送缓冲区staticuint8tgRxBuf1〔UART1RXBUFSIZE〕;接收缓冲区endif12345678910111213141516171819202122232425262728293031323334353637383940414243 怎样才叫做回调函数?回调函数,是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。 区别指针函数和函数指针:指针函数:intfun(intx,inty)intxfun(4,5);在调用指针函数时,需要同类型的指针来接收函数返回值是函数,返回值时指针属于数据类型123456函数指针:int(fun)(intx,inty)intx(intx,inty);xfun;funx;x(fun)(1,3);是指针,指向函数。属于函数名称123456781。初始化串口FIFO对应的相关的变量staticvoidUartVarInit(void){ifUART1FIFOEN1gtUart1。uartUSART1;STM32串口设备gtUart1。pTxBufgTxBuf1;发送缓冲区指针gtUart1。pRxBufgRxBuf1;接收缓冲区指针gtUart1。usTxBufSizeUART1TXBUFSIZE;发送缓冲区大小gtUart1。usRxBufSizeUART1RXBUFSIZE;接收缓冲区大小gtUart1。usTxWrite0;发送FIFO写索引gtUart1。usTxRead0;发送FIFO读索引gtUart1。usRxWrite0;接收FIFO写索引gtUart1。usRxRead0;接收FIFO读索引gtUart1。usRxCount0;接收到的新数据个数gtUart1。usTxCount0;待发送的数据个数gtUart1。SendBefor0;发送数据前的回调函数gtUart1。SendOver0;发送完毕后的回调函数gtUart1。ReciveNew0;接收到新数据后的回调函数gtUart1。Sending0;正在发送中标志endif}1234567891011121314151617181920 明确中断服务程序的顺序:中断函数处理:voidUSART1IRQHandler(void)》UART中断请求:HALUARTIRQHandler(UARTHandleTypeDefhuart)》中断使能:UARTReceiveIT》中断回调函数HALUARTRxCpltCallback(huart);ifUART1FIFOEN1voidUSART1IRQHandler(void)系统中串口的中断函数入口{UartIRQ(gtUart1);}endif1234567 嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去! 无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。 点击这里找小助理0元领取:嵌入式物联网学习资料(头条) 2。编辑自定义的UART中断请求staticvoidUartIRQ(UARTTpUart){uint32tisrflagsREADREG(pUartuartISR);uint32tcr1itsREADREG(pUartuartCR1);uint32tcr3itsREADREG(pUartuartCR3);if((isrflagsUSARTISRRXNE)!RESET){从串口接收数据寄存器读取数据存放到接收FIFOuint8tch;chREADREG(pUartuartRDR);读串口接收数据寄存器pUartpRxBuf〔pUartusRxWrite〕ch;填入串口接收FIFOif(pUartusRxWritepUartusRxBufSize)接收FIFO的写指针1{pUartusRxWrite0;}if(pUartusRxCountpUartusRxBufSize)统计未处理的字节个数{pUartusRxCount;}回调函数,通知应用程序收到新数据,一般是发送1个消息或者设置一个标记if(pUartusRxWritepUartusRxRead)if(pUartusRxCount1){if(pUartReciveNew){pUartReciveNew(ch);比如,交给MODBUS解码程序处理字节流}}}处理发送缓冲区空中断if(((isrflagsUSARTISRTXE)!RESET)(cr1itsUSARTCR1TXEIE)!RESET){if(pUartusTxReadpUartusTxWrite)if(pUartusTxCount0)发送缓冲区已无数据可取{发送缓冲区的数据已取完时,禁止发送缓冲区空中断(注意:此时最后1个数据还未真正发送完毕)USARTITConfig(pUartuart,USARTITTXE,DISABLE);CLEARBIT(pUartuartCR1,USARTCR1TXEIE);使能数据发送完毕中断USARTITConfig(pUartuart,USARTITTC,ENABLE);SETBIT(pUartuartCR1,USARTCR1TCIE);}Else还有数据等待发送{pUartSending1;从发送FIFO取1个字节写入串口发送数据寄存器USARTSendData(pUartuart,pUartpTxBuf〔pUartusTxRead〕);pUartuartTDRpUartpTxBuf〔pUartusTxRead〕;if(pUartusTxReadpUartusTxBufSize){pUartusTxRead0;}pUartusTxCount;}}数据bit位全部发送完毕的中断if(((isrflagsUSARTISRTC)!RESET)((cr1itsUSARTCR1TCIE)!RESET)){if(pUartusTxReadpUartusTxWrite)if(pUartusTxCount0){如果发送FIFO的数据全部发送完毕,禁止数据发送完毕中断USARTITConfig(pUartuart,USARTITTC,DISABLE);CLEARBIT(pUartuartCR1,USARTCR1TCIE);回调函数,一般用来处理RS485通信,将RS485芯片设置为接收模式,避免抢占总线if(pUartSendOver){pUartSendOver();}pUartSending0;}else{正常情况下,不会进入此分支如果发送FIFO的数据还未完毕,则从发送FIFO取1个数据写入发送数据寄存器USARTSendData(pUartuart,pUartpTxBuf〔pUartusTxRead〕);pUartuartTDRpUartpTxBuf〔pUartusTxRead〕;if(pUartusTxReadpUartusTxBufSize){pUartusTxRead0;}pUartusTxCount;}}清除中断标志SETBIT(pUartuartICR,UARTCLEARPEF);SETBIT(pUartuartICR,UARTCLEARFEF);SETBIT(pUartuartICR,UARTCLEARNEF);SETBIT(pUartuartICR,UARTCLEAROREF);SETBIT(pUartuartICR,UARTCLEARIDLEF);SETBIT(pUartuartICR,UARTCLEARTCF);SETBIT(pUartuartICR,UARTCLEARLBDF);SETBIT(pUartuartICR,UARTCLEARCTSF);SETBIT(pUartuartICR,UARTCLEARCMF);SETBIT(pUartuartICR,UARTCLEARWUF);SETBIT(pUartuartICR,UARTCLEARTXFECF);}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293943。填写数据到UART发送缓冲区。 并启动发送中断,中断处理函数发送完毕后,自动关闭发送中断。staticvoidUartSend(UARTTpUart,uint8tucaBuf,uint16tusLen){uint16ti;for(i0;iusLen;i){如果发送缓冲区已经满了,则等待缓冲区空while(1){IOuint16tusCount;DISABLEINT();usCountpUartusTxCount;ENABLEINT();if(usCountpUartusTxBufSize){break;}elseif(usCountpUartusTxBufSize)数据已填满缓冲区{if((pUartuartCR1USARTCR1TXEIE)0){SETBIT(pUartuartCR1,USARTCR1TXEIE);}}}将新数据填入发送缓冲区pUartpTxBuf〔pUartusTxWrite〕ucaBuf〔i〕;DISABLEINT();if(pUartusTxWritepUartusTxBufSize){pUartusTxWrite0;}pUartusTxCount;ENABLEINT();}SETBIT(pUartuartCR1,USARTCR1TXEIE);使能发送中断(缓冲区空)}123456789101112131415161718192021222324252627282930313233344。向串口发送一组数据。 数据放到发送缓冲区后立即返回,由中断服务程序在后台完成发送voidcomSendBuf(COMPORTEucPort,uint8tucaBuf,uint16tusLen){UARTTpUart;pUartComToUart(ucPort);if(pUart0){return;}if(pUart!0){pUartSendBefor();如果是RS485通信,可以在这个函数中将RS485设置为发送模式}UartSend(pUart,ucaBuf,usLen);}123456789101112131415 向串口发送1个字节。数据放到发送缓冲区后立即返回,由中断服务程序在后台完成发送voidcomSendChar(COMPORTEucPort,uint8tucByte){comSendBuf(ucPort,ucByte,1);}123456 函数comSendChar是发送一个字节,通过调用函数comSendBuf实现,而函数comSendBuf又是通过调用函数UartSend实现,这个函数是重点。5。将COM端口号转换为UART指针UARTTComToUart(COMPORTEucPort){if(ucPortCOM1){ifUART1FIFOEN1returngtUart1;elsereturn0;endif}else{ErrorHandler(FILE,LINE);return0;}}12345678910111213141516176。从串口接收缓冲区读取1字节数据staticuint8tUartGetChar(UARTTpUart,uint8tpByte){uint16tusCount;usRxWrite变量在中断函数中被改写,主程序读取该变量时,必须进行临界区保护DISABLEINT();usCountpUartusRxCount;ENABLEINT();如果读和写索引相同,则返回0if(pUartusRxReadusRxWrite)if(usCount0)已经没有数据{return0;}else{pBytepUartpRxBuf〔pUartusRxRead〕;从串口接收FIFO取1个数据改写FIFO读索引DISABLEINT();if(pUartusRxReadpUartusRxBufSize){pUartusRxRead0;}pUartusRxCount;ENABLEINT();return1;}}1234567891011121314151617181920212223242526272829 从接收缓冲区读取1字节,非阻塞。无论有无数据均立即返回。uint8tcomGetChar(COMPORTEucPort,uint8tpByte){UARTTpUart;pUartComToUart(ucPort);if(pUart0){return0;}returnUartGetChar(pUart,pByte);}123456789101112 接收数据的调用顺序是:comGetChar》UartGetChar7。判断发送缓冲区是否为空uint8tUartTxEmpty(COMPORTEucPort){UARTTpUart;uint8tSending;pUartComToUart(ucPort);if(pUart0){return0;}SendingpUartSending;if(Sending!0){return0;}return1;}12345678910111213141516171819208。清零串口接收缓冲区voidcomClearRxFifo(COMPORTEucPort){UARTTpUart;pUartComToUart(ucPort);if(pUart0){return;}pUartusRxWrite0;pUartusRxRead0;pUartusRxCount0;}1234567891011129。清零串口发送缓冲区voidcomClearTxFifo(COMPORTEucPort){UARTTpUart;pUartComToUart(ucPort);if(pUart0){return;}pUartusTxWrite0;pUartusTxRead0;pUartusTxCount0;}123456789101112131410。输入输出的重定向intfputc(intch,FILEf){if1将需要printf的字符通过串口中断FIFO发送出去,printf函数会立即返回comSendChar(COM1,ch);returnch;else采用阻塞方式发送每个字符,等待数据发送完毕写一个字节到USART1USART1DRch;等待发送结束while((USART1SRUSARTSRTC)0){}returnch;endif}intfgetc(FILEf){if1从串口接收FIFO中取1个数据,只有取到数据才返回uint8tucData;while(comGetChar(COM1,ucData)0);returnucData;else等待接收到数据while((USART1SRUSARTSRRXNE)0){}return(int)USART1DR;endif}123456789101112131415161718192021222324252627282911。应用层初始化:FIFO串口初始化UartVarInit(void);串口参数配置voidbspSetUartParam(USARTTypeDefInstance,uint32tBaudRate,uint32tParity,uint32tMode){UARTHandleTypeDefUartHandle;1配置串口硬件参数异步串口模式(UARTMode)配置如下:字长8位停止位1个停止位校验参数Parity波特率参数BaudRate硬件流控制关闭(RTSandCTSsignals)UartHandle。InstanceInstance;UartHandle。Init。BaudRateBaudRate;UartHandle。Init。WordLengthUARTWORDLENGTH8B;UartHandle。Init。StopBitsUARTSTOPBITS1;UartHandle。Init。ParityParity;UartHandle。Init。HwFlowCtlUARTHWCONTROLNONE;UartHandle。Init。ModeMode;UartHandle。Init。OverSamplingUARTOVERSAMPLING16;if(HALUARTInit(UartHandle)!HALOK){ErrorHandler(FILE,LINE);}}对应的串口波特率配置voidcomSetBaud(COMPORTEucPort,uint32tBaudRate){USARTTypeDefUSARTx;USARTxComToUSARTx(ucPort);if(USARTx0){return;}bspSetUartParam(USARTx,BaudRate,UARTPARITYNONE,UARTMODETXRX);}硬件初始化voidInitHardUart(void){GPIO复用。。。。配置NVICtheNVICforUARTHALNVICSetPriority(USART1IRQn,0,1);HALNVICEnableIRQ(USART1IRQn);配置波特率、奇偶校验bspSetUartParam(USART1,UART1BAUD,UARTPARITYNONE,UARTMODETXRX);CLEARBIT(USART1SR,USARTSRTC);清除TC发送完成标志CLEARBIT(USART1SR,USARTSRRXNE);清除RXNE接收标志USARTCR1PEIEUSARTCR1RXNEIESETBIT(USART1CR1,USARTCR1RXNEIE);使能PE。RX接受中断}在主循环中comGetChar(COM1,read);获取一个字节数据comSendBuf(COM1,(uint8t)buf,strlen(buf));发送数据12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 文章链接:https:mp。weixin。qq。comsh1L6sQ8OOiFf3KJq0ZjnVg 转载自:技术让梦想更伟大 文章来源:对串口接收FIFO处理机制的解读 版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。 END