既然串口中断能收数据了。那么第一步就是要及时保存中断接收到的数据,这里就要用利用到”循环缓存“的方法进行数据保存。
什么是循环缓存?
它是代码定义了一段数组缓存Buff,并定义两个下标变量(In = 0, Out = 0),分别指向数组的写和读两端。
通常使用unsigned char类型定义读写下标变量In和Out,因为该类型变量最大值为256,计数到最大值时会自动变成0,这样就方便代码处理,所有设定的缓存数组大小也是256字节。
代码实现逻辑是什么?
当有数据时,数据将逐个放在写(In)端所指向的数组位置,同时写(In)端+1,当写(In)端一直增加指到数组边界时自动复位到数组起始位重新开始写,以此循环。
当需要读取数据时,判断读(Out)端与写(In)端不重合即不相等,则说明数组缓存中有数据未读。此时可逐个读取读端(Out)所指向的数组内容,同时读(Out)端将+1,直到读(Out)与写(In)端重合为止,确保数据全部读完。并且,当读(Out)端也到数组边界时同样会自动复位到数组起始位。
下面几幅图详细说明的循环缓存数组的工作流程
循环数组缓存起始状态写端(In)写入3个字符时,写端(In)所指向的数组位置
读端(Out)读出3个字符时,读端所指向的数组位置,此时读写端重合,即数据读完写端(In)增加到数组边界位置时,写端将返回到初始位置写端(In)返回到起始位置读端(Out)增加到数据边界时,读端将返回到起始位置读端(Out)返回到起始位置接下来,再用代码实现以上逻辑(代码开发环境为STM32CubeIDE)
uint8_t ucUart3Ch = 0;uint8_t aucUsart3_Rev_Buf[256] = {0};// 循环缓存数组,大小256字节volatile uint8_t In = 0, Out = 0; // 写端下标In, 读端下标Out,当计数累计到 256 时自动变成 0// 串口中断接收函数void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle){ if(USART3 == UartHandle->Instance) { // 将收到的1个字节写到缓存数组,写端(In) +1 aucUsart3_Rev_Buf[In++] = ucUart3Ch; HAL_UART_Receive_IT(&huart3, &ucUart3Ch, 1); }}// 获取缓存中未读的所有字节,并返回读取的长度int UART_Get_Data(uint8_t *pData, int iSize){ int pos = 0; if(In != Out){//读写端不相等,即缓存中有数据可读 do{ pData[pos++] = aucUsart3_Rev_Buf[Out++];// 读取1个字节,读端(Out)+1 if(iSize == pos) break;//读到的数据长度已经达到最大值 }while(In != Out); } return pos;}int main(void){ uint8_t aucData[512] = {0}; int iDataLen = 0; while(1){ //发送AT命令,命令发送后最好延时一会,确保模块能返回响应数据 //读取模块响应数据 iDataLen = UART_Get_Data(aucData, sizeof(aucData)); if(0 < iDataLen){ printf("UART Recv:%s\r\n", aucData); /* 响应数据读到后,就可以对其进行处理了 */ } }}另外,这里再举例一个解析AT命令响应的函数,仅供参考。本函数仅仅是解析AT+CSQ命令的响应,即获取信号值强度。(代码开发环境为STM32CubeIDE)
typedef enum{ NB_OK = 0, NB_TIMEOUT = 1, NB_ERROR = 2, NB_DATA_IN = 3, NB_NET_CLOSED = 4, NB_REG_CHANGED = 5}NB_STA;HAL_StatusTypeDef UART_Send_AT(char *pcAt){ return = HAL_UART_Transmit_IT(&huart3, (uint8_t *)pcAt, strlen(pcAt));}int UART_Get_Ch(void){ int ch = -1; if(In != Out){ ch = aucUsart3_Rev_Buf[Out++]; } return ch;}NB_STA AT_Get_CSQ_Val(char *pcSigVal){ //+CSQ:19,xx NB_STA atRet = NB_TIMEOUT; char *AT_CSQ = "AT+CSQ\r\n"; int iTimeout = 3, ch = 0; char csqFlag = 0, csqOk = 0, atOK = 0, atErr = 0; char *acCSQ = "+CSQ:";//定义解析关键字 char acOK[] = {'O','K',0x0D,0x0A}, acErr[] = {'E','R','R','O','R',0x0D,0x0A};//AT命令响应成功和失败关键字 pcSigVal[0] = 0;pcSigVal[1] = 0;//初始化参数 UART_Send_AT(AT_CSQ);HAL_Delay(500);//发送AT+CSQ命令,并延时500ms等待模块响应 do{ ch = UART_Get_Ch();//读取循环缓存数组中的一个字节数据 if(-1 != ch){ //KE1_Put_Console_Ch((uint8_t)ch);//将字节输出到调试串口,方便查看 if(acOK[atOK] == ch){// 检查响应是否包含OK atOK++; }else{ atOK = 0; } if(4 == atOK && 1 == csqOk){ atRet = NB_OK; break; } if(acErr[atErr] == ch){// 检查响应是否包含ERROR atErr++; }else{ atErr = 0; } if(7 == atErr){ atRet = NB_ERROR; break; } if(1 == csqOk){// 响应包含关键字"+CSQ:",并开始获取需要的值 if(',' == ch) { csqFlag = 0x0F; } if(3 > csqFlag){ pcSigVal[csqFlag++] = ch; } continue; } if(acCSQ[csqFlag] == ch){// 检查响应是否包含关键字"+CSQ:" csqFlag++; }else{ csqFlag = 0; } if(5 == csqFlag){csqOk = 1; csqFlag = 0;}// 响应包含关键字"+CSQ:" }else{ if(0 != iTimeout) { iTimeout--; if(0 != iTimeout){ HAL_Delay(500); } } } }while(iTimeout);//判断模块是否响应超时 return atRet;}
|