CAN通信软件配置工程

CAN通信的软件配置工程方式(怎么用)

代码链接:https://pan.baidu.com/s/12m-lQy_yZz7sduUJ-nReEw?pwd=e7a8 
提取码:e7a8 
--来自百度网盘超级会员V8的分享

1. 应用展示

robomaster开发板a型二进制流水灯(中科大)_哔哩哔哩_bilibili

2. 讲解工程

 a.元件

  • 大疆公司RoboMaster赛事A型主控板
    • 元件与引脚对应关系
      • LED1-8:PG1-PG8,电路原理图中低电平点亮(故默认输出高电平)
      • CAN1:PD0、PD1,在APB1总线上
    • ·12MHz晶振,可配置最高主频180MHZ
    • 主控单元为STM32F427IIH6

 b. 功能描述:

  • CAN1总线自收自发
    • 万般归一,单片机的精髓在于点灯
    • 接收方接收后直接在FIFO中断函数中处理信息,熄灭对应的灯,直到下次指令到来重新点灯
    • 发送方发送一字节数据指明灯的序号,由滤波器配置决定FIFO0或FIFO1接收

3. 配置CubeMX

  • 选择板子STM32F427IIH6
 a. LED参数(配置八个LED灯)
  • 8个GPIO引脚在Pinout view设为output模式
    • 上拉电阻,下拉也可以,尽量别浮空
    • 初始状态高电平表示灯灭

 b. SerialWire、外部时钟

 c. 时钟树

 d. CAN参数配置
  • 在Pinout view将PD0和PD1分别设置为CAN1_RX和CAN1_TX
  • BS1(传播段+相位缓冲段1)和BS2(相位缓冲段2)分别为2,采样在两者间
  • Prescaler是9,从APB1的45MHz分频出来

配置成回环模式

  • 使能接收中断

4. 代码编写

 a. keil中配置

(C99,关闭编译优化,reset and run)

 b. 初始化配置

   i. 一些必要的宏定义
// 滤波器编号
#define CAN_FILTER(x) ((x) << 3)

// 接收队列
#define CAN_FIFO_0 (0 << 2)
#define CAN_FIFO_1 (1 << 2)

//标准帧或扩展帧
#define CAN_STDID (0 << 1)
#define CAN_EXTID (1 << 1)

// 数据帧或遥控帧
#define CAN_DATA_TYPE (0 << 0)
#define CAN_REMOTE_TYPE (1 << 0)
   ii. 初始化配置函数定义
/**
 * @brief 初始化CAN总线
 *
 * @param hcan CAN编号
 * @param Callback_Function 处理回调函数
 */
void CAN_Init(CAN_HandleTypeDef *hcan)
{
  HAL_CAN_Start(hcan);//打开CAN
  __HAL_CAN_ENABLE_IT(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
  __HAL_CAN_ENABLE_IT(hcan, CAN_IT_RX_FIFO1_MSG_PENDING);//使能两个FIFO的中断
}
  • 可以将这部分初始化,写在滤波器的配置函数中
  iii. 初始化配置函数调用
CAN_Init(&hcan1);

 c. 发送报文

   i. 发送报文函数定义
/**
 * @brief 发送数据帧
 *
 * @param hcan CAN编号
 * @param ID ID
 * @param Data 被发送的数据指针
 * @param Length 长度
 * @return uint8_t 执行状态(HAL_CAN_AddTxMessage)
 */
uint8_t CAN_Send_Data(CAN_HandleTypeDef *hcan, uint16_t ID, uint8_t *Data, uint16_t Length)
{
  CAN_TxHeaderTypeDef tx_header;
  uint32_t used_mailbox;

// 检测关键传参
  assert_param(hcan != NULL);

  tx_header.StdId = ID;
  tx_header.ExtId = 0;
  tx_header.IDE = 0;
  tx_header.RTR = 0;
  tx_header.DLC = Length;

return (HAL_CAN_AddTxMessage(hcan, &tx_header, Data, &used_mailbox));
}

HAL 库提供了实现 CAN 发送的函数 HAL_CAN_AddTXMessage

HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, 
CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox)    

函数名

HAL_CAN_AddTXMessage

函数功能

将一段数据通过 CAN 总线发送

返回值

HAL_StatusTypeDef,HAL 库定义的几种状态,如果本次 CAN 发送成功,则返回 HAL_OK

参数1

