打印
[经验分享]

串口数据处理-循环数组缓存

[复制链接]
30|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
uptown|  楼主 | 2024-12-19 13:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
既然串口中断能收数据了。那么第一步就是要及时保存中断接收到的数据,这里就要用利用到”循环缓存“的方法进行数据保存。
什么是循环缓存?
它是代码定义了一段数组缓存Buff,并定义两个下标变量(In = 0, Out = 0),分别指向数组的写和读两端。
通常使用unsigned char类型定义读写下标变量In和Out,因为该类型变量最大值为256,计数到最大值时会自动变成0,这样就方便代码处理,所有设定的缓存数组大小也是256字节。
代码实现逻辑是什么?
当有数据时,数据将逐个放在写(In)端所指向的数组位置,同时写(In)端+1,当写(In)端一直增加指到数组边界时自动复位到数组起始位重新开始写,以此循环。
当需要读取数据时,判断读(Out)端与写(In)端不重合即不相等,则说明数组缓存中有数据未读。此时可逐个读取读端(Out)所指向的数组内容,同时读(Out)端将+1,直到读(Out)与写(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;}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

40

主题

3525

帖子

2

粉丝