如何解析通过USART串口环形缓冲区里的数据

我通过USART接收中断将收到的数据用环形缓冲区存储在了一个数组RxBuffer[128]里,上位机每隔20ms向该数组写入23个数据(含帧头0x55),通过仿真发现RxBuffer[128]里确实收到了[55 01 02 …22 0x55 01 02…22 55…]当超过128会覆盖RxBuffer[0],以此类推。至此认为环形缓冲接收已完成。但我在20ms任务里想把RxBuffer[128]里的数据解析出来,也就是想得到[55 01 02 …22],这部分20ms的程序怎么编写?

基于new bing的编写:
根据您的描述,我可以给出如下解析代码的思路:

首先定义两个指针变量 start 和 end,分别代表环形缓冲区中有效数据段的起始位置和结束位置。初始时,start = end = 0。

在每次接收到新的数据时,将数据存储在 RxBuffer[end] 处,并更新 end 的值,具体实现可以参考以下代码:

void USART_RX_IRQHandler(void)
{
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        RxBuffer[end] = USART_ReceiveData(USART1);
        end = (end + 1) % BUFFER_SIZE;
    }
}

其中,BUFFER_SIZE 宏定义了环形缓冲区的大小。

在数据解析部分,首先计算有效数据段的长度 len,这里可以使用以下代码:

int len = (end >= start) ? (end - start) : (end + BUFFER_SIZE - start);

然后从有效数据段的起始位置 start 开始遍历,查找帧头为 0x55 的数据段,并判断其长度是否为 23。如果是,则表示找到一帧完整的数据,进行解析处理。处理完成后,更新 start 的值。具体实现可以参考以下代码:

while(len >= 23)
{
    if(RxBuffer[start] == 0x55 && RxBuffer[(start + 22) % BUFFER_SIZE] == 0x55)
    {
        // 找到一帧完整的数据
        uint8_t data[23];
        for(int i = 0; i < 23; i++)
        {
            data[i] = RxBuffer[(start + i) % BUFFER_SIZE];
        }
        // 进行解析处理
        parse_data(data);
        // 更新 start 的值
        start = (start + 23) % BUFFER_SIZE;
        // 更新有效数据段的长度
        len = (end >= start) ? (end - start) : (end + BUFFER_SIZE - start);
    }
    else
    {
        // 数据不完整,跳过
        start = (start + 1) % BUFFER_SIZE;
        len = (end >= start) ? (end - start) : (end + BUFFER_SIZE - start);
    }
}

其中,parse_data 函数需要根据您实际的需求来编写。


#define BUFFER_SIZE 128
uint8_t RxBuffer[BUFFER_SIZE];
uint8_t *readPtr = RxBuffer;
uint8_t *writePtr = RxBuffer;

void ParseFrame(void)
{
    while (readPtr != writePtr) {
        // 检查是否有完整的一帧数据
        if (*readPtr == 0x55 && (writePtr - readPtr >= 23)) {
            // 读取帧头、数据长度和数据内容
            uint8_t length = *(readPtr + 1);
            uint8_t data[20];
            for (int i = 0; i < length; i++) {
                data[i] = *(readPtr + 2 + i);
            }
            uint8_t checksum = *(readPtr + 2 + length);

            // 处理一帧数据
            if (VerifyChecksum(data, length, checksum)) {
                ProcessFrame(data, length);
            }

            // 移动指针
            readPtr += length + 3;
        } else {
            break;
        }
    }

    // 缓冲区已经为空,重置指针
    if (readPtr == writePtr) {
        readPtr = writePtr = RxBuffer;
    }
}

void USART_IRQHandler(void)
{
    if (USART_GetITStatus(USARTx, USART_IT_RXNE) != RESET) {
        uint8_t data = USART_ReceiveData(USARTx);

        // 将数据存储到缓冲区中
        *writePtr++ = data;
        if (writePtr >= RxBuffer + BUFFER_SIZE) {
            writePtr = RxBuffer;
        }
    }
}

void TIM_IRQHandler(void)
{
    if (TIM_GetITStatus(TIMx, TIM_IT_Update) != RESET) {
        // 解析缓冲区中的数据
        ParseFrame();

        TIM_ClearITPendingBit(TIMx, TIM_IT_Update);
    }
}

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
参考代码如下:

// 定义接收缓冲区
#define RX_BUFFER_SIZE 128
uint8_t RxBuffer[RX_BUFFER_SIZE];
// 接收缓冲区读写指针
volatile uint8_t RxReadPos = 0;
volatile uint8_t RxWritePos = 0;

// USART接收中断处理函数
void USART_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        // 读取接收到的数据
        uint8_t data = (uint8_t)USART_ReceiveData(USART1);
        // 将数据存入环形缓冲区
        RxBuffer[RxWritePos++] = data;
        RxWritePos %= RX_BUFFER_SIZE;
    }
}

