当前位置: 首页 > news >正文

在STM32F7上实现CAN总线收发队列

下面我将提供一个完整的STM32F7 CAN总线通信实现方案,包含中断驱动的收发队列管理。

1. CAN总线配置与队列定义
can_bus.h

#ifndef __CAN_BUS_H
#define __CAN_BUS_H

#include "stm32f7xx_hal.h"
#include "queue.h"

// CAN消息结构体
typedef struct {
    uint32_t id;        // 标准ID或扩展ID
    uint8_t data[8];    // 数据域
    uint8_t len;        // 数据长度
    uint8_t format;     // 0-标准帧, 1-扩展帧
    uint8_t type;       // 0-数据帧, 1-远程帧
} CAN_Msg_t;

// CAN队列配置
#define CAN_TX_QUEUE_SIZE  16
#define CAN_RX_QUEUE_SIZE  32

// CAN总线初始化
void CAN_Init(CAN_HandleTypeDef *hcan);

// 消息发送/接收接口
bool CAN_SendMsg(CAN_HandleTypeDef *hcan, CAN_Msg_t *msg);
bool CAN_GetMsg(CAN_Msg_t *msg);

// 中断处理函数
void CAN_IRQHandler(CAN_HandleTypeDef *hcan);

#endif

can_bus.c

#include "can_bus.h"

// CAN发送接收队列
static Queue_t can_tx_queue;
static Queue_t can_rx_queue;

// CAN初始化
void CAN_Init(CAN_HandleTypeDef *hcan) {
    // 初始化队列
    Queue_Init(&can_tx_queue);
    Queue_Init(&can_rx_queue);
    
    // CAN过滤器配置 (示例配置)
    CAN_FilterTypeDef filter;
    filter.FilterBank = 0;
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    filter.FilterIdHigh = 0x0000;
    filter.FilterIdLow = 0x0000;
    filter.FilterMaskIdHigh = 0x0000;
    filter.FilterMaskIdLow = 0x0000;
    filter.FilterFIFOAssignment = CAN_RX_FIFO0;
    filter.FilterActivation = ENABLE;
    filter.SlaveStartFilterBank = 14;
    HAL_CAN_ConfigFilter(hcan, &filter);
    
    // 启动CAN
    HAL_CAN_Start(hcan);
    
    // 激活CAN中断
    HAL_CAN_ActivateNotification(hcan, 
        CAN_IT_RX_FIFO0_MSG_PENDING | 
        CAN_IT_TX_MAILBOX_EMPTY);
}

// 发送CAN消息 (非阻塞)
bool CAN_SendMsg(CAN_HandleTypeDef *hcan, CAN_Msg_t *msg) {
    // 尝试直接发送
    if (HAL_CAN_GetTxMailboxesFreeLevel(hcan) > 0) {
        CAN_TxHeaderTypeDef tx_header;
        tx_header.StdId = msg->format ? 0 : msg->id;
        tx_header.ExtId = msg->format ? msg->id : 0;
        tx_header.IDE = msg->format ? CAN_ID_EXT : CAN_ID_STD;
        tx_header.RTR = msg->type ? CAN_RTR_REMOTE : CAN_RTR_DATA;
        tx_header.DLC = msg->len;
        tx_header.TransmitGlobalTime = DISABLE;
        
        uint32_t mailbox;
        if (HAL_CAN_AddTxMessage(hcan, &tx_header, msg->data, &mailbox) == HAL_OK) {
            return true;
        }
    }
    
    // 发送邮箱已满,放入队列
    return Queue_SendToBackFromISR(&can_tx_queue, msg);
}

// 接收CAN消息 (非阻塞)
bool CAN_GetMsg(CAN_Msg_t *msg) {
    return Queue_Receive(&can_rx_queue, msg);
}

// CAN中断处理
void CAN_IRQHandler(CAN_HandleTypeDef *hcan) {
    // RX FIFO0中断处理
    if (__HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FOV0) || __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FF0)) {
        __HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FOV0 | CAN_FLAG_FF0);
    }
    
    if (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_RX_FIFO0_MSG_PENDING)) {
        CAN_RxHeaderTypeDef rx_header;
        CAN_Msg_t msg;
        
        if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, msg.data) == HAL_OK) {
            msg.id = rx_header.IDE == CAN_ID_STD ? rx_header.StdId : rx_header.ExtId;
            msg.len = rx_header.DLC;
            msg.format = rx_header.IDE == CAN_ID_EXT;
            msg.type = rx_header.RTR == CAN_RTR_REMOTE;
            
            Queue_SendToBackFromISR(&can_rx_queue, &msg);
        }
    }
    
    // TX邮箱空中断处理
    if (__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_TX_MAILBOX_EMPTY)) {
        // 检查并发送队列中的消息
        while (HAL_CAN_GetTxMailboxesFreeLevel(hcan) > 0) {
            CAN_Msg_t msg;
            if (Queue_Receive(&can_tx_queue, &msg)) {
                CAN_SendMsg(hcan, &msg);  // 递归调用,但不会无限递归
            } else {
                break;
            }
        }
    }
}

2. 主程序集成示例
main.c

#include "stm32f7xx_hal.h"
#include "can_bus.h"

CAN_HandleTypeDef hcan1;