CAN_HandleTypeDef *hcan,即 can 的句柄指针,如果是 can1 就输入&hcan1,can2 就输入&hcan2

参数2

CAN_TxHeaderTypeDef *pHeader,待发送的 CAN 数据帧信息的结构体指针,包含了 CAN 的 ID,格式等重要信息

参数3

uint8_t aData[],装载了待发送的数据的数组名称

参数4

uint32_t *pTxMailbox,用于存储 CAN 发送所使用的邮箱号

在使用HAL_CAN_AddTXMessage前,需要把 帧格式、长度 通过对结构体赋值确定好,在作为该函数的参数(发送报文的作用)

   ii. 发送报文函数调用
CAN_Send_Data(&hcan1,0x01,&Send_Data,1);

 d. 接收报文

   i. 接收报文滤波器配置函数定义
/**
 * @brief 配置CAN的滤波器
 *
 * @param h    can CAN编号
     * @param Object_Para 编号 | FIFOx | ID类型 | 帧类型
     * @param ID ID
     * @param Mask_ID 屏蔽位(0x3ff, 0x1fffffff)
 */
void CAN_Filter_Mask_Config(CAN_HandleTypeDef *hcan, uint8_t Object_Para, uint32_t ID, uint32_t Mask_ID)
{
  CAN_FilterTypeDef can_filter_init_structure;

  // 检测关键传参
  assert_param(hcan != NULL);

  if ((Object_Para & 0x02))
  {
    // 扩展帧
    // 掩码后ID的高16bit
    can_filter_init_structure.FilterIdHigh = ID << 3 << 16;
    // 掩码后ID的低16bit
    can_filter_init_structure.FilterIdLow = ID << 3 | ((Object_Para & 0x03) << 1);
    // ID掩码值高16bit
    can_filter_init_structure.FilterMaskIdHigh = Mask_ID << 3 << 16;
    // ID掩码值低16bit
    can_filter_init_structure.FilterMaskIdLow = Mask_ID << 3 | ((Object_Para & 0x03) << 1);
  }
  else
  {
    // 标准帧
    // 掩码后ID的高16bit
    can_filter_init_structure.FilterIdHigh = ID << 5;
    // 掩码后ID的低16bit
    can_filter_init_structure.FilterIdLow = ((Object_Para & 0x03) << 1);
    // ID掩码值高16bit
    can_filter_init_structure.FilterMaskIdHigh = Mask_ID << 5;
    // ID掩码值低16bit
    can_filter_init_structure.FilterMaskIdLow = ((Object_Para & 0x03) << 1);
  }

  // 滤波器序号, 0-27, 共28个滤波器, can1是0~13, can2是14~27
  can_filter_init_structure.FilterBank = Object_Para >> 3;
  // 滤波器绑定FIFOx, 只能绑定一个
  can_filter_init_structure.FilterFIFOAssignment = (Object_Para >> 2) & 0x01;
  // 使能滤波器
  can_filter_init_structure.FilterActivation = ENABLE;
  // 滤波器模式, 设置ID掩码模式
  can_filter_init_structure.FilterMode = CAN_FILTERMODE_IDMASK;
  // 32位滤波   
  can_filter_init_structure.FilterScale = CAN_FILTERSCALE_32BIT;
    //从机模式选择开始单元
  can_filter_init_structure.SlaveStartFilterBank = 14;

  HAL_CAN_ConfigFilter(hcan, &can_filter_init_structure);
   //将这些配置写入寄存器
}
   ii. 接收报文滤波器配置函数调用
CAN_Filter_Mask_Config(&hcan1, 
CAN_FILTER(13) | CAN_FIFO_1 | CAN_STDID | CAN_DATA_TYPE,
 0x114, 0x7ff);
//掩码ID设为0x7ff,意为所有的都接收
   iii. 接受报文中断回调函数定义
/**
 * @brief HAL库CAN接收FIFO1中断
 *
 * @param hcan CAN编号
 */
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
  CAN_RxHeaderTypeDef header;
  uint8_t data;
  HAL_CAN_GetRxMessage(hcan, CAN_FILTER_FIFO1, &header, &data);
