STC15F104八脚单片机入门:10个例程带你掌握51核心外设
简介本资源是一套面向嵌入式初学者与硬件开发入门者的STC15F104E系列八脚单片机实战代码合集聚焦基础外设驱动与典型应用场景有效解决新手在裸机编程中缺乏系统性练习案例、外设协同调试经验不足等痛点。压缩包共144个文件含10个完整可运行的C语言源程序.c、配套Keil工程文件.uvproj/.uvopt、编译生成的目标文件.hex/.obj/.lst及启动代码STARTUP.A51整体仅236KB轻量易导入便于在最小系统板上快速验证。已有1063人学习下载覆盖闪烁灯、流水灯、按键识别、继电器控制、串口通信、EEPROM掉电存储、PWM输出及多外设综合应用等10个递进式例程每个程序均含中文逐行注释关键逻辑与寄存器配置说明清晰特别适合从零掌握STC15F104E的定时器、外部中断、串口、内置EEPROM等核心功能。 STC15F104这颗八脚单片机我从第一次在朋友那看到到后来自己拿它做了好几个小项目前后折腾了快两年。要说入门51单片机它绝对是被低估的一颗好芯片——体积小到可以塞进任何电路四条腿在干活却把定时器、中断、串口这些核心外设都保留了下来。这份资料把10个入门例程按难度排好注释写到几乎每行都解释对刚接触单片机的人来说省下的不只是查手册的时间更是建立起看代码就知道硬件怎么动的直觉。如果你手头正好有这个压缩包或者正打算用它入门这篇内容会帮你把例程背后的设计逻辑、引脚复用关系、以及那些数据手册不会明说的坑都梳理清楚。无论你是做毕设的学生、想转嵌入式的在职开发还是纯粹图一乐的自制小玩意爱好者这套例程都能让你在半天内把基础外设玩明白而且代码风格可以直接沿用下去。1. 项目整体设计与资源拆解1.1 STC15F104到底是一颗什么样的芯片STC15F104属于STC15系列核心是增强型8051SOP8封装。这可能是很多人的第一反应引脚这么少能做什么实际上它内部集成了4KB Flash程序存储器和256字节RAM工作电压范围比较宽内部自带高精度RC时钟不需要额外接晶振也不需要复位电路——两个最占PCB面积的元件全省了这对于做小体积控制板来说非常友好。引脚分配上除了电源和地真正能被你使用的IO大约是5到6个具体要看型号后缀和配置寄存器。可别小看这几条腿通过寄存器复用它们能承担UART串口、外部中断、定时器输入等不同功能。这意味着你不需要再买一块几十个引脚的开发板来学基础在一块指甲盖大小的电路板上就能把单片机最重要、最常用的几项外围功能全部接触一遍。这也是为什么我始终觉得这款芯片对入门者的价值非常大——资源少反而逼你去理解引脚复用和寄存器配置而不是像用Arduino那样库函数一调就完事底层完全黑盒。要知道内部时钟、掉电唤醒、看门狗这些功能在这个级别的芯片里都有保留这些恰恰是做产品化项目最需要的功能点。所以它不只是一个学习工具很多小家电、小控制器的量产方案里照样能看到它的身影。1.2 十个例程的完整清单与学习路线拿到资料后我建议你先别急着打开代码而是按下面这个清单把整体脉络过一遍。这套例程的编排逻辑很有规律基本遵循GPIO控制 - 基础外设 - 通信接口 - 低功耗与安全机制这条主线跟官方参考设计里的学习路径也一致。序号例程主题核心知识点建议学习位置01点亮一颗LEDGPIO输出、寄存器操作、sbit位定义第1天必做02LED闪烁与流水灯延时函数、循环结构、代码可读性第1天必做03按键检测输入GPIO输入、按键消抖、电平判断第2天必做04定时器0中断定时器初值计算、中断服务函数、中断优先级第3天重点05外部中断唤醒INT0/INT1触发方式、下降沿/低电平第3天重点06串口发送数据UART初始化、波特率设置、SBUF寄存器第4天必做07串口接收命令串口中断、帧解析、命令匹配第4天进阶08定时器模拟PWM占空比调节、呼吸灯效果第5天拓展09看门狗实验看门狗超时复位、喂狗时序第5天必做10掉电唤醒实验低功耗模式、唤醒源配置第6天拓展如果你是完全零基础我推荐的路线不是线性从头看到尾而是按01-02-03-04-06-09这个组合先走一遍剩下的05、07、08、10放在有实际需要时再回来看。因为串口发送和定时器是后面所有调试工具的基础而看门狗和掉电唤醒虽然看起来偏工程化但它们能让你理解单片机是怎么在无人值守环境里稳定运行的这是很多教程没讲透的地方。2. 开发环境搭建与代码结构解析2.1 从Keil到STC-ISP的完整工具链这套例程的代码格式是典型的Keil C51工程所以我建议你直接装Keil C51版本——注意不是Keil MDK那主要是给ARM内核用的。安装过程不复杂但有一个我经常被问到的问题要不要装破解版的许可证我的建议是官方评估版有2KB代码限制而这套入门例程每个工程代码量都不大评估版完全可以跑等以后做真实项目再去考虑授权问题没必要从一开始就折腾环境。需要特别注意的是STC15系列芯片在Keil中添加设备时默认找不到具体型号。STC官方提供了在Keil中导入STC型号库的插件这个在STC-ISP烧录软件里就有对应入口。如果不导入其实也能编译只是Device下拉框里选不到STC15F104需要选一个标准8051型号替代。但我建议你还是花一分钟导入一下因为型号库中会预置正确的存储器映射和启动代码配置避免后面因为内存模型不对产生奇怪的问题。STC-ISP这个软件本身就是STC官方的烧录工具也兼职做串口调试助手。它最常用的几个功能程序烧录、硬件选项配置、串口调试、定时器计算器。我每次新装环境一定会先把定时器计算器打开看一眼因为里面能按你输入的晶振频率和波特率直接生成对应的定时器初值代码这个对初学者来说非常方便比手算要省事太多后面讲到定时器的时候我们再细说。2.2 工程目录与注释体系打开压缩包你会发现每个例程是一个独立的文件夹命名规律是序号_功能名称比如04_TIM0_Interrupt。文件夹内部结构统一一个.uvproj工程文件、一个 main.c 源文件部分例程还带 config.h 或 delay.c 之类的辅助文件。这种按功能拆分的命名方式让我在找代码时几乎不需要看内部内容光靠文件夹名就能定位——这个习惯我后来沿用到所有正经项目里。代码注释是这份资料最值得称道的地方。它不仅仅是简单地在每行后面加个//而是形成了层层递进的注释体系文件头有功能说明和硬件连接描述每个函数前面有输入输出参数的解释关键寄存器配置后面会标注该寄存器当前值的位含义最后还有宏定义开的头告诉你可以用替换数值的方式改变功能。就以串口波特率为例代码里会写// 波特率计算SMOD0定时器1模式2自动重装晶振22.1184MHz // 初值 256 - (晶振 / 12 / 32 / 波特率) // 256 - (22118400 / 12 / 32 / 9600) 256 - 6 250 (0xFA) TL1 0xFA; // 低字节定时初值 TH1 0xFA; // 高字节重装值这种写法对新手非常友好你能直观看到一个寄存器数值是怎么从公式推导出来的而不是机械地去抄一个魔法数字。我自己的经验是学了这套注释风格之后再回去看很多开源项目里那些一坨没有注释的寄存器赋值会产生一种强烈的动手欲望——把它们的计算过程也补上。这个习惯会让你在日后Debug时少走很多弯路。3. 核心例程深度拆解与实现逻辑3.1 GPIO输出从点亮一颗LED开始第一个例程点灯看起来最基础但里面隐含了一个很重要的概念寄存器操作与位定义。STC15的P3口有对应的特殊功能寄存器P3它的每一位对应一个物理引脚。你要让某个引脚输出高电平就对这个寄存器按位赋值。代码里通常会这样写sbit LED P3^2; // 将P3.2引脚定义为LED void main() { LED 0; // 输出低电平LED点亮取决于外部电路 while (1) { // 主循环可以空转 } }这里有两个坑值得新手注意。第一LED的点亮电平取决于你的硬件接法如果LED正极接VCC负极接单片机引脚那输出低电平才会点亮反过来则输出高电平点亮。所以拿到例程后第一次烧录前最好先确认自己的接法和例程注释里的说明一致否则会遇到代码没问题灯就是不亮的情况。第二sbit定义的位置有讲究必须在所有函数之外、文件头部进行否则编译器会报错。这类GPIO操作背后的思想是单片机是通过修改寄存器来操控硬件行为的而寄存器本质上就是一片特殊的内存地址。所以你对P3寄存器的赋值操作最终会变成总线上的电平变化再经过内部驱动电路到达引脚。理解了这一层后面所有外设的寄存器配置都能举一反三。3.2 定时器不让主循环瞎忙流水灯例程里如果只靠延时函数你会发现两个问题延时时CPU什么都干不了而且多个任务叠在一起时时序会乱。定时器中断就是为解决这类问题而生的。STC15F104内部集成了多个定时器其中Timer0是最常用也最容易上手的。定时器初值的计算方法代码注释里一般会这样推演假设系统时钟频率为12MHz定时器0工作在模式1即16位定时器模式。每到计满65535再溢出一次触发一次中断。如果我们要实现1毫秒的定时计数脉冲频率 12MHz / 12 1MHz12T模式每机器周期计数一次 要计数的个数 1ms * 1MHz 1000个 初值 65536 - 1000 64536 0xFC18 所以 TH0 0xFC, TL0 0x18代码里初始化部分通常长这样TMOD 0xF0; // 清空Timer0的模式位 TMOD | 0x01; // Timer0设为模式116位定时器 TH0 0xFC; // 装入初值高字节 TL0 0x18; // 装入初值低字节 ET0 1; // 开启Timer0中断使能 EA 1; // 开启总中断 TR0 1; // 启动Timer0然后中断服务函数里要记得重装初值否则定时器溢出后会从0开始计中断间隔就不对了void timer0_isr() interrupt 1 { TH0 0xFC; // 重装高字节 TL0 0x18; // 重装低字节 counter; if (counter 500) // 500ms翻转一次 { counter 0; LED !LED; } }这段代码是整套例程里含金量最高的地方之一。你要特别留意的是中断函数的写法interrupt 1这个关键字告诉编译器这是Timer0的中断服务入口服务函数不能有参数也不能有返回值。另外中断服务函数里做的事情越少越好最好只置标志位真正耗时的工作放到主循环里做否则中断嵌套或长时间占会直接导致主流程卡顿。这份资料在注释里对这种中断处理原则也有明确提醒算是贴心到了极致。3.3 外部中断为什么按键要消抖按键输入例程里基本上都会引入外部中断。STC15F104的INT0和INT1两个外部中断输入引脚可以被配置为下降沿触发或低电平触发。使用中断的好处是CPU不用一直去查询引脚状态按键按下时自动跳入中断服务函数省电且响应快。但新手最容易在这里碰到的问题是按键按一下中断却触发了两次。原因大家都听过但不一定真正理解机械按键在按下和松开的瞬间会因为金属弹片的物理振动产生一系列不稳定的电平跳变这个过程叫抖动持续时间一般在5到20毫秒之间。消抖的核心思路就一句话等信号稳定了再判断。常见的做法有两种硬件上并联一个104电容滤除高频抖动或者软件上延时10ms再确认一次电平状态。推荐的做法是void ext0_isr() interrupt 0 { // 进入中断后先延时消抖 delay_ms(10); if (KEY 0) { // 再次确认确实是低电平 counter; // 处理按键逻辑 } }这里要提醒的是在中断服务函数里做延时并不是一个优雅的方案它会导致中断阻塞时间变长。更规范的做法是在中断里只标记一个有按键事件的全局标志位主循环检测到标志后再进行消抖确认和业务处理。不过对于入门例程理解消抖是为了避免误判才是重点优化可以放到第二步。3.4 串口通信让PC和单片机对话当你能用单片机输出电平控制LED之后下一步必然是想要看到单片机内部的数据——串口通信是最好的窗口。STC15F104的UART通过P3.0RXD和P3.1TXD这两个引脚与外部通信注意这两条腿和程序烧录引脚是复用的这也是STC系列最经典的串口下载原理来源。串口通信的关键参数是波特率它决定了每秒传输多少个数据位。我见很多人一上来就抄例程里的波特率设置代码但从不搞懂为什么要用11.0592MHz晶振、为什么初值刚好是0xFA。其实波特率的本质就是通过定时器产生一个精确的时钟脉冲串口收发双方用这个时钟来采样数据线上的电平。标准计算公式波特率 (2^SMOD / 32) × (定时器溢出率) 定时器溢出率 系统时钟 / (12 × (256 - TH1))把式子整理一下就能从目标波特率反推出TH1初值。例程里通常用22.1184MHz晶振算9600波特率因为22.1184MHz恰好能被9600整除误差为零数据传输出错的概率最低。这也是为什么STC官方手册特别建议入门阶段直接用官方烧录软件里的波特率计算器让它帮你算好避免因为手算出错导致串口乱码。串口发送的代码量非常少核心就是往SBUF寄存器写入一个字节然后等待发送完成标志位TI置1void UART_SendByte(unsigned char dat) { SBUF dat; // 数据装载到发送缓冲器 while (!TI); // 等待发送完成标志TI为1 TI 0; // 标志位需要软件清零 }很多新手第一次跑通串口发送时都特别兴奋因为这是第一次从控制硬件跨入与外界通信的层次。而这份资料里的接收例程更进一步会在串口中断里把收到的字节放到缓冲区主循环解析命令并执行对应的LED控制这已经是一个简化版的小型交互协议了。4. 实操踩坑记录与常见问题排查4.1 八脚芯片最容易被忽略的引脚复用问题STC15F104的引脚复用是入门时最大的拦路虎。很多新手按照例程把代码写好后发现某几个功能互相冲突比如用了串口就没办法同时使用外部中断、或用了某个IO的PWM功能后发现另一个功能不工作。原因在于硬件设计上多个功能可能共享同一个引脚而引脚当前的用途取决于特殊功能寄存器P_SW1、P_SW2等的配置。我拿项目里的串口例程打个比方串口引脚默认映射在P3.0和P3.1但在某些情况下你可以通过配置寄存器把串口切换到其他引脚。这个功能在多引脚封装上很方便但在八脚芯片上受引脚数量限制实际可切换的余地非常小。代码注释里通常会有一行注意本例中P3.0/P3.1复用为UART不可同时作为普通IO使用新手一定要养成改功能前先看引脚分配表的习惯。我在实际做一个小项目时曾踩过一个坑同一个引脚既想接按键又想接中断硬件上没做跳线结果只能通过改代码、把中断功能切到另一个引脚上才解决。这类问题在八脚芯片上尤其常见因为可用引脚太少一个引脚往往被寄予多重功能期待。建议你在动手画板子之前先把每个引脚的功能分配表列出来做一张Excel或纸上的表格都要比直接开画稳妥。4.2 烧录失败的几种典型原因与解决思路STC15系列没有专用的SWD或JTAG调试口它靠串口ISP方式烧录。这意味着程序下载需要经过两个步骤冷启动进入ISP引导区然后在引导区中接收来自串口的数据并写入Flash。烧录失败的情况几乎每个人都遇到过最常见的几种原因如下。第一接线错误。八脚芯片的串口烧录要求将USB转TTL模块的TXD连接到单片机的RXDP3.0RXD连接到TXDP3.1共地。我见过太多新手把TXD和TXD连在一起结果自然是毫无反应。第二上电时序问题。STC官方ISP软件要求先点下载/编程按钮然后再给单片机上电或断电复位如果次序反了单片机直接运行用户程序就不会进入引导区。第三供电不稳。USB转TTL模块的3.3V或5V输出能力有限如果测试板上还有其他外设耗电较大可能导致烧录过程中电压跌落下载失败。解决这些问题的通用排查思路是先看STC-ISP软件左下角的状态栏有没有正在检测目标单片机的提示点下载后观察单片机有没有从断电状态重新上电的动作。如果一直没有检测到先量一下P3.0和P3.1的电平是否正常再确认USB转TTL模块的驱动是否装好。到这里如果还不行你可以试试把波特率调低比如从115200降到9600往往能绕过一些杂散干扰导致的握手失败。4.3 常见问题速查与独家避坑技巧我把这段时间里被问得最多的几个问题整理成了速查表方便你以后遇到类似情况时直接对照。现象可能原因解决方法LED不亮接反了高低电平引脚未配置为推挽模式确认公共端接法将P3M1/P3M0配为00或01串口输出乱码波特率误差过大晶振频率选错电脑串口参数不对用STC定时器计算器核对初值确认无外部晶振时应选内部IRC频率按键触发两次没有消抖或消抖时间不够加10ms软件延时或104电容烧录失败接线错误上电顺序错误驱动问题对照4.2节逐步排查换USB口试试掉电唤醒不工作唤醒源引脚电平状态匹配不上未开启唤醒使能确认INT0/INT1配置与唤醒信号方向一致这里再说一个资料里可能没有细讲但非常有用的独家技巧STC15F104内部时钟频率可以通过烧录软件里的硬件选项进行配置不是所有例程默认都用同一个频率。如果你烧录后发现延时时间比预期慢了一倍很可能是内部时钟被配置成了6MHz而代码注释里是按12MHz计算的延时参数。修改方式是重新打开STC-ISP在硬件选项中把IRC频率选中到12MHz再重新下载。这个坑我亲眼见过好几个人卡了一整个晚上其实说到底就是烧录配置和代码假设不一致。另外如果你的电路是需要省电的产品方案掉电唤醒例程很值得深挖。STC15的掉电模式电流可以降到微安级别通过外部中断唤醒后能迅速恢复运行。这种平时睡觉按键来了才干活的思路和很多物联网传感器节点的运行逻辑完全一致。理解了这个例程你就能把一块纽扣电池的寿命从几天拉长到几个月。我在实际做项目时还发现一个规律凡是用到这个八脚单片机的小产品十有八九都离不开掉电唤醒 外部中断 看门狗这三件套。它们分别对应着省电、事件响应、异常自恢复三大需求而这套例程恰好把这几个点都覆盖了。这也是为什么我一直建议哪怕你觉得某些例程暂时用不上也至少要通读一遍因为未来做产品时几乎必然会回头用到它们。5. 进阶扩展从例程走向实际项目设计5.1 怎么把多个例程组合成一个完整的小系统学完10个例程之后下一个自然而然的问题是我能不能把它们组合起来做一个稍微完整一点的东西答案是完全可以而且这种组合练习才是真正把知识内化的关键一步。以智能温控风扇为例你可以先用ADC例程里没直接包含但原理和定时器初值计算一脉相承采集温度传感器的电压再用定时器中断做定时采样把采样结果经过串口发送到电脑显示同时用定时器模拟PWM控制风扇转速。这样一个项目就把GPIO、定时器、串口、PWM全部串联了起来。组合的过程中你会被迫思考模块之间的数据流谁负责采集、谁负责运算、谁负责输出这就是嵌入式系统架构设计的雏形。不过要提醒你的是组合例程时不要直接复制粘贴所有代码然后期待它能跑。这种例程堆积方式大概率会出问题因为多个模块会竞争同一个定时器、多个中断会互相抢占资源。正确的做法是先画一张简单的状态流程图确定每个模块的优先级和触发条件再动手写代码。就像搭积木不是积木越多越好而是要让每块积木在自己的位置上受力均匀。5.2 从八脚芯片延伸出去的技能树当你觉得八脚芯片的引脚不够用了或者想跑更复杂的算法可以考虑顺着两条路线升级。一条是继续留在STC15系列换用更大封装、更多引脚的型号比如STC15W408AS它的编程模型和寄存器布局几乎一致迁移成本非常低唯一的门槛是需要重新熟悉引脚的复用表。另一条路线是转向STC32系列或者ARM Cortex-M系列但这意味着要重新学习一部分寄存器和中断控制器逻辑认知切换成本更高。我的个人建议是先别急着换平台把STC15F104这十来个例程吃透特别是掌握好看数据手册和配置寄存器这两个通用能力。因为不管以后用哪种单片机最终都逃不开这两件事。很多老工程师看一份新芯片的数据手册只需要半天就能上手写代码靠的就是这些年在老芯片上练出来的底子——芯片型号会过时但底层能力不会。八脚单片机只是你进入嵌入式世界的一扇小门但它能让你看清的是整个8051生态的骨架。把一个引脚少得可怜、资源极其有限的芯片玩明白再去驾驭其他复杂芯片时你会发现自己比那些一上来就用大而全开发板的人更有底气因为你从底层就知道每一个功能是怎么抠出来的。本文还有配套的精品资源点击获取