// 解析接收缓冲区中数据的函数
void parseRxBuffer(void) {
    // 检查缓冲区是否有足够的数据可供解析
    if (RxWritePos < RxReadPos) {
        RxWritePos += RX_BUFFER_SIZE;
    }
    if (RxWritePos - RxReadPos < 23) {
        // 数据不完整,等待下一个20ms任务再解析
        return;
    }
    // 查找帧头0x55
    for (uint8_t i = 0; i < RX_BUFFER_SIZE - 22; i++) {
        if (RxBuffer[(RxReadPos + i) % RX_BUFFER_SIZE] == 0x55) {
            if (RxBuffer[(RxReadPos + i + 22) % RX_BUFFER_SIZE] == 0x55) {
                // 找到了帧头和帧尾,提取数据
                uint8_t frame[23];
                for (uint8_t j = 0; j < 23; j++) {
                    frame[j] = RxBuffer[(RxReadPos + i + j) % RX_BUFFER_SIZE];
                }
                // 处理数据(例如解析数据中的命令和参数)
                // ...
                // 更新缓冲区读指针
                RxReadPos = (RxReadPos + i + 23) % RX_BUFFER_SIZE;
                break;
            }
        }
    }
}

int main(void) {
    // 初始化USART和中断
    // ...
    while (1) {
        // 等待20ms
        delay_ms(20);
        // 解析接收缓冲区中的数据
        parseRxBuffer();
    }
}

上述代码中,RxReadPosRxWritePos分别表示缓冲区的读指针和写指针。每当接收到一个字节时,将其存入缓冲区并将写指针加1,如果写指针达到缓冲区末尾,则将其置为0。在解析数据时,如果缓冲区中的数据不足一帧,则等待下一个20ms的任务再解析。如果缓冲区中的数据足够解析一帧,则在缓冲区中查找帧头0x55和帧尾0x55,并提取这一帧的数据,处理完数据后将读指针更新到下一帧的起始位置。

需要注意的是,由于缓冲区是循环使用的,所以在计算缓冲区指针的值时需要使用取模操作。同时,如果写指针超过缓冲区末尾时,需要将其置为0。
如果我的回答解决了您的问题,请采纳!

引用chatGPT作答,您可以通过使用一个指针来记录接收到数据的位置,从而解析通过USART串口环形缓冲区里的数据。假设您的缓冲区名称为RxBuffer,缓冲区长度为128,每个帧的长度为23,帧头为0x55,则可以按照以下步骤解析数据:

1.定义两个指针,一个指向接收数据的起始位置,一个指向当前正在解析的位置。初始时,这两个指针指向RxBuffer[0]。

uint8_t* pStart = &RxBuffer[0];
uint8_t* pCurrent = &RxBuffer[0];

2.在20ms任务中,检查接收到的数据是否足够一个完整的帧。如果缓冲区中可用的数据长度小于一个帧的长度,则等待下一次任务。

if (available_data_length() < FRAME_LENGTH) {
    return;
}

这里的available_data_length()函数可以返回缓冲区中可用数据的长度,可以根据指针的位置计算得到。

3.解析当前位置的数据,判断是否为帧头0x55。如果是,将pCurrent指针移动到下一个位置,开始解析下一个帧的数据;否则,继续解析当前帧的数据,直到解析完整个帧。

while (pCurrent < pStart + available_data_length()) {
    if (*pCurrent == 0x55) {
        pCurrent++;
        if (pCurrent + FRAME_LENGTH <= pStart + available_data_length()) {
            parse_frame(pCurrent);
            pCurrent += FRAME_LENGTH;
        }
    } else {
        parse_data(*pCurrent);
        pCurrent++;
    }
}

4.这里的parse_frame()函数可以解析一个完整的帧,parse_data()函数可以解析帧中的数据。

解析完缓冲区中所有可用的数据后,将pStart指针移动到pCurrent指针的位置,以便下一次任务从正确的位置开始解析数据。

pStart = pCurrent;

下面是完整的代码示例:

#define BUFFER_SIZE 128
#define FRAME_LENGTH 23
#define FRAME_HEADER 0x55

uint8_t RxBuffer[BUFFER_SIZE];
uint8_t* pStart = &RxBuffer[0];
uint8_t* pCurrent = &RxBuffer[0];

void USART_IRQHandler() {
    // 处理USART接收中断,将数据存储到RxBuffer中
}

void parse_frame(uint8_t* pData) {
    // 解析帧数据
}

void parse_data(uint8_t data) {
    // 解析帧中的数据
}

void task_20ms() {
    if (available_data_length() < FRAME_LENGTH) {
        return;
    }
    while (pCurrent < pStart + available_data_length()) {
        if (*pCurrent == FRAME_HEADER) {
            pCurrent++;
            if (pCurrent + FRAME_LENGTH <= pStart + available_data_length()) {
                parse_frame(pCurrent);
                pCurrent += FRAME_LENGTH;
            }
        } else {
            parse_data(*pCurrent);
            pCurrent++;
        }
    }
    pStart = pCurrent;
}

uint32_t available_data_length() {
    return (pStart <= pCurrent) ? (pCurrent - pStart) : (BUFFER_SIZE - (pStart - pCurrent));
}