舵机MX-64AR与MX-28AR驱动
背景:硬件采用485通信,在tb上采购的无需收发控制的串口转RS485模块(485通信为半双工,一般情况需要控制收发模式)。在使用该模块后,即可完全使用一个普通地串口来对485通信的舵机进行操作。
模块链接:https://item.taobao.com/item.htm?spm=a1z09.2.0.0.66dc2e8d3QIPTQ&id=541473495140&_u=j33333sl4f32
驱动代码:
平台:STM32F405RGT6
舵机:MX64AR —— 固件版本1.0
软件部分见另一篇博客。
舵机通信串口配置:
/* UART2 for MX64 */
void vUart2Config(void)
{
USART_InitTypeDef usart2;
GPIO_InitTypeDef gpio;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2);
gpio.GPIO_Pin = GPIO_Pin_2;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Speed = GPIO_Speed_100MHz;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA,&gpio);
gpio.GPIO_Pin = GPIO_Pin_3;
gpio.GPIO_Mode = GPIO_Mode_IN;
gpio.GPIO_Speed = GPIO_Speed_100MHz;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA,&gpio);
usart2.USART_BaudRate = 115200;
usart2.USART_WordLength = USART_WordLength_8b;
usart2.USART_StopBits = USART_StopBits_1;
usart2.USART_Parity = USART_Parity_No;
usart2.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
usart2.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART2,&usart2);
// USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
USART_Cmd(USART2,ENABLE);
// USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
// {
// DMA_InitTypeDef dma;
// RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);
// DMA_DeInit(DMA1_Stream6);
// dma.DMA_Channel= DMA_Channel_4;
// dma.DMA_PeripheralBaseAddr = (uint32_t)&(USART2->DR);
// dma.DMA_Memory0BaseAddr = (uint32_t)sbus_rx_buffer;
// dma.DMA_DIR = DMA_DIR_MemoryToPeripheral;
// dma.DMA_BufferSize = TX_USART2_BUFFER;
// dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
// dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
// dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
// dma.DMA_Mode = DMA_Mode_Circular;
// dma.DMA_Priority = DMA_Priority_VeryHigh;
// dma.DMA_FIFOMode = DMA_FIFOMode_Disable;
// dma.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
// dma.DMA_MemoryBurst = DMA_Mode_Normal;
// dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
// DMA_Init(DMA2_Stream5,&dma);
// DMA_ITConfig(DMA2_Stream5,DMA_IT_TC,ENABLE);
// DMA_Cmd(DMA2_Stream5,ENABLE);
// }
}
目前该品牌舵机有两个版本的通信协议(1.0和2.0)。2.0协议增加了很多新的控制模式和功能。
升级固件版本后舵机可以支持2.0通信协议,但是升级固件需要使用专用的编程器(有点对=贵),一般没有必要购买。手中的舵机是哪个版本的协议就用那个就好了。
以下分别总结两个版本的通信协议控制代码:
通信协议1.0:(详情见官网协议说明:http://emanual.robotis.com/docs/en/dxl/protocol1/#status-packet)
舵机EEPROM及RAM地址:
舵机驱动函数:
这里由于仅需要使用舵机的最基本功能:单圈的位置模式,因此在此只实现了部分控制函数。如有其它需求,仿照一下函数即可。但是关于舵机的控制模式切换以及一些高级的设置,需要使用舵机的编程卡配合上位机配置。
/* Protocol 1.0 */
void send_MX64_action(void)
{
uint8_t i = 0;
uint8_t MX64_buffer[6];
MX64_buffer[0]=0xFF;
MX64_buffer[1]=0xFF;
MX64_buffer[2]=0xFE;
MX64_buffer[3]=0x02;
MX64_buffer[4]=0x05; //commend:action
MX64_buffer[5]=0xFA; //check sum
for(i=0;i<=5;i++)
{
USART_SendData(USART2,MX64_buffer[i]);
while( USART_GetFlagStatus(USART2,USART_FLAG_TC)!= SET);
}
}
/* MX64 position set commend */
void send_MX64_position(unsigned short Control_Angle)
{
uint8_t j = 0;
uint8_t MX64_buffer[15];
MX64_buffer[0]=0xFF; //header byte
MX64_buffer[1]=0xFF; //header byte
MX64_buffer[2]=0xFE; //motor id
MX64_buffer[3]=0x05; //length
MX64_buffer[4]=0x04; //Reg Write:0x04
MX64_buffer[5]=0X1E; //goal position reg address
/* goal position */
MX64_buffer[6]=(Control_Angle<<8)>>8;
MX64_buffer[7]=Control_Angle>>8;
/* check */
MX64_buffer[8]=~(MX64_buffer[2]+MX64_buffer[3]+MX64_buffer[4]+MX64_buffer[5]+MX64_buffer[6]+MX64_buffer[7]);
for(j=0;j<=8;j++)
{
USART_SendData(USART2,MX64_buffer[j]);
while( USART_GetFlagStatus(USART2,USART_FLAG_TC)!= SET);
}
}
注意:在使用位置模式控制舵机时,需要先设置目标位置,目标位置设定成功后,舵机并不会动,而是还需要发送一条ACTION指令(即send_MX64_action()函数),舵机才会运动。每次设定一个新的目标位置都需要调用该函数,使舵机开始运动。
由于MX-28AR、64AR舵机的初始通信波特率均为57600,我之前一直没有注意,用115200的波特率调试了很久也没有成功(因为之前买过的一个二手的舵机买来的时候就被设置好了115200的波特率,就用那套代码在调)
因此又添加了设定波特率的一个简单功能(统一设定为115200):
/* MX64 position set commend */
void set_MX64_baudrate(void)
{
uint8_t j = 0;
uint8_t MX64_buffer[15];
MX64_buffer[0]=0xFF; //header byte
MX64_buffer[1]=0xFF; //header byte
MX64_buffer[2]=0xFE; //motor id
MX64_buffer[3]=0x04; //length
MX64_buffer[4]=0x03; //Write:0x03
MX64_buffer[5]=0X04; //baudrate reg address
/* goal baudrate : 115200 = 2000000/(16+1) */
MX64_buffer[6]=0x10;
/* check */
MX64_buffer[7]=~(MX64_buffer[2]+MX64_buffer[3]+MX64_buffer[4]+MX64_buffer[5]+MX64_buffer[6]);
for(j=0;j<=7;j++)
{
USART_SendData(USART2,MX64_buffer[j]);
while( USART_GetFlagStatus(USART2,USART_FLAG_TC)!= SET);
}
}
通信协议2.0:(详情见:http://emanual.robotis.com/docs/en/dxl/mx/mx-64-2/#control-table)
寄存器地址见上一行网址(官网协议说明)
控制代码:
/* Protocol 2.0 */
/* MX64 action commend */
void send_MX64_action(void)
{
uint8_t i = 0;
uint8_t MX64_buffer[10];
uint16_t CRC_result;
MX64_buffer[0] = 0xFF; //header byte
MX64_buffer[1] = 0xFF; //header byte
MX64_buffer[2] = 0xFD; //header byte
MX64_buffer[3] = 0x00; //reserve byte
MX64_buffer[4] = 0xFE; //motor id(broadcast)
MX64_buffer[5] = 0x03; //length(Low ) = parameter length + 3
MX64_buffer[6] = 0x00; //length(High) = parameter length + 3
MX64_buffer[7] = 0x05; //commend (0x05: action)
/* CRC calculate */
CRC_result = update_crc(0,MX64_buffer,8);
MX64_buffer[8] = CRC_result & (0xFF); //Low
MX64_buffer[9] = (CRC_result>>8) & (0xFF);
for(i=0;i<=9;i++)
{
USART_SendData(USART2,MX64_buffer[i]);
while( USART_GetFlagStatus(USART2,USART_FLAG_TC)!= SET);
}
}
/* MX64 position set commend */
void send_MX64_position(unsigned short Control_Angle)
{
uint8_t j = 0;
uint8_t MX64_buffer[16];
uint16_t CRC_result;
/* head */
MX64_buffer[0] = 0xFF; //header byte
MX64_buffer[1] = 0xFF; //header byte
MX64_buffer[2] = 0xFD; //header byte
MX64_buffer[3] = 0x00; //reserve byte
MX64_buffer[4] = 0xFE; //motor id(broadcast)
MX64_buffer[5] = 0x09; //length(Low ) = parameter length + 3
MX64_buffer[6] = 0x00; //length(High) = parameter length + 3
MX64_buffer[7] = 0x04; //commend (0x04: reg write)
/* reg address */
MX64_buffer[8] = 0x74; //Low
MX64_buffer[9] = 0x00;
/* goal position(4 bytes) */
MX64_buffer[10] = (Control_Angle<<8)>>8; //Low
MX64_buffer[11] = Control_Angle>>8;
MX64_buffer[12] = 0x00;
MX64_buffer[13] = 0x00; //High
/* CRC calculate */
CRC_result = update_crc(0,MX64_buffer,14);
MX64_buffer[14] = CRC_result & (0xFF); //Low
MX64_buffer[15] = (CRC_result>>8) & (0xFF);
for(j=0;j<=15;j++)
{
USART_SendData(USART2,MX64_buffer[j]);
while( USART_GetFlagStatus(USART2,USART_FLAG_TC)!= SET);
}
}
其中2.0版本的协议校验方式改为CRC16校验(1.0版本为简单的和校验),CRC校验函数实现如下(官网也有):
/* MX28 Servo CRC16 function (for Protocol 2.0)*/
static unsigned short update_crc(unsigned short crc_accum, unsigned char *data_blk_ptr, unsigned short data_blk_size)
{
unsigned short i, j;
unsigned short crc_table[256] = {
0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
};
for(j = 0; j < data_blk_size; j++)
{
i = ((unsigned short)(crc_accum >> 8) ^ data_blk_ptr[j]) & 0xFF;
crc_accum = (crc_accum << 8) ^ crc_table[i];
}
return crc_accum;
}
——cloud over sky
——2020/1/18