折腾工控方案的朋友看过来——STM32+三菱FX2N兼容方案,原理图、源码全开源,想加功能随便造

张开发
2026/4/3 17:35:27 15 分钟阅读
折腾工控方案的朋友看过来——STM32+三菱FX2N兼容方案,原理图、源码全开源,想加功能随便造
stm32 三菱plc fx2n方案原理图源代码都有可自己扩展功能欢迎 1、支持三菱PLC软件GX Developer、GX Works2编程 2、支持模拟量n路AD和2路DA 3、支持掉电保存功能 4、支持在线监控写入功能 5、支持RTC时钟 万年历 6、支持2路485 Modbus RTU 主从机模式等先唠最核心的能直接用GX Developer、GX Works2编程三菱官方软件完全兼容。本质是STM32模拟了FX2N的通信协议GX软件发啥指令STM32就按FX2N的规则拆包、响应软件根本分不出真假。比如帧解析这块我随手贴段核心代码// 处理GX软件发来的通信帧 void PLC_Protocol_Parse(uint8_t *buf, uint16_t len) { // 先校验帧头帧尾FX2N用0x02开头0x03结尾 if(buf[0] ! 0x02 || buf[len-1] ! 0x03) return; uint8_t cmd buf[2]; // 提取功能码 switch(cmd) { case 0x03: // 读寄存器 Handle_Read_Register(buf[3], buf[len-2]); // 解析地址和长度 break; case 0x10: // 写多个寄存器 Handle_Write_Multi_Register(buf[3], buf[len-2]); break; // 其他指令像读输入、写输出全在这分支里 } }说白了就是做个“协议翻译官”把GX的命令拆解成STM32能懂的操作再把结果打包成FX2N格式发回去完美蒙混过关。接下来是模拟量n路AD2路DA。不用额外加模块直接用STM32的ADC/DAC外设多通道采样用DMA省资源比如8路AD采样uint16_t adc_buf[8]; // 存8路AD采样值 // 启动AD多通道DMA采样 void AD_Sample_Init(void) { HAL_ADC_Start_DMA(hadc1, (uint32_t*)adc_buf, 8); } // 转换为PLC格式数值0-4000对应0-10V uint16_t AD_To_PLC_Value(uint16_t adc_val) { return (uint16_t)(((float)adc_val / 4095) * 4000); }AD采样用DMA自动存数据不用CPU轮询DA就更简单用DAC外设输出// DA输出对应PLC数值400010V void DA_Set_PLC_Value(uint16_t plc_val) { float volt (float)plc_val / 4000 * 10; uint16_t dac_val (uint16_t)((volt / 3.3) * 4095); // 3.3V参考电压 HAL_DAC_SetValue(hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_val); }想加多少路AD只要STM32引脚够在源码里改个采样通道数就行预留了扩展接口。掉电保存功能必须有PLC里的D寄存器、计数器数据不能一断电就没。我用的是外部EEPROM比如AT24C02也能直接用STM32内部的后备区域BKP看你需求。代码示例#include at24c02.h // 保存D寄存器数据从D0到D99 void PLC_PowerSave_Save(void) { AT24_Write(0x00, (uint8_t*)PLC_D_Reg[0], 100*2); // 每个寄存器2字节 } // 上电恢复数据 void PLC_PowerSave_Restore(void) { AT24_Read(0x00, (uint8_t*)PLC_D_Reg[0], 100*2); }定时器或者按键触发保存都可以我在源码里加了个10分钟自动保存的任务也支持PLC指令触发比如M0导通就保存。stm32 三菱plc fx2n方案原理图源代码都有可自己扩展功能欢迎 1、支持三菱PLC软件GX Developer、GX Works2编程 2、支持模拟量n路AD和2路DA 3、支持掉电保存功能 4、支持在线监控写入功能 5、支持RTC时钟 万年历 6、支持2路485 Modbus RTU 主从机模式等在线监控写入和实时性挂钩说白了就是PLC不停读寄存器状态STM32实时回传。比如处理读输入寄存器的逻辑// 处理读输入寄存器指令 void Handle_Read_Register(uint8_t *params, uint16_t crc) { uint16_t start_addr (params[0]8) | params[1]; uint16_t reg_num (params[2]8) | params[3]; uint8_t send_buf[256]; send_buf[0] 0x02; send_buf[1] 0x03; send_buf[2] reg_num * 2; // 数据字节数 // 把对应地址的寄存器数据拷贝到发送缓冲区 memcpy(send_buf[3], PLC_D_Reg[start_addr], reg_num*2); send_buf[3 reg_num*2] 0x03; // 计算CRC并追加到帧尾这里省略CRC计算函数 uint16_t crc_val CRC_Calculate(send_buf, 3 reg_num*2); send_buf[3 reg_num*2 1] crc_val 8; send_buf[3 reg_num*2 2] crc_val 0xFF; // 串口发回给GX软件 HAL_UART_Transmit(huart1, send_buf, 3 reg_num*2 3, 100); }在线写入就是反过来接收PLC发的新值直接更新内存里的寄存器数组操作和真FX2N完全一致监控的时候数值实时跳。RTC万年历这块直接用STM32的RTC外设把时间映射到FX2N的特殊寄存器里比如D8017是年D8016是月D8015是日GX软件里直接读这些寄存器就能拿时间。代码很简单RTC_TimeTypeDef sTime {0}; RTC_DateTypeDef sDate {0}; // 同步RTC时间到PLC特殊寄存器 void RTC_Sync_To_PLC(void) { HAL_RTC_GetTime(hrtc, sTime, RTC_FORMAT_BIN); HAL_RTC_GetDate(hrtc, sDate, RTC_FORMAT_BIN); // 映射到FX2N特殊寄存器 PLC_D_Reg[8013] sTime.Seconds; // 秒 PLC_D_Reg[8014] sTime.Minutes; // 分 PLC_D_Reg[8015] sTime.Hours; // 时 PLC_D_Reg[8016] sDate.Month; // 月 PLC_D_Reg[8017] 2000 sDate.Year;// 年 PLC_D_Reg[8018] sDate.Date; // 日 PLC_D_Reg[8019] sDate.WeekDay; // 星期 }上电自动读取RTC时间掉电靠后备电池供电时间不会跑丢还支持用PLC指令修改时间和真PLC一模一样。最后是2路485 Modbus RTU主从机模式。STM32加个MAX485芯片就行Modbus协议也简单主从模式切换个标志位就搞定。比如从站处理读寄存器的代码// 处理Modbus 03功能码读保持寄存器 void Modbus_Handle_03(uint8_t *buf) { uint16_t start_addr (buf[2]8) | buf[3]; uint16_t reg_num (buf[4]8) | buf[5]; uint8_t send_buf[256]; send_buf[0] buf[0]; // 从站地址 send_buf[1] 0x03; // 功能码 send_buf[2] reg_num * 2; // 数据长度 memcpy(send_buf[3], PLC_D_Reg[start_addr], reg_num*2); // 计算CRC并发送 uint16_t crc Modbus_CRC(send_buf, 3 reg_num*2); send_buf[3 reg_num*2] crc 0xFF; send_buf[3 reg_num*2 1] crc 8; HAL_UART_Transmit(huart2, send_buf, 3 reg_num*2 2, 100); }做主站的话就轮询其他设备比如读变频器参数、触摸屏数据源码里主从站的例子都有直接改地址和寄存器就行。总之这个方案就是用STM32的性能把FX2N的功能全复刻了还留了大把扩展空间——想加以太网插个W5500模块就行想加WIFI整个ESP8266通迅甚至想加CAN总线STM32带CAN外设的直接用。原理图、源码全放出来了链接就挂GitHub上随便fork、改代码有问题直接提Issue一起折腾

更多文章