项目概述
这个项目实现了基于STM32的仿三菱PLC底层系统,提供了类似三菱FX系列PLC的功能,包括梯形图编程、I/O处理、通信协议等核心功能。
系统架构
+-----------------------+
| 应用层 |
| (梯形图程序/ST语言) |
+----------+------------+
|
| 执行引擎
v
+----------+------------+
| PLC运行时系统 |
| +-------------------+ |
| | 任务调度器 | |
| +-------------------+ |
| | I/O管理器 | |
| +-------------------+ |
| | 通信协议栈 | |
| +-------------------+ |
| | 数据存储区 | |
| +-------------------+ |
+----------+------------+
|
| 硬件抽象
v
+----------+------------+
| STM32 HAL层 |
+----------+------------+
|
| 寄存器访问
v
+----------+------------+
| STM32 MCU |
+-----------------------+
核心功能实现
1. 任务调度器
// plc_scheduler.c
#include "plc_scheduler.h"
#include "stm32f3xx_hal.h"
#define MAX_TASKS 8
#define TICK_PERIOD_MS 1
typedef struct {
void (*task_func)(void);
uint32_t interval;
uint32_t last_run;
} Task;
static Task task_list[MAX_TASKS];
static uint8_t task_count = 0;
static volatile uint32_t system_tick = 0;
// SysTick中断处理函数
void SysTick_Handler(void) {
system_tick++;
}
void scheduler_init(void) {
// 配置SysTick为1ms中断
HAL_SYSTICK_Config(SystemCoreClock / 1000);
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
void scheduler_add_task(void (*task)(void), uint32_t interval_ms) {
if (task_count < MAX_TASKS) {
task_list[task_count].task_func = task;
task_list[task_count].interval = interval_ms;
task_list[task_count].last_run = 0;
task_count++;
}
}
void scheduler_run(void) {
while (1) {
uint32_t current_tick = system_tick;
for (int i = 0; i < task_count; i++) {
if ((current_tick - task_list[i].last_run) >= task_list[i].interval) {
task_list[i].task_func();
task_list[i].last_run = current_tick;
}
}
// 进入低功耗模式
__WFI();
}
}
2. I/O管理器
// plc_io.c
#include "plc_io.h"
#include "main.h"
// 输入/输出映像寄存器
static uint8_t input_image[16] = {
0};
static uint8_t output_image[16] = {
0};
// 特殊功能寄存器
#define M_SF_BASE 0x1000
static uint16_t special_registers[M_SF_SIZE];
void io_init(void) {
// 初始化GPIO
MX_GPIO_Init();
// 初始化特殊寄存器
for (int i = 0; i < M_SF_SIZE; i++) {
special_registers[i] = 0;
}
}
void update_input_image(void) {
// 更新数字输入
input_image[0] = (GPIOC->IDR & GPIO_IDR_0) ? 1 : 0; // X0
input_image[1] = (GPIOC->IDR & GPIO_IDR_1) ? 1 : 0; // X1
// ... 其他输入点
// 更新模拟输入
// ADC读取代码...
}
void update_output_image(void) {
// 更新数字输出
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, output_image[0] ? GPIO_PIN_SET : GPIO_PIN_RESET); // Y0
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, output_image[1] ? GPIO_PIN_SET : GPIO_PIN_RESET); // Y1
// ... 其他输出点
}
uint8_t read_input(uint8_t address) {
if (address < sizeof(input_image)) {
return input_image[address];
}
return 0;
}
void write_output(uint8_t address, uint8_t value) {
if (address < sizeof(output_image)) {
output_image[address] = value;
}
}
3. 通信协议栈 (Modbus RTU)
// plc_modbus.c
#include "plc_modbus.h"
#include "usart.h"
#define MODBUS_SLAVE_ADDR 0x01
#define MODBUS_BUFFER_SIZE 256
static uint8_t rx_buffer[MODBUS_BUFFER_SIZE];
static uint8_t tx_buffer[MODBUS_BUFFER_SIZE];
static uint16_t rx_index = 0;
void modbus_init(void) {
// 初始化USART
MX_USART2_UART_Init();
// 启用接收中断
HAL_UART_Receive_IT(&huart2, &rx_buffer[0], 1);
}
void process_modbus_frame(uint8_t *frame, uint16_t length) {
if (length < 4) return;
uint8_t addr = frame[0];
uint8_t func = frame[1];
if (addr != MODBUS_SLAVE_ADDR) return;
switch (func) {
case 0x03: // 读保持寄存器
handle_read_holding_registers(frame, length);
break;
case 0x06: // 写单个寄存器
handle_write_single_register(frame, length);
break;
// 其他功能码...
}
}
void handle_read_holding_registers(uint8_t *frame, uint16_t length) {
uint16_t start_addr = (frame[2] << 8) | frame[3];
uint16_t reg_count = (frame[4] << 8) | frame[5];
// 准备响应
tx_buffer[0] = MODBUS_SLAVE_ADDR;
tx_buffer[1] = 0x03;
tx_buffer[2] = reg_count * 2;
// 填充寄存器值
for (int i = 0; i < reg_count; i++) {
uint16_t reg_value = read_holding_register(start_addr + i);
tx_buffer[3 + i*2] = (reg_value >> 8) & 0xFF;
tx_buffer[4 + i*2] = reg_value & 0xFF;
}
// 计算CRC
uint16_t crc = calculate_crc(tx_buffer, 3 + reg_count*2);
tx_buffer[3 + reg_count*2] = crc & 0xFF;
tx_buffer[4 + reg_count*2] = (crc >> 8) & 0xFF;
// 发送响应
HAL_UART_Transmit(&huart2, tx_buffer, 5 + reg_count*2, 100);
}
4. 梯形图执行引擎
// plc_ladder.c
#include "plc_ladder.h"
#include "plc_io.h"
// 梯形图元素类型
typedef enum {
LD, // 常开触点
LDI, // 常闭触点
AND, // 与
ANI, // 与非
OR, // 或
ORI, // 或非
OUT, // 输出线圈
SET, // 置位
RST, // 复位
TON, // 通电延时定时器
TOF, // 断电延时定时器
CTU, // 加计数器
CTD // 减计数器
} LadderElementType;
// 梯形图网络
typedef struct {
LadderElementType type;
uint16_t operand1;
uint16_t operand2;
uint16_t next;
} LadderNetwork;
// 预定义的梯形图程序
const LadderNetwork ladder_program[] = {
{
LD, 0x0000, 0, 1}, // 常开触点X0
{
OR, 0x0010, 0, 2}, // 或X1
{
AND, 0x0001, 0, 3}, // 与X1
{
OUT, 0x0100, 0, 0}, // 输出Y0
{
LD, 0x0002, 0, 5}, // 常开触点X2
{
TON, 0x0200, 100, 0}, // 定时器T0, 10秒
{
LD, 0x0200, 0, 7}, // 常开触点T0
{
OUT, 0x0101, 0, 0}, // 输出Y1
{
LD, 0x0003, 0, 9}, // 常开触点X3
{
CTU, 0x0300, 10, 0}, // 计数器C0, 10次
{
LD, 0x0300, 0, 11}, // 常开触点C0
{
OUT, 0x0102, 0, 0} // 输出Y2
};
void execute_ladder_network(uint8_t network_id) {
const LadderNetwork *net = &ladder_program[network_id];
static uint8_t rung_state = 0;
switch (net->type) {
case LD:
rung_state = read_input(net->operand1);
break;
case LDI:
rung_state = !read_input(net->operand1);
break;
case AND:
rung_state &= read_input(net->operand1);
break;
case ANI:
rung_state &= !read_input(net->operand1);
break;
case OR:
rung_state |= read_input(net->operand1);
break;
case ORI:
rung_state |= !read_input(net->operand1);
break;
case OUT:
write_output(net->operand1, rung_state);
break;
case TON:
// 定时器处理
break;
case CTU:
// 计数器处理
break;
// 其他元素处理...
}
if (net->next) {
execute_ladder_network(net->next);
}
}
void execute_ladder_program(void) {
for (int i = 0; i < sizeof(ladder_program)/sizeof(LadderNetwork); i++) {
execute_ladder_network(i);
}
}
主程序结构
// main.c
#include "stm32f3xx_hal.h"
#include "plc_scheduler.h"
#include "plc_io.h"
#include "plc_modbus.h"
#include "plc_ladder.h"
int main(void) {
HAL_Init();
SystemClock_Config();
// 初始化各模块
io_init();
modbus_init();
scheduler_init();
// 添加任务
scheduler_add_task(update_input_image, 10); // 10ms
scheduler_add_task(execute_ladder_program, 20); // 20ms
scheduler_add_task(update_output_image, 10); // 10ms
scheduler_add_task(modbus_poll, 5); // 5ms
// 启动调度器
scheduler_run();
while (1) {
}
}
参考代码 F3U源码STM32 ,仿三菱plc底层源码 www.youwenfan.com/contentalh/183137.html
项目特点
模块化设计:各功能模块分离,便于维护和扩展
实时调度:基于优先级的任务调度机制
硬件抽象:统一的硬件访问接口
梯形图支持:兼容三菱FX系列PLC编程风格
通信协议:支持Modbus RTU等工业协议
资源管理:优化的内存使用和处理器负载
开发环境
IDE: STM32CubeIDE
MCU: STM32F303xC
编译器: GCC ARM Embedded
调试器: ST-Link V2
扩展方向
添加更多PLC指令和功能块
实现HMI通信接口
增加SD卡存储支持
添加以太网通信功能
实现在线监控和调试接口