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

怎样实现CAN数据的接收和发送?

在裸机环境下实现CAN数据的接收和发送,需要通过 硬件寄存器操作 或 HAL库函数 结合 手动实现的队列 来完成。以下是完整的接收和发送流程实现:

1. 硬件初始化
首先初始化CAN控制器和GPIO:

void CAN_Init(void) {
    // 1. 使能CAN时钟
    __HAL_RCC_CAN1_CLK_ENABLE();
    
    // 2. 配置CAN GPIO(以PB8/PB9为例)
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;  // CAN_RX/CAN_TX
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    // 3. 配置CAN控制器
    hcan.Instance = CAN1;
    hcan.Init.Prescaler = 6;          // 波特率 = APB1时钟/(Prescaler*(BS1+BS2+SJW))
    hcan.Init.Mode = CAN_MODE_NORMAL; // 正常模式
    hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
    hcan.Init.TimeSeg1 = CAN_BS1_8TQ; 
    hcan.Init.TimeSeg2 = CAN_BS2_3TQ;
    hcan.Init.TimeTriggeredMode = DISABLE;
    hcan.Init.AutoBusOff = DISABLE;
    hcan.Init.AutoWakeUp = DISABLE;
    hcan.Init.AutoRetransmission = ENABLE; // 自动重传
    hcan.Init.ReceiveFifoLocked = DISABLE;
    hcan.Init.TransmitFifoPriority = DISABLE;
    HAL_CAN_Init(&hcan);

    // 4. 配置CAN过滤器(接收所有消息)
    CAN_FilterTypeDef filter;
    filter.FilterIdHigh = 0x0000;
    filter.FilterIdLow = 0x0000;
    filter.FilterMaskIdHigh = 0x0000;
    filter.FilterMaskIdLow = 0x0000;
    filter.FilterFIFOAssignment = CAN_RX_FIFO0; // 使用FIFO0
    filter.FilterBank = 0;
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    filter.FilterActivation = ENABLE;
    HAL_CAN_ConfigFilter(&hcan, &filter);

    // 5. 启动CAN和中断
    HAL_CAN_Start(&hcan);
    HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // 启用FIFO0中断
}

2. 数据接收实现
(1)中断中接收数据并存入队列

void CAN_RX0_IRQHandler(void) {
    CanRxMsg_t rxMsg;
    
    // 从CAN硬件读取数据
    if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &rxMsg.id, rxMsg.data, &rxMsg.length) == HAL_OK) {
        // 存入接收队列(中断安全)
        if (!Queue_SendFromISR(&canRxQueue, &rxMsg, sizeof(CanRxMsg_t))) {
            // 队列满时的处理(如点亮LED警告)
            HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
        }
    }
    HAL_CAN_IRQHandler(&hcan); // 处理其他CAN中断
}

(2)主循环处理接收队列

while (1) {
    CanRxMsg_t rxMsg;
    
    // 从队列取出数据
    if (Queue_Receive(&canRxQueue, &rxMsg, sizeof(CanRxMsg_t))) {
        // 处理接收到的数据(示例:通过串口打印)
        printf("ID:0x%lX, Data:", rxMsg.id);
        for (int i = 0; i < rxMsg.length; i++) {
            printf("%02X ", rxMsg.data[i]);
        }
        printf("\n");
    }
    
    // 其他任务...
}

3. 数据发送实现
(1)发送数据到队列

void SendCANMessage(uint32_t id, uint8_t *data, uint8_t length) {
    CanTxMsg_t txMsg;
    txMsg.id = id;
    txMsg.length = length;
    memcpy(txMsg.data, data, length);
    
    // 非阻塞发送到队列
    if (!Queue_Send(&canTxQueue, &txMsg, sizeof(CanTxMsg_t))) {
        // 队列满处理
        printf("TX Queue Full!\n");
    }
}

(2)主循环处理发送队列

