引言
在工业控制领域,Modbus RTU、EtherCAT 都是十分常用的通信协议,借此次电源网比赛的机会,使用树莓派5开发板来只做一个兼容Modbus RTU和EtherCAT的多协议网关
硬件设计
对于Modbus RTU部分,使用了自己制作的一款Modbus RFID读卡器,照片如下,使用的是STM32F0进行的设计,支持Modbus RTU通讯、RS485接口

原理图如下,采用FM17550作为RFID射频芯片

对于EtherCAT从站设备,使用了AX58100作为ESC芯片,STM32F411作为主控MCU,使用核心板上面的LED来交与主站来进行控制

AX58100部分的原理图如下,接出两个RJ45网口来作为EtherCAT通讯的IN和OUT

本次使用了一块1280*800的电容触摸屏来作为显示设备,将Modbus RFID读卡器、EtherCAT从站设备、树莓派、屏幕的线都接好,如下图

软件设计
开源协议栈软件
针对项目需求,需要进行通信协议栈软件的选择,软件方案设计如下
对于EtherCAT通讯,采用SOEM作为EtherCAT主站协议栈软件使用 对于Modbus通讯,采用libmodbus作为modbus主站协议栈软件使用 对于UI交互部分,采用dear imgui作为界面库软件使用几个开源软件链接如下:https://github.com/OpenEtherCATsociety/SOEM.git
https://github.com/ocornut/imgui.git
https://github.com/epezent/implot.git
https://github.com/stephane/libmodbus.git
然后需要对于几个协议栈软件进行整合,本项目采用了cmake作为构建工具,编写cmake脚本如下,imgui的.cmake文件内容如下
project(imgui)
message("imgui cmake..")
add_library(imgui
lib/imgui/imgui.cpp
lib/imgui/imgui_demo.cpp
lib/imgui/imgui_draw.cpp
lib/imgui/imgui_tables.cpp
lib/imgui/imgui_widgets.cpp
lib/imgui/backends/imgui_impl_sdl2.cpp
lib/imgui/backends/imgui_impl_opengl3.cpp
)
find_package(SDL2 REQUIRED)
find_package(OpenGL REQUIRED)
target_link_libraries(imgui PUBLIC SDL2::SDL2)
target_link_libraries(imgui PUBLIC OpenGL::GL)
include_directories(lib/imgui)
include_directories(lib/imgui/backends)
工程的CMakeLists.txt文件内容如下,软件上的依赖库主要有SOEM、SDL2、OpenGL、imgui、libmodbus、pthread这些
# project info
cmake_minimum_required(VERSION 3.28)
project(master VERSION 0.1.0 LANGUAGES C CXX)
# set some option
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(SOEM_BUILD_SAMPLES ON)
# libs
add_subdirectory(lib/SOEM)
include(lib/imgui.cmake)
add_executable(master master.cpp imgui_main.cpp fieldbus.c)
# link libraries
target_link_libraries(master PRIVATE soem)
target_link_libraries(master PRIVATE imgui)
target_link_libraries(master PRIVATE modbus)
target_link_libraries(master PRIVATE pthread)
代码开发
对于UI部分,实现了两个窗口,一个窗口负责EtherCAT的主站UI交互、另一个窗口负责Modbus的主站UI交互,完整代码由于比较冗长,就不在文中展示了,可以到文末下载完整代码查看对于主函数代码核心部分如下,创建了三个线程,一个线程负责EtherCAT通讯,同步周期为5ms,一个线程负责modbus rtu通讯,周期为10ms,另一个线程为UI线程,来运行imgui开发的界面
void ImGui::mywindow(void)
{
main_window();
DrawModbusSerialPanel();
}
// 线程函数
void * thread_func_ecat(void *arg)
{
for (;;)
{
op_loop();
usleep(5000);
}
return NULL;
}
// 线程函数
void * thread_func_modbus(void *arg)
{
for (;;)
{
if (g_connected && g_modbusCtx)
{
uint16_t regs[2];
/* 固定站号 2 */
modbus_set_slave(g_modbusCtx, 2);
/* 写保持寄存器 1 = 0 */
modbus_write_register(g_modbusCtx, 0, 0);
/* 读保持寄存器 2、3 */
if (modbus_read_registers(g_modbusCtx, 1, 2, regs) == 2)
{
g_reg2 = regs[0];
g_reg3 = regs[1];
}
}
usleep(10000);
}
return NULL;
}
// 线程函数
void * thread_func_ui(void *arg)
{
imgui_init();
return NULL;
}
int main(int argc, char *argv[])
{
printf("EtherCAT Debug Tool\n");
printf("A GUI tool for debugging EtherCAT networks\n");
pthread_t tid1, tid2, tid3;
int id1 = 1, id2 = 2, id3 = 3;
// create thread
if (pthread_create(&tid1, NULL, thread_func_ui, &id1) != 0) {
perror("thread_func_ui is created.");
return 1;
}
if (pthread_create(&tid2, NULL, thread_func_ecat, &id2) != 0) {
perror("thread_func_ecat is created.");
return 1;
}
if (pthread_create(&tid3, NULL, thread_func_modbus, &id3) != 0) {
perror("thread_func_modbus is created.");
return 1;
}
// wait for threads to finish
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
return 0;
}
软件运行截图如下,左边的窗口即为EtherCAT的主站UI,实现了使用PDO方式通讯,操作EtherCAT从站板子上的LED控制,右边的窗口为Modbus的主站UI,实现了对于Modbus RFID读卡器的卡ID读取
视频演示:


