尝试用STM32与odrive进行can通信
记录一下学习过程,自用。
国外使用odirve进行开发时,基本用的都是树莓派+usb串口,考虑到学习成本和物料成本问题,暂定用stm32can来控制odrive。
1.can通信简介:
CAN,全称为“Controller Area Network”,即控制器局域网,是一种多主方式的异步串行通讯总线,是国际上应用最广泛的现场总线之一。
2. stm32can的配置
基于正点原子的探索者开发板,通过CUBEMX进行CAN的基本配置。
-
波特率配置,配置为250KHz
-
开启接收中断
-
在cubemx配置之后,还需要在Keil里编写CAN的发送_接收函数以及过滤器的配置等等。
配置代码如下:
3.1cang过滤器配置
void CAN_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
/*##-2- Configure the CAN Filter ###########################################*/
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(&hcan1, &sFilterConfig) != HAL_OK)
{
/* Filter configuration Error */
Error_Handler();
}
/*##-3- Start the CAN peripheral ###########################################*/
if (HAL_CAN_Start(&hcan1) != HAL_OK)
{
/* Start Error */
Error_Handler();
}
/*##-4- Activate CAN RX notification #######################################*/
if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
/* Notification Error */
Error_Handler();
}
}
3.2发送函数
注,添加了Frame_type参数是为了方便修改发送帧的类型(数据帧和远程帧)
在odrive中 数据帧为控制指令,远程帧为请求返回数据(比如编码器计数,电压,电流等等)
uint8_t CAN_Send_Msg(CAN_HandleTypeDef *hcan,uint16_t StdID,uint8_t *msg,uint8_t len,uint8_t Frame_type)
{
uint8_t index=0;
uint32_t TxMailbox; //邮箱
uint8_t send_buf[8] = {0};
TxHeader.StdId=StdID; //标准标识符
TxHeader.ExtId=0; //扩展标识符(29位)
TxHeader.IDE=CAN_ID_STD; //使用标准帧
TxHeader.RTR = Frame_type != CAN_RTR_REMOTE ? CAN_RTR_DATA : CAN_RTR_REMOTE; //数据帧为发送数据,远程帧为请求返回数据
//TxHeader.RTR = CAN_RTR_DATA;
TxHeader.DLC=len;
/*****填充消息******/
for ( index = 0; index < len; index++) {
send_buf[index] = msg[index];
}
/*****发送消息*****/
if(HAL_CAN_AddTxMessage(&hcan1, &TxHeader, send_buf, &TxMailbox) != HAL_OK)//发送
{
return 1;
}
return 0;
}
接收函数
uint8_t CAN1_Receive_Msg(uint8_t *buf)
{
uint32_t i;
uint8_t RxData[8];
//数据在这里已经被填充到了RxData中
if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
{
return 0xF2;
}
/*****填充消息******/
for(i=0;i<RxHeader.DLC;i++)
buf[i]=RxData[i];
return RxHeader.DLC;
}
3.Odirve控制函数:
配置完can后,编写odrive的控制函数。根据官方给的通信协议编写,协议中提到有节点id和指令id,要完成对odrive的控制,需要将指令ID和节点id的合成为标准id ,同时对数据进行填充。
/*
* 函数名:odrive_set_pos
* 函数作用:向目标odrive发送位置指令
* 函数参数:Node_id,DATA
* * */
uint8_t odrive_set_pos(int node_id,float pos)
{
uint16_t id = (node_id<<5)|SET_INPUT_POS;//合成标准id
union ByteToFloat can_bf;//数据转换并赋值
can_bf.value = pos/4;/*传动比为4*/
uint8_t pos_data[8] = {0};
memcpy(pos_data,(const void *)can_bf.data,4);
if(CAN_Send_Msg(&hcan1 ,id,pos_data,8,CAN_RTR_DATA) == 0) {//发送
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);
return 0;
}
else {
return -1;
}
}
/* 函数名:get_pos_data
* 函数作用:更新电机的位置信息,速度信息
* 返回值:无
* */
uint8_t get_pos_data(int node_id){
uint16_t id = (node_id<<5)|GET_ENCODER_COUNT;//合成指令id
uint8_t pos_data[8];
pos_data[0] = 16;
pos_data[1] = 0;
pos_data[2] = 0;
pos_data[3] = 0;
pos_data[4] = 0;
pos_data[5] = 0;
pos_data[6] = 0;
pos_data[7] = 0;
if(CAN_Send_Msg(&hcan1 ,id,pos_data,8,CAN_RTR_REMOTE) == 0) {//发送
return 0;
}
else {
return -1;
}
}
本人stm32属于初学阶段,若描述有误,或者代码编写不规范的地方,请各位指出。