引言
因能力有限,该文章为个人只是简单配置操作为培训新人转电机,纯实践教程,理论知识在网上各是,这里不再多赘述。
cubamx配置
有个错误修改(对学习了上一个培训视频的,新建工程或者来学习的也可看看)
在上一个培训视频里配置好串口和定时器中断的工程基础上继续配置can口,但在此之前我们需要改一改时钟树的参数
我们需要把晶振频率设置和板子的一致
can参数设置
对activited勾选后对其basic parammeter参数配置,虽然不修改对设置can波特率没有啥影响,但个人操作下来不这么配置使用之后的函数不会正常运行
fdcan2配置一样,看先前学长的配置不同之处在fdcan2的Message Ram Offset 给了1280
波特率计算配置
在Bit Timings Parameters界面中,需要进行CAN的波特率的配置,在B设置完分频系数 (Prescaler) 后,cubeMX 会自动完成 Time Quantum(简写为 tq)的计算,将 tq 乘以 seg1 (Time Quanta in Bit Segment 1),seg2 (Time Quanta in Bit Segment 1),再加一的和刚好为 1 微秒,对应波特率为 1M,这是 CAN 总线支持的最高通讯速率。
如图25*(31+8+1)ns=1us
如果发现Prescaler为3
时tq和图里不一样不是25ns
可以试试seg1和seg2换几个参数使normal time for one bit计算出1000ns,也可以在时钟树里修改frcan的分频把tq大小配置为25ns
中断打开并优先分配
这里没啥好说的,can1,can2都是使能interrupt0
以can1为主can,优先级更高一些
代码部分
发送数据
完成以上配置后我们生成工程添加新的.c和.h文件来写电机单独的代码
首先需要初始化CAN通信过滤器并使能can控制器和中断
void CAN_Open(FDCAN_HandleTypeDef* can)
{
FDCAN_FilterTypeDef filter; //< 声明局部变量 can过滤器结构体
filter.IdType = FDCAN_STANDARD_ID; //< id设置为标准id
filter.FilterIndex = 0; //< 设值筛选器的编号,标准id选择0-127
filter.FilterType = FDCAN_FILTER_MASK; //< 设置工作模式为掩码模式
filter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; //< 将经过过滤的数据存储到 fifo0
filter.FilterID1 = 0x000; //< 筛选器的id
filter.FilterID2 = 0x000;
HAL_FDCAN_ConfigFilter(can, &filter); //< 配置过滤器
check=HAL_FDCAN_Start(can); //< 使能can
//该check来测试can控制器是否使能,可以把该赋值去掉
HAL_FDCAN_ActivateNotification(can, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0); // 使能fifo0接收到新信息中断
}
初始化滤波器之后就可以正常发送了,这里参考了官方的代码和学长代码凑了一个
/**
* @brief CAN发送函数
*/
uint8_t chassis_can_send_data[8];
uint8_t CAN_Send(FDCAN_HandleTypeDef* can,int16_t motor1, int16_t motor2, int16_t motor3, int16_t motor4 )
{
FDCAN_TxHeaderTypeDef txHeader;
txHeader.Identifier = (uint32_t)0x200;
txHeader.IdType = FDCAN_STANDARD_ID;
txHeader.TxFrameType = FDCAN_DATA_FRAME;
txHeader.DataLength = FDCAN_DLC_BYTES_8;
txHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
txHeader.BitRateSwitch = FDCAN_BRS_OFF;
txHeader.FDFormat = FDCAN_CLASSIC_CAN;
txHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
txHeader.MessageMarker = 0x00;
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;
if(HAL_FDCAN_AddMessageToTxFifoQ(can, &txHeader,chassis_can_send_data) != HAL_OK)
{
return 0;
}
else
{
return 1;
}
}
HAL_FDCAN_AddMessageToTxFifoQ(FDCAN_HandleTypeDef *hfdcan, FDCAN_TxHeaderTypeDef *pTxHeader, uint8_t *pTxData)
与上述图片里讲解使用差不多,但没参数4,可参考了解一下该函数
对于txHeader已是固定的了,不过标识符需要注意一下,如果不是3508前4个ID的电机,标识符会不一样,CAN_send函数可修改的是传入参数的can口和4个电机的电流,那看这个函数为什么通过给个8长度的数组就能确定不同电机的电流呢,欸,看这个表就知道数据在哪位对应哪个电机的电流都分配好了。所以这样写是没错的。
那接下来就是引用这个发送函数了,那就放到上一次我们建好了定时器中断里,没有定时器中断写在while循环里也行。在此之前肯定还需把初始化滤波器放到mian里执行一次对吧
CAN_Open(&hfdcan1);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
c=CAN_Send(&hfdcan1,400,400,400,400);
//c是来测试是否正常发出的,未正常发出c会返回0
}
这里说明一些,这些电流值-16384 ~ 16384对应电流值-20 ~ 20A
这样电机就正常开环跑起来了,由于是给的电流值,那如果没什么阻力电机是会一直加速的,正常
接收中断
接下来任务就是我们需要接收电机反馈回来的速度角度等信息,这可为之后做闭环控制
首先定义一个结构体存放电机的返回状态
再定义一个结构体存放不同id的电机名称
typedef struct
{
uint16_t ecd;
int16_t speed_rpm;
int16_t given_current;
uint8_t temperate;
int16_t last_ecd;
} motor_measure_t;
/*CAN接收的ID*/
typedef enum
{
//add by langgo
CAN_3508Moto1_ID = 0x201,
CAN_3508Moto2_ID = 0x202,
CAN_3508Moto3_ID = 0x203,
CAN_3508Moto4_ID = 0x204;
}CAN_Message_ID;
然后我们把接收状态结构体定义成四个电机变量
motor_measure_t motor_chassis[4] = {0};//4 chassis motor
宏定义一个函数来对接收到数据进行处理
#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]; \
}
最后把这些东西引入到can接收回调函数里
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hcan,uint32_t RxFifo0ITs)
{
if(hcan==&hfdcan1)
{
FDCAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[8];
HAL_FDCAN_GetRxMessage(hcan, FDCAN_RX_FIFO0, &rx_header, rx_data);
switch (rx_header.Identifier)
{
case CAN_3508Moto1_ID:
case CAN_3508Moto2_ID:
case CAN_3508Moto3_ID:
case CAN_3508Moto4_ID:
{
static uint8_t i = 0;
//get motor id
i = rx_header.Identifier - CAN_3508Moto1_ID;
get_motor_measure(&motor_chassis[i], rx_data);
break;
}
default:
{
break;
}
}
}
}
至此,电机就能正常收发了
本教程参考了官方c板例程和实验室库,因为实验室库的这套代码的can太复杂而官方例程不是H750板子,于是个人擅自主张凑出来这套简单的转电机代码,旨在培训新人。