前言
CAN(Controller Area Network)总线是一种用于车辆内部通信的强大协议,适用于实时应用。在汽车电子系统中,消息的及时发送和调度至关重要。本文介绍了一种基于STM32微控制器的CAN总线消息发送调度系统,通过消息池和定时发送机制,确保CAN消息按时且有序地发送。
一、使用步骤
1.定义CAN消息数组
首先,定义一个包含多个CAN消息的数组,每个消息包括消息ID、数据、数据长度、发送间隔和下一次发送时间。
#include "can_signal_pool.h"
// 定义CAN消息数组
CAN_Message can_messages[] = {
{0X200, {0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00}, 8, 200, 0},
{0X201, {0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00}, 8, 180, 0},
{0X202, {0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00}, 8, 500, 0},
};
2.定义时间和最小公倍数计算函数
为了确保消息按设定的时间间隔发送,我们需要计算当前时间以及所有消息间隔的最小公倍数
// 当前时间,单位:毫秒
uint32_t current_time = 0;
// 当前消息数组中所有间隔的最小公倍数
uint32_t lcm_interval = 1;
// 计算两个数的最大公约数
uint32_t gcd(uint32_t a, uint32_t b) {
while (b != 0) {
uint32_t temp = b;
b = a % b;
a = temp;
}
return a;
}
// 计算两个数的最小公倍数
uint32_t lcm(uint32_t a, uint32_t b) {
return (a * b) / gcd(a, b);
}
3.检查并发送CAN消息函数
该函数用于判断是否到达发送CAN消息的时间间隔,并发送需要发送的消息。同时还会检查是否到达所有消息间隔的整数倍,以完成一轮循环发送。
/**
* @brief 判断是否到达发送CAN消息的时间间隔以及完成一轮循环发送的函数
* @note 该函数用于检查是否到达发送CAN消息的时间间隔,并发送需要发送的消息。
* 同时,该函数还会检查是否到达所有消息间隔的整数倍,以完成一轮循环发送。
* @retval None
*/
void CheckCANMessages(CAN_Message* can_messages, u8 len) {
static u8 heartbeat1;
// 初始化lcm_interval为所有间隔的最小公倍数
for (int i = 0; i < len; i++) {
lcm_interval = lcm(lcm_interval, can_messages[i].interval);
}
// 判断是否到达发送CAN消息的时间间隔
for (int i = 0; i < len; i++) {
if (T1.CAN_send_Ms > can_messages[i].next_send_time) {
// 发送当前消息
CAN2_Send_Msg_WithPool(can_messages[i].id, can_messages[i].data, can_messages[i].length);
// 更新下一条消息的发送时间
can_messages[i].next_send_time = T1.CAN_send_Ms + can_messages[i].interval;
}
}
// 检查是否到达所有消息间隔的整数倍
if (T1.CAN_send_Ms % lcm_interval == 0) {
T1.CAN_send_Ms = 0;
for (int i = 0; i < len; i++) {
can_messages[i].next_send_time = 0;
}
}
}
4.CAN消息池与发送调度
为了避免消息发送过程中出现阻塞,我们使用消息池存储待发送的CAN消息,并按顺序从消息池中发送消息。
// CAN消息池
CAN_Msg Msg_Pool[100];
u8 Msg_Index, Msg_Count;
/**
* @brief 将CAN消息存入消息池中
* @param canid: CAN消息的ID
* @param msg: 指向CAN消息数据的指针
* @param len: CAN消息数据的长度
* @retval None
*/
void CAN_Send_Msg_WithPool(u32 canid, u8* msg, u8 len) {
// 如果消息池未满
if (Msg_Count <= 100) {
u8 index = (Msg_Index + Msg_Count) % 100; // 计算下一个可用位置的索引
memcpy(Msg_Pool[index].buf, msg, 8); // 复制消息数据到消息池中
Msg_Pool[index].ID = canid; // 设置消息ID
Msg_Pool[index].Length = len; // 设置消息长度
Msg_Count++; // 增加消息计数
}
}
/**
* @brief 从消息池中按顺序发送CAN消息
* @param None
* @retval None
*/
void CAN_Send_Msg_Schedule() {
// 如果消息池中有消息
if (Msg_Count > 0) {
Can_Send_Msg(Msg_Pool[Msg_Index].ID, Msg_Pool[Msg_Index].buf, Msg_Pool[Msg_Index].length); // 发送消息
Msg_Index++; // 移动索引到下一条消息
Msg_Count--; // 减少消息计数
if (Msg_Index == 100) {
Msg_Index = 0; // 如果索引达到消息池末尾,则重置为起始位置
}
}
}
总结
通过本文介绍的代码实现,我们成功地在STM32微控制器上构建了一个高效的CAN总线消息发送调度系统。该系统不仅能够按设定的时间间隔发送CAN消息,还能通过消息池机制避免发送过程中的阻塞。在实际应用中,可以根据具体需求对发送间隔和消息池进行调整,以满足更多的功能需求。