CAN总线是一种常用的实时通信协议,用于在微控制器之间进行可靠的数据传输。在本文中,我们将介绍STM32微控制器上CAN总线通信的基本原理,并提供一些代码示例来帮助您理解和实现CAN总线通信。

第一部分:CAN总线基础知识

  1. CAN总线概述 CAN(Controller Area Network)总线是一种串行通信协议,最初由德国公司Bosch开发用于汽车电子系统。CAN总线特点包括高可靠性、实时性、抗干扰能力强等。它广泛应用于汽车、工业控制、航空航天等领域。

  2. CAN总线结构 CAN总线由两根差分信号线CAN_H和CAN_L组成。CAN_H和CAN_L的电位差表示逻辑0或逻辑1。CAN总线可以支持多个节点,每个节点可以作为发送器和接收器。

  3. CAN帧格式 CAN总线的数据传输单位是帧(Frame)。CAN总线的帧包括数据帧(Data Frame)和远程帧(Remote Frame)。数据帧用于实际数据传输,而远程帧用于请求远程节点发送数据。

  4. CAN标识符 每个CAN帧都有一个唯一的标识符(Identifier)。标识符可以是11位或29位长,用于标识不同的帧。标识符可以用于过滤和识别帧。

第二部分:STM32上的CAN总线通信

在STM32微控制器上,CAN总线通信需要使用CAN控制器和CAN引脚。STM32提供了多个CAN控制器和相应的引脚,可以与其他设备进行CAN通信。

以下是一个简单的CAN总线通信示例,演示了如何在两个STM32微控制器之间进行数据的发送和接收。

发送端代码:

#include "stm32f4xx.h"

CAN_TypeDef* CANx = CAN1;

void CAN_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  CAN_InitTypeDef        CAN_InitStructure;
  CAN_FilterInitTypeDef  CAN_FilterInitStructure;
  
  // 启用CAN1时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
  // 启用GPIOB时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  
  // 配置CAN1引脚
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  
  // 将CAN引脚配置为CAN功能
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_CAN1);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_CAN1);
  
  // CAN配置
  CAN_DeInit(CANx);
  CAN_InitStructure.CAN_TTCM = DISABLE;
  CAN_InitStructure.CAN_ABOM = DISABLE;
  CAN_InitStructure.CAN_AWUM = DISABLE;
  CAN_InitStructure.CAN_NART = DISABLE;
  CAN_InitStructure.CAN_RFLM = DISABLE;
  CAN_InitStructure.CAN_TXFP = DISABLE;
  CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
  CAN_InitStructure.CAN_SJW  = CAN_SJW_1tq;
  CAN_InitStructure.CAN_BS1  = CAN_BS1_6tq;
  CAN_InitStructure.CAN_BS2  = CAN_BS2_8tq;
  CAN_InitStructure.CAN_Prescaler = 5;
  CAN_Init(CANx, &CAN_InitStructure);
  
  // CAN过滤器配置
  CAN_FilterInitStructure.CAN_FilterNumber = 0;
  CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
  CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
  CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
  CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
  CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
  CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
  CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
  CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
  CAN_FilterInit(&CAN_FilterInitStructure);
  
  // 启用CAN1发送邮箱0中断
  CAN_ITConfig(CANx, CAN_IT_TME, ENABLE);
}

void CAN_SendData(uint8_t data)
{
  CAN_TxMailBox_TypeDef        *mailbox;
  uint8_t mailbox_number = 0;
  
  mailbox = CANx->sTxMailBox;
  
  // 等待CAN发送邮箱空闲
  while(!(mailbox->TIR & CAN_TI0R_TXRQ));
  
  // 设置发送数据
  mailbox->TDLR = data;
  mailbox->TDTR = 1;
  
  // 设置标识符
  mailbox->TIR = (0x123 << 21) | CAN_TI0R_TXRQ;
}

int main(void)
{
  // 初始化CAN总线
  CAN_Configuration();
  
  while(1)
  {
    // 发送数据
    CAN_SendData(0xAA);
    
    // 延时
    for(int i = 0; i < 1000000; i++);
  }
}

接收端代码:

#include "stm32f4xx.h"

CAN_TypeDef* CANx = CAN1;

void CAN_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  CAN_InitTypeDef        CAN_InitStructure;
  CAN_FilterInitTypeDef  CAN_FilterInitStructure;
  
  // 启用CAN1时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
  // 启用GPIOB时钟
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  
  // 配置CAN1引脚
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  
  // 将CAN引脚配置为CAN功能
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_CAN1);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_CAN1);
  
  // CAN配置
  CAN_DeInit(CANx);
  CAN_InitStructure.CAN_TTCM = DISABLE;
  CAN_InitStructure.CAN_ABOM = DISABLE;
  CAN_InitStructure.CAN_AWUM = DISABLE;
  CAN_InitStructure.CAN_NART = DISABLE;
  CAN_InitStructure.CAN_RFLM = DISABLE;
  CAN_InitStructure.CAN_TXFP = DISABLE;
  CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
  CAN_InitStructure.CAN_SJW  = CAN_SJW_1tq;
  CAN_InitStructure.CAN_BS1  = CAN_BS1_6tq;
  CAN_InitStructure.CAN_BS2  = CAN_BS2_8tq;
  CAN_InitStructure.CAN_Prescaler = 5;
  CAN_Init(CANx, &CAN_InitStructure);
  
  // CAN过滤器配置
  CAN_FilterInitStructure.CAN_FilterNumber = 0;
  CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
  CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
  CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
  CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
  CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
  CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
  CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
  CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
  CAN_FilterInit(&CAN_FilterInitStructure);
  
  // 启用CAN1接收邮箱0中断
  CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);
}

void CAN_ReceiveData(void)
{
  CAN_FIFOMailBox_TypeDef* mailbox;
  
  mailbox = CANx->sFIFOMailBox[0];
  
  // 接收到数据
  if((mailbox->RIR & CAN_RI0R_RTR) == 0 && (mailbox->RIR & CAN_RI0R_IDE) == 0

作者:大黄鸭duck.

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 CAN总线通信详解

发表回复