单片机第二周重制版

:memo: 单片机备考核心笔记:外设控制与交互

一、 基础输出:LED 与 延时

1. 软件延时 (Software Delay)

:warning: 备考重点:不要死记硬背延时函数的具体数值(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 (低电平) → 亮 :white_check_mark:

    • 给 1 (高电平) → 灭 :cross_mark:

    • 注:蓝桥杯 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 扫描一次按键状态(蓝桥杯进阶推荐)。