我通过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();
}
}
上述代码中,RxReadPos
和RxWritePos
分别表示缓冲区的读指针和写指针。每当接收到一个字节时,将其存入缓冲区并将写指针加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));
}