HAL库+CubeMX CAN总线的使用


CAN 控制 RM 电机

1、CAN 概述

CAN是控制器局域网络(Controller Area Network, CAN)的简称,是一种能够实现分布式实时控制的串行通信网络。

CAN总线由CAN_H和CAN_L两根线构成,各个设备一起挂载在总线上。
在这里插入图片描述
RoboMaster系列电机也采用CAN协议进行通信,CAN协议比较复杂,一个完整的数据帧由下图中的各个部分组成:
在这里插入图片描述
每一个挂载在CAN总线上的CAN都有一个自己独属的ID,每当一个设备发送一帧数据时,总线其他设备会检查这个ID是否是自己需要接收数据的对象,如果是则接收本帧数据,如果不是则忽略。

1.1、仲裁场

在这里插入图片描述
如上图所示,ID存储在数据帧最前头的仲裁场内,CAN的ID分为标准ID和拓展ID两类,标准ID长度为11位。如果设备过多,标准ID不够用的情况下,可以使用拓展ID,拓展ID的长度有29位。

1.2、控制场和数据场

在这里插入图片描述
在通过ID判断本帧数据可以接收后,控制场中的DLC规定了本帧数据的长度,而数据场内的数据的大小为8Byte,即8个8位数据。CAN总线的一个数据帧中所需要传输的有效数据实际上就是这8Byte(即DLC长度一般都设置为0x08)。

2、RM 3508电机使用

2.1、电调发送报文格式

在这里插入图片描述
这是电调发送报文格式,即如果要发送数据给1号到4号电调,控制电机的输出电流,从而控制电机转速时,需要按照表中的内容,将发送的CAN数据帧的ID设置为0x200,数据域中的8Byte数据按照电调1到4的高八位和低八位的顺序装填,帧格式和DLC也按照表中内容进行设置,最后进行数据的发送。

2.2、电调接收报文格式

在这里插入图片描述
首先根据接收到的ID判断究竟接收到的是哪个电调发送来的数据,手册中规定1号电调ID为0x201,2号为0x202,3号为0x203,4号为0x204。判断完数据来源之后,就可以按照手册中的数据格式进行解码,通过高八位和低八位拼接的方式,得到电机的转子机械角度,转子转速,转矩电流,电机温度等数据。

3、CAN在cubeMX中的配置

  1. 首先在cubeMX中将CAN1开启,打开Connectivity下的CAN1,进行CAN1的配置。
    在这里插入图片描述
  2. 在Mode中,将Master Mode选中打勾。
  3. 在Configuration界面中,需要进行CAN的波特率的配置,设置完分频系数 (Prescaler) 后,cubeMX会自动完成Time Quantum(简写为tq)的计算,将tq乘以tBS1 (Time Quanta in Bit Segment 1),tBS2 (Time Quanta in Bit Segment 1),RJW (ReSynchronization Jump Width) 之和刚好为1微秒,对应波特率为1M,这是CAN总线支持的最高通讯速率
    在这里插入图片描述
    在这里插入图片描述
    友情提示:将Prescaler调为3后,先将Time…Segment 1 设置为12或以上,再将Time…Segment 2设置为3,最后再将Time…Segment 1 设置为10
  4. 使能中断
    在这里插入图片描述
  5. can2的配置方式与can1相同,Mode为Slave Mode。

4、CAN发送函数

程序中提供了CAN_cmd_chassis函数和CAN_cmd_gimbal函数,用于向底盘电机和云台电机发送CAN信号,控制电机运动。

4.1、CAN_cmd_chassis函数

CAN_cmd_chassis函数的输入为电机1到电机4的驱动电流期望值motor1到motor4,函数会将期望值拆分成高八位和低八位,放入8Byte的CAN的数据域中,然后添加ID (CAN_CHASSIS_ALL_ID 0x200),帧格式,数据长度等信息,形成一个完整的CAN数据帧,发送给各个电调。

