項目場景:
使用STM32L0單片機主頻設定2.097M,使用LPUART,115200波特率的序列槽進行通信,會出現偶發性的序列槽當機現象。
問題描述
1.序列槽配置
2.回調函數接收資料
嵌入式物聯網需要學的東西真的非常多,千萬不要學錯了路線和内容,導緻工資要不上去!
無償分享大家一個資料包,差不多150多G。裡面學習内容、面經、項目都比較新也比較全!某魚上買估計至少要好幾十。
點選這裡找小助理0元領取:加微信領取資料
理論上通過以上配置,可以正常接收到DMA收到的不定長資料,經簡單測試,每次通過序列槽助手,手動發送資料,确實都能正常接收并處理。
但是,一旦發送資料幀的周期太快,或者發送的資料量超過20個位元組後,序列槽就很容易出現當機的現象。
通過進入仿真發現,暴力發送資料後,不會再進入中斷。把資料解析函數屏蔽掉以後,再進入仿真,進行同樣的測試,每次又都能進入中斷。
通過比較兩次的仿真,發現ISR中斷狀态寄存器ORE标志位被置位後,不會再進入中斷。
原因分析:
查閱資料手冊:
這個ORE的檢測是CubeMX預設打開的,在UART的配置裡面,如下:
産生中斷和狀态寄存器:
**Interrupt & status register (LPUART_ISR)**
清除中斷和狀态寄存器:
**Interrupt flag clear register (LPUART_ICR)**
通過以上資料手冊中内容可知,ORE标志位置位後,必須通過ORECF寄存器清除ORE,否則接收不到新資料。
分析HAL庫的代碼發現:
發生溢出錯誤後,HAL_UART_IRQHandler,經過了三個步驟:
1.清除ORE标志位
2.關閉序列槽接收
3.調用錯誤回調函數
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t isrflags = READ_REG(huart->Instance->ISR);
uint32_t cr1its = READ_REG(huart->Instance->CR1);
uint32_t cr3its = READ_REG(huart->Instance->CR3);
uint32_t errorflags;
uint32_t errorcode;
/* If some errors occur */
if ((errorflags != 0U)
&& (((cr3its & USART_CR3_EIE) != 0U)
|| ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR1_RTOIE)) != 0U)))
{
/* UART Over-Run interrupt occurred -----------------------------------------*/
if (((isrflags & USART_ISR_ORE) != 0U)
&& (((cr1its & USART_CR1_RXNEIE) != 0U) ||
((cr3its & USART_CR3_EIE) != 0U)))
{
__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF);//1.清除ORE标志位
huart->ErrorCode |= HAL_UART_ERROR_ORE;
}
/* If Error is to be considered as blocking :
- Receiver Timeout error in Reception
- Overrun error in Reception
- any error occurs in DMA mode reception
*/
errorcode = huart->ErrorCode;
if ((HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)) ||
((errorcode & (HAL_UART_ERROR_RTO | HAL_UART_ERROR_ORE)) != 0U))
{
/* Blocking error : transfer is aborted
Set the UART state ready to be able to start again the process,
Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
UART_EndRxTransfer(huart);//2.關閉序列槽接收
}
/*Call legacy weak error callback*/
HAL_UART_ErrorCallback(huart);//3.調用錯誤回調函數
}
}
是以解決辦法是,隻需要在HAL_UART_ErrorCallback錯誤回調函數中重新打開序列槽接收即可。
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if(huart == &hlpuart1)
{
__HAL_UNLOCK(huart);
HAL_UARTEx_ReceiveToIdle_DMA(&hlpuart1, (uint8_t*)receive_buff, 20);
}
}
經過以上分析,已經知道了序列槽當機的原因,和初步解決方案,在錯誤回調函數中重新打開序列槽。
但是在實際使用過程中,難免會有大量資料的收發,通過錯誤回調函數中重新打開序列槽隻是治标不治本,ORE溢出錯誤的本意是告知使用者目前通信超負荷需要進行調整,要麼波特率太高,要麼資料量太大資料處理不過來。
波特率一般是終端使用者指定要求的,無法更改,那麼隻能更改在中斷中的資料處理函數了。
解決方案:
要想知道怎麼改中斷中的資料處理函數,就需要明白在一個2MHz主頻系統下,使用中斷(或DMA)模式來接收序列槽資料,波特率為115200的情況下,中斷處理程式允許的理論最大安全時間是多少?
115200的波特率在典型的“1起始位+1終止位+無校驗位+8資料位”的配置下(每個資料幀對應10個bit),實際上對應最大11.52KB/s的資料率——或者說,USART完成中斷每秒鐘發生 11.52K次。即在這一系統中最大允許屏蔽中斷多長時間——1/11.52KHz ≈87us。
由此可知:中斷中的資料處理函數最長執行時間不能超過87us,否則将出現通信超載。
通過perf_counter工具實際測量data_parser代碼的周期消耗:
修改代碼如下:
經測量處理函數執行了2211個指令周期,由1MHz對應1us,1us對應一個指令周期可計算,2.097M的主頻,1us可以執行2.097個指令周期,是以一共需要2211/2.097=1054us,超過了87us。
優化掉data_parser内部的耗時代碼後,重新測量指令周期如下:
經測量處理函數執行了137個指令周期,一共需要137/2.097=66us,低于87us,經暴力測試,未出現溢出中斷,符合預期。
注意:
STM32并不是所有的系列都有這個OverrunDisable開關,相應的也沒有OVRDIS這個寄存器,清除ORE flag标志的方式也不相同。通過在STM32F103上測試,預設是開啟了過載檢測,由軟體序列将其清零(先讀USART_SR,然後讀USART_CR)。
如果有OverrunDisable開關,在不使用HAL庫的中斷回調來實作序列槽接收時最好禁止Overrun,不然出現序列槽接收溢出,RDR寄存器中的值将不會改變,也就讀不到新資料了。
轉載自:AIoT開源項目分享
文章來源于你遇到過序列槽當機嗎
原文連結:https://mp.weixin.qq.com/s/Acj1GrexC57HDULj4voffQ