单片机备考核心笔记:外设控制与交互
一、 基础输出:LED 与 延时
1. 软件延时 (Software Delay)
备考重点:不要死记硬背延时函数的具体数值(
i=195等),必须学会使用 STC-ISP 软件自动生成。
标准模板 (12MHz 晶振):
C
void Delay(unsigned char xms) //@12.000MHz
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239; // 具体数值由 STC-ISP 生成
do
{
while (--j);
} while (--i);
}
}
2. 流水灯控制 (Flowing Light)
相比于普通的左移 <<(移出的位会丢失,低位补0),流水灯通常使用循环移位。
-
头文件:
#include <intrins.h> -
核心函数:
-
_crol_(val, n):循环左移 (Circular Rotate Left) -
_cror_(val, n):循环右移 (Circular Rotate Right)
-
代码示例:
C
#include <REGX52.H>
#include <intrins.h>
unsigned char ucLed = 0xFE; // 初始状态:1111 1110 (仅最低位亮)
void main()
{
while(1)
{
P1 = ucLed; // 输出状态
Delay(500); // 延时
ucLed = _crol_(ucLed, 1); // 循环左移:位0移到位1,位7移回位0
}
}
二、 显示模块:7段数码管 (7-Segment Display)
1. 硬件结构
数码管由 8 个 LED 组成(a, b, c, d, e, f, g 和小数点 dp)。
-
共阳极 (Common Anode - CA):
-
公共端接 VCC(高电平)。
-
给 0 (低电平) → 亮

-
给 1 (高电平) → 灭

-
注:蓝桥杯 CT107D 开发板使用的是共阳极数码管。
-
2. 段位映射 (Mapping)
通常对应一个字节 (8-bit),顺序为 dp g f e d c b a (MSB → LSB)。
| 位 (Bit) | 对应段 | 物理位置 |
|---|---|---|
| Bit 7 | dp | 右下角小数点 |
| Bit 6 | g | 中间横杠 |
| Bit 5 | f | 左上竖杠 |
| Bit 4 | e | 左下竖杠 |
| Bit 3 | d | 底部横杠 |
| Bit 2 | c | 右下竖杠 |
| Bit 1 | b | 右上竖杠 |
| Bit 0 | a | 顶部横杠 |
三、 基础输入:独立按键 (Independent Key)
1. 状态检测算法 (三行代码消抖法)
这是一种比 Delay 消抖更高级、不阻塞主程序的检测方式。
变量定义:
-
Key_Val: 当前扫描到的键值。 -
Key_Old: 上一次的键值。 -
Key_Down: 下降沿(按下瞬间为 1,其他时候为 0)。 -
Key_Up: 上升沿(松开瞬间为 1,其他时候为 0)。
核心逻辑:
C
Key_Val = Key_Read(); // 1. 获取当前电平状态
// 2. 下降沿检测:(当前是按下) 且 (上次是没按)
Key_Down = Key_Val & (Key_Val ^ Key_Old);
// 3. 上升沿检测:(当前是没按) 且 (上次是按下)
Key_Up = ~Key_Val & (Key_Val ^ Key_Old);
Key_Old = Key_Val; // 4. 更新历史状态
2. 独立按键读取函数
C
unsigned char Key_Read()
{
unsigned char temp = 0;
// 如果是独立按键,直接判断引脚电平
if(P3_0 == 0) temp = 1;
if(P3_1 == 0) temp = 2;
if(P3_2 == 0) temp = 3;
if(P3_3 == 0) temp = 4;
return temp; // 返回键码,0表示无按键
}
四、 进阶输入:矩阵键盘 (Matrix Keypad)
1. 扫描原理 (逐列扫描法)
矩阵键盘为了节省 I/O 口,采用行列交叉检测。
-
输出:依次拉低某一列(置 0),其他列置高(置 1)。
-
输入:检测行线电平。如果某一行读到低电平,说明该行与当前拉低的列被按键接通了。
2. 标准扫描代码 (Key_Read_Matrix)
C
unsigned char MatrixKey_Read()
{
unsigned char temp = 0;
// --- 扫描第 1 列 ---
P3_0=0; P3_1=1; P3_2=1; P3_3=1; // 拉低第1列
// Delay(1); // 某些高速单片机可能需要微小延时
if(P3_4==0) temp=1; // 检测行
if(P3_5==0) temp=2;
if(P3_6==0) temp=3;
if(P3_7==0) temp=4;
// --- 扫描第 2 列 ---
P3_0=1; P3_1=0; P3_2=1; P3_3=1; // 拉低第2列
if(P3_4==0) temp=5;
if(P3_5==0) temp=6;
if(P3_6==0) temp=7;
if(P3_7==0) temp=8;
// --- 扫描第 3 列 ---
P3_0=1; P3_1=1; P3_2=0; P3_3=1; // 拉低第3列
if(P3_4==0) temp=9;
if(P3_5==0) temp=10;
if(P3_6==0) temp=11;
if(P3_7==0) temp=12;
// --- 扫描第 4 列 ---
P3_0=1; P3_1=1; P3_2=1; P3_3=0; // 拉低第4列
if(P3_4==0) temp=13;
if(P3_5==0) temp=14;
if(P3_6==0) temp=15;
if(P3_7==0) temp=16;
return temp;
}
五、 编程规范与避坑指南
1. 数据类型选择
| 类型 | 关键字 | 范围 | 用途 |
|---|---|---|---|
| 单字节 | unsigned char |
0 ~ 255 | 计数器、标志位、键值、LED状态 |
| 双字节 | unsigned int |
0 ~ 65535 | 较大延时数值、频率计数 |
2. Switch-Case 陷阱
在处理按键逻辑时,千万别忘了 break,否则会发生“穿透”执行。
错误示例:
C
switch(Key_Down) {
case 1:
Flag = 1; // 忘记 break,执行完这里会继续执行 case 2
case 2:
Flag = 0;
break;
}
3. 消抖建议
-
硬件消抖:并联电容(了解即可)。
-
软件消抖:
-
简单法:在读取按键后
Delay(10)再次判断。 -
状态机/中断法:利用定时器每 10ms-20ms 扫描一次按键状态(蓝桥杯进阶推荐)。
-