一、FIFOFIFO
是First Input First Output的縮寫,先入先出隊列。
使用的場景:一般是在不同的時鐘域之間的數據傳輸(簡單理解即:一個(讀寫)快,另外的一個(讀寫)慢的場景中。)
本質操作:就是將收的數據存儲的一個線性的數組中,通過指針指向該數組的自加1(偏移)來遍歷數據,達到讀取或者寫入的目的。
作用:起到緩沖環節,可防止數據的丟失。
對數據量大的進行存儲,避免頻繁的總線操作。并且可滿足dma的操作。
在fifo中需要理解連個重要成員:
寬度:指一次寫讀操作的數據位數。
深度:存儲多少個寬度的數據。(如:存儲8個16位寬的數據)。
第一類、FIFO處理機制如下:
FIFO信息的定義:
/*
該結構體定義成員有
緩存區,
長度,
輸出,
輸入的計數。
*/
typedefstructfifo_t{
uint8_t*buf;
uint32_tsize;
uint32_tin;
uint32_tout;
}_fifo_str;
#definemin(x,y)((x)(y)?(x):(y))??
1234567891011121314
1、初始化FIFO
fifo_strfifo_str;
intFifoInit(uint8_t*fifo_addr,uint32_tfifo_size)//初始化fifo
{
_fifo_str*p=&fifo_str;
if(fifo_addr==NULL||fifo_size==0)//判斷數據是否為空
return-1;
memset((char*)p,0,sizeof(_fifo_str));//初始化結構體
p->buf=fifo_addr;//對應寬度
p->in=0;//輸入計數
p->out=0;//輸出計數
p->size=fifo_size;//對應深度
return0;
}
12345678910111213141516
2、數據的長度獲取
//數據的實際使用數據空間長度
intFifoDataLen(void)
{
_fifo_str*p=&fifo_str;
return(p->in-p->out);//輸入計數-輸出計數
}
//剩余數據空間長度
intFifoSpaceLen(void)
{
_fifo_str*p=&fifo_str;
return(p->size-(p->in-p->out));//定義長度-(實際長度)
}
12345678910111213
3、FIFO的進和出處理
//獲取fifo數據
//數據的內容緩存區,要讀的長度
intFifoRead(uint8_t*buf,uint32_tlen)
{
uint32_ti=0,j=0;
_fifo_str*p=&fifo_str;
j=(p->out%p->size);//獲取剩余空間長度未讀量
len=min(len,p->in-p->out);//防止長度為超出實際擁有的數據長度,即讓讀取的長度在(0<設定len<定義的緩存區長度len?)這間的實際長度
i=min(len,p->size-j);//獲取實際內容的長度,的數據長度
memcpy(buf,p->buf+j,i);//將數據通道里的數據拷貝給緩存區
memcpy(buf+i,p->buf,len-i);//將未有數據的區域存入,對應為寫入數據的區域數據(即,有數據的填數據,沒數據的地方補0)
p->out+=len;//已讀的數據量
returnlen;//實際讀到的數據長度
}
//對fifo寫入數據
intFifoWrite(uint8_t*buf,uint32_tlen)
{
uint32_ti=0,j=0;
_fifo_str*p=&fifo_str;
j=p->in%p->size;//獲取要寫入的剩余空間長度
len=min(len,p->size-p->in+p->out);//得到實際寫入的長度
i=min(len,p->size-j);//實際寫入數據的長度
memcpy(p->buf+j,buf,i);//將寫入的數據的內容拷貝值數據中。
memcpy(p->buf,buf+i,len-i);//補充多余空間的內容
p->in+=len;//記錄實際寫入數據的數量
returnlen;//返回寫入的長度
}
123456789101112131415161718192021222324252627282930
4、置位記錄量
//清空fifo中的記錄量
voidFifoClear(void)
{
_fifo_str*p=&fifo_str;
p->in=0;
p->out=0;
}
1234567
5、應用處理機制
#defineLEN2048
uint8_tpdata[LEN]={0};
FifoInit(pdata,LEN);//初始化FIFO
uint8_tbuf[32]={0};
inttx_len=0;
uint8_ttx_buf[100]={0};
HAL_UART_Receive_IT(&huart1,buf,IT_LEN);//串口的接收中斷開啟
while(1)
{
tx_len=FifoDataLen();//獲取數據長度
if(tx_len>0)
{
tx_len=(tx_len>100)?100:tx_len;//判讀數據長度是否越界
FifoRead(tx_buf,tx_len);//讀取在中斷中寫入FIFO緩存的數據
HAL_UART_Transmit(&huart1,tx_buf,tx_len,1000);//將讀到的數據通過串口發送出來
}
}
接收回調函數中的處理
HAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)
{
if(FifoSpaceLen()>=串口記錄的接收數據長度)//判斷寫入FIFO空間的數據量是否大于接收的數據量
{
FifoWrite(huart->pRxBuffPtr,huart->RxXferCount);//想FIFO中寫入數據
}
}
123456789101112131415161718192021222324252627
該FIFO的處理機制中用的記錄是通過uint32t類型進行記錄的,可能在遇到超出其數據極限的情況,導致數據通信異常。(該類型的數據極限較大,為特殊情況可能出現的情況)。
第二類、FIFO處理機制如下:
/*定義串口波特率和FIFO緩沖區大小,
分為發送緩沖區和接收緩沖區*/
#ifUART1_FIFO_EN==1
#defineUART1_BAUD115200
#defineUART1_TX_BUF_SIZE1*1024
#defineUART1_RX_BUF_SIZE1*1024
#endif
/*串口設備結構體
設置發送、接收緩存區(長度),
并設置兩個變量,一個是指針,一個是計數
*/
typedefstruct
{
USART_TypeDef*uart;/*STM32內部串口設備指針*/
uint8_t*pTxBuf;/*發送緩沖區*/
uint8_t*pRxBuf;/*接收緩沖區*/
uint16_tusTxBufSize;/*發送緩沖區大小*/
uint16_tusRxBufSize;/*接收緩沖區大小*/
__IOuint16_tusTxWrite;/*發送緩沖區寫指針*/
__IOuint16_tusTxRead;/*發送緩沖區讀指針*/
__IOuint16_tusTxCount;/*等待發送的數據個數*/
__IOuint16_tusRxWrite;/*接收緩沖區寫指針*/
__IOuint16_tusRxRead;/*接收緩沖區讀指針*/
__IOuint16_tusRxCount;/*還未讀取的新數據個數*/
void(*SendBefor)(void);/*開始發送之前的回調函數指針(主要用于RS485切換到發送模式)*/
void(*SendOver)(void);/*發送完畢的回調函數指針(主要用于RS485將發送模式切換為接收模式)*/
void(*ReciveNew)(uint8_t_byte);/*串口收到數據的回調函數指針*/
uint8_tSending;/*正在發送中*/
}UART_T;
/*定義每個串口結構體變量*/
#ifUART1_FIFO_EN==1
staticUART_Tg_tUart1;
staticuint8_tg_TxBuf1[UART1_TX_BUF_SIZE];/*發送緩沖區*/
staticuint8_tg_RxBuf1[UART1_RX_BUF_SIZE];/*接收緩沖區*/
#endif
12345678910111213141516171819202122232425262728293031323334353637383940414243
怎樣才叫做回調函數?回調函數,是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用為調用它所指向的函數時,我們就說這是回調函數。
區別指針函數和函數指針:
//指針函數:
int*fun(intx,inty)
int*x=fun(4,5);
在調用指針函數時,需要同類型的指針來接收函數返回值
是函數,返回值時指針
屬于數據類型
123456
//函數指針:
int(*fun)(intx,inty)
intx(intx,inty);
x=fun;
fun=&x;
x=(*fun)(1,3);
是指針,指向函數。
屬于函數名稱
12345678
1.初始化串口FIFO對應的相關的變量
staticvoidUartVarInit(void)
{
#ifUART1_FIFO_EN==1
g_tUart1.uart=USART1;/*STM32串口設備*/
g_tUart1.pTxBuf=g_TxBuf1;/*發送緩沖區指針*/
g_tUart1.pRxBuf=g_RxBuf1;/*接收緩沖區指針*/
g_tUart1.usTxBufSize=UART1_TX_BUF_SIZE;/*發送緩沖區大小*/
g_tUart1.usRxBufSize=UART1_RX_BUF_SIZE;/*接收緩沖區大小*/
g_tUart1.usTxWrite=0;/*發送FIFO寫索引*/
g_tUart1.usTxRead=0;/*發送FIFO讀索引*/
g_tUart1.usRxWrite=0;/*接收FIFO寫索引*/
g_tUart1.usRxRead=0;/*接收FIFO讀索引*/
g_tUart1.usRxCount=0;/*接收到的新數據個數*/
g_tUart1.usTxCount=0;/*待發送的數據個數*/
g_tUart1.SendBefor=0;/*發送數據前的回調函數*/
g_tUart1.SendOver=0;/*發送完畢后的回調函數*/
g_tUart1.ReciveNew=0;/*接收到新數據后的回調函數*/
g_tUart1.Sending=0;/*正在發送中標志*/
#endif
}
1234567891011121314151617181920
明確中斷服務程序的順序:中斷函數處理:void USART1_IRQHandler(void)—》UART中斷請求:HAL_UART_IRQHandler(UART_HandleTypeDef *huart)—》中斷使能:UART_Receive_IT— 》中斷回調函數 HAL_UART_RxCpltCallback(huart);
#ifUART1_FIFO_EN==1
voidUSART1_IRQHandler(void)//系統中串口的中斷函數入口
{
UartIRQ(&g_tUart1);
}
#endif
1234567
2.編輯自定義的UART中斷請求
staticvoidUartIRQ(UART_T*_pUart)
{
uint32_tisrflags=READ_REG(_pUart->uart->ISR);
uint32_tcr1its=READ_REG(_pUart->uart->CR1);
uint32_tcr3its=READ_REG(_pUart->uart->CR3);
if((isrflags&USART_ISR_RXNE)!=RESET)
{
/*從串口接收數據寄存器讀取數據存放到接收FIFO*/
uint8_tch;
ch=READ_REG(pUart->uart->RDR);
/*讀串口接收數據寄存器*/
_pUart->pRxBuf[_pUart->usRxWrite]=ch;/*填入串口接收FIFO*/
if(++_pUart->usRxWrite>=_pUart->usRxBufSize)/*接收FIFO的寫指針+1*/
{
_pUart->usRxWrite=0;
}
if(_pUart->usRxCountusRxBufSize)/*統計未處理的字節個數*/
{
_pUart->usRxCount++;
}
/*回調函數,通知應用程序收到新數據,一般是發送1個消息或者設置一個標記*/
//if(_pUart->usRxWrite==_pUart->usRxRead)
//if(_pUart->usRxCount==1)
{
if(_pUart->ReciveNew)
{
_pUart->ReciveNew(ch);/*比如,交給MODBUS解碼程序處理字節流*/
}
}
}
/*處理發送緩沖區空中斷*/
if(((isrflags&USART_ISR_TXE)!=RESET)&&(cr1its&USART_CR1_TXEIE)!=RESET)
{
//if(_pUart->usTxRead==_pUart->usTxWrite)
if(_pUart->usTxCount==0)/*發送緩沖區已無數據可取*/
{
/*發送緩沖區的數據已取完時,禁止發送緩沖區空中斷(注意:此時最后1個數據還未真正發送完畢)*/
//USART_ITConfig(_pUart->uart,USART_IT_TXE,DISABLE);
CLEAR_BIT(_pUart->uart->CR1,USART_CR1_TXEIE);/*使能數據發送完畢中斷*/
//USART_ITConfig(_pUart->uart,USART_IT_TC,ENABLE);
SET_BIT(_pUart->uart->CR1,USART_CR1_TCIE);
}
Else/*還有數據等待發送*/
{
_pUart->Sending=1;/*從發送FIFO取1個字節寫入串口發送數據寄存器*/
//USART_SendData(_pUart->uart,_pUart->pTxBuf[_pUart->usTxRead]);
_pUart->uart->TDR=_pUart->pTxBuf[_pUart->usTxRead];
if(++_pUart->usTxRead>=_pUart->usTxBufSize)
{
_pUart->usTxRead=0;
}
_pUart->usTxCount--;
}
}
/*數據bit位全部發送完畢的中斷*/
if(((isrflags&USART_ISR_TC)!=RESET)&&((cr1its&USART_CR1_TCIE)!=RESET))
{
//if(_pUart->usTxRead==_pUart->usTxWrite)
if(_pUart->usTxCount==0)
{/*如果發送FIFO的數據全部發送完畢,禁止數據發送完畢中斷*/
//USART_ITConfig(_pUart->uart,USART_IT_TC,DISABLE);
CLEAR_BIT(_pUart->uart->CR1,USART_CR1_TCIE);/*回調函數,一般用來處理RS485通信,將RS485芯片設置為接收模式,避免搶占總線*/
if(_pUart->SendOver)
{
_pUart->SendOver();
}
_pUart->Sending=0;
}
else
{/*正常情況下,不會進入此分支*/
/*如果發送FIFO的數據還未完畢,則從發送FIFO取1個數據寫入發送數據寄存器*/
//USART_SendData(_pUart->uart,_pUart->pTxBuf[_pUart->usTxRead]);
_pUart->uart->TDR=_pUart->pTxBuf[_pUart->usTxRead];
if(++_pUart->usTxRead>=_pUart->usTxBufSize)
{
_pUart->usTxRead=0;
}
_pUart->usTxCount--;
}
}/*清除中斷標志*/
SET_BIT(_pUart->uart->ICR,UART_CLEAR_PEF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_FEF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_NEF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_OREF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_IDLEF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_TCF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_LBDF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_CTSF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_CMF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_WUF);
SET_BIT(_pUart->uart->ICR,UART_CLEAR_TXFECF);
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
3.填寫數據到UART發送緩沖區。
并啟動發送中斷,中斷處理函數發送完畢后,自動關閉發送中斷 .
staticvoidUartSend(UART_T*_pUart,uint8_t*_ucaBuf,uint16_t_usLen)
{
uint16_ti;
for(i=0;i/*如果發送緩沖區已經滿了,則等待緩沖區空*/
while(1)
{
__IOuint16_tusCount;DISABLE_INT();
usCount=_pUart->usTxCount;
ENABLE_INT();
if(usCountusTxBufSize)
{
break;
}
elseif(usCount==_pUart->usTxBufSize)/*數據已填滿緩沖區*/
{
if((_pUart->uart->CR1&USART_CR1_TXEIE)==0)
{
SET_BIT(_pUart->uart->CR1,USART_CR1_TXEIE);
}
}
}/*將新數據填入發送緩沖區*/
_pUart->pTxBuf[_pUart->usTxWrite]=_ucaBuf[i];
DISABLE_INT();
if(++_pUart->usTxWrite>=_pUart->usTxBufSize)
{
_pUart->usTxWrite=0;
}
_pUart->usTxCount++;
ENABLE_INT();
}
SET_BIT(_pUart->uart->CR1,USART_CR1_TXEIE);/*使能發送中斷(緩沖區空)*/
}
12345678910111213141516171819202122232425262728293031323334
4.向串口發送一組數據。
數據放到發送緩沖區后立即返回,由中斷服務程序在后臺完成發送
voidcomSendBuf(COM_PORT_E_ucPort,uint8_t*_ucaBuf,uint16_t_usLen)
{
UART_T*pUart;
pUart=ComToUart(_ucPort);
if(pUart==0)
{
return;
}
if(pUart->!=0)
{
pUart->SendBefor();/*如果是RS485通信,可以在這個函數中將RS485設置為發送模式*/
}
UartSend(pUart,_ucaBuf,_usLen);
}
123456789101112131415
向串口發送1個字節。數據放到發送緩沖區后立即返回,由中斷服務程序在后臺完成發送
voidcomSendChar(COM_PORT_E_ucPort,uint8_t_ucByte)
{
comSendBuf(_ucPort,&_ucByte,1);
}
123456
函數comSendChar是發送一個字節,通過調用函數comSendBuf實現,而函數comSendBuf又是通過調用函數UartSend實現,這個函數是重點。
5.將COM端口號轉換為UART指針
UART_T*ComToUart(COM_PORT_E_ucPort)
{
if(_ucPort==COM1)
{
#ifUART1_FIFO_EN==1
return&g_tUart1;
#else
return0;
#endif
}
else
{
Error_Handler(__FILE__,__LINE__);
return0;
}
}
1234567891011121314151617
6.從串口接收緩沖區讀取1字節數據
staticuint8_tUartGetChar(UART_T*_pUart,uint8_t*_pByte)
{
uint16_tusCount;/*usRxWrite變量在中斷函數中被改寫,主程序讀取該變量時,必須進行臨界區保護*/
DISABLE_INT();usCount=_pUart->usRxCount;ENABLE_INT();/*如果讀和寫索引相同,則返回0*/
//if(_pUart->usRxRead==usRxWrite)
if(usCount==0)/*已經沒有數據*/
{
return0;
}
else
{
*_pByte=_pUart->pRxBuf[_pUart->usRxRead];/*從串口接收FIFO取1個數據*/
/*改寫FIFO讀索引*/
DISABLE_INT();
if(++_pUart->usRxRead>=_pUart->usRxBufSize)
{
_pUart->usRxRead=0;
}
_pUart->usRxCount--;
ENABLE_INT();
return1;
}
}
1234567891011121314151617181920212223242526272829
從接收緩沖區讀取1字節,非阻塞。無論有無數據均立即返回。
uint8_tcomGetChar(COM_PORT_E_ucPort,uint8_t*_pByte)
{
UART_T*pUart;
pUart=ComToUart(_ucPort);
if(pUart==0)
{
return0;
}
returnUartGetChar(pUart,_pByte);
}
123456789101112
接收數據的調用順序是:comGetChar–》UartGetChar
7.判斷發送緩沖區是否為空
uint8_tUartTxEmpty(COM_PORT_E_ucPort)
{
UART_T*pUart;
uint8_tSending;
pUart=ComToUart(_ucPort);
if(pUart==0)
{
return0;
}
Sending=pUart->Sending;
if(Sending!=0)
{
return0;
}
return1;
}
1234567891011121314151617181920
8.清零串口接收緩沖區
voidcomClearRxFifo(COM_PORT_E_ucPort)
{
UART_T*pUart;
pUart=ComToUart(_ucPort);
if(pUart==0)
{
return;
}
pUart->usRxWrite=0;
pUart->usRxRead=0;
pUart->usRxCount=0;
}
123456789101112
9.清零串口發送緩沖區
voidcomClearTxFifo(COM_PORT_E_ucPort)
{
UART_T*pUart;
pUart=ComToUart(_ucPort);
if(pUart==0)
{
return;
}
pUart->usTxWrite=0;
pUart->usTxRead=0;
pUart->usTxCount=0;
}
1234567891011121314
10.輸入輸出的重定向
intfputc(intch,FILE*f)
{
#if1/*將需要printf的字符通過串口中斷FIFO發送出去,printf函數會立即返回*/
comSendChar(COM1,ch);
returnch;
#else/*采用阻塞方式發送每個字符,等待數據發送完畢*/
/*寫一個字節到USART1*/
USART1->DR=ch;
/*等待發送結束*/
while((USART1->SR&USART_SR_TC)==0)
{}
returnch;
#endif
}
intfgetc(FILE*f)
{
#if1/*從串口接收FIFO中取1個數據,只有取到數據才返回*/
uint8_tucData;
while(comGetChar(COM1,&ucData)==0);
returnucData;
#else
/*等待接收到數據*/
while((USART1->SR&USART_SR_RXNE)==0)
{}
return(int)USART1->DR;
#endif
}
1234567891011121314151617181920212223242526272829
11.應用層初始化:
//FIFO串口初始化
UartVarInit(void);
//串口參數配置
voidbsp_SetUartParam(USART_TypeDef*Instance,uint32_tBaudRate,uint32_tParity,uint32_tMode)
{
UART_HandleTypeDefUartHandle;
/*##-1-配置串口硬件參數######################################*/
/*異步串口模式(UARTMode)*/
/*配置如下:
-字長=8位
-停止位=1個停止位
-校驗=參數Parity
-波特率=參數BaudRate
-硬件流控制關閉(RTSandCTSsignals)*/
UartHandle.Instance=Instance;
UartHandle.Init.BaudRate=BaudRate;
UartHandle.Init.WordLength=UART_WORDLENGTH_8B;
UartHandle.Init.StopBits=UART_STOPBITS_1;
UartHandle.Init.Parity=Parity;
UartHandle.Init.HwFlowCtl=UART_HWCONTROL_NONE;
UartHandle.Init.Mode=Mode;
UartHandle.Init.OverSampling=UART_OVERSAMPLING_16;
if(HAL_UART_Init(&UartHandle)!=HAL_OK)
{
Error_Handler(__FILE__,__LINE__);
}
}
//對應的串口波特率配置
voidcomSetBaud(COM_PORT_E_ucPort,uint32_t_BaudRate)
{
USART_TypeDef*USARTx;
USARTx=ComToUSARTx(_ucPort);
if(USARTx==0)
{
return;
}
bsp_SetUartParam(USARTx,_BaudRate,UART_PARITY_NONE,UART_MODE_TX_RX);
}
//硬件初始化
voidInitHardUart(void)
{
GPIO復用....
/*配置NVICtheNVICforUART*/
HAL_NVIC_SetPriority(USART1_IRQn,0,1);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/*配置波特率、奇偶校驗*/
bsp_SetUartParam(USART1,UART1_BAUD,UART_PARITY_NONE,UART_MODE_TX_RX);
CLEAR_BIT(USART1->SR,USART_SR_TC);/*清除TC發送完成標志*/
CLEAR_BIT(USART1->SR,USART_SR_RXNE);/*清除RXNE接收標志*/
//USART_CR1_PEIE|USART_CR1_RXNEIE
SET_BIT(USART1->CR1,USART_CR1_RXNEIE);/*使能PE.RX接受中斷*/
}
在主循環中
comGetChar(COM1,&read);//獲取一個字節數據
comSendBuf(COM1,(uint8_t*)buf,strlen(buf));//發送數據
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
審核編輯:湯梓紅
-
fifo
+關注
關注
3文章
388瀏覽量
43690 -
FIRST
+關注
關注
0文章
11瀏覽量
11698 -
串口
+關注
關注
14文章
1555瀏覽量
76541
原文標題:對串口接收FIFO處理機制的解讀
文章出處:【微信號:技術讓夢想更偉大,微信公眾號:技術讓夢想更偉大】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論