驱动开发,队列,环形缓冲区:以GD32 CAN 消息处理为例

发布于:2025-06-20 ⋅ 阅读:(19) ⋅ 点赞:(0)

对环形缓冲区进行进一步的优化和功能扩展,以应对更复杂的实际应用场景,特别是针对 CAN 总线消息处理的场景。

一、优化点

1:动态配置环形缓冲区大小在原始实现中,我们固定了缓冲区大小为 RINGBUFF_LEN = 64。这种方式虽然简单,但在实际应用中缺乏灵活性。例如,在资源受限的嵌入式系统中,我们可能需要根据内存情况和数据流量动态调整缓冲区大小。

我们通过修改初始化函数,引入动态缓冲区容量配置:

void ring_buffer_init(can_ring_buffer_t *rb, can_receive_message_struct *buffer, uint32_t capacity, uint8_t mode) 
{    
rb->buffer = buffer;      
rb->in_index = 0;    
rb->out_index = 0;    
rb->length = 0;    
rb->capacity = capacity;  // 动态配置缓冲区大小    
rb->mode = mode;
}

在 main 函数中,我们可以通过调整传入的 capacity 参数,轻松设置缓冲区大小:

can_receive_message_struct recv_buf[50]; can_ring_buffer_t can_ring_buffer;ring_buffer_init(&can_ring_buffer, recv_buf, 50, RING_BUFFER_MODE_NORMAL);

这种方式使缓冲区大小完全可控,能够适应不同场景下的需求。
二、优化点

2:增强错误处理机制在实际系统运行中,缓冲区溢出或数据丢失是常见的问题。我们对写入函数进行了改进,增加了对缓冲区状态的详细检查和处理:

int ring_buffer_write(can_ring_buffer_t *rb, can_receive_message_struct *msg) 
{    
if (rb->length < rb->capacity) 
{        
rb->buffer[rb->in_index] = *msg;        
rb->in_index = (rb->in_index + 1) % rb->capacity;        
rb->length++;        
return 1;    
} 
else 
{        
if (rb->mode == RING_BUFFER_MODE_OVERWRITE) {            
rb->buffer[rb->in_index] = *msg;            
rb->in_index = (rb->in_index + 1) % rb->capacity;            
rb->out_index = (rb->out_index + 1) % rb->capacity;            
return 1;        
} 
else 
{            
return 0;  // 在正常模式下,丢弃数据        
}    
}}

在缓冲区满的情况下,根据模式决定是否覆盖旧数据。
我们还提供了状态检查函数:

int ring_buffer_is_empty(can_ring_buffer_t *rb) 
{    
return rb->length == 0;
}
int ring_buffer_is_full(can_ring_buffer_t *rb) 
{    
return rb->length == rb->capacity;
}

这些函数可以帮助开发者实时监控缓冲区状态,及时发现潜在问题。

三、优化点
3:提供缓冲区状态监控功能为了更好地理解缓冲区的使用情况,我们增加了缓冲区占用百分比计算功能:

float ring_buffer_occupancy(can_ring_buffer_t *rb) {    
return (float)rb->length / rb->capacity * 100.0f;
}

在 main 函数中,我们可以通过以下方式输出缓冲区占用情况:

printf("Buffer occupancy: %.2f%%\n", ring_buffer_occupancy(&can_ring_buffer));

这个功能对于系统调优和资源分配具有重要意义。

四、优化点
4:改进消息处理流程我们完善了消息读取和处理函数,使其能够批量处理缓冲区中的消息:

int read_messages_from_buffer(can_ring_buffer_t *rb) 
{    
can_receive_message_struct msg;    
int read_count = 0;    
while (!ring_buffer_is_empty(rb)) 
{        
if (ring_buffer_read(rb, &msg)) 
{            
// 处理读取到的 CAN 消息            
printf("Received CAN message:\n");            printf("SFID: 0x%X, EFID: 0x%X, FF: %d, FT: %d, DLEN: %d\n",                    msg.rx_sfid, msg.rx_efid, msg.rx_ff, msg.rx_ft, msg.rx_dlen);            
printf("Data: ");            
for (int i = 0; i < msg.rx_dlen; i++) 
{                
printf("%d ", msg.rx_data[i]);            
}            
printf("\n");            
read_count++;        
}    
}    
return read_count;
}

在 main 函数中调用:

int read_count = read_messages_from_buffer(&can_ring_buffer);
printf("Total messages read: %d\n", read_count);

这种批量处理方式提高了系统效率。

五、完整代码实现
以下是优化后的完整代码实现:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>  // 用于动态内存分配

#define RING_BUFFER_MODE_OVERWRITE 1
#define RING_BUFFER_MODE_NORMAL 0// CAN接收消息结构体
typedef struct 
{    
uint32_t rx_sfid;   /*!< standard format frame identifier */    
uint32_t rx_efid;   /*!< extended format frame identifier */    
uint8_t rx_ff;      /*!< format of frame, standard or extended format */    
uint8_t rx_ft;      /*!< type of frame, data or remote */    
uint8_t rx_dlen;    /*!< data length */    
uint8_t rx_data[8]; /*!< receive data */    
uint8_t rx_fi;      /*!< filtering index */} 
can_receive_message_struct;// 环形缓冲区结构体
typedef struct 
{    
can_receive_message_struct *buffer;  // 指向缓冲区的指针    
uint32_t in_index;    
uint32_t out_index;    
uint32_t length;    
uint32_t capacity;    
uint8_t mode;
} can_ring_buffer_t;// 初始化环形缓冲区

