#include "board.h" #include "poweroff_save_app.h" #include "modbus_encoder.h" #include "modbus_slave.h" #include "modbus_master.h" #include "variable.h" #include "SEGGER_RTT.h" #include "encrypt_md5.h" #include "encrypt_xxtea.h" #include "modbus_app.h" #include #define MODBUS_APP_NUMBER 3 typedef struct { unsigned char uart_no; //串口号 unsigned char modbus_dir; //modbus方向,0是从机,1是主机 //当做为从机时,master_or_slave保存modbus_slave_t 对象指针 //当做为主机时,master_or_slave保存modbus_master_t 对象指针 void *master_or_slave; //主站信息 unsigned char count; unsigned char step; //状态步 unsigned char rx_buffer[1024]; unsigned char tx_buffer[512]; } modbus_status_t; //modbus主站对象信息 modbus_master_t master1; static modbus_slave_t slaves[2]; void servo_on_respone(modbus_master_cmd_t *cmd, unsigned char *respone, int length); //modbus协议的一些状态信息 static modbus_status_t modbus_status[MODBUS_APP_NUMBER] = { { 0, 0, &slaves[0] ,2}, //组0,ID2,串口2,从站 //{ 2, 0, &slaves[0] ,2}, //组0,ID2,串口2,从站 { 1, 0, &slaves[0] ,2}, //组1,ID1,串口3主站 { 3, 1, &master1 ,1} }; /** * 系统默认的回调函数 * * @author lxz (2019/6/12/周三) * * @param address * @param number */ static void slave1_on_comm(unsigned char id, unsigned char cmd, unsigned short address, unsigned short number) { //通知软件需要退出进入bootloader if (cmd == 0x23) { extern void app_save_parameter(void); //先保存参数 app_save_parameter(); //设置标志位 //Stm32_Clock_DeInit(); //设置标志 hw_noinit_write(0, 0xA5A5A5A5); //设置ID hw_noinit_write(3, id); hw_noinit_write(2, 115200); hw_noinit_write(4, 115200); hw_noinit_write(6, 115200); hw_board_reboot(); } } /** * modbus应用环境的初始化,包括了一些数据域的初始化注册 * * @author lxz (2019/5/17/周五) */ void mosbus_app_init(void) { modbus_slave_t *slave = &slaves[0]; //保存ID码,保证bootloader会同步回来 hw_noinit_write(1, 1); hw_noinit_write(3, 1); hw_noinit_write(5, 1); //从站初始化 memset(&slaves[0], 0, sizeof(slaves)); slave->id = 1; //设置从站ID slave->on_comm = slave1_on_comm; //设置从站通讯的回调函数 //添加从站有效的数据域信息 modbus_slave_add_dataarea(slave, 0x01, //数据类型为位 0, //映射开始地址为0 MIDDLE_COILS_NUMBER, //数据单位个数 (unsigned char *)&middle_coils[0] //数据所在地址 ); modbus_slave_add_dataarea(slave, 0x01, //数据类型为位 0x0800, //映射开始地址为0x800,用于兼容台达DVP协议的M码地址 MIDDLE_COILS_NUMBER, //数据单位个数 (unsigned char *)&middle_coils[0] //数据所在地址 ); modbus_slave_add_dataarea(slave, 0x03, //数据类型为16位数值 0, //映射开始地址为0 USER_DATA_NUMBER, //数据单位个数 (unsigned char *)&user_datas[0] //数据所在地址 ); modbus_slave_add_dataarea(slave, 0x03, //数据类型为16位数值 0x1000, //映射开始地址为0x1000,用于兼容台达DVP协议的D码地址 USER_DATA_NUMBER, //数据单位个数 (unsigned char *)&user_datas[0] //数据所在地址 ); //升级系统必须的命令 modbus_slave_add_dataarea(slave, 0xFE, //数据类型为系统定制,升级时使用 0, //映射开始地址为0 512, //数据单位个数 0 //数据所在地址 ); //WIFI一些对应的通讯缓冲 slave = &slaves[1]; slave->id = 0x7F; modbus_slave_add_dataarea(slave, 0x01, //数据类型为系统定制,升级时使用 0x00, //映射开始地址为0 24, //数据单位个数 (unsigned char *)&middle_coils[1000 >> 3] //数据所在地址 ); modbus_slave_add_dataarea(slave, 0x03, //数据类型为系统定制,升级时使用 0x00, //映射开始地址为0 128, //数据单位个数 (unsigned char *)&user_datas[1600] //数据所在地址 ); #if 1 //初始化主站环境 modbus_master_init(); //初始化主站信息 memset(&master1, 0, sizeof(master1)); master1.encoder_type = MODBUS_ENCODER_RTU; //RTU模式 master1.interval = 10000; //10ms的命令间隔 master1.one_byte_time = 1000; //一个字节的时间为1ms master1.retry = 3; //命令的重试次数为3次 #endif } /** * modbus 从台应用代码 * * @author lxz (2019/5/17/周五) * * @param status */ static void modbus_slave_app(modbus_status_t *status) { int length; int index = 0; modbus_encoder_t *encoder; switch (status->step) { case 0: hw_dma_uart_begin_read(status->uart_no, &status->rx_buffer[0], 1024); status->step++; break; case 1: length = hw_dma_uart_read_finish(status->uart_no); if (length > 0) { status->step = 0; encoder = modbus_encoder_match(status->rx_buffer, length); if (encoder != (void *)0) { length = encoder->decode(&status->rx_buffer[0], &status->rx_buffer[0], length); if (length > 0) { while (index < status->count) { length = modbus_slave_run( &((modbus_slave_t *)status->master_or_slave)[index], &status->tx_buffer[0], &status->rx_buffer[0]); if (length > 0) { length = encoder->encode(&status->tx_buffer[0], &status->tx_buffer[0], length); if (length > 0) { hw_dma_uart_begin_write(status->uart_no, (const char *)&status->tx_buffer[0], length); status->step = 2; } break; } index++; } } } } break; case 2: if (hw_dma_uart_write_finish(status->uart_no)) { status->step = 0; } break; } } /** * 主站应用代码 * * @author lxz * * @param status */ static void modbus_master_app(modbus_status_t *status) { int length; switch (status->step) { case 0: //判断是否有包需要处理 length = hw_dma_uart_read_finish(status->uart_no); //将接收信息提供给主站,并获取主站输出 length = modbus_master_run( (modbus_master_t *)status->master_or_slave, status->tx_buffer, status->rx_buffer, length); if (length > 0) { //主站有需要输出时进行输出 hw_dma_uart_begin_write(status->uart_no, (const char *)&status->tx_buffer[0], length); status->step++; } break; case 1: //等待输出完毕 if (hw_dma_uart_write_finish(status->uart_no)) { status->step = 0; hw_dma_uart_begin_read(status->uart_no, &status->rx_buffer[0], 1024); } break; } } /** * modbus应用的循环运行函数,根据自己需求编写 * * @author lxz (2019/5/17/周五) * * @param void */ void modbus_app_proc(void) { int index = 0; while (index < MODBUS_APP_NUMBER) { if (modbus_status[index].modbus_dir == 0) { modbus_slave_app(&modbus_status[index]); } else { modbus_master_app(&modbus_status[index]); } index++; } }