while (1) {
    CanTxMsg_t txMsg;
    
    // 检查发送队列
    if (Queue_Receive(&canTxQueue, &txMsg, sizeof(CanTxMsg_t))) {
        // 通过CAN硬件发送
        CAN_TxHeaderTypeDef txHeader;
        txHeader.StdId = txMsg.id;
        txHeader.IDE = CAN_ID_STD;
        txHeader.RTR = CAN_RTR_DATA;
        txHeader.DLC = txMsg.length;
        
        uint32_t mailbox;
        if (HAL_CAN_AddTxMessage(&hcan, &txHeader, txMsg.data, &mailbox) != HAL_OK) {
            printf("CAN Send Failed!\n");
        }
    }
    
    // 其他任务...
    HAL_Delay(1); // 防止CPU占用率100%
}

4. 关键机制说明
机制 实现方式
中断安全 在中断中使用 __disable_irq()/__enable_irq() 保护队列操作
非阻塞设计 所有队列操作立即返回,不等待
硬件发送 主循环轮询发送队列,通过 HAL_CAN_AddTxMessage 发送
错误处理 队列满时触发标志位或LED警告
数据一致性 使用 memcpy 保证数据完整拷贝
5. 完整示例流程
初始化

int main(void) {
    HAL_Init();
    SystemClock_Config();
    Queues_Init();
    CAN_Init();
    
    while (1) {
        ProcessRxQueue(); // 处理接收队列
        ProcessTxQueue(); // 处理发送队列
    }
}

外部调用发送:

// 发送示例数据
uint8_t data[] = {0x01, 0x02, 0x03};
SendCANMessage(0x123, data, 3);

中断自动接收:

数据到达 → 触发 CAN_RX0_IRQHandler → 存入 canRxQueue → 主循环处理。

6. 性能优化建议
DMA发送:对于高频发送,配置CAN TX DMA。

双缓冲接收:使用两个队列交替处理,避免数据覆盖。

优先级控制:为CAN中断设置合适优先级(通常高于普通任务)。

通过这种方式,裸机系统可以实现与RTOS类似的可靠通信,同时保持更高的实时性和更低的内存开销。

相关文章:

  • 基于Python的3D贴图制作技术研究与实践
  • 数据库基础知识点(系列五)
  • 深入了解 Git Merge:用法、类型与合并冲突处理
  • GPT Researcher 的win docker安装攻略
  • Postman 下载文件指南:如何请求 Excel/PDF 文件?
  • 递归、搜索与回溯第四讲:floodfill算法
  • ​macro-F1(宏平均F1)​和micro-F1(微平均F1)
  • 在kali linux中配置hive(mysql元数据库)
  • 现阶段高校的人工智能方案培训如何?
  • MVC基础概念及相应代码示例
  • Nextjs15 - middleware的使用
  • LeetCode算法题(Go语言实现)_11
  • (每日一道算法题) K 个一组翻转链表
  • 2025宠物智能喂养设备全栈开发方案:Python驱动的高效实现路径
  • IntelliJ IDEA创建Maven工程
  • Ardupilot开源无人机之Geek SDK进展2025Q2
  • 我的世界1.20.1forge模组进阶开发教程——结构(3)
  • 华为 eNSP 链路聚合:从面试问题到实战解析
  • MySQL数据库精研之旅第四期:解锁库操作高阶技能
  • Python数据可视化与数据处理全解析:Matplotlib图形控制与Pandas高效数据分析实战
  • 成都公积金新政征求意见:购买保障性住房最高贷款额度上浮50%
  • 巴国家安全委员会授权军方自主决定对印反击措施
  • 当年的你,现在在哪里?——新民晚报杯40周年寻人启事
  • 上海乐高乐园度假区将于7月5日开园
  • 央行行长详解降息:将通过利率自律机制引导商业银行相应下调存款利率
  • 金融监管总局将推出8项增量政策:涉房地产金融、险资入市、稳外贸等