CAN总线实战项目:使用STM32和PCAN-View实现数据采集与监控系统(附完整代码)

摘要: 本文深入浅出地介绍CAN(Controller Area Network,控制器局域网络)总线协议,涵盖其基础概念、报文帧格式、仲裁机制、错误处理等关键知识。同时,文章结合STM32平台,从硬件设计、软件开发到实战案例,全面讲解CAN总线应用开发,并提供详细的代码示例和注释,帮助读者快速掌握CAN总线技术。

关键词: CAN总线, STM32, 嵌入式系统, 汽车电子, 工业控制, 物联网


一、 项目概述

1.1 项目背景

CAN总线作为一种高可靠性、实时性强的串行通信协议,广泛应用于汽车电子、工业自动化、医疗设备等领域。 掌握CAN总线技术,对于嵌入式工程师和物联网开发者至关重要。

1.2 项目目标
  • 深入理解CAN总线协议的原理和特点
  • 掌握基于STM32平台的CAN总线应用开发
  • 通过实战项目,提升CAN总线应用开发能力

二、 CAN总线基础

2.1 CAN总线简介

CAN总线是一种多主站的串行通信协议,其特点包括:

  • 高可靠性: 具有强大的错误检测和纠错机制。
  • 实时性: 采用非破坏性总线仲裁技术,保证高优先级报文优先传输。
  • 灵活性: 支持多主站通信,易于扩展。
  • 成本效益: 硬件结构简单,成本较低。
2.2 CAN 标准与报文帧格式

常见的CAN标准包括CAN 2.0A、CAN 2.0B和CAN FD。 其中,CAN FD (Flexible Data-Rate) 支持更高的数据速率和更长的数据长度。

CAN总线数据以报文帧的形式传输,常见的报文帧格式包括:

  • 数据帧 (Data Frame): 用于传输数据。
  • 远程帧 (Remote Frame): 请求其他节点发送数据。
  • 错误帧 (Error Frame): 节点检测到错误时发送。
  • 过载帧 (Overload Frame): 节点需要延迟发送数据时发送。
2.3 仲裁机制与标识符

CAN总线采用基于标识符的非破坏性仲裁机制,保证高优先级报文优先传输。 标识符越小,优先级越高。

2.4 错误检测与处理

CAN总线具有多种错误检测机制,例如CRC校验、位填充等,并支持错误恢复机制,确保通信的可靠性。

三、 系统设计

3.1 硬件设计

本项目基于STM32F103C8T6微控制器和STM32CubeMX进行开发。

  • MCU: STM32F103C8T6
  • CAN收发器: TJA1050
  • CAN连接器: DB9

连接示意图:

3.2 软件设计
  • 开发环境: Keil MDK
  • HAL库: STM32 HAL库
  • 功能模块:
    • CAN初始化
    • CAN报文发送
    • CAN报文接收
    • 中断处理

四、 代码实现

4.1 CAN初始化
/* CAN 初始化 */
void CAN_Init(void)
{
  /* CAN 时钟使能 */
  __HAL_RCC_CAN1_CLK_ENABLE();

  /* 初始化 CAN 句柄 */
  hcan1.Instance = CAN1;
  /* 设置 CAN 工作模式为普通模式 */
  hcan1.Init.Mode = CAN_MODE_NORMAL;
  /* 设置波特率为 500Kbps */
  hcan1.Init.Prescaler = 6; 
  hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan1.Init.TimeSeg1 = CAN_BS1_13TQ;
  hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
  /* 设置自动离线管理模式 */
  hcan1.Init.AutoBusOff = ENABLE;
  /* 设置自动重传模式 */
  hcan1.Init.AutoRetransmission = ENABLE;
  /* 设置接收 FIFO 锁定模式 */
  hcan1.Init.ReceiveFifoLockedMode = ENABLE;
  /* 设置发送 FIFO 优先级模式 */
  hcan1.Init.TransmitFifoPriority = ENABLE;
  /* 初始化 CAN */
  if (HAL_CAN_Init(&hcan1) != HAL_OK)
  {
    Error_Handler();
  }

  /* 配置 CAN 过滤器 */
  CAN_FilterTypeDef can_filter_config;
  can_filter_config.FilterBank = 0;
  can_filter_config.FilterMode = CAN_FILTERMODE_IDMASK;
  can_filter_config.FilterScale = CAN_FILTERSCALE_32BIT;
  can_filter_config.FilterIdHigh = 0x0000;
  can_filter_config.FilterIdLow = 0x0000;
  can_filter_config.FilterMaskIdHigh = 0x0000;
  can_filter_config.FilterMaskIdLow = 0x0000;
  can_filter_config.FilterFIFOAssignment = CAN_RX_FIFO0;
  can_filter_config.FilterActivation = ENABLE;
  can_filter_config.SlaveStartFilterBank = 14;

  if (HAL_CAN_ConfigFilter(&hcan1, &can_filter_config) != HAL_OK)
  {
    Error_Handler();
  }

  /* 启动 CAN */
  if (HAL_CAN_Start(&hcan1) != HAL_OK)
  {
    Error_Handler();
  }

  /* 开启 CAN 接收中断 */
  if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
  {
    Error_Handler();
  }
}

