F3U源码STM32仿三菱PLC底层实现

简介: 这个项目实现了基于STM32的仿三菱PLC底层系统,提供了类似三菱FX系列PLC的功能,包括梯形图编程、I/O处理、通信协议等核心功能。

项目概述

这个项目实现了基于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

项目特点

  1. 模块化设计:各功能模块分离,便于维护和扩展

  2. 实时调度:基于优先级的任务调度机制

  3. 硬件抽象:统一的硬件访问接口

  4. 梯形图支持:兼容三菱FX系列PLC编程风格

  5. 通信协议:支持Modbus RTU等工业协议

  6. 资源管理:优化的内存使用和处理器负载

开发环境

  • IDE: STM32CubeIDE

  • MCU: STM32F303xC

  • 编译器: GCC ARM Embedded

  • 调试器: ST-Link V2

扩展方向

  1. 添加更多PLC指令和功能块

  2. 实现HMI通信接口

  3. 增加SD卡存储支持

  4. 添加以太网通信功能

  5. 实现在线监控和调试接口

相关文章
|
11天前
|
人工智能 安全 Linux
【OpenClaw保姆级图文教程】阿里云/本地部署集成模型Ollama/Qwen3.5/百炼 API 步骤流程及避坑指南
2026年,AI代理工具的部署逻辑已从“单一云端依赖”转向“云端+本地双轨模式”。OpenClaw(曾用名Clawdbot)作为开源AI代理框架,既支持对接阿里云百炼等云端免费API,也能通过Ollama部署本地大模型,完美解决两类核心需求:一是担心云端API泄露核心数据的隐私安全诉求;二是频繁调用导致token消耗过高的成本控制需求。
5574 13
|
19天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
22164 118

热门文章

最新文章