时钟数配置
这里只展示CAN的配置:
在配置的过程中要注意,需要查找所用电机手册的can通讯波特率是多少
计算公式:
can通讯波特率=APB1时钟频/分频系数Prescaler*(BS1+BS2+同步时间段)
在配置分频系数的时候可能会遇到这个问题,是因为在配置Prescaler、BS1、BS2参数错误,按照提示要求,需要更改参数:将Bit Segment (BS2)的设置,将其值增加到至少3;将Prescaler增加到至少3。
can通信包含两部分,发送和接收,顾名思义
发送是电脑发送指令给电机,通常指发送电机电流多少;
接收是电脑通过通信协议读取到电机的数据,比如线速度和角速度等等相关信息
发送包含两部分:筛选器和发送信息
筛选器相当于邮局,用于中转信息,确保发送的ID是否正确。
配置CAN滤波器,启动CAN总线,并激活接收中断,以便在有CAN消息到达时能够及时处理。这通常是在使用CAN总线进行通信时的一般设置过程。
//筛选器
void can_filter_init(void)
{
CAN_FilterTypeDef can_filter_st; //CAN滤波器配置结构体,用于配置CAN过滤器的参数
can_filter_st.FilterActivation = ENABLE; //启用CAN过滤器功能
can_filter_st.FilterMode = CAN_FILTERMODE_IDMASK;//设置CAN滤波器的模式为标识符掩码模式
can_filter_st.FilterScale = CAN_FILTERSCALE_32BIT;//设置CAN滤波器的比例为32位,表示过滤器的标识符和掩码的总位数。
can_filter_st.FilterIdHigh = 0x0000;//设置CAN滤波器的高16位标识符。
can_filter_st.FilterIdLow = 0x0000;//设置CAN滤波器的低16位标识符。
can_filter_st.FilterMaskIdHigh = 0x0000;//设置CAN滤波器的高16位掩码。
can_filter_st.FilterMaskIdLow = 0x0000;//设置CAN滤波器的低16位掩码。
can_filter_st.FilterBank = 0;//设置CAN滤波器的编号
can_filter_st.FilterFIFOAssignment = CAN_RX_FIFO0;//设置CAN滤波器匹配的消息将被传送到的FIFO0缓冲区
HAL_CAN_ConfigFilter(&hcan1, &can_filter_st);//配置CAN滤波器,应用到CAN1总线
HAL_CAN_Start(&hcan1);//启动CAN1总线。
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);//激活CAN1总线的接收中断通知,当RX_FIFO0中有消息待处理时触发中断。
}
将数值(电流值)发送给电机,类似于PWM占空比
static CAN_TxHeaderTypeDef can_3508_tx_message;
void can_msg_send(int16_t motor1,int16_t motor2, int16_t motor3, int16_t motor4)
{
uint32_t send_mail_box;
can_3508_tx_message.StdId = CAN_3508_ALL_ID;//CAN消息的标准标识符,类似于电机ID
can_3508_tx_message.IDE = CAN_ID_STD;//使用标准标识符
can_3508_tx_message.RTR = CAN_RTR_DATA;//使用数据帧
can_3508_tx_message.DLC = 0x08;//数据长度8字节
can_3508_tx_send_data[0] = motor1 >> 8;//高八位
can_3508_tx_send_data[1] = motor1;//低八位
can_3508_tx_send_data[2] = motor2 >> 8;
can_3508_tx_send_data[3] = motor2;
can_3508_tx_send_data[4] = motor3 >> 8;
can_3508_tx_send_data[5] = motor3;
can_3508_tx_send_data[6] = motor4 >> 8;
can_3508_tx_send_data[7] = motor4;
HAL_CAN_AddTxMessage(&CAN_3508, &can_3508_tx_message, can_3508_tx_send_data, &send_mail_box);
}
//这里是将四个3508都写入进去,可以控制4个3508电机
StdId
(Standard Identifier):
意义:存储CAN消息的标准标识符(11位长),用于标识CAN网络中的消息源或目标节点。
IDE
(Identifier Extension):
意义:表示CAN消息的标识符类型。
可填值:
CAN_ID_STD
:标准标识符,11位长。
CAN_ID_EXT
:扩展标识符,29位长。
RTR
(Remote Transmission Request):
意义:表示CAN消息的远程传输请求。
可填值:
CAN_RTR_DATA
:数据帧,用于传输实际数据。
CAN_RTR_REMOTE
:远程帧,用于请求其他节点发送数据。
DLC
(Data Length Code):
意义:表示CAN消息携带的数据长度。
可填值:整数,通常范围为0到8,表示数据的字节数。
发送给电机的数据类型是int16_t,发送CAN消息之前,这些16位整数被拆分成高8位和低8位,然后分别存储在 can_3508_tx_send_data
数组的相应位置。
在大疆3508电机使用手册中:
将手册中的数据一一对应到代码中去。
在CAN通信中,数据通常以字节为单位进行传输。高8位和低8位的分离使得可以更容易地在接收端重新组合数据。
包含两部分,hal库的can回调函数HAL_CAN_RxFifo0MsgPendingCallback(),和缓冲区的信息(收到的数据处理)。
这里给出两种方式去写接收:
先获取接收数据长度,在做数据处理的时候根据标准标识符存放数据
用于做电机信息比对,确保收到的数据正确
//hal库CAN回调函数,接收电机数据
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)//回调函数
{
CAN_RxHeaderTypeDef rx_header1;//定义结构体变量:用于存储接收到的CAN消息的头部信息,包括标识符、标识符类型、远程传输请求和数据长度等。
uint8_t rx_data1[8];//定义了一个长度为8的数组,用于存储接收到的CAN消息的数据部分。
if(hcan == &hcan1)
{
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header1, rx_data1);//从FIFO0中获取CAN消息的头部信息和数据。将接收到的CAN消息的相关信息填充到 rx_header1 和 rx_data1 中。
static uint8_t i = 0;//用于循环遍历接收到的CAN消息的数据部分
for( i = 0; i < rx_header1.DLC; ++i)//rx_header1.DLC 表示接收到的数据的长度,循环遍历的目的是将接收到的数据存储到 Can1_ReceiveBuffer 数组中。
{
Can1_ReceiveBuffer[i] = rx_data1[i];//将接收到的CAN消息的数据存储到 Can1_ReceiveBuffer 数组中。
}
//从缓冲区提取信息
Get3508_Info(rx_header1.StdId, Can1_ReceiveBuffer);//将接收到的CAN消息的标准标识符和数据作为参数传递做数据处理。
}
}
由于收到的数据都是原始数据,需要将收到的数据转化为自己需要使用的
typedef struct
{
uint16_t ecd;
int16_t speed_rpm;
int16_t given_current;
uint8_t temperate;
} motor_measure_t;//这里建议把结构体放进.h文件中去
motor_measure_t chassis_motor_info[4];
void Get3508_Info(uint32_t motor_id, uint8_t *canbuf_receive)
{
static uint32_t cnt = 0;
if( (motor_id - 0x201 >= 0) && (motor_id - 0x201 < 4))
{
//读取当前信息
chassis_motor_info[motor_id - 0x201].ecd = canbuf_receive[0] << 8 | canbuf_receive[1];
chassis_motor_info[motor_id - 0x201].speed_rpm = (signed short)(canbuf_receive[2] << 8 | canbuf_receive[3]);
chassis_motor_info[motor_id - 0x201].given_current = (signed short)(canbuf_receive[4] << 8 | canbuf_receive[5]);
chassis_motor_info[motor_id - 0x201].temperate = canbuf_receive[6];
}
}
先获取标准标识符,在做数据处理的时候填放数据
typedef struct
{
int16_t ecd;
int16_t speed;
int16_t given_current;
uint8_t temperate;
} motor_measure_t;
motor_measure_t motor_chassis[4];
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);
if(hcan==&hcan1)
{
switch (rx_header.StdId)//接收到的CAN消息的标准标识符 StdId 进行分支选择。
{
case M3508_1_ID:
case M3508_2_ID:
case M3508_3_ID:
case M3508_4_ID:
{
static uint8_t i = 0;
// get motor id
i = rx_header.StdId - M3508_1_ID;//算电机id数,便于存放数据
Get3508_Info(&motor_chassis[i], rx_data);//将对应电机的信息和接收到的CAN消息数据传递给该函数进行处理。
break;
}
default:
{
break;
}
}
}
void Get3508_Info(motor_measure_t *motor, uint8_t *canbuf_receive)
{
motor.ecd = canbuf_receive[0] << 8 | canbuf_receive[1];
motor.speed_rpm = (signed short)(canbuf_receive[2] << 8 | canbuf_receive[3]);
motor.given_current = (signed short)(canbuf_receive[4] << 8 | canbuf_receive[5]);
motor.temperate = canbuf_receive[6];
}
接收这里有两种不同方式的编写方式,
第一种是先存放数据信息,再区分ID存放到相应结构体中去;
第二种是先根据ID区分,再存放数据到结构体中。
两种方式都可以,我个人更倾向于使用第二种方式获取数据,更符合逻辑些。
CAN通信的重点在于信息的截取与存放,ID是否与电机配对。这里详细介绍一下HAL库中CAN消息接收头部的结构体定义:
typedef struct
{
uint32_t StdId; /*!< Specifies the standard identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF. */
uint32_t ExtId; /*!< Specifies the extended identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF. */
uint32_t IDE; /*!< Specifies the type of identifier for the message that will be transmitted.
This parameter can be a value of @ref CAN_identifier_type */
uint32_t RTR; /*!< Specifies the type of frame for the message that will be transmitted.
This parameter can be a value of @ref CAN_remote_transmission_request */
uint32_t DLC; /*!< Specifies the length of the frame that will be transmitted.
This parameter must be a number between Min_Data = 0 and Max_Data = 8. */
uint32_t Timestamp; /*!< Specifies the timestamp counter value captured on start of frame reception.
@note: Time Triggered Communication Mode must be enabled.
This parameter must be a number between Min_Data = 0 and Max_Data = 0xFFFF. */
uint32_t FilterMatchIndex; /*!< Specifies the index of matching acceptance filter element.
This parameter must be a number between Min_Data = 0 and Max_Data = 0xFF. */
} CAN_RxHeaderTypeDef;
uint32_t StdId;
uint32_t ExtId;
uint32_t IDE;
CAN_ID_STD
:标准标识符。CAN_ID_EXT
:扩展标识符。uint32_t RTR;
CAN_RTR_DATA
:数据帧。CAN_RTR_REMOTE
:远程帧。uint32_t DLC;
uint32_t Timestamp;
uint32_t FilterMatchIndex;
最常用的为前五个,
StdId
标准标识符/扩展标识符,IDE
标识符类型,RTR
传输请求类型与DLC
数据长度。用于配置接收到的CAN消息的头部信息,以便可以根据这些信息正确地处理接收到的消息。
以上就是can的全部代码,有问题可以评论或者私聊我,找到错误也可以说出来我改正。谢谢大家~希望能对大家有所帮助