完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
最近在驱动一个无刷电机,驱动这个不是我的重点研究,只是拿来玩。
拿到我手上的VESC是国内某工作室的改版VESC V6.4(应部分网友需求,放出链接)。硬件方案是STM32F405 + DRV8301 + NVMFS5C628,带有CAN口,PPM口,USB口。 一个不知道参数的星型一完全无刷电机,就1分钟就可以拥有。不得不说,本杰明神的VESC工具简直就是个,傻瓜式的一键调参神器。,自己的需要不是通过VESC工具让电机向来,摩托车通过CAN口来口VESC下发指令, 私人地源控制代码无电机电动智能捕捉。一开始深入了解VESC,把VESC的线程串来串去ChibiOS,本杰明大神怎么选择了这个系统?! 在网上没完没了地搜索,也没有发现有把VESC的CAN通讯控制给说明白的。个CSDN的博主到时写了篇标题疑似的,但是VIP可见,这。。。想爆粗口。 只能自己慢慢摸索吧。 VESC里面的CAN通讯程序里,其实写得比较明白。只是一开始没看懂。我们来看看店家给我的VESC 3.4代码破解里面装CAN指令的: 静态 THD_FUNCTION(cancom_process_thread, arg) { (void)arg; chRegSetThreadName(“Cancom 进程”); process_tp = chThdGetSelfX(); int32_t ind = 0; 无符号整数 rxbuf_len;无符号整数 rxbuf_ind; uint8_t crc_low; uint8_t crc_high; 布尔命令_发送;for(;;) { chEvtWaitAny((eventmask_t) 1); 而 (rx_frame_read != rx_frame_write) { CANRxFrame rxmsg = rx_frames[rx_frame_read++]; if (rxmsg.IDE == CAN_IDE_EXT) { uint8_t id = rxmsg.EID & 0xFF; CAN_PACKET_ID cmd = rxmsg.EID 》》 8; can_status_msg *stat_tmp; if (id == 255 || id == app_get_configuration()-》 controller_id) { switch (cmd) { case CAN_PACKET_SET_DUTY: ind = 0; mc_interface_set_duty(buffer_get_float32(rxmsg.data8, 1e5f, &ind)); timeout_reset(); 休息; case CAN_PACKET_SET_CURRENT: ind = 0; mc_interface_set_current(buffer_get_float32(rxmsg.data8, 1e3f, &ind)); timeout_reset(); 休息; 案例 CAN_PACKET_SET_CURRENT_BRAKE: ind = 0; mc_interface_set_brake_current(buffer_get_float32(rxmsg.data8, 1e3f, &ind)); timeout_reset(); 休息; 案例 CAN_PACKET_SET_RPM: ind = 0; mc_interface_set_pid_speed(buffer_get_float32(rxmsg.data8, 1e0f, &ind)); timeout_reset(); 休息; case CAN_PACKET_SET_POS: ind = 0; mc_interface_set_pid_pos(buffer_get_float32(rxmsg.data8, 1e6f, &ind)); timeout_reset(); 休息; case CAN_PACKET_FILL_RX_BUFFER: memcpy(rx_buffer + rxmsg.data8[0], rxmsg.data8 + 1, rxmsg.DLC - 1); 休息; case CAN_PACKET_FILL_RX_BUFFER_LONG: rxbuf_ind = (unsigned int)rxmsg.data8[0] 《《 8; rxbuf_ind |= rxmsg.data8[1]; 如果(rxbuf_ind 《 RX_BUFFER_SIZE) { memcpy(rx_buffer + rxbuf_ind, rxmsg.data8 + 2, rxmsg.DLC - 2); } 休息; 案例 CAN_PACKET_PROCESS_RX_BUFFER: ind = 0; rx_buffer_last_id = rxmsg.data8[ind++]; commands_send = rxmsg.data8[ind++]; rxbuf_len = (unsigned int)rxmsg.data8[ind++] 《《 8; rxbuf_len |= (unsigned int)rxmsg.data8[ind++]; 如果(rxbuf_len 》 RX_BUFFER_SIZE) { 中断;} crc_high = rxmsg.data8[ind++]; crc_low = rxmsg.data8[ind++]; if (crc16(rx_buffer, rxbuf_len) == ((unsigned short) crc_high 《《 8 | (unsigned short) crc_low)) { if (commands_send) { commands_send_packet(rx_buffer, rxbuf_len); } else { commands_set_send_func(send_packet_wrapper); 命令_process_packet(rx_buffer,rxbuf_len);} } 休息; 案例 CAN_PACKET_PROCESS_SHORT_BUFFER: ind = 0; rx_buffer_last_id = rxmsg.data8[ind++]; commands_send = rxmsg.data8[ind++]; if (commands_send) { commands_send_packet(rxmsg.data8 + ind, rxmsg.DLC - ind); } else { commands_set_send_func(send_packet_wrapper); commands_process_packet(rxmsg.data8 + ind, rxmsg.DLC - ind); } 休息; 案例 CAN_PACKET_SET_CURRENT_REL: ind = 0; mc_interface_set_current_rel(buffer_get_float32(rxmsg.data8, 1e5f, &ind)); timeout_reset(); 休息; 案例 CAN_PACKET_SET_CURRENT_BRAKE_REL: ind = 0; mc_interface_set_brake_current_rel(buffer_get_float32(rxmsg.data8, 1e5f, &ind)); timeout_reset(); 休息; 案例 CAN_PACKET_SET_CURRENT_HANDBRAKE: ind = 0; mc_interface_set_handbrake(buffer_get_float32(rxmsg.data8, 1e3f, &ind)); timeout_reset(); 休息; 案例 CAN_PACKET_SET_CURRENT_HANDBRAKE_REL: ind = 0; mc_interface_set_handbrake_rel(buffer_get_float32(rxmsg.data8, 1e5f, &ind)); timeout_reset(); 休息; 默认值:中断;} } switch (cmd) { case CAN_PACKET_STATUS: for (int i = 0;i 《 CAN_STATUS_MSGS_TO_STORE;; if (stat_tmp-》id == id || stat_tmp-》id == -1) { ind = 0; stat_tmp-》id = id; stat_tmp-》rx_time = chVTGetSystemTime(); stat_tmp-》rpm = (float)buffer_get_int32(rxmsg.data8, &ind); stat_tmp-》current = (float)buffer_get_int16(rxmsg.data8, &ind) / 10.0f; stat_tmp-》duty = (float)buffer_get_int16(rxmsg.data8, &ind) / 1000.0f; break; } } break; default: break; } } else { if (sid_callback) { sid_callback(rxmsg.SID, rxmsg.data8, rxmsg.DLC); } } if (rx_frame_read == RX_FRAMES_SIZE) { rx_frame_read = 0; } } }} 我们可以看到,当VESC接收到扩展帧时,才会进行响应;接收到扩展帧后,将CAN ID作运算,取低8位识别为id;取其高21位(29-8)为指令。 uint8_t id = rxmsg.EID & 0xFF;//取低8位识别为idCAN_PACKET_ID cmd = rxmsg.EID 》》 8;//取其高21位(29-8)为指令can_status_msg *stat_tmp; 当id为255(即0xFF)或者为控制器ID时(即app_get_configuration()-》controller_id)),才对“指令”进行响应,也就是正式进入switch (cmd) {}语句里面。 VESC的指令表如下: [tr]指令指令值数据数据长度数据类型单位[/tr]CAN_PACKET_SET_DUTY0设置电机占空比4字节有符号整数Thousandths of percent (5000 0 –》 50%) CAN_PACKET_SET_CURREN T1设置电机电流4字节有符号整数mA CAN_PACKET_SET_CURREN T_BRAKE2设置制动电流4字节有符号整数mA CAN_PACKET_SET_RPM3设置(电)转速4字节有符号整数ERPM CAN_PACKET_SET_POS4设置电机转角位置 CAN_PACKET_FILL_RX_B UFFER5 CAN_PACKET_FILL_RX_B UFFER_LONG6 CAN_PACKET_PROCESS_RX _BUFFER7 CAN_PACKET_PROCESS_SH ORT_BUFFER8 请求状态CAN_PACKET_STATUS9Request statusN/A CAN_PACKET_SET_CURREN T_REL10 CAN_PACKET_SET_CURREN T_BRAKE_REL11 通过上表,再对比CAN接收指令的程序,我们就可以逐一明白我们该怎么通过CAN口去控制VESC转动。话不多说,直接上数据,这里以转速控制为例,其他的请自行举一反三。 通过CAN口驱动VESC控制电机转速: 注意,VESC这里给定的是电转速,它等于机械转速与电机极对数的乘积,即: E R P M ( 电 转 速 ) = 机 械 转 速 × 极 对 数 。 ERPM(电转速) = 机械转速×极对数。 ERPM(电转速)=机械转速×极对数。 我们直接CANVESC TOOL上设置ID,直接x0FF后缀给。以3对极以无刷电机为主题,当我们要以200rpm时间控制电机,直接在CAN总线上发送ID为0x3FF、数据长度为 4 的扩展帧,发送的数据为“00 00 17 70”,就是0x00001770(6000 ERPM):看到也 可以捕捉到了。但是它转一下就停了。你只要在上位控制器中,定周期地向 VESC 发送文数据数据, 这里,定周期向VESC给定电生命指令的dbc可以这样定义: 其他的请诸位举一反三(自己摸索)吧,百试百通。 |
|
|
|
只有小组成员才能发言,加入小组>>
603浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-29 07:17 , Processed in 0.588641 second(s), Total 77, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号