这是一款舵机,性能不错……主要是贵。
本着贵的就是好的,他们买了好些 XM430-W210 舵机回来造机械臂。

首先呢,他们通过ROS接口去控制舵机,控制起来没问题,但是说碍于ROS本身的实时性不是很好,于是就开始了移植之路。
DYNAMIXEL SDK主要说明在:
https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_sdk/overview/
代码地址:
https://github.com/ROBOTIS-GIT/DynamixelSDK
还需要一个上位机:
https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_wizard2/
如果对ROS控制感兴趣可以看:
https://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_workbench/


DYNAMIXEL的C++接口

从github上下载的程序文件目录如下

它的C++程序结构比较好,移植起来比较方便。使用doxygen生成的文档也比较清楚。

在源码里面没有针对于单片机的驱动程序,他们家有一个OpenCR是基于arduino来的。
需要的程序在src和include这两个文件夹下。
可以看到,其实主要就是两类,一个port操作串口设备,一个packet操作数据。
PortHandler和PacketHandler这两个类提供了许多虚函数,被继承重写。
然后通过getPortHandler和getPacketHandler返回对象指针,最终结果是我们在使用的过程中貌似只调用了PortHandler和PacketHandler的接口。
总之还是按照它的规范来吧。


port操作重写
因为它并没有我想要的单片机支持(arduino不算),所以对于通信port的操作需要我自己去重新写。
我直接将port_handler_arduino.cpp复制了一份,然后重新命名为port_handler_stm32.cpp。
重写,比较重要的有getBytesAvailable()、readPort(uint8_t _packet, int length)、writePort(uint8_t _packet, int length)这几个函数。
其它函数我有写干脆无操作了。


packet程序修改
对应文件protocol2_packet_handler.cpp
数据的协议规范在:https://emanual.robotis.com/docs/en/dxl/protocol2/
在程序最开始的地方,有

#define TXPACKET_MAX_LEN    (1*1024)
#define RXPACKET_MAX_LEN    (1*1024)

把这后面的值改为(1*40)。
在一些程序里面有用到malloc这样子的函数,在单片机里面频繁使用malloc和free,会在RAM的堆上产生内存碎片,运行时间长可能会出现内存溢出的情况。

uint8_t *rxpacket           = (uint8_t *)malloc(RXPACKET_MAX_LEN);
...
...
...
free(rxpacket);

我将它改为了类似于下面这种形式,这样在运行过程中,变量被储存到当前函数对应的栈里(裸机开发的话就是main函数的栈)里,函数允许完自动释放,不会出现内存碎片的问题。

  uint8_t rxpacket[14]        = {0};

以上修改都不影响正常使用。

单片机移植

通过cubemx生成了对应工程,就是配置了一个串口,使能串口接收中断。
然后新增了一个irq.cpp
主要目的也简单,串口接收中断函数的填充,将串口中断接收到的数据放入指定容器。

然后又写了一个buffer.cpp文件用来处理容器内串口接收到的数据。
然后在前面提到的port操作重写的文件,里面调用/继承buffer.cpp的函数,具体看附件工程里的port_handler_stm32.cpp。


最后,将程序整理,留出接口。
参考example文件夹里的示例文件。
在stm32_hander.cpp文件里,最后留出一个测试函数。

在C8T6(master分支)的工程里,使用芯片是STM32F103C8T6,是最初版本,代码结构与接口什么的没有仔细去看。
在RCT6(RCT6分支)的工程里,使用芯片是STM32F103RCT6,这个工程扩展了一些接口,然后代码结构也稍微梳理了一下。
关于接口的问题,代码里面有一些宏定义,和调用的packet函数,需要注意,每一款舵机都是不一样的。
具体得去DYNAMIXEL网站找到对应的舵机,比如我目前是XM430-W210这个舵机,就看到网址:https://emanual.robotis.com/docs/en/dxl/x/xm430-w350/
在这里对应的地址和取值范围以及数据字节大小都可以看到。
地址对应需要修改宏定义。
取值范围需要自己调用的时候注意。
数据字节大小对应程序里的write1ByteTxRx、write4ByteTxRx、read2ByteTxRx、read4ByteTxRx的选择。

单片机C/C++混合编程

在keil里,为了支持C++编程,需要修改Target -> Code Generater -> ARM Compiler为Use default compiler version 6

同时,在linux下借助cubemx生成的Makefile文件,稍作修改,可以支持C++编程。
主要添加的几句是

CXX_SOURCES = \
...
...
...
CXX = $(PREFIX)g++
...
...
...
CXX_INCLUDES =  \
...
...
...
CXXFLAGS = -lstdc++ $(CFLAGS) $(CXX_DEFS) $(CXX_INCLUDES) -g -ggdb3 -fno-rtti -fno-exceptions \
-fverbose-asm -fdata-sections -ffunction-sections -fpermissive -Wa,-ahlms=$(BUILD_DIR)/$(notdir $(<:.cpp=.lst))
...
...
...
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(CXX_SOURCES:.cpp=.o)))
vpath %.cpp $(sort $(dir $(CXX_SOURCES)))

以及最后要注意
在$(BUILD_DIR)/$(TARGET).elf出,也就是链接.o文件的时候,要添加-lstdc++ -specs=nosys.specs这俩库,不然会出现对C++一些特性不支持的情况。
修改后如

$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
    $(CC) $(OBJECTS) $(LDFLAGS) -lstdc++ -specs=nosys.specs -o $@ 
    $(SZ) $@

当然还需要针对GUN交叉编译器的启动文件和链接脚本,具体参考工程里面makefile文件和GCC文件夹。
利用这个makefile,我们可以在linux里编译我们的工程,当然前提是得安装好交叉编译器gcc-arm-none-eabi。

在进行C/C++混合编程的时候,要注意,C++文件是可以调用C文件的函数的,但是C++的函数如果想要被C调用,就需要将那个函数标记为extern “C”
网上有很多人的干法是直接将cubemx生成的.c代码后缀改为.cpp,这样也可以编译通过也不影响裸机,但是我不建议这样做,它是.c文件就是C文件好了,不要去改它,然后注意将C++文件与C文件在调用上解耦就行。比如main.c文件需要调用舵机控制,那么就做了一个DynamixelTest函数,然后extern “C”,就可以被main.c调用了。
这样子在逻辑上,可以让底层代码与C++偏应用的代码分隔开来,避免一些不必要的矛盾,底层驱动还是用C的好,在Linux下通过makefile编译的时候也会好整理许多。
具体的makefile文件和工程文件参考码云代码,地址https://gitee.com/TerryAAA/dynamixel-sdk.git
它有两个分支,master是stm32f102c8t6,RCT6分支对应stm32f102rct6。

记录完毕~