如何使用本资源
- 准备工作:您需要一块开发板(例如基于STC89C52、STM32F103等主流型号)和相应的下载/调试工具(如STC-ISP、ST-Link)。
- 循序渐进:请严格按照模块顺序学习,后面的实例会综合运用前面学到的知识。
- 动手实践:最重要的一点! 请务必亲手将代码敲入编译器,编译、下载到开发板中观察现象,调试和修改是学习的最佳途径。
- 理解原理:不要只复制粘贴代码,在运行之前,先阅读“实例说明”和“代码解析”,理解代码背后的硬件原理和编程思想。
目录与代码实例
以下实例主要以经典的 STC89C52RC 单片机为例,因为它的寄存器操作和引脚定义对于初学者非常直观,大部分代码稍作修改即可应用于其他51内核单片机或AVR、STM32等。

(图片来源网络,侵删)
第一部分:基础入门篇 (1-20例)
这个阶段的目标是熟悉开发环境、掌握GPIO(通用输入/输出)的基本操作、最简单的延时函数和C51语言特点。
| 序号 | 实例名称 | 核心知识点 | 实例说明 | 代码解析 |
|---|---|---|---|---|
| 1 | 点亮一个LED灯 | GPIO输出、最小系统 | 最经典的“Hello, World!”程序,控制一个LED灯的亮灭。 | - 定义sbit LED = P1^0;,指定P1.0口控制LED。- LED = 0; 点亮LED(低电平点亮)。- LED = 1; 熄灭LED。 |
| 2 | LED流水灯 | 循环移位、数组控制 | 8个LED灯从左到右循环点亮,形成流水灯效果。 | - 使用_crol_(a, b)或_cror_(a, b)函数(需包含intrins.h)实现循环移位。- 在 while(1)循环中不断移位并延时。 |
| 3 | LED闪烁 | 延时函数、定时器 | 让一个LED灯以固定的时间间隔(如1秒)闪烁。 | - 编写一个简单的软件延时函数void delay_ms(unsigned int ms)。- 在 while(1)中交替执行LED=0; delay_ms(500);和LED=1; delay_ms(500);。 |
| 4 | 按键控制LED | GPIO输入、消抖 | 按下一个独立按键,控制一个LED灯的状态翻转(亮变灭,灭变亮)。 | - 定义sbit KEY = P3^2;,指定P3.2口为按键输入。- 消抖是关键:检测到按键按下后,延时10-20ms再次检测,确认是真实按下。 - 使用 if(KEY == 0)检测低电平。 |
| 5 | 独立按键控制LED亮灭 | GPIO输入、状态判断 | 按下按键,LED亮;松开按键,LED灭。 | - 实时检测按键状态,并根据状态直接控制LED。 - if(KEY == 0) { LED = 0; } else { LED = 1; } |
| 6 | 双按键控制LED | 多路输入、逻辑判断 | 按键K1控制LED亮,按键K2控制LED灭。 | - 定义两个按键引脚,分别检测它们的状态。 - 使用 if-else if结构判断是哪个按键被按下。 |
| 7 | 数码管静态显示 | 数码管原理、段选/位选 | 在一个共阴/共阳数码管上稳定显示一个数字(如'8')。 | - 了解数码管是7段LED组成的。 - 定义一个共阴极数码管的段选码数组 unsigned char code seg_code[] = {0x3f, ...}。- P0 = seg_code[8]; 即可显示数字8。 |
| 8 | 数码管动态扫描显示 | 动态扫描、人眼视觉暂留 | 在两个或多个数码管上分别显示不同的数字(如"12")。 | - 核心思想:快速轮流点亮每个数码管,利用人眼视觉暂留效应,看起来是同时亮的。 - 分时操作:打开第一个数码管的位选,送第一个数字的段选码 -> 延时几毫秒 -> 关闭位选 -> 打开第二个数码管的位选,送第二个数字的段选码 -> ... 循环。 |
| 9 | 独立按键计数器 | 按键消抖、变量累加 | 每按下一次按键,数码管显示的数字加1,到9后归0。 | - 在按键检测成功后,执行一个计数变量count++。- 处理进位: if(count > 9) count = 0;- 将 count的值通过数码管显示出来。 |
| 10 | 中断控制LED | 外部中断、中断服务函数 | 使用外部中断0(INT0,P3.2)来控制LED状态翻转,不占用主循环。 | - 初始化IT0 = 1;(下降沿触发),EX0 = 1;(允许INT0中断),EA = 1;(开启总中断)。- 编写 void EX0_interrupt() interrupt 0中断服务函数,在里面执行LED = ~LED;。- 主循环可以执行其他任务。 |
| 11 | 定时器中断实现精确延时 | 定时器、中断、中断优先级 | 使用定时器0中断,实现比软件延时更精确的1秒定时,并控制LED闪烁。 | - 初始化定时器0,工作在模式1(16位定时器)。 - 计算初值,使其每50ms中断一次。 - 在中断服务函数中设置一个软件计数变量,中断20次就是1秒。 - 1秒到后,翻转LED状态。 |
| 12 | PWM控制LED亮度 | PWM(脉冲宽度调制)、定时器 | 通过改变PWM的占空比,实现LED的呼吸灯效果(亮度渐变)。 | - 使用定时器中断,产生固定频率(如1kHz)的方波。 - 在中断服务函数中,用一个变量 duty_cycle控制高电平的时间。- 逐渐改变 duty_cycle的值(如从0到100再回到0),LED的亮度就会随之变化。 |
| 13 | 继电器控制 | 三极管驱动、电磁兼容 | 用单片机的一个I/O口控制继电器的吸合和断开,从而控制220V电器。 | - 注意安全! 强弱电隔离! - 单片机I/O口驱动能力弱,需要用一个NPN三极管(如8050)来放大电流驱动继电器线圈。 - 继电器两端并联一个续流二极管(1N4007),保护三极管不被线圈的反向电动势击穿。 |
| 14 | 蜂鸣器发声 | 无源/有源蜂鸣器、频率控制 | 让蜂鸣器发出不同频率的声音,甚至简单的旋律。 | - 区分有源蜂鸣器(直接通电响)和无源蜂鸣器(需要PWM信号驱动)。 - 控制无源蜂鸣器发声,就是用定时器产生不同频率的方波信号。 |
| 15 | 串口发送数据到电脑 | UART串口、波特率配置 | 单片机通过串口向电脑发送“Hello World!”字符串。 | - 配置串口工作方式(SCON)、定时器1作为波特率发生器(TMOD)。 - 使用 SBUF = 'H';发送数据,while(!TI); TI=0;等待发送完成。- 在电脑端使用串口调试助手接收。 |
| 16 | 串口接收电脑指令 | UART串口、中断接收 | 单片机接收电脑发来的单个字符(如'A'),并控制LED。 | - 使能串口中断ES = 1;。- 编写 void UART_interrupt() interrupt 4中断服务函数,接收数据if(RI) { RI = 0; val = SBUF; }。- 在主循环中判断 val的值并执行相应操作。 |
| 17 | AD0804模数转换 | ADC、参考电压、时序 | 读取一个电位器的电压值(0-5V),并通过串口发送到电脑显示。 | - 理解ADC0804的工作时序:拉低CS,拉低WR启动转换,查询INTR变低或延时后,拉低RD读取数据。 - 读取到的8位数字量 0-255对应0-5V的电压。 |
| 18 | DAC0832数模转换 | DAC、双缓冲、参考电压 | 将单片机产生的数字量(如锯齿波数据)转换成模拟电压,用示波器观察。 | - 理解DAC0832的三种工作方式,这里使用直通方式最简单。 - 将 P0口的数据直接送给DAC0832的数据输入端,WR1和XFER接地,CS接地,ILE接高电平。- 循环送入 0-255的数字量,即可在输出端看到0-Vref的锯齿波电压。 |
| 19 | DS18B20温度传感器 | 单总线协议、时序精度 | 读取DS18B20的温度值,并在数码管上显示。 | - 单总线协议是难点:精确实现复位脉冲、写时序(0/1)、读时序。 - 读取64位ROM序列号和暂存器中的温度数据。 - 对读取的温度数据进行处理(包括符号位和温度值转换)。 |
| 20 | LCD1602字符液晶显示 | LCD初始化、指令集、数据写入 | 在LCD1602屏幕上显示两行固定的字符串。 | - 理解LCD1602的4位或8位并行通信方式。 - 严格按照时序进行初始化:设置显示模式、清屏、进入模式设置、开显示等。 - 写入数据时, RS=1, RW=0;写入指令时,RS=0, RW=0。 |
第二部分:核心外设篇 (21-50例)
这个阶段深入掌握单片机内部和外部常用模块,如I2C、SPI、EEPROM、实时时钟等。
| 序号 | 实例名称 | 核心知识点 | 实例说明 | 代码解析 |
|---|---|---|---|---|
| 21 | I2C协议读写AT24C02 | I2C协议、模拟时序 | 使用软件模拟I2C时序,读写EEPROM芯片AT24C02中的数据。 | - I2C是两线制(SDA, SCL)。 - 需要模拟:起始信号、停止信号、应答信号、发送字节、接收字节。 - AT24C02的器件地址是 0xA0(写)和0xA1(读)。 |
| 22 | SPI协议驱动AD转换器 | SPI协议、模拟时序 | 使用软件模拟SPI时序,驱动一个SPI接口的ADC(如MCP3008)读取电压。 | - SPI是四线制(MOSI, MISO, SCLK, CS)。 - 模拟:拉低CS -> 移出控制字节(通道选择) -> 移入10位数据 -> 拉高CS。 |
| 23 | DS1302实时时钟 | 三线制串行通信、时钟芯片 | 读取DS1302芯片记录的年、月、日、时、分、秒,并在LCD1602上显示。 | - DS1302使用I/O, SCLK, CE三根线。- 需要严格模拟其读写时序,包括命令字节(地址+读写位)。 - 注意BCD码和十进制数的转换。 |
| 24 | 红外遥控解码 | NEC协议、外部中断 | 解码常见的红外遥控器按键,并在数码管上显示对应的键值。 | - 使用外部中断引脚接收红外信号。 - NEC协议有引导码、地址码、地址反码、命令码、命令反码和结束码。 - 在中断服务函数中,通过测量高电平持续时间来判断是'0'还是'1'。 |
| 25 | 舵机控制 | PWM信号、角度控制 | 控制一个舵机旋转到指定的角度(如0度、90度、180度)。 | - 舵机需要周期为20ms(50Hz)的PWM信号。 - 脉冲宽度(高电平时间)决定了舵机的角度:0.5ms对应0度,1.5ms对应90度,2.5ms对应180度。 - 可以使用定时器中断来精确生成这个PWM信号。 |
| 26 | 超声波测距 | TRIG/TIOE时序、定时器 | 使用HC-SR04超声波模块测量与前方障碍物的距离,并在LCD上显示。 | - TRIG引脚发送一个10us的高电平触发测距。- 模块自动发送8个40kHz的方波,并等待回波。 - ECHO引脚变为高电平开始计时,变为低电平停止计时。- 距离 = (高电平时间 * 声速) / 2。 |
| 27 | 矩阵键盘扫描 | 行列扫描、编码 | 读取4x4矩阵键盘的按键值,并在数码管上显示。 | - 行输出低电平,列输入高电平,逐行扫描。 - 如果某列读到低电平,则说明该行该列的按键被按下。 - 可以用编码法或计数法来确定是哪个键。 |
| 28 | EEPROM数据掉电保存 | AT24C02、数据存储 | 将一个变量的值(如计数值)存入AT24C02,断电后重新上电,读取并显示该值。 | - 在程序初始化时,先从AT24C02读取数据到变量。 - 在变量值改变时,及时将新值写回AT24C02。 - 实现数据的掉电不丢失功能。 |
| 29 | DS18B20多点测温 | ROM匹配、单总线 | 在总线上挂载多个DS18B20,分别读取它们的温度。 | - 先发送跳过ROM命令,然后启动所有DS18B20进行温度转换。 - 再次发送跳过ROM命令,然后读取温度,此时读取的是所有器件的平均值(错误做法)。 - 正确做法:先发送复位脉冲,然后逐个读取每个DS18B20的64位ROM序列号,对目标器件进行“匹配ROM”操作,再进行单点测温。 |
| 30 | DAC0832输出正弦波 | 查表法、数模转换 | 在DAC0832的输出端产生一个平滑的正弦波。 | - 在程序中建立一个正弦函数值的查找表(一个0-255的数组)。 - 使用定时器中断,每隔一段时间从表中取出一个值送给DAC0832。 - 中断频率越高,正弦波频率越高,波形越平滑。 |
| ... | ... | ... | ... | ... |
第三部分:综合应用篇 (51-100例)
这个阶段将多个知识点融合,完成复杂的功能,是向项目开发过渡的关键。
| 序号 | 实例名称 | 核心知识点 | 实例说明 | 代码解析 |
|---|---|---|---|---|
| 31 | 智能温控系统 | DS18B20、继电器、PID算法 | 实时监测环境温度,当温度低于设定值时,通过继电器启动加热器。 | - DS18B20负责测温。 - 继电器负责控制加热器(如电热丝)。 - 可以加入简单的阈值控制或更高级的PID算法来控制加热功率,使温度稳定在设定值。 |
| 32 | 电子密码锁 | 矩阵键盘、LCD1602、EEPROM | 通过键盘输入密码,正确则开锁(点亮LED),错误则报警(蜂鸣器响),密码可修改并保存在EEPROM中。 | - 矩阵键盘输入密码,LCD显示提示信息(如“请输入密码”)。 - 将输入的密码与存储在EEPROM中的正确密码进行比较。 - 实现密码修改功能,修改后存入EEPROM。 |
| 33 | 多功能电子钟 | DS1302、矩阵键盘、LCD1602 | 具备时间显示、时间校准、闹钟设置功能的电子钟。 | - DS1302提供精确时间。 - 矩阵键盘用于切换模式(正常/设置/闹钟)和调整数值。 - LCD1602显示当前时间、设置界面和闹钟时间。 - 到达闹钟时间,蜂鸣器响起。 |
| 34 | 超声波测距避小车 | 超声波模块、电机驱动、逻辑判断 | 制作一个能自动避开障碍物的小车,当超声波测距小于阈值时,小车后退并转向。 | - 超声波模块作为“眼睛”。 - L298N或类似电机驱动模块控制两个直流电机。 - 主循环中不断测距,根据距离值决定前进、后退或转向的逻辑。 |
| 35 | 简易示波器 | ADC、定时器、LCD显示 | 利用ADC和定时器,采集一个模拟信号的电压值,并在LCD1602上绘制出其波形。 | - 定时器以固定频率触发ADC转换。 - 将ADC转换得到的数据点存储在一个数组中。 - 在LCD上,根据数组中的值计算出每个点在屏幕上的Y坐标,并画点或连线。 |
| 36 | MP3播放器(模拟) | VS1053模块、SPI、FAT32文件系统 | 通过SPI接口控制VS1053解码芯片,从SD卡中读取MP3文件并进行播放。 | - 高级项目,需要掌握SPI协议、FAT32文件系统解析。 - 初始化VS1053芯片,设置其工作模式。 - 读取SD卡中的MP3文件数据,通过SPI接口将数据流发送给VS1053进行解码和播放。 |
| 37 | 无线数传(NRF24L01) | SPI、无线通信 | 两块单片机通过NRF24L01模块进行无线通信,一块发送数据,另一块接收并显示在LCD上。 | - SPI接口配置NRF24L01的工作频道、地址、速率等。 - 发送端:将要发送的数据打包,通过SPI写入NRF24L01的TX FIFO,然后发送。 - 接收端:不断检查RX FIFO是否有数据,有则读出并处理。 |
| 38 | WiFi模块(ESP8266)控制 | UART、AT指令 | 通过单片机向ESP8266发送AT指令,使其连接WiFi,并将传感器数据(如温度)上传到云平台。 | - 单片机通过UART与ESP8266通信。 - 学习常用的AT指令: AT+RST, AT+CWMODE, AT+CWJAP, AT+CIPMUX, AT+CIPSTART, AT+CIPSEND等。- 将传感器数据封装成HTTP或TCP数据包发送。 |
| 39 | OLED屏显示图形 | I2C、OLED驱动、显存 | 在0.96寸的OLED屏幕上绘制点、线、矩形、图片等。 | - OLED通常使用I2C接口,驱动芯片为SSD1306。 - 核心是操作一个显存数组(如128x64=1024字节),每个bit对应一个像素点。 - 先在显存中修改数据,然后将整个显存一次性写入OLED驱动芯片。 |
| 40 | PID电机调速 | PWM、编码器、PID算法 | 使用PID算法控制直流电机的转速,使其稳定在设定值。 | - 编码器(霍尔或光电)用于测量电机的实际转速。 - PWM信号控制电机的平均电压,从而控制转速。 - PID算法根据设定转速和实际转速的差值,计算出合适的PWM占空比,进行闭环控制。 |
| ... | ... | ... | ... | ... |
学习建议与资源
- 官方文档:这是最权威的资料! 一定要仔细阅读您所用单片机(如STC89C52、STM32F103)的数据手册(Datasheet)和参考手册(Reference Manual),里面包含了所有寄存器的详细说明和时序图。
- 开发板原理图:理解开发板上各个外设(LED、按键、数码管、串口等)是如何连接到单片机的引脚上的,这是硬件连接的基础。
- 善用调试工具:学习使用Keil C51或STM32CubeIDE等IDE的软件仿真和硬件调试功能(如单步执行、设置断点、查看变量),这能极大地提高学习效率。
- 从模仿到创造:先完全理解并实现上述实例,然后尝试修改它们(比如改变流水灯的速度、修改按键的功能),最后尝试自己设计一个小项目(如一个环境监测仪、一个智能台灯)。
希望这份精心整理的资源能对您的单片机C语言实训之路有所帮助!祝您学习愉快,早日成为单片机高手!

(图片来源网络,侵删)

(图片来源网络,侵删)
