在上篇文章中,介绍了移植正点原子工程文件以使用can通信控制若干EPOS4控制板,现介绍具体在初始化与驱动过程中发送的can命令。
(1)正点原子官方can发送函数:
/**
* @brief CAN 发送一组数据
* @note 发送格式固定为: 标准ID, 数据帧
* @param id : 标准ID(11位)
* @param msg : 数据指针
* @param len : 数据长度
* @retval 发送状态 0, 成功; 1, 失败;
*/
uint8_t can_send_msg(uint32_t id, uint8_t *msg, uint8_t len)
{
uint16_t t = 0;
uint32_t TxMailbox = CAN_TX_MAILBOX0;
g_canx_txheader.StdId = id; /* 标准标识符 */
g_canx_txheader.ExtId = id; /* 扩展标识符(29位) */
g_canx_txheader.IDE = CAN_ID_STD; /* 使用标准帧 */
g_canx_txheader.RTR = CAN_RTR_DATA; /* 数据帧 */
g_canx_txheader.DLC = len;
if (HAL_CAN_AddTxMessage(&g_canx_handler, &g_canx_txheader, msg, &TxMailbox) != HAL_OK) /* 发送消息 */
{
return 1;
}
while (HAL_CAN_GetTxMailboxesFreeLevel(&g_canx_handler) != 3) /* 等待发送完成,所有邮箱为空 */
{
t++;
if (t > 0xFFF)
{
HAL_CAN_AbortTxRequest(&g_canx_handler, TxMailbox); /* 超时,直接中止邮箱的发送请求 */
return 1;
}
}
return 0;
}
目前使用电机仅需发送can指令驱动即可,之后回读数据可能需要用到其他函数。在函数定义中可以看到,调用此函数,需要给定can的结点编号、需要发送的数组地址以及需要发送的数据位数。
其中需要指定的ID号即为can通信原理中的COB-ID,由功能段+地址段构成,其中功能段在can通信中的定义为0x600,地址段为控制板拨码开关决定的0-32的NODE-ID。例如下图在控制板上将拨码开关调为“1”时,COB-ID为0x600+1 = 0x601.
要发送的指令在函数中以数组形式提前定义好,在发送时填写好函数名和数组长度即可.
(2)EPOS4初始化指令:
void EPOS4_Init(uint16_t node){
node += 0x0600;
uint8_t canbuf0[]={0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//000100
uint8_t canbuf1[]={0x22,0x60,0x60,0x00,0x01,0x00,0x00,0x00};//MODE P
uint8_t canbuf3[]={0x22,0x40,0x60,0x00,0x06,0x00,0x00,0x00};//DISABLE
uint8_t canbuf4[]={0x22,0x40,0x60,0x00,0x0f,0x00,0x00,0x00};//ENABIE
can_send_msg(node,canbuf0,8);
delay_ms(10);
can_send_msg(node,canbuf1,8);
delay_ms(10);
can_send_msg(node,canbuf3,8);
delay_ms(10);
can_send_msg(node,canbuf4,8);
delay_ms(10);
}
在初始化过程中,以轮廓位置模式举例,需要发送000100后,对运动模式进行设置,并在失能后使能变可完成初始化.(参考官方轮廓位置模式应用手册)初始化后的效果为绿灯由闪烁变为常亮时初始化成功.
需要注意的是EPOS4控制板上电后的约3秒钟内处于系统自检状态,此时进行初始化可能会无响应,并且在每一条can指令发送后要予以适当延时.否则也会导致系统无法处理can指令.
EPOS4_Init(0x01);
delay_ms(100);
EPOS4_Init(0x02);
delay_ms(100);
EPOS4_Init(0x03);
delay_ms(100);
EPOS4_Init(0x04);
delay_ms(100);
封装好初始化函数后,在主函数中调用即可.
(3)位置驱动函数
void Profile_Position_Mode(uint16_t node,uint32_t TargetPosition,uint32_t TargetVelocity){
uint8_t TargetVelocity_1,TargetVelocity_2,TargetVelocity_3,TargetVelocity_4;
uint8_t TargetPosition_1,TargetPosition_2,TargetPosition_3,TargetPosition_4;
uint8_t canbuf1[]={0x22,0x81,0x60,0x00,0xE8,0x03,0x00,0x00};
uint8_t canbuf2[]={0x22,0x7A,0x60,0x00,0xE8,0x03,0x00,0x00};
uint8_t canbuf3[]={0x22,0x40,0x60,0x00,0x3f,0x00,0x00,0x00};//开始运动,绝对立即
uint8_t canbuf4[]={0x22,0x40,0x60,0x00,0x06,0x00,0x00,0x00};//disable
uint8_t canbuf5[]={0x22,0x40,0x60,0x00,0x0f,0x00,0x00,0x00};//enable
uint8_t canbuf6[]={0x22,0x83,0x60,0x00,0x30,0x75,0x00,0x00};//对加速度,减速度进行配置
uint8_t canbuf7[]={0x22,0x84,0x60,0x00,0xA8,0x61,0x00,0x00};
uint8_t canbuf8[]={0x22,0x85,0x60,0x00,0x20,0x4E,0x00,0x00};//quick stop
uint8_t canbuf9[]={0x22,0x86,0x60,0x00,0x00,0x00,0x00,0x00};//profile type
node += 0x600;
TargetVelocity_1 = TargetVelocity >> 24;
TargetVelocity_2 = TargetVelocity >> 16;
TargetVelocity_3 = TargetVelocity >> 8;
TargetVelocity_4 = TargetVelocity;
canbuf1[4] = TargetVelocity_4;
canbuf1[5] = TargetVelocity_3;
canbuf1[6] = TargetVelocity_2;
canbuf1[7] = TargetVelocity_1;
TargetPosition_1 = TargetPosition >> 24;
TargetPosition_2 = TargetPosition >> 16;
TargetPosition_3 = TargetPosition >> 8;
TargetPosition_4 = TargetPosition;
canbuf2[4] = TargetPosition_4;
canbuf2[5] = TargetPosition_3;
canbuf2[6] = TargetPosition_2;
canbuf2[7] = TargetPosition_1;
can_send_msg(node,canbuf4,8);
delay_ms(10);
can_send_msg(node,canbuf5,8);
delay_ms(10);
can_send_msg(node,canbuf6,8); //
delay_ms(10);
can_send_msg(node,canbuf7,8); //
delay_ms(10);
can_send_msg(node,canbuf8,8); //
delay_ms(10);
can_send_msg(node,canbuf9,8); //
delay_ms(10);
can_send_msg(node,canbuf1,8); //send V
delay_ms(10);
can_send_msg(node,canbuf2,8); //send P
delay_ms(10);
can_send_msg(node,canbuf3,8); //开始运动
delay_ms(10);
}
在位置驱动函数中分为三部分:
失能与使能以确保前后指令不相干扰(4\5指令)
加速度\减速带\快停与轮廓类型的设置(6\7\8\9指令)
目标速度与目标位置设置(1\2\3指令)
在完成上述配置后,输入3指令开始运动即可.
需要注意的是,can发送指令中低位在前,需要在函数中将目标位置拆分成若干8字节数据后倒置发送.
key = key_scan(0);
if (key == KEY0_PRES) {
switch (cnt){
case 0:
Profile_Position_Mode(0x01,0x00000000,0x01F4);
Profile_Position_Mode(0x02,0x00000000,0x07D0);
Profile_Position_Mode(0x03,0x0000012C,0x01F4);
Profile_Position_Mode(0x04,0x00000190,0x07D0);
cnt++;
break;
case 1:
Profile_Position_Mode(0x01,0xFFFFFED4,0x01F4);
Profile_Position_Mode(0x02,0xFFFFFE70,0x07D0);
Profile_Position_Mode(0x03,0xFFFFFE0C,0x01F4);
Profile_Position_Mode(0x04,0x00000320,0x07D0);
cnt++;
break;
case 2:
Profile_Position_Mode(0x01,0x000001F4,0x01F4);
Profile_Position_Mode(0x02,0xFFFFFCE0,0x07D0);
Profile_Position_Mode(0x03,0x00000000,0x01F4);
Profile_Position_Mode(0x04,0x00000000,0x07D0);
cnt++;
break;
}
if(cnt == 3){
cnt = 0;
}
}
以上为函数中实现按键按下后四个电机同时运动到目标位置代码.