void CAN_cmd_chassis(int16_t motor1, int16_t motor2, int16_t motor3, int16_t motor4) 
{ 
	uint32_t send_mail_box; 
	
	chassis_tx_message.StdId = CAN_CHASSIS_ALL_ID; 
	chassis_tx_message.IDE = CAN_ID_STD; 
	chassis_tx_message.RTR = CAN_RTR_DATA;
	chassis_tx_message.DLC = 0x08; 
	
	chassis_can_send_data[0] = motor1 >> 8; 
	chassis_can_send_data[1] = motor1; 
	chassis_can_send_data[2] = motor2 >> 8; 
	chassis_can_send_data[3] = motor2; 
	chassis_can_send_data[4] = motor3 >> 8; 
	chassis_can_send_data[5] = motor3; 
	chassis_can_send_data[6] = motor4 >> 8; 
	chassis_can_send_data[7] = motor4; 
	
	HAL_CAN_AddTxMessage(&CHASSIS_CAN, &chassis_tx_message, chassis_can_send_data, &send_mail_box); 
}
4.1.1、CAN发送的函数 HAL_CAN_AddTXMessage

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

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

在这里插入图片描述

4.2、CAN_cmd_chassis函数

CAN_cmd_gimbal函数的功能为向云台电机和发射机构电机发送控制信号,输入参数为yaw轴电机,pitch轴电机,发射机构电机的驱动电流期望值yaw,pitch,shoot(rev为保留值),函数会将期望值拆分成高八位和第八位,放入8Byte的CAN的数据域中,然后添加ID(CAN_GIMBAL_ALL_ID 0x1FF),帧格式,数据长度等信息,形成一个完整的CAN数据帧,发送给各个电调。

void CAN_cmd_gimbal(int16_t yaw, int16_t pitch, int16_t shoot, int16_t rev) 
{ 
	uint32_t send_mail_box; 
	gimbal_tx_message.StdId = CAN_GIMBAL_ALL_ID; 
	gimbal_tx_message.IDE = CAN_ID_STD; 
	gimbal_tx_message.RTR = CAN_RTR_DATA; 
	gimbal_tx_message.DLC = 0x08; 
	gimbal_can_send_data[0] = (yaw >> 8); 
	gimbal_can_send_data[1] = yaw; 
	gimbal_can_send_data[2] = (pitch >> 8); 
	gimbal_can_send_data[3] = pitch; 
	gimbal_can_send_data[4] = (shoot >> 8); 
	gimbal_can_send_data[5] = shoot; 
	gimbal_can_send_data[6] = (rev >> 8); 
	gimbal_can_send_data[7] = rev; 
	HAL_CAN_AddTxMessage(&GIMBAL_CAN, &gimbal_tx_message, gimbal_can_send_data, &send_mail_box); 
}

5、CAN接收中断回调

HAL库提供了CAN的接收中断回调函数 HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan),每当CAN完成一帧数据的接收时,就会触发一次CAN接收中断处理函数,接收中断函数完成一些寄存器的处理之后会调用CAN接收中断回调函数。

在中断回调函数中首先判断接收对象的ID,是否是需要的接收的电调发来的数据。完成判断之后,进行解码,将对应的电机的数据装入电机信息数组motor_chassis各个对应的位中。

/*
motor data,  0:chassis motor1 3508;1:chassis motor3 3508;2:chassis motor3 3508;3:chassis motor4 3508;
4:yaw gimbal motor 6020;5:pitch gimbal motor 6020;6:trigger motor 2006;
电机数据, 0:底盘电机1 3508电机,  1:底盘电机2 3508电机,2:底盘电机3 3508电机,3:底盘电机4 3508电机;
4:yaw云台电机 6020电机; 5:pitch云台电机 6020电机; 6:拨弹电机 2006电机*/
static motor_measure_t motor_chassis[7];

static CAN_TxHeaderTypeDef  gimbal_tx_message;
static uint8_t              gimbal_can_send_data[8];
static CAN_TxHeaderTypeDef  chassis_tx_message;
static uint8_t              chassis_can_send_data[8];

/**
  * @brief          hal CAN fifo call back, receive motor data
  * @param[in]      hcan, the point to CAN handle
  * @retval         none
  */
