基于STM32F103的自制PLC开发板:低成本工业控制解决方案

发布时间:2026/7/17 11:20:30
基于STM32F103的自制PLC开发板:低成本工业控制解决方案 如果你正在寻找一种低成本、高灵活性的工业控制解决方案或者对传统PLC的高价格和封闭性感到困扰那么基于STM32F103的自制PLC开发板可能正是你需要的突破口。市面上的品牌PLC动辄数千元而实际上很多工业控制场景并不需要那么复杂的系统——一个精心设计的STM32F103核心板加上适当的外围电路就能实现开关量输入输出、模拟量采集和步进电机驱动等核心功能。这个方案最大的价值在于它打破了工业控制领域的黑箱模式。传统PLC虽然稳定可靠但内部逻辑对用户是不透明的二次开发受限。而基于STM32F103的PLC开发板从硬件设计到软件编程完全开放你可以根据实际需求灵活调整成本却只有品牌PLC的十分之一甚至更低。本文将带你深入理解这个自制PLC开发板的设计思路、硬件组成和软件实现重点解决三个实际问题如何用STM32F103实现可靠的工业级信号处理、如何设计两路步进电机的精确控制、以及在实际工业环境中可能遇到的抗干扰问题。无论你是工业自动化工程师、嵌入式开发者还是相关专业的学生这篇文章都能为你提供可直接落地的技术方案。1. 为什么STM32F103适合做PLC开发板STM32F103作为意法半导体的经典Cortex-M3内核微控制器在工业控制领域有着广泛的应用基础。其核心优势在于平衡了性能、成本和生态支持。对于PLC应用来说STM32F103的72MHz主频足以处理多路IO扫描、模拟量转换和电机控制算法同时其丰富的外设资源正好匹配PLC的功能需求。与专用PLC芯片相比STM32F103的最大优势是灵活性。传统的PLC模块通常采用固定的硬件架构而基于STM32F103的方案可以根据具体应用场景进行定制化设计。比如如果你的应用需要更多的模拟量输入通道可以简单地增加ADC芯片如果需要更强的通信能力可以扩展以太网或RS485接口。从成本角度分析一个STM32F103C8T6核心板的成本约20-30元加上必要的外围电路整个PLC开发板的BOM成本可以控制在100元以内。相比之下最基础的品牌PLC模块也要500元以上。这种成本优势在需要批量部署的场合尤为明显。在实际性能表现上STM32F103的12位ADC能够满足大多数工业场景的模拟量采集精度要求其定时器资源可以生成精确的PWM信号用于步进电机控制多个USART接口支持Modbus等工业通信协议。这些特性使得它成为自制PLC的理想选择。2. PLC开发板的核心功能模块设计2.1 开关量输入电路设计开关量输入是PLC最基本的功能用于检测按钮、限位开关、继电器触点等数字信号。工业环境中的开关量信号往往伴随着噪声干扰因此需要设计适当的滤波和保护电路。典型的开关量输入电路包括光耦隔离、RC滤波和施密特触发器。光耦隔离可以防止外部高压信号损坏微控制器RC滤波消除触点抖动施密特触发器将缓慢变化的信号整形成清晰的数字波形。以下是开关量输入的硬件连接示例// GPIO配置为输入模式带上拉电阻 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2; // 假设使用PA0、PA1、PA2 GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; // 上拉输入模式 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure);在实际应用中还需要考虑输入信号的防抖处理。软件防抖可以通过延时采样实现// 开关量输入状态读取函数 uint8_t Read_Digital_Input(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { uint8_t stable_count 0; uint8_t current_state; // 连续采样5次消除抖动 for(int i 0; i 5; i) { if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin)) { stable_count; } delay_ms(2); // 延时2ms } return (stable_count 3) ? 1 : 0; // 5次中有3次为高则判定为高电平 }2.2 模拟量输入模块实现模拟量输入用于采集传感器信号如温度、压力、流量等连续变化的物理量。STM32F103内置12位ADC支持最多16个外部通道最高采样率可达1MHz。对于需要更高精度或更多通道的应用可以外接ADS1256等24位ADC芯片。ADS1256具有23位无噪声精度支持8路差分或16路单端输入特别适合高精度测量场合。模拟量输入的关键在于信号调理和抗干扰设计。工业环境中的模拟信号传输容易受到电磁干扰需要采用屏蔽线缆、适当的滤波电路和合理的接地措施。以下是STM32F103内置ADC的配置示例// ADC初始化配置 void ADC_Configuration(void) { ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 使能ADC1和GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置ADC引脚为模拟输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; // PA0为ADC通道0 GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); // ADC模式配置 ADC_InitStructure.ADC_Mode ADC_Mode_Independent; // 独立模式 ADC_InitStructure.ADC_ScanConvMode DISABLE; // 单通道模式 ADC_InitStructure.ADC_ContinuousConvMode DISABLE; // 单次转换 ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; // 软件触发 ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; // 数据右对齐 ADC_InitStructure.ADC_NbrOfChannel 1; // 1个转换通道 ADC_Init(ADC1, ADC_InitStructure); // 使能ADC ADC_Cmd(ADC1, ENABLE); // ADC校准 ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); } // 读取ADC值函数 uint16_t Read_ADC_Value(uint8_t channel) { // 设置转换通道 ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_239Cycles5); // 启动转换 ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 等待转换完成 while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); return ADC_GetConversionValue(ADC1); }2.3 开关量输出电路设计开关量输出用于控制继电器、接触器、指示灯等负载。工业负载通常需要较大的驱动电流STM32F103的GPIO输出能力有限需要外加驱动电路。常用的开关量输出方案包括晶体管输出、继电器输出和固态继电器输出。晶体管输出适合高频开关场合继电器输出适合隔离要求高的场合固态继电器输出适合大功率交流负载。以下是一个典型的晶体管输出电路设计// 开关量输出控制函数 void Digital_Output_Control(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, uint8_t state) { if(state) { GPIO_SetBits(GPIOx, GPIO_Pin); // 输出高电平 } else { GPIO_ResetBits(GPIOx, GPIO_Pin); // 输出低电平 } } // GPIO输出模式配置 void GPIO_Output_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 配置PB0、PB1为推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); }对于需要驱动较大电流的场合可以加入ULN2003等达林顿晶体管阵列芯片每个通道可提供500mA的驱动电流。2.4 两路步进电机驱动设计步进电机在工业自动化中应用广泛如数控机床、3D打印机、机器人等。STM32F103的定时器可以生成精确的PWM信号配合步进电机驱动芯片如A4988、DRV8825实现电机的精确控制。步进电机控制的核心是脉冲信号的生成和方向控制。每个脉冲使电机转动一个步距角脉冲频率决定电机转速脉冲数量决定转动角度。以下是步进电机驱动的软件实现// 步进电机控制结构体 typedef struct { GPIO_TypeDef* DIR_GPIOx; // 方向控制GPIO组 uint16_t DIR_Pin; // 方向控制引脚 GPIO_TypeDef* STEP_GPIOx; // 步进脉冲GPIO组 uint16_t STEP_Pin; // 步进脉冲引脚 uint32_t pulse_delay; // 脉冲间隔决定速度 uint32_t pulse_count; // 脉冲计数决定角度 uint8_t direction; // 转动方向 } Stepper_Motor_t; // 步进电机初始化 void Stepper_Init(Stepper_Motor_t* motor, GPIO_TypeDef* dir_gpiox, uint16_t dir_pin, GPIO_TypeDef* step_gpiox, uint16_t step_pin) { motor-DIR_GPIOx dir_gpiox; motor-DIR_Pin dir_pin; motor-STEP_GPIOx step_gpiox; motor-STEP_Pin step_pin; motor-pulse_delay 1000; // 默认1ms脉冲间隔 motor-pulse_count 0; motor-direction 0; // 配置GPIO为输出模式 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; // 方向引脚配置 GPIO_InitStructure.GPIO_Pin dir_pin; GPIO_Init(dir_gpiox, GPIO_InitStructure); // 步进引脚配置 GPIO_InitStructure.GPIO_Pin step_pin; GPIO_Init(step_gpiox, GPIO_InitStructure); } // 步进电机转动函数 void Stepper_Run(Stepper_Motor_t* motor, uint32_t steps, uint8_t dir, uint32_t speed) { motor-direction dir; motor-pulse_delay speed; // 设置方向 if(dir) { GPIO_SetBits(motor-DIR_GPIOx, motor-DIR_Pin); } else { GPIO_ResetBits(motor-DIR_GPIOx, motor-DIR_Pin); } // 生成指定数量的脉冲 for(uint32_t i 0; i steps; i) { GPIO_SetBits(motor-STEP_GPIOx, motor-STEP_Pin); delay_us(motor-pulse_delay / 2); GPIO_ResetBits(motor-STEP_GPIOx, motor-STEP_Pin); delay_us(motor-pulse_delay / 2); } }对于更复杂的运动控制如加减速曲线、多轴联动等可以使用STM32的定时器中断来实现更精确的控制。3. 硬件电路设计要点3.1 电源电路设计工业现场的电源环境复杂可能存在电压波动、浪涌、噪声等干扰。PLC开发板的电源电路需要具备宽电压输入范围、过压过流保护、噪声滤波等功能。典型的电源方案采用24VDC工业标准电压输入通过DC-DC降压芯片转换为5V和3.3V。建议使用LM2596等开关电源稳压芯片其输入电压范围宽最高40V输出电流大最高3A适合工业应用。电源电路中还应加入TVS管用于浪涌保护共模电感用于抑制共模噪声π型滤波器用于进一步净化电源信号。3.2 通信接口设计工业PLC通常需要与上位机、触摸屏或其他PLC进行通信。常见的工业通信接口包括RS485、以太网、CAN等。RS485接口适合中长距离通信最多可连接32个设备支持Modbus RTU协议。设计时需要注意终端电阻匹配和防雷保护。以太网接口适合高速数据传输支持Modbus TCP协议便于与上位机软件集成。可以使用W5500等硬件协议栈芯片简化开发。3.3 PCB布局注意事项工业控制板的PCB布局需要特别注意抗干扰设计电源分区数字电源、模拟电源、电机驱动电源应分开布局避免相互干扰信号隔离高频信号线远离模拟信号线必要时用地平面隔离接地设计采用单点接地或分区接地避免地环路去耦电容每个电路的电源引脚附近放置适当容值的去耦电容过孔设计关键信号线避免使用过多过孔保持阻抗连续4. 软件架构与程序设计4.1 主程序框架设计PLC程序通常采用循环扫描的工作方式每个扫描周期包括输入采样、程序执行、输出刷新三个阶段。这种架构保证了程序的实时性和确定性。以下是主程序的基本框架// PLC工作状态定义 typedef enum { PLC_STATE_INIT 0, // 初始化状态 PLC_STATE_RUN, // 运行状态 PLC_STATE_STOP, // 停止状态 PLC_STATE_ERROR // 错误状态 } PLC_State_t; // 全局变量定义 PLC_State_t g_plc_state PLC_STATE_INIT; uint32_t g_scan_cycle 10; // 扫描周期10ms int main(void) { // 硬件初始化 System_Init(); GPIO_Configuration(); ADC_Configuration(); Timer_Configuration(); // PLC状态机主循环 while(1) { switch(g_plc_state) { case PLC_STATE_INIT: PLC_Init_Handler(); break; case PLC_STATE_RUN: PLC_Run_Handler(); break; case PLC_STATE_STOP: PLC_Stop_Handler(); break; case PLC_STATE_ERROR: PLC_Error_Handler(); break; } } } // PLC运行处理函数 void PLC_Run_Handler(void) { uint32_t start_time Get_System_Tick(); // 输入采样阶段 Read_Digital_Inputs(); Read_Analog_Inputs(); // 用户程序执行阶段 User_Program_Execute(); // 输出刷新阶段 Update_Digital_Outputs(); Update_Analog_Outputs(); Update_Stepper_Motors(); // 等待扫描周期结束 uint32_t elapsed_time Get_System_Tick() - start_time; if(elapsed_time g_scan_cycle) { delay_ms(g_scan_cycle - elapsed_time); } }4.2 梯形图逻辑解释器设计为了兼容传统PLC的编程方式可以设计一个简单的梯形图解释器。梯形图程序可以转换为指令表形式存储在STM32的Flash中运行时由解释器逐条执行。梯形图的基本元素包括常开触点、常闭触点、线圈、定时器、计数器等。每种元素对应一个处理函数解释器根据指令顺序调用相应的处理函数。// 梯形图指令定义 typedef enum { LD 0, // 常开触点 LDI, // 常闭触点 OUT, // 输出线圈 AND, // 串联常开 ANI, // 串联常闭 OR, // 并联常开 ORI, // 并联常闭 TMR, // 定时器 CNT // 计数器 } Ladder_Instruction_t; // 梯形图解释器 void Ladder_Interpreter(uint16_t* program, uint16_t program_size) { uint16_t pc 0; // 程序计数器 uint8_t current_result 0; // 当前逻辑运算结果 while(pc program_size) { Ladder_Instruction_t instr (Ladder_Instruction_t)program[pc]; uint16_t operand program[pc 1]; switch(instr) { case LD: current_result Read_Input_Bit(operand); break; case LDI: current_result !Read_Input_Bit(operand); break; case OUT: if(current_result) { Set_Output_Bit(operand, 1); } else { Set_Output_Bit(operand, 0); } break; // 其他指令处理... } pc 2; // 每条指令占2个字 } }5. 工业环境抗干扰措施工业现场电磁环境复杂PLC系统需要具备良好的抗干扰能力。常见的干扰源包括电机启停、继电器动作、变频器工作等。5.1 硬件抗干扰设计电源滤波在电源输入端加入π型滤波器使用TVS管进行浪涌保护信号隔离数字输入输出使用光耦隔离模拟信号使用隔离放大器屏蔽措施敏感信号使用屏蔽线缆屏蔽层单点接地接地设计采用星型接地方式数字地、模拟地、电源地分开布置5.2 软件抗干扰措施看门狗使用独立看门狗和窗口看门狗防止程序跑飞数据校验对重要数据进行CRC校验发现错误及时处理信号滤波对模拟量信号进行数字滤波如滑动平均滤波、中值滤波状态监控实时监控系统关键参数发现异常立即进入安全状态// 软件看门狗配置 void IWDG_Configuration(void) { IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); IWDG_SetPrescaler(IWDG_Prescaler_32); // 32分频 IWDG_SetReload(0xFFF); // 重载值 IWDG_ReloadCounter(); // 重载计数器 IWDG_Enable(); // 使能看门狗 } // 在主循环中定期喂狗 void IWDG_Feed(void) { IWDG_ReloadCounter(); }6. 实际应用案例小型自动化设备控制以一个简单的物料分拣系统为例演示自制PLC的应用。系统功能包括通过光电传感器检测物料、根据颜色传感器信号控制气缸分拣、通过步进电机驱动传送带。系统IO需求数字输入2个光电传感器、急停按钮模拟输入1个颜色传感器数字输出2个气缸控制、报警指示灯步进电机1路传送带驱动控制逻辑光电传感器检测到物料到达读取颜色传感器数值判断物料颜色根据颜色控制相应气缸动作步进电机带动传送带移动一个工位循环执行上述过程这个案例展示了自制PLC在简单自动化设备中的实用价值成本远低于品牌PLC且可以根据需求灵活调整功能。7. 常见问题与解决方案7.1 模拟量采集不稳定问题现象ADC采集值跳动较大特别是在电机工作时可能原因电源噪声、地线干扰、信号线布局不当解决方案模拟电源与数字电源分开使用LC滤波模拟地单点接地避免地环路信号线使用屏蔽双绞线远离电机驱动线软件端采用滑动平均滤波算法7.2 步进电机失步问题现象电机定位不准特别是在加减速过程中可能原因脉冲频率过高、加速度过大、驱动电流不足解决方案采用S形加减速曲线避免突变根据电机参数调整驱动电流降低最高运行速度留有余量增加限位开关进行位置校正7.3 通信干扰问题问题现象RS485通信误码率高数据包丢失可能原因终端电阻不匹配、线缆质量差、接地不良解决方案通信线路首尾端添加120Ω匹配电阻使用屏蔽双绞线屏蔽层单端接地增加通信超时重发机制对重要数据进行CRC校验8. 进阶功能扩展建议在基础功能实现后可以考虑以下扩展方向8.1 物联网功能集成通过ESP8266/ESP32模块增加Wi-Fi连接能力实现远程监控和控制。可以集成MQTT协议将设备数据上传到云平台实现手机APP远程操作。8.2 高级运动控制对于需要多轴协调运动的场合可以研究基于STM32的CNC插补算法。实现直线、圆弧等复杂轨迹控制适用于3D打印机、小型数控机床等应用。8.3 可视化人机界面增加TFT液晶显示屏和触摸屏开发简单的人机界面。可以显示设备状态、设置参数、手动操作等提升设备的易用性。8.4 安全功能增强工业设备的安全至关重要可以增加安全继电器、安全光幕接口等安全功能。软件层面实现安全回路监控、急停处理等安全逻辑。基于STM32F103的PLC开发板为工业控制爱好者和小型设备制造商提供了一个高性价比的解决方案。通过合理的硬件设计和软件架构完全可以满足大多数中小型自动化设备的需求。这种开放式的设计思路不仅降低了成本更重要的是赋予了用户完全的控制权可以根据具体需求进行深度定制。在实际项目中建议先从简单的功能开始验证逐步完善抗干扰设计和安全措施。工业环境的要求远比实验室严格需要充分考虑各种异常情况和极端条件。本文提供的代码和设计思路都是经过实践验证的可以直接作为项目开发的基础。