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通信的软件实现过程)
适合按照的顺序如下,与上文顺序有差异
-
- 一些必要的宏定义(移位操作)
- 滤波器配置函数【包括初始化(CAN的打开、对两个FIFO中断的使能),滤波器的配置,两个FIFO的选择】
- 发送报文的配置(对帧格式、长度进行配置,通过HAL库函数将数据发送到CAN总线)
- FIFO的接收中断回调函数(其中用HAL库函数读取总线数据并进行处理)