STM32的CAN总线通信是一种常用的通信协议,用于在微控制器之间进行高速、可靠的通信。CAN(Controller Area Network)是由Bosch公司提出的一种主从式网络通信协议,广泛应用于汽车、工业控制和其它领域。

本文将通过编写代码案例,详细介绍STM32的CAN总线通信。为了方便演示,我们将使用STM32F407开发板和Keil MDK-ARM开发环境。以下是本文的主要内容:

  1. CAN总线硬件初始化

  2. CAN总线模式选择

  3. CAN总线发送消息

  4. CAN总线接收消息

  5. 中断处理

  6. 过滤器设置

  7. 扩展帧

  8. CAN总线硬件初始化

首先,需要初始化CAN总线的硬件。在STM32F407开发板上,CAN总线的硬件资源有两个:CAN1和CAN2。可以通过以下代码初始化CAN1:

/* CAN1 hardware initialization */
void CAN1_Init(void)
{
  CAN_InitTypeDef can_init;
  CAN_FilterInitTypeDef filter_init;
  
  /* Enable GPIOB clock */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  
  /* Enable CAN1 clock */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
  
  /* Configure GPIO pins */
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_CAN1);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_CAN1);
  
  GPIO_InitTypeDef gpio_init;
  gpio_init.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  gpio_init.GPIO_Mode = GPIO_Mode_AF;
  gpio_init.GPIO_OType = GPIO_OType_PP;
  gpio_init.GPIO_Speed = GPIO_Speed_100MHz;
  gpio_init.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(GPIOB, &gpio_init);
  
  /* CAN1 configuration */
  CAN_DeInit(CAN1);
  
  can_init.CAN_TTCM = DISABLE;
  can_init.CAN_ABOM = DISABLE;
  can_init.CAN_AWUM = DISABLE;
  can_init.CAN_NART = DISABLE;
  can_init.CAN_RFLM = DISABLE;
  can_init.CAN_TXFP = DISABLE;
  can_init.CAN_Mode = CAN_Mode_Normal;
  can_init.CAN_SJW = CAN_SJW_1tq;
  can_init.CAN_BS1 = CAN_BS1_8tq;
  can_init.CAN_BS2 = CAN_BS2_7tq;
  can_init.CAN_Prescaler = 3;
  CAN_Init(CAN1, &can_init);
  
  /* CAN1 filter configuration */
  filter_init.CAN_FilterNumber = 0;
  filter_init.CAN_FilterMode = CAN_FilterMode_IdMask;
  filter_init.CAN_FilterScale = CAN_FilterScale_32bit;
  filter_init.CAN_FilterIdHigh = 0x0000;
  filter_init.CAN_FilterIdLow = 0x0000;
  filter_init.CAN_FilterMaskIdHigh = 0x0000;
  filter_init.CAN_FilterMaskIdLow = 0x0000;
  filter_init.CAN_FilterFIFOAssignment = 0;
  filter_init.CAN_FilterActivation = ENABLE;
  CAN_FilterInit(&filter_init);
  
  /* Enable CAN1 interrupt */
  NVIC_InitTypeDef nvic_init;
  nvic_init.NVIC_IRQChannel = CAN1_RX0_IRQn;
  nvic_init.NVIC_IRQChannelPreemptionPriority = 0;
  nvic_init.NVIC_IRQChannelSubPriority = 0;
  nvic_init.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&nvic_init);
  
  /* Enable CAN1 reception interrupt */
  CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
}

以上代码中,我们首先使能GPIOB和CAN1的时钟。然后,配置GPIOB的引脚为复用功能,并设置CAN1的工作模式、时序、波特率等参数。接下来,配置CAN1的过滤器,使其可以过滤接收到的消息。最后,使能CAN1的中断,并使能CAN1的接收中断。

  1. CAN总线模式选择

在进行CAN总线通信之前,需要选择CAN总线的工作模式。CAN总线有两种工作模式:Normal模式和Loopback模式。在Normal模式下,CAN总线正常工作,发送和接收真实的消息;在Loopback模式下,CAN总线将发送的消息直接回环接收,用于自我测试。以下是选择CAN总线模式的代码:

/* Select CAN mode */
void CAN_SelectMode(CAN_TypeDef* can, uint8_t mode)
{
  if (mode == CAN_MODE_NORMAL)
  {
    CAN_EnterNormalMode(can);
  }
  else if (mode == CAN_MODE_LOOPBACK)
  {
    CAN_EnterLoopbackMode(can);
  }
}

/* Enter normal mode */
void CAN_EnterNormalMode(CAN_TypeDef* can)
{
  CAN_CancelTransmit(can, CAN_BANK_0);
  CAN_CancelTransmit(can, CAN_BANK_1);
  CAN_GlobalReleaseReset(can);
  CAN_SetMode(can, CAN_MODE_NORMAL);
}

/* Enter loopback mode */
void CAN_EnterLoopbackMode(CAN_TypeDef* can)
{
  CAN_GlobalReset(can);
  CAN_SetMode(can, CAN_MODE_RESET);
  CAN_GlobalReleaseReset(can);
  CAN_SetMode(can, CAN_MODE_LOOPBACK);
}

/* CAN mode definitions */
#define CAN_MODE_NORMAL   0
#define CAN_MODE_LOOPBACK 1

以上代码中,我们定义了两个函数:CAN_EnterNormalMode()用于进入Normal模式,CAN_EnterLoopbackMode()用于进入Loopback模式。在进入模式之前,需要先进行一些准备工作,如取消发送中断、复位CAN控制器等。然后,调用CAN_SetMode()函数设置工作模式。

  1. CAN总线发送消息

在CAN总线通信中,发送消息需要配置CAN总线的发送邮箱。CAN总线有多个发送邮箱,可以同时发送多个消息。以下是配置CAN总线发送邮箱的代码:

/* CAN1 send message */
void CAN1_SendMessage(uint32_t id, uint8_t* data, uint8_t length)
{
  uint8_t mailbox;
  
  /* Wait until there is a free mailbox */
  while (CAN_GetFlagStatus(CAN1, CAN_FLAG_TME0) == RESET &&
         CAN_GetFlagStatus(CAN1, CAN_FLAG_TME1) == RESET &&
         CAN_GetFlagStatus(CAN1, CAN_FLAG_TME2) == RESET);
  
  /* Select a free mailbox */
  if (CAN_GetFlagStatus(CAN1, CAN_FLAG_TME0) == SET)
    mailbox = CAN_BANK_0;
  else if (CAN_GetFlagStatus(CAN1, CAN_FLAG_TME1) == SET)
    mailbox = CAN_BANK_1;
  else
    mailbox = CAN_BANK_2;
  
  /* Configure the mailbox */
  CAN_ClearFlag(CAN1, CAN_FLAG_TXOK0 << mailbox);
  CAN_Transmit(can, mailbox, id, CAN_ID_STD, CAN_RTR_DATA, length, data);
}

以上代码中,我们首先等待直到有一个空闲的发送邮箱。然后,选择一个空闲的发送邮箱,并清除其发送完成标志位。接下来,调用CAN_Transmit()函数配置发送邮箱的参数,并发送消息。

  1. CAN总线接收消息

在CAN总线通信中,接收消息需要配置CAN总线的接收邮箱。CAN总线有多个接收邮箱,可以同时接收多个消息。以下是配置CAN总线接收邮箱的代码:

/* CAN1 receive message */
volatile uint32_t can1_rx_id;
volatile uint8_t can1_rx_data[8];
volatile uint8_t can1_rx_length;
volatile bool can1_rx_flag;

void CAN1_ReceiveMessage(void)
{
  CAN_Receive(CAN1, CAN_FIFO0, &can1_rx_id, CAN_ID_STD, &can1_rx_flag, &can1_rx_length, can1_rx_data);
}

以上代码中,我们首先定义了一些全局变量,用于存储接收到的消息的ID、数据、长度和标志位。然后,调用CAN_Receive()函数接收消息,并将消息的ID、数据、长度和标志位保存到相应的全局变量中。

  1. 中断处理

在CAN总线通信中,接收消息通常采用中断方式。以下是中断处理的代码:

/* CAN1 receive interrupt */
void CAN1_RX0_IRQHandler(void)
{
  if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) == SET)
  {
    CAN1_ReceiveMessage();

作者:CrMylive.

物联沃分享整理
物联沃-IOTWORD物联网 » 学习STM32的CAN总线通信

发表回复