void ring_buffer_init(can_ring_buffer_t *rb, can_receive_message_struct *buffer, uint32_t capacity, uint8_t mode) 
{    
rb->buffer = buffer;      
rb->in_index = 0;    
rb->out_index = 0;    
rb->length = 0;    
rb->capacity = capacity;  // 动态配置缓冲区大小    
rb->mode = mode;
}// 写入数据

int ring_buffer_write(can_ring_buffer_t *rb, can_receive_message_struct *msg) 
{    
if (rb->length < rb->capacity) 
{        
rb->buffer[rb->in_index] = *msg;        
rb->in_index = (rb->in_index + 1) % rb->capacity;        
rb->length++;        
return 1;    
} 
else 
{        
if (rb->mode == RING_BUFFER_MODE_OVERWRITE) {            
rb->buffer[rb->in_index] = *msg;            
rb->in_index = (rb->in_index + 1) % rb->capacity;            
rb->out_index = (rb->out_index + 1) % rb->capacity;            
return 1;        
} 
else 
{            
return 0;  // 在正常模式下,丢弃数据        
}    
}
}// 读取数据

int ring_buffer_read(can_ring_buffer_t *rb, can_receive_message_struct *msg) 
{    
if (rb->length > 0) 
{        
*msg = rb->buffer[rb->out_index];        
rb->out_index = (rb->out_index + 1) % rb->capacity;        
rb->length--;        
return 1;    
} 
else 
{        
return 0;  // 如果没有数据,返回0    
}
}// 检查是否为空

int ring_buffer_is_empty(can_ring_buffer_t *rb) 
{    
return rb->length == 0;
}// 检查是否已满

int ring_buffer_is_full(can_ring_buffer_t *rb) {    
return rb->length == rb->capacity;

}// 获取缓冲区占用的百分比

float ring_buffer_occupancy(can_ring_buffer_t *rb) {    
return (float)rb->length / rb->capacity * 100.0f;
}// 读取并处理缓冲区中的消息

int read_messages_from_buffer(can_ring_buffer_t *rb) 
{    
can_receive_message_struct msg;    
int read_count = 0;    

while (!ring_buffer_is_empty(rb)) 
{        
if (ring_buffer_read(rb, &msg)) 
{            
// 处理读取到的 CAN 消息            
printf("Received CAN message:\n");            
printf("SFID: 0x%X, EFID: 0x%X, FF: %d, FT: %d, DLEN: %d\n",                    msg.rx_sfid, msg.rx_efid, msg.rx_ff, msg.rx_ft, msg.rx_dlen);            
printf("Data: ");            

for (int i = 0; i < msg.rx_dlen; i++) 
{                
printf("%d ", msg.rx_data[i]);            
}            
printf("\n");            
read_count++;        
}    
}    

return read_count;

}// 模拟接收CAN消息的中断处理函数

void CAN1_IRQHandler(can_ring_buffer_t *rb) {    
can_receive_message_struct temp;    // 模拟从硬件获取 CAN 消息    
temp.rx_sfid = 0x123;    
temp.rx_efid = 0x1ABC;    
temp.rx_ff = 0;    
temp.rx_ft = 1;    
temp.rx_dlen = 8;    
for (int i = 0; i < 8; i++) 
{        
temp.rx_data[i] = i;    
}    
temp.rx_fi = 1;    
if (rb->length < rb->capacity) 
{        
ring_buffer_write(rb, &temp);    
} 
else 
{        
if (rb->mode == RING_BUFFER_MODE_OVERWRITE) {            
ring_buffer_write(rb, &temp);        
}    
}
}

int main(void) 
{    
// 定义接收缓存数组,容量为50    
can_receive_message_struct recv_buf[50];     // 初始化环形缓冲区,设置容量为50,正常模式    
can_ring_buffer_t can_ring_buffer;    
ring_buffer_init(&can_ring_buffer, recv_buf, 50, RING_BUFFER_MODE_NORMAL);    

// 模拟接收10条CAN消息    
for (int i = 0; i < 10; i++) 
{        
CAN1_IRQHandler(&can_ring_buffer);      
}    

// 输出缓冲区的占用情况    
printf("Buffer occupancy: %.2f%%\n", ring_buffer_occupancy(&can_ring_buffer));    // 读取并处理缓冲区中的消息    
int read_count = read_messages_from_buffer(&can_ring_buffer);    
printf("Total messages read: %d\n", read_count);    

return 0;

}

六、未来扩展方向

  1. 支持多线程环境:在多线程系统中,我们可以在写入和读取操作时添加互斥锁(mutex),防止数据竞争。例如:
// 在写入函数中添加锁保护
pthread_mutex_lock(&rb->mutex);// 写入操作
pthread_mutex_unlock(&rb->mutex);
  1. 进一步优化消息处理:可以根据 CAN 消息的内容添加过滤和优先级处理。例如,对特定 ID 的消息进行优先处理。
  2. 错误日志和统计:可以添加日志记录功能,记录缓冲区溢出、数据丢失等事件,便于系统调试和优化。通过这些优化和扩展,我们的环形缓冲区实现变得更加健壮和实用,能够更好地适应实际嵌入式系统中的 CAN 消息处理需求。

网站公告

今日签到

点亮在社区的每一天
去签到