从零开始:用Keil5把PLC逻辑“烧”进STM32的实战全记录
你有没有过这样的经历?
写好了代码,点了“Download”,结果弹出一行红字:“Cannot access target.”
调试器明明插着,线也没接错,板子也供电了——可就是下不进去。
别急,这几乎是每个嵌入式新手都会踩的坑。而今天我们要做的,不只是教会你怎么点那个按钮,而是带你亲手实现一个能跑PLC逻辑的STM32系统,并通过Keil5 下载把它真正“激活”。
我们不讲空话,不堆术语,只走一遍从创建工程到LED随按钮亮灭的真实流程。你会发现,“keil5下载”不是魔法,它是有逻辑、可掌控、必须理解的关键一步。
为什么选Keil5来做PLC仿真?
在工业控制领域,PLC(可编程逻辑控制器)就像大脑,负责读输入、做判断、控输出。但真实PLC价格高、扩展难,学习成本也不低。
那能不能用便宜又灵活的单片机来“模拟”一个PLC?
当然可以!尤其是像STM32F103这类 Cortex-M3 芯片,性能强、外设多、生态成熟,完全能胜任小型PLC的功能仿真。
而在这个过程中,Keil MDK-ARM(俗称Keil5)就成了最顺手的工具链之一:
- 图形化界面友好,配置直观;
- 对 STM32 支持极佳,Flash算法开箱即用;
- 调试功能强大,支持实时变量观察和指令级跟踪;
- “下载”操作一键完成,适合快速迭代。
更重要的是——它能让开发者深入到底层机制,搞清楚每一条GPIO是怎么被驱动的,每一次扫描周期是如何精确控制的。
这不是黑盒操作,这是真正在“造脑”。
我们要做什么?目标系统长这样
先说清楚最终效果:
我们将会搭建一个微型PLC仿真系统,功能非常简单但极具代表性:
当你按下连接到 PA0 的按钮时,PB0 上的 LED 灯点亮;松开则熄灭。
听起来很简单?没错,但它已经包含了PLC的核心工作模式:
- 输入采样→ 读取PA0电平
- 逻辑处理→ 判断是否为高
- 输出刷新→ 控制PB0状态
- 循环执行→ 每10ms扫描一次
这就是经典的“顺序扫描 + 输出映射”模型,也是所有PLC运行的基础。
硬件连接如下:
PC (Keil5) ↓ USB ST-Link V2 ↓ SWD 接口(SWCLK, SWDIO, GND, VCC) STM32F103RCT6 最小系统板 ├── PA0 ← 按钮开关(上拉,按下接地) └── PB0 → LED + 限流电阻 → GND软件环境要求:
- Keil MDK-ARM v5.37 或更高版本
- STM32F1xx Device Family Pack 已安装
- ST-Link 驱动正常识别
准备好了吗?我们现在就开始。
第一步:新建工程并配置核心参数
打开 uVision5,点击Project → New uVision Project,保存项目文件后选择芯片型号:STM32F103RCT6。
Keil会自动提示是否添加启动文件(startup_stm32f10x_hd.s),选“是”。接着手动添加必要的源码:
system_stm32f10x.c—— 系统时钟初始化stm32f10x_gpio.c—— GPIO驱动库(如果你没用HAL)
然后在项目中新建main.c,粘贴以下代码:
#include "stm32f10x.h" #include "delay.h" #define PLC_SCAN_CYCLE 10 // 扫描周期 10ms int main(void) { SystemInit(); // 初始化系统时钟(默认72MHz) delay_init(); // 延时函数初始化(基于SysTick) // 开启GPIOA和GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStruct; // 配置PA0为浮空输入(用于检测按键) GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStruct); // 配置PB0为推挽输出(驱动LED) GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); while (1) { uint8_t key_pressed = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0); if (key_pressed == Bit_RESET) { // 按键按下(低电平) GPIO_SetBits(GPIOB, GPIO_Pin_0); // 点亮LED } else { GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 熄灭LED } delay_ms(PLC_SCAN_CYCLE); // 固定扫描间隔 } }💡 注意:这里按键检测使用的是“低电平触发”,因为常见开发板上的按键是接到GND的,配合内部或外部上拉电阻工作。
这个程序虽然短,但结构清晰,体现了PLC的基本行为模型。接下来最关键的一步来了——怎么把它“送进”STM32里?
第二步:搞定“keil5下载”的三大关键设置
很多人以为编译完就能下,其实不然。下载失败90%出在配置环节。我们必须正确设置三个核心部分。
✅ 1. 选择调试器(Debug Tab)
进入Options for Target → Debug选项卡:
- 左侧选择“ST-Link Debugger”
- 点击右侧的“Settings”
在新窗口中切换到“Debug” 子页,确认接口类型为SWD,速度建议设为 1MHz(稳定优先)。
再切到“Flash Download” 子页,勾选“Program”和“Verify”,确保下载后自动校验数据。
✅ 2. 加载正确的 Flash Algorithm
这是最容易出错的地方!
仍在“Flash Download”页面,你会看到一个空白区域写着“No Algorithm Selected”。
点击“Add”按钮,在列表中找到:
STM32F10x High-density Flash
(如果是RC型芯片,请根据Flash大小选择 Medium 或 High Density 版本)
这个Flash Algorithm是什么?
它是Keil用来指导如何擦除、写入特定MCU闪存的一段小程序。没有它,Keil根本不知道该怎么操作STM32的Flash。
一旦加载成功,你会看到类似信息:
Algorithm: STM32F10x_HD.flm [0x08000000 - 0x0807FFFF] Size: 512KB说明已准备好对主Flash进行编程。
✅ 3. 启用自动运行(Run to main)
仍在Debug Settings中,回到“Debug”子页,勾选:
✔ Reset and Run
这意味着:下载完成后,MCU将自动复位并跳转到main函数运行,无需你手动按复位键。
这对调试非常友好,尤其当你反复修改代码时,F8一下就能看到最新效果。
第三步:编译 → 下载 → 验证
现在回到主界面,点击Build(F7)编译整个工程。
如果一切顺利,底部Build Output会显示:
".\output\plc_sim.axf" - 0 Error(s), 0 Warning(s).恭喜,可以下载了!
按下F8或点击工具栏上的“Download”图标,你会在Output Window看到如下流程:
Erase Done. Programming... Program Done. Verify OK.几秒钟后,程序已写入Flash,并开始运行!
此时操作按钮,观察LED是否响应。如果一切正常,你就完成了人生第一个基于Keil5的PLC仿真部署。
常见问题与避坑指南(亲测有效)
别高兴太早,现实往往更复杂。以下是我在教学中总结的高频故障清单,附带解决方案:
| 问题现象 | 根本原因 | 解决方法 |
|---|---|---|
| Cannot access target | ST-Link未被识别或通信异常 | 换USB口、重装驱动、检查SWD线序(SWCLK/SWDIO不能反) |
| No target connected | 目标板未供电或NRST悬空 | 测量VDD是否3.3V,检查复位电路是否有10k上拉电阻 |
| Flash programming failed | Flash算法不匹配 | 更换为对应密度的算法(Medium/High) |
| Download success but LED不亮 | 主函数未进入或时钟未起振 | 在main开头加LED快闪测试,排除SystemInit问题 |
| 按键无反应 | 输入模式配置错误 | 改成GPIO_Mode_IPU(上拉输入)避免浮空误判 |
📌最佳实践建议:
- 使用独立电源给STM32供电,避免ST-Link供电能力不足导致不稳定;
- 在代码开头加一段“启动指示”:
c for(int i=0; i<5; i++) { GPIO_ToggleBits(GPIOB, GPIO_Pin_0); delay_ms(200); }
这样一眼就能看出程序是否真的跑起来了; - 写一个
debug_init.ini初始化脚本,放在“Initialization File”中,提前关闭看门狗、配置时钟等。
keil5下载的本质:不只是“烧程序”
你以为“keil5下载”只是把.hex文件写进Flash?
其实背后是一整套精密协作机制:
- 通过SWD协议,Keil与目标MCU建立JTAG-like通信;
- 暂停CPU运行,进入调试状态;
- 将Flash算法下载到SRAM并执行,获得对存储器的控制权;
- 分页擦除原程序区域,再逐块写入新的机器码;
- 执行校验比对,确保烧录数据准确无误;
- 释放控制权,跳转至复位向量或main函数。
整个过程依赖于调试接口 + Flash算法 + 目标运行时环境三者的协同。任何一个环节断裂,都会导致失败。
这也是为什么——
同样是STM32,有人轻松下载,有人折腾半天都连不上。
差的不是工具,是对底层机制的理解深度。
从这里出发:你能走多远?
掌握了“keil5下载”,你就拿到了嵌入式世界的入场券。但这仅仅是起点。
你可以继续拓展的方向包括:
- 加入Modbus RTU通信,让这个“软PLC”能被HMI或上位机读取;
- 实现定时中断扫描,替代delay_ms,提升实时性;
- 构建简单的梯形图解释器,支持类似Codesys的图形化编程前端;
- 集成Bootloader,实现远程固件升级(OTA);
- 接入FreeRTOS,模拟多任务PLC扫描周期;
- 使用UV4命令行工具,将下载过程自动化,融入CI/CD流程。
这些都不是遥不可及的技术,它们都建立在你今天学会的这一套基础流程之上。
最后的话:动手才是硬道理
这篇文章没有华丽辞藻,也没有故弄玄虚的概念堆砌。
我们只做了一件事:从零开始,把一段PLC风格的代码,通过keil5下载,真正运行在一块STM32板子上。
你可能还会遇到各种问题,比如驱动装不上、算法加载失败、程序跑飞……
但请记住:每一个报错都是成长的机会。
下次当你再看到“Download Failed”时,不要慌,打开Settings,一步步排查:
- 调试器连了吗?
- 接口选对了吗?
- Flash算法匹配吗?
- 供电稳吗?
- 复位正常吗?
把这些问一遍,答案自然浮现。
正如一位老工程师所说:“会用Keil下载的人很多,但懂它为什么能下载的人,才能走得更远。”
现在,轮到你动手了。
拿起你的开发板,打开Keil5,按下F8,让第一行PLC逻辑,在你的掌控之下运转起来。
如果你在实现过程中遇到了其他挑战,欢迎在评论区分享讨论。