在使用STM32单片机串口接收不固定长度数据包时,采用了每个字节中断,然后接收存储的方式,前面一直会有错误,尤其是波特率比较高(可能相对于该款单片机的主频,其他中断任务过多),甚至到后面没法接收到数据的情况。所以,修改整理了串口处理流程如下,以备后面需要时套用框架:
1 初始化
...
MX_USART4_UART_Init();
if (huart4.gState == HAL_UART_STATE_READY)HAL_UART_Receive_IT(&huart4, &rec_ch_uart4,1);
_cdcCmd.rawVal=0;
...
static void MX_USART4_UART_Init(void)
{
huart4.Instance = USART4;
huart4.Init.BaudRate = 57600;
huart4.Init.WordLength = UART_WORDLENGTH_8B;
huart4.Init.StopBits = UART_STOPBITS_1;
huart4.Init.Parity = UART_PARITY_NONE;
huart4.Init.Mode = UART_MODE_TX_RX;
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart4.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart4) != HAL_OK)
{
Error_Handler();
}
}
MX_USART4_UART_Init(void)是设置后参数后,CubeMX自动生成的
2 错误处理中断回调函数重载
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART4) {
if (huart->ErrorCode & HAL_UART_ERROR_ORE) {
// 处理溢出错误
__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF);
}
if (huart->ErrorCode & HAL_UART_ERROR_FE) {
// 处理帧错误
__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_FEF);
}
HAL_UART_AbortReceive(&huart4); // 重启接收
HAL_UART_Receive_IT(&huart4, &rec_ch_uart4,1);
}
}
3 接收中断回调重载
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance == USART4){
if(rec_counter_uart4<MAX_LEN_USART4){
Uart4_RxBuffer[rec_counter_uart4]=rec_ch_uart4;
rec_counter_uart4++;
usartIdleCounter=UsartIdleMax;
}
if (huart4.gState == HAL_UART_STATE_READY) HAL_UART_Receive_IT(&huart4, &rec_ch_uart4,1);
}
}
保存收到的字节到缓存区,重启接收(1字节中断)
usartIdleCounter计数变成10(或20)ms
4 ms中断处理(systick)
void _embed_ms_loop()
{
msCounter++;
if(msCounter>=1000)
{
if(TTL)
{
_cdcCmd.bits.ReportStateCmd=1;
TTL--;
}else{
TTL=20; //Only for debug
}
msCounter=0;
}
if(usartIdleCounter)
{
usartIdleCounter--;
}else{
_cdcCmd.bits.PackageReady=1;
}
}
如果usartIdleCounter减到0,说明有段时间没有收到字节了,可以处理缓存区里的包
5 主循环
void baidu_interface_embed_main_loop()
{
if(_cdcCmd.bits.PackageReady)
{
parseCDCPackage();
_cdcCmd.bits.PackageReady=0;
}
if(_cdcCmd.bits.SetExtrasCmd)
{
SetSurcharge();
_cdcCmd.bits.SetExtrasCmd=0;
}
if(_cdcCmd.bits.ReportStateCmd)
{
toCDC_reportState();
_cdcCmd.bits.ReportStateCmd=0;
}
if(_cdcCmd.bits.SimpleRspCmd)
{
Respond2Cmd(RspCmd,RspErrCode);
_cdcCmd.bits.SimpleRspCmd=0;
}
if(_cdcCmd.bits.SetRtcCmd)
{
rtc_set();
_cdcCmd.bits.SetRtcCmd=0;
}
}
所有任务主体都在主循环里处理,中断里面只是做些命令置位、清零、缓存、计数等动作以避免长时间在中断内执行。
解析包子函数
void parseCDCPackage()
{
uint8_t packageLen;
if( rec_counter_uart4>6) //7 is the least length of a package
{
if((Uart4_RxBuffer[0]==0xFF) && (Uart4_RxBuffer[1]==0xCD)) //Correct Header and device
{
packageLen=Uart4_RxBuffer[3]+7;
if(rec_counter_uart4>=packageLen)// enough length.
{
//copy package
memcpy(packageBuff,(unsigned char*)(&Uart4_RxBuffer[2]),Uart4_RxBuffer[3]+2);
//check CRC and tail byte
uint8_t ucReadCRC_L=Uart4_RxBuffer[Uart4_RxBuffer[3]+4];
uint8_t ucReadCRC_H=Uart4_RxBuffer[Uart4_RxBuffer[3]+5];
uint16_t ReadCRC = ((uint16_t)ucReadCRC_H<< 8)+ ucReadCRC_L;
uint16_t calc_CRC=Get_CRC(packageBuff,Uart4_RxBuffer[3]+2);
if(calc_CRC==ReadCRC && Uart4_RxBuffer[Uart4_RxBuffer[3]+6]==0xEF)
{
UseCOMData(packageBuff,Uart4_RxBuffer[3]+2);//apply the command;
}
}
}
rec_counter_uart4=0; //clear buffer
}
else
{
HAL_UART_AbortReceive(&huart4); // 重启接收
HAL_UART_DeInit(&huart4);
HAL_UART_Init(&huart4);
rec_counter_uart4=0; //clear buffer
HAL_UART_Receive_IT(&huart4, &rec_ch_uart4,1);
}
usartIdleCounter=250;
}
解析完一个包后,把usartIdleCounter设置为大些(根据需要),那么长时间接收不到包,也会重启串口模块。