/**
  * @brief          hal库CAN回调函数,接收电机数据
  * @param[in]      hcan:CAN句柄指针
  * @retval         none
  */
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;
        }
    }
}

5.1、接收函数 HAL_CAN_GetRxMessage

接收时调用了HAL库提供的接收函数HAL_CAN_GetRxMessage

HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, 
uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[])

在这里插入图片描述
motor_chassis为motor_measure_t类型的数组,其中装有电机转子角度,电机转子转速,控制电流,温度等信息。

typedef struct { 
	uint16_t ecd; 
	int16_t speed_rpm; 
	int16_t given_current; 
	uint8_t temperate; 
	int16_t last_ecd; 
} motor_measure_t;
5.1.1、解码函数 get_motor_measure

解码功能实际上完成的工作是将接收到的数据按照高八位和低八位的方式进行拼接,从而得到电机的各个参数。

#define get_motor_measure(ptr, data) \
	{ \ 
			(ptr)->last_ecd = (ptr)->ecd; \ 
			(ptr)->ecd = (uint16_t)((data)[0] << 8 | (data)[1]); \ 
			(ptr)->speed_rpm = (uint16_t)((data)[2] << 8 | (data)[3]); \ 
			(ptr)->given_current = (uint16_t)((data)[4] << 8 | (data)[5]); \ 
			(ptr)->temperate = (data)[6]; \ 
	}

6、配置接收过滤器(筛选器)

过滤器是需要自己配置的,而且不配置过滤器CAN不能正常接收数据。
其初始化要在进入while循环前进行调用

extern CAN_HandleTypeDef hcan1;
extern CAN_HandleTypeDef hcan2;

void can_filter_init(void)
{
    CAN_FilterTypeDef can_filter_st;
    can_filter_st.FilterActivation = ENABLE; //筛选器激活
    can_filter_st.FilterMode = CAN_FILTERMODE_IDMASK; //标识符屏蔽位模式
    can_filter_st.FilterScale = CAN_FILTERSCALE_32BIT; //过滤器位宽为单个32位
    can_filter_st.FilterIdHigh = 0x0000;
    can_filter_st.FilterIdLow = 0x0000;
    can_filter_st.FilterMaskIdHigh = 0x0000;
    can_filter_st.FilterMaskIdLow = 0x0000;
    can_filter_st.FilterBank = 0;
    can_filter_st.FilterFIFOAssignment = CAN_RX_FIFO0; //FIFO0的中断和FIFO1的中断是不一样的,这里是把接收到的报文放入到FIFO0中
    HAL_CAN_ConfigFilter(&hcan1, &can_filter_st);
    HAL_CAN_Start(&hcan1);
    HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);

    can_filter_st.SlaveStartFilterBank = 14; //为从属can选择开始的过滤库,对于单个CAN实例,这个参数没有意义
    can_filter_st.FilterBank = 14;
    HAL_CAN_ConfigFilter(&hcan2, &can_filter_st);
    HAL_CAN_Start(&hcan2);
    HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING);

}

标识符屏蔽位模式:FxR0为标识符匹配值,FxR1为屏蔽码。若FxR1中某一位为1,FxR0中相应的位必须与收到的帧的标识符中的相应位吻合才能通过过滤器。FxR1中为0的位表示FxR0的相应位可不必与收到的帧进行匹配。
例如:标识符寄存器的bit15=0,屏蔽位寄存器的bit15=1,那么接受的Message里面的标识符的bit15必须为0才可能被硬件接受。如果屏蔽位寄存器的bit15=0,Message里面的标识符的bit15无论为什么值,bit15都能匹配通过。当bit0~bit31都能通过时。此Message就会被硬件接受。

标识符列表模式:FR1和FR2都是要匹配的标识符,收到的帧的标识符必须与其中一个吻合才能通过过滤。
例如:现在设置为0x602和0x601,那么只有ID为0x602和0x601时才会通过过滤器

所有过滤器是并联的,一个报文只要通过了一个过滤器就算是有效的