int main(void) {
    HAL_Init();
    SystemClock_Config();
    
    // CAN初始化
    hcan1.Instance = CAN1;
    hcan1.Init.Prescaler = 6;
    hcan1.Init.Mode = CAN_MODE_NORMAL;
    hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
    hcan1.Init.TimeSeg1 = CAN_BS1_13TQ;
    hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
    hcan1.Init.TimeTriggeredMode = DISABLE;
    hcan1.Init.AutoBusOff = DISABLE;
    hcan1.Init.AutoWakeUp = DISABLE;
    hcan1.Init.AutoRetransmission = ENABLE;
    hcan1.Init.ReceiveFifoLocked = DISABLE;
    hcan1.Init.TransmitFifoPriority = DISABLE;
    HAL_CAN_Init(&hcan1);
    
    CAN_Init(&hcan1);
    
    while (1) {
        // 接收处理
        CAN_Msg_t rx_msg;
        if (CAN_GetMsg(&rx_msg)) {
            // 处理接收到的CAN消息
            // ...
        }
        
        // 示例:周期发送测试消息
        static uint32_t last_tick = 0;
        if (HAL_GetTick() - last_tick > 1000) {
            last_tick = HAL_GetTick();
            
            CAN_Msg_t tx_msg;
            tx_msg.id = 0x123;
            tx_msg.len = 8;
            tx_msg.format = 0; // 标准帧
            tx_msg.type = 0;   // 数据帧
            for (int i = 0; i < 8; i++) {
                tx_msg.data[i] = i;
            }
            
            CAN_SendMsg(&hcan1, &tx_msg);
        }
        
        // 其他任务...
    }
}

// CAN中断服务函数
void CAN1_RX0_IRQHandler(void) {
    CAN_IRQHandler(&hcan1);
}

void CAN1_TX_IRQHandler(void) {
    CAN_IRQHandler(&hcan1);
}

3. 关键实现细节说明
双队列设计:

发送队列(can_tx_queue):缓存待发送消息

接收队列(can_rx_queue):缓存接收到的消息

中断驱动机制:

接收中断:当FIFO收到消息时触发,将消息存入接收队列

发送中断:当发送邮箱空闲时触发,从发送队列取出消息发送

流量控制:

当CAN控制器发送邮箱满时,消息暂存到发送队列

当接收队列满时,新消息将被丢弃(可根据需求修改)

性能优化:

直接使用HAL库提供的邮箱状态检查

在中断中只做必要的最小操作

扩展性考虑:

支持标准帧和扩展帧

支持数据帧和远程帧

可轻松扩展为多CAN总线支持

4. 实际应用建议

  1. 错误处理增强:
// 在CAN_Init中添加错误中断使能
HAL_CAN_ActivateNotification(hcan, 
    CAN_IT_RX_FIFO0_MSG_PENDING |
    CAN_IT_TX_MAILBOX_EMPTY |
    CAN_IT_ERROR |
    CAN_IT_BUSOFF |
    CAN_IT_LAST_ERROR_CODE);
  1. 诊断功能:
// 添加CAN状态监控
typedef struct {
    uint32_t tx_count;
    uint32_t rx_count;
    uint32_t tx_queue_full;
    uint32_t rx_queue_full;
    uint32_t errors;
} CAN_Stats_t;

// 在中断处理中更新统计信息
  1. 多帧传输支持:
// 对于长消息,可以实现分段/重组逻辑
#define CAN_MTU 8  // CAN最大传输单元

typedef struct {
    uint32_t id;
    uint16_t total_len;
    uint16_t offset;
    uint8_t data[64];  // 假设最大支持64字节数据
} CAN_LongMsg_t;

这个实现提供了完整的CAN总线通信框架,您可以根据具体应用需求进行调整和扩展。

相关文章:

  • MongoDB 与 Elasticsearch 使用场景区别及示例
  • 在 React 中,组件之间传递变量的常见方法
  • C语言贪吃蛇实现
  • 判定字符是否唯一
  • 【网络】HTTP 和 HTTPS
  • Apache Tomcat RCE漏洞(CVE-2025-24813)
  • [Windows] Edge浏览器_134.0.3124.83绿色便携增强版-集成官方Deepseek侧边栏
  • 常见框架漏洞之六:Nginx
  • 体育直播系统趣猜功能开发技术实现方案
  • 简单的shell编程
  • Java8 LocalDate LocalTime LocalDateTime的使用
  • 玄机-第五章 Windows 实战-evtx 文件分析的测试报告
  • position embedding
  • 【测试报告】论坛系统
  • 语言解码双生花:人类经验与AI算法的镜像之旅
  • 树状数组模板
  • 【redis】哨兵:搭建主从/哨兵节点详解和细节
  • 【WebGIS教程2】Web服务与地理空间服务解析
  • Java:JDK8 新特性:Lambda表达式
  • Vulnhub-Thales通关攻略
  • 山西做网站/seo课程培训要多少钱
  • 建筑室内设计软件/盐城seo营销
  • 华为云做网站不能修改页面/网络营销方法有哪些?
  • b2c的网站有哪些/哈尔滨企业网站模板建站
  • 微信小程序开发网站/seo网站内部优化方案
  • 台州seo网站排名/湖南长沙关键词推广电话