//如果有多个CANID的话还需要在这里用switch的结构 不同的ID用不同的处理例如下面一行代码
  LED_Control(data);
}
  • 示例(robomaster电机控制)
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
 CAN_RxHeaderTypeDef rx_header;
 uint8_t rx_data[8];
 HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data);
 switch (rx_header.StdId)
 {
     case CAN_3508_M1_ID:
     case CAN_3508_M2_ID:
     case CAN_3508_M3_ID:
     case CAN_3508_M4_ID:
     case CAN_YAW_MOTOR_ID:
     case CAN_PIT_MOTOR_ID:
     case CAN_TRIGGER_MOTOR_ID:
     {
         static uint8_t i = 0;
         //get motor id
         i = rx_header.StdId - CAN_3508_M1_ID;
         get_motor_measure(&motor_chassis[i], rx_data);
         break;
     }
     default:
     {
         break;
     }
 }
}

  e. 点灯程序

/**
 * @brief 点灯
 *
 * @param data 收到的数据
 */
void LED_Control(uint8_t data)
{
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_1, ((data & 1) == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_2, ((data & 2) == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_3, ((data & 4) == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_4, ((data & 8) == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_5, ((data & 16) == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, ((data & 32) == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_7, ((data & 64) == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_8, ((data & 128) == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

5. 总结(CAN通信的软件实现过程)

适合按照的顺序如下,与上文顺序有差异

    1. 一些必要的宏定义(移位操作)
    2. 滤波器配置函数【包括初始化(CAN的打开、对两个FIFO中断的使能),滤波器的配置,两个FIFO的选择】
    3. 发送报文的配置(对帧格式、长度进行配置,通过HAL库函数将数据发送到CAN总线)
    4. FIFO的接收中断回调函数(其中用HAL库函数读取总线数据并进行处理)
以下是一个基于STM32F103C8T6的CAN通信例程,你可以参考: ```c #include "stm32f10x.h" #include "stm32f10x_can.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #define CANx CAN1 #define CAN_CLK RCC_APB1Periph_CAN1 #define CAN_RX_PIN GPIO_Pin_11 #define CAN_TX_PIN GPIO_Pin_12 #define CAN_GPIO_PORT GPIOA #define CAN_GPIO_CLK RCC_APB2Periph_GPIOA #define CAN_AF_PORT GPIO_AF_CAN1 #define CAN_RX_SOURCE GPIO_PinSource11 #define CAN_TX_SOURCE GPIO_PinSource12 CanTxMsg TxMessage; CanRxMsg RxMessage; uint8_t flag = 0; void CAN1_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; CAN_InitTypeDef CAN_InitStructure; RCC_APB2PeriphClockCmd(CAN_GPIO_CLK, ENABLE); RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(CAN_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(CAN_GPIO_PORT, &GPIO_InitStructure); GPIO_PinRemapConfig(GPIO_Remap_CAN, ENABLE); 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 = 6; CAN_Init(CANx, &CAN_InitStructure); CAN_FilterInitTypeDef CAN_FilterInitStructure; 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); } int main(void) { uint8_t i = 0; CAN1_Config(); TxMessage.StdId = 0x12; TxMessage.ExtId = 0x12; TxMessage.RTR = CAN_RTR_DATA; TxMessage.IDE = CAN_ID_STD; TxMessage.DLC = 2; TxMessage.Data[0] = 'A'; TxMessage.Data[1] = 'B'; while (1) { CAN_Transmit(CANx, &TxMessage); delay_ms(1000); if(flag == 1) { flag = 0; printf("Receive Data:"); for(i = 0; i < RxMessage.DLC; i++) { printf("%c", RxMessage.Data[i]); } printf("\r\n"); } } } void USB_LP_CAN1_RX0_IRQHandler(void) { if(CAN_GetITStatus(CANx, CAN_IT_FMP0) != RESET) { CAN_Receive(CANx, CAN_FIFO0, &RxMessage); CAN_ClearITPendingBit(CANx, CAN_IT_FMP0); flag = 1; } } ``` 这个例程演示了如何通过CAN1接口发送和接收数据。在这个例程中,我们使用CAN1接口和PA11(CAN_RX)和PA12(CAN_TX)引脚。 在主函数中,我们首先配置CAN1接口,并设置发送消息。然后,我们不断地发送消息,并等待接收消息。如果接收到消息,则打印接收到的数据。 在中断处理程序中,我们检查是否收到了一个新的消息。如果是,则接收消息并设置一个标志以指示主函数中有新数据可用。 请注意,这个例程还需要一个延迟函数,你需要自己实现。你可以使用定时器或其他方法来实现延迟函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值