CAN_FxR1与CAN_FxR2寄存器分别被拆成两段,CAN_FxR1寄存器的高16位对应着代码中的FilterIdHigh,低16位对应着FilterIdLow,而CAN_FxR2寄存器的高16位对应着FilterMaskIdHigh,低16位对应着FilterMaskIdLow。

Copy From 大疆学习文档

  • 17
    点赞
  • 120
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
CAN总线是一种高速、可靠的通信协议,可用于连接多个节点,例如汽车电子和工业控制系统。在STM32CubeMXHAL库中,使用CAN总线需要以下步骤: 1. 配置CAN硬件 在STM32CubeMX中,选择正确的芯片型号并打开CAN总线选项卡。在该选项卡中,您可以配置CAN的时钟、波特率和过滤器等参数。根据您的具体应用需求进行配置。 2. 初始化CAN总线 使用HAL库的CAN初始化函数`HAL_CAN_Init()`初始化CAN总线。该函数需要传入CAN_HandleTypeDef结构体,该结构体包含了CAN的基本参数。 ```c CAN_HandleTypeDef hcan; hcan.Instance = CANx; hcan.Init.Prescaler = 10; hcan.Init.Mode = CAN_MODE_NORMAL; hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan.Init.TimeSeg1 = CAN_BS1_13TQ; hcan.Init.TimeSeg2 = CAN_BS2_2TQ; hcan.Init.TimeTriggeredMode = DISABLE; hcan.Init.AutoBusOff = ENABLE; hcan.Init.AutoWakeUp = DISABLE; hcan.Init.AutoRetransmission = ENABLE; hcan.Init.ReceiveFifoLocked = DISABLE; hcan.Init.TransmitFifoPriority = DISABLE; if (HAL_CAN_Init(&hcan) != HAL_OK) { Error_Handler(); } ``` 3. 配置CAN过滤器 使用HAL库的CAN过滤器配置函数`HAL_CAN_ConfigFilter()`配置CAN过滤器,以确保只有特定的消息可以通过。该函数需要传入CAN_HandleTypeDef结构体和CAN_FilterTypeDef结构体,后者包括过滤器的掩码和ID等参数。 ```c CAN_FilterTypeDef sFilterConfig; sFilterConfig.FilterBank = 0; sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh = 0x0000; sFilterConfig.FilterIdLow = 0x0000; sFilterConfig.FilterMaskIdHigh = 0x0000; sFilterConfig.FilterMaskIdLow = 0x0000; sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; sFilterConfig.FilterActivation = ENABLE; sFilterConfig.SlaveStartFilterBank = 14; if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK) { Error_Handler(); } ``` 4. 发送CAN消息 使用HAL库的CAN消息发送函数`HAL_CAN_AddTxMessage()`发送CAN消息。该函数需要传入CAN_HandleTypeDef结构体和CAN_TxHeaderTypeDef结构体,后者包括消息的ID和数据等参数。 ```c CAN_TxHeaderTypeDef TxHeader; uint8_t aData[8]; TxHeader.StdId = 0x123; TxHeader.ExtId = 0x00; TxHeader.IDE = CAN_ID_STD; TxHeader.RTR = CAN_RTR_DATA; TxHeader.DLC = 8; TxHeader.TransmitGlobalTime = DISABLE; aData[0] = 0x01; aData[1] = 0x02; aData[2] = 0x03; aData[3] = 0x04; aData[4] = 0x05; aData[5] = 0x06; aData[6] = 0x07; aData[7] = 0x08; if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, aData, &TxMailbox) != HAL_OK) { Error_Handler(); } ``` 5. 接收CAN消息 使用HAL库的CAN消息接收函数`HAL_CAN_GetRxMessage()`接收CAN消息。该函数需要传入CAN_HandleTypeDef结构体和CAN_RxHeaderTypeDef结构体,后者包括消息的ID和数据等参数。 ```c CAN_RxHeaderTypeDef RxHeader; uint8_t aData[8]; if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, aData) != HAL_OK) { Error_Handler(); } ``` 以上就是使用STM32CubeMXHAL库CAN总线应用教程。根据您的应用需求,您可以进一步优化CAN总线的配置和使用
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值