代码说明:

  • 首先,启用 CAN1 的时钟。
  • 然后,初始化 CAN 句柄 hcan1,并设置 CAN 的工作模式、波特率、自动离线管理、自动重传、接收 FIFO 锁定模式、发送 FIFO 优先级模式等参数。
  • 配置 CAN 过滤器,设置过滤器模式、标识符、掩码等参数。
  • 启动 CAN 模块。
  • 最后,开启 CAN 接收中断,以便在接收到数据时及时进行处理。
4.2 CAN 报文发送
/* CAN 报文发送 */
void CAN_Send_Msg(uint32_t std_id, uint8_t *data, uint8_t len)
{
  CAN_TxHeaderTypeDef tx_header;
  uint32_t mailbox;

  /* 设置报文 ID */
  tx_header.StdId = std_id;
  /* 设置报文类型为数据帧 */
  tx_header.RTR = CAN_RTR_DATA;
  /* 设置数据长度 */
  tx_header.DLC = len;

  /* 发送 CAN 报文 */
  if (HAL_CAN_AddTxMessage(&hcan1, &tx_header, data, &mailbox) != HAL_OK)
  {
    Error_Handler();
  }
}

 

代码说明:

  • 首先,定义一个 CAN_TxHeaderTypeDef 类型的变量 tx_header,用于设置 CAN 报文的 ID、类型、数据长度等信息。
  • 然后,调用 HAL_CAN_AddTxMessage() 函数将 CAN 报文添加到发送邮箱。其中,hcan1 是 CAN 句柄,tx_header 是报文头信息,data 是要发送的数据,mailbox 是发送邮箱的编号。
  • 如果发送成功,HAL_CAN_AddTxMessage() 函数返回 HAL_OK,否则返回错误码。
4.3 CAN 报文接收

/* CAN 报文接收 */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
  CAN_RxHeaderTypeDef rx_header;
  uint8_t rx_data[8];

  /* 接收 CAN 报文 */
  if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data) != HAL_OK)
  {
    Error_Handler();
  }

  /* 处理接收到的数据 */
  if (rx_header.StdId == 0x100)  // 判断报文 ID
  {
    // 处理 ID 为 0x100 的报文
  }
}

代码说明:

  • HAL_CAN_RxFifo0MsgPendingCallback() 函数是 CAN 接收中断回调函数,当接收到 CAN 报文时,该函数会被调用。
  • 首先,定义一个 CAN_RxHeaderTypeDef 类型的变量 rx_header,用于存储接收到的 CAN 报文头信息,以及一个 uint8_t 类型的数组 rx_data,用于存储接收到的数据。
  • 然后,调用 HAL_CAN_GetRxMessage() 函数从接收 FIFO 中读取 CAN 报文。
  • 根据接收到的报文 ID 进行相应处理。
4.4 中断处理

在 STM32CubeMX 中,可以图形化地配置 CAN 中断。 在代码中,需要实现 HAL_CAN_RxFifo0MsgPendingCallback() 函数来处理接收到的 CAN 报文。

五、 项目总结

本项目介绍了 CAN 总线的基本概念、帧格式、仲裁机制和错误处理等关键知识,并结合 STM32 平台,详细讲解了 CAN 总线的硬件设计、软件开发和实战案例。 通过学习本项目,读者可以快速掌握 CAN 总线技术,并将其应用到实际项目中。

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-12 10:48:01       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-12 10:48:01       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-12 10:48:01       57 阅读
  4. Python语言-面向对象

    2024-07-12 10:48:01       68 阅读

热门阅读

  1. qt 自定义信号号槽 简单举例

    2024-07-12 10:48:01       23 阅读
  2. 2024年啦,你的信息系统还没做等保吗?

    2024-07-12 10:48:01       22 阅读
  3. 引入时间概念的分布式系统浅谈

    2024-07-12 10:48:01       21 阅读
  4. Vue3框架搭建4:配置说明-eslint配置

    2024-07-12 10:48:01       21 阅读
  5. linux 查看 io使用率iotop

    2024-07-12 10:48:01       24 阅读
  6. 【番外】Springboot集成推荐配置及十问RocketMQ

    2024-07-12 10:48:01       27 阅读
  7. 软设之职责链模式

    2024-07-12 10:48:01       20 阅读
  8. vue中ref()与reactive(的区别)

    2024-07-12 10:48:01       19 阅读
  9. Flask发布一个及时止损(止盈)服务(二)

    2024-07-12 10:48:01       20 阅读