嵌入式——51单片机1

【51单片机学习笔记】 最小系统、GPIO、数码管、按键、LED、外部中断、 定时器中断

📖今日学习:51单片机最小系统、GPIO口使用、LED点亮、数码管显示、按键检测;外部中断、定时器/计数器中断。

🔍文章大纲

  1. 51单片机最小系统
  2. GPIO口基础概念
  3. LED灯驱动(输出)
  4. 数码管显示(静态、动态扫描)
  5. 按键输入检测(查询方式)
  6. 中断系统基础概念
  7. 外部中断
  8. 定时器/计数器中断
  9. ✨学习重点汇总
  10. ⚠️易错点&踩坑记录
  11. 📝今日学习小结

---

1. 51单片机最小系统

最小系统是单片机能够正常运行的最少硬件电路,4部分组成:

  1. 单片机主控芯片(如STC89C52)
  2. 电源电路:给单片机供电,一般5V直流供电
  3. 晶振电路 :提供系统时钟,常见11.0592MHz、12MHz。
    • 12MHz:机器周期 = 1us,方便延时计算
    • 11.0592MHz:串口波特率可以精准匹配,串口项目首选
  4. 复位电路:上电复位,让单片机程序从0地址从头开始执行。

机器周期:51单片机12个时钟周期 = 1个机器周期。

晶振12MHz,时钟周期1/12us,机器周期恰好1us。

2. GPIO口基础概念

51单片机标准有4组IO口:P0、P1、P2、P3,每组8位(P0.0~P0.7)。

IO口 特点
P0 开漏输出,无上拉电阻;做输出必须外接上拉电阻;可地址/数据复用
P1 准双向口,内部带上拉电阻,普通IO,无复用功能
P2 准双向口,带上拉;外部存储器高8位地址复用
P3 准双向口,带上拉;引脚复用功能(外部中断、定时器、串口)

准双向口:作为输入之前,必须先向端口写1,释放引脚,才能正确读取外部电平。

P0口特殊:内部没有上拉,输出低电平有效,输出高电平是高阻态。

寄存器操作示例:

复制代码
// P1口全部置0
P1 = 0x00;
// P1.0单独置1
P1_0 = 1;

3. LED灯点亮(GPIO输出)

LED二极管:阳极接高电平,阴极接低电平才会点亮

两种硬件接法:

  1. 共阴极:阴极接GND,IO输出高电平,灯亮。
  2. 共阳极:阳极接VCC,IO输出低电平,灯亮。

必须串联限流电阻(220Ω左右),防止电流过大烧毁LED和IO引脚。

示例代码(P1.0控制LED,低电平点亮)

复制代码
#include <reg52.h>
sbit led = P1^0;

void main(void)
{
    while(1)
    {
        led = 0; //点亮
    }
}

简单延时函数,实现LED闪烁

复制代码
void delay_ms(unsigned int t)
{
    unsigned int i,j;
    for(i=t;i>0;i--)
        for(j=110;j>0;j--);
}

4. 数码管显示

数码管由a~g七个段 + dp小数点组成。

  • 共阳数码管 :公共端接VCC;段脚输出低电平点亮段
  • 共阴数码管 :公共端接GND;段脚输出高电平点亮段

静态数码管

每个数码管段脚独立;只要赋值段码,数字持续显示。

优点:程序简单,无闪烁;缺点:占用IO多,数码管数量多时不适合。

段码表:

共阴:0~90x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F

动态扫描数码管

多位数码管,段选共用一套IO,位选控制哪一个数码管点亮

流程:

  1. 送当前要显示数字的段码;
  2. 打开对应位选,点亮这一位;
  3. 短暂延时;
  4. 关闭位选(消影);
  5. 循环切换下一位;

利用人眼视觉暂留,高速循环扫描,人眼看到多位同时显示。

⚠️消影很关键:不关闭位选直接切换段码,会出现重影乱码。

动态扫描简单逻辑伪代码:

复制代码
while(1)
{
    送第一位段码;
    开第一位位选;
    delay();
    关闭所有位选; //消影

    送第二位段码;
    开第二位位选;
    delay();
    关闭所有位选;
}

5. 按键输入检测(查询方式)

按键原理:按下引脚电平翻转;松开恢复。

机械按键存在机械抖动:按下/松开瞬间电平抖动5‑10ms,直接读取会误判多次。

解决抖动两种方案:

  1. 软件延时消抖:检测到电平变化,延时10ms再读一次电平确认。
  2. 硬件消抖:外接电容。

51准双向口做输入:读取按键前,IO口先写1。

查询方式按键示例:

复制代码
sbit key = P3^2;
void key_scan(void)
{
    if(key == 0) //按键按下低电平
    {
        delay_ms(10); //软件消抖
        if(key == 0)
        {
            //按键处理业务
        }
        while(key == 0); //等待松开
        delay_ms(10);
    }
}

查询方式缺点:CPU一直循环扫描按键,占用CPU资源。可以使用外部中断实现按键检测。

6. 51中断系统基础

中断:CPU正在执行主程序,收到外部事件信号,暂停主程序,跳转去执行中断服务函数;执行完毕回到断点继续运行主程序

51单片机5个中断源:

中断源 中断号 说明
外部中断0 0 INT0(P3.2)
定时器0中断 1 T0
外部中断1 2 INT1(P3.3)
定时器1中断 3 T1
串口中断 4 UART

关键寄存器:

  1. IE:中断允许寄存器,总中断EA,各个中断使能位。
    • EA:总中断开关,必须置1,所有中断才能生效。
  2. IP:中断优先级寄存器,高优先级可以打断低优先级中断。
  3. 外部中断:TCON寄存器,设置触发方式(下降沿触发 / 低电平触发)。
  4. 定时器:TMOD定时器模式寄存器;THx TLx定时器初值寄存器。

中断服务函数格式:

复制代码
void 中断服务函数名(void) interrupt 中断号
{
    //中断处理代码
}

⚠️中断服务函数不要写延时、复杂业务;尽量简短快速执行完。

7. 外部中断

外部中断0:P3.2(INT0);外部中断1:P3.3(INT1)

两种触发方式:

  1. 低电平触发:引脚持续低电平,会不断反复触发中断;适合外部电平信号。
  2. 下降沿触发(边沿触发):引脚从高电平跳变到低电平一瞬间触发一次,按键推荐使用。

配置步骤:

  1. 设置IT0/IT1选择触发方式;
  2. EX0 / EX1打开对应外部中断;
  3. EA=1开启总中断;
  4. 编写interrupt对应中断号的中断服务函数。

示例:外部中断0,下降沿触发按键

复制代码
#include <reg52.h>
sbit led = P1^0;

void int0_init(void)
{
    IT0 = 1;     //下降沿触发
    EX0 = 1;     //开启外部中断0
    EA  = 1;     //打开总中断
}

//外部中断0服务函数,中断号0
void int0_isr(void) interrupt 0
{
    led = ~led; //按键触发翻转LED状态
}

void main(void)
{
    int0_init();
    while(1)
    {
        //主循环可以做别的事情
    }
}

边沿触发:只会跳变瞬间进一次中断;低电平触发,如果引脚一直低电平,会反复进入中断。

8. 定时器/计数器中断

定时器本质:对机器周期计数;计数值溢出,触发定时器中断。

  • 定时器模式:对单片机内部机器周期计数,用来定时。
  • 计数器模式:对外部T0(P3.4)、T1(P3.5)引脚外部脉冲计数。

TMOD寄存器:设置T0、T1工作模式;模式1最常用:16位定时器,TH高8位,TL低8位,最大计数65536

12M晶振机器周期1us;模式1,最多定时65536us = 65.536ms。

定时时间 = (65536‑初值) * 机器周期。

定时器配置步骤:

  1. TMOD设置工作模式;
  2. 给TH0、TL0装载计数初值;
  3. ET0打开定时器0中断;
  4. TR0启动定时器;
  5. EA=1开启总中断;
  6. 写定时器中断服务函数,中断里面重新给TH0 TL0赋初值!模式1不会自动重装

示例:12M晶振,定时器0模式1定时50ms中断

复制代码
void timer0_init(void)
{
    TMOD |= 0x01; //T0模式1,16位定时器
    //50ms 初值 65536‑50000 = 15536
    TH0 = 15536 / 256;
    TL0 = 15536 % 256;
    ET0 = 1;  //开启T0中断
    TR0 = 1;  //启动定时器0
    EA  = 1;  //总中断打开
}

//定时器0中断服务函数,中断号1
void timer0_isr(void) interrupt 1
{
    //模式1,必须手动重装初值!!
    TH0 = 15536 / 256;
    TL0 = 15536 % 256;

    //定时到,业务处理
}

✨学习重点

  1. 最小系统四大部分:电源、晶振、复位、单片机;分清12M与11.0592M晶振用途,机器周期概念。
  2. 4组GPIO:P0开漏无上拉;P1/P2/P3准双向口;准双向口做输入要先写1;P3口具备引脚复用功能。
  3. LED:分清共阳、共阴极接法,必须串联限流电阻。
  4. 数码管:分清共阴/共阳段码;动态扫描原理,消影操作必不可少,防止重影;视觉暂留实现多位同时显示。
  5. 按键:机械抖动,软件延时消抖原理;查询扫描按键优缺点。
  6. 中断核心概念:打断主程序,执行中断服务函数;EA总中断开关,不打开所有中断无效。
  7. 外部中断:低电平触发 vs 下降沿(边沿触发);按键优先边沿触发。
  8. 定时器中断:模式1为16位,不会自动重装初值,中断服务函数内手动重新赋值THx TLx;定时时间计算公式。
  9. 中断服务函数规则:interrupt + 中断号;函数不能有参数、返回值;中断内部尽量简短,禁止大量延时。

⚠️易错点&踩坑记录

  1. ❌准双向IO做输入,忘记先写1,读按键一直电平错误。
  2. ❌P0口输出忘记加上拉电阻,输出高电平是高阻态,LED/外设工作异常。
  3. ❌LED省略限流电阻,直接接IO,烧坏LED或者单片机IO引脚。
  4. ❌数码管动态扫描没有消影,切换段码时出现重影、乱码。
  5. ❌按键不做消抖,按下一次多次触发,逻辑错乱。
  6. ❌外部中断使用低电平触发,按键按住不放,不断重复进中断;按键场景优先下降沿触发。
  7. ❌中断忘记打开总中断EA=1,中断完全不响应。
  8. ❌定时器模式1,中断服务函数忘记手动重装TH0 TL0初值,只进一次中断,之后不再触发。
  9. ❌定时器初值计算错误,定时时间和预期完全不符。
  10. ❌中断服务函数里面写长延时、复杂业务逻辑;中断要快进快出,耗时逻辑放到主循环。
  11. ❌中断服务函数写错中断号,跑到错误的中断处理,程序跑飞。
  12. ❌混淆共阳、共阴数码管段码,写反,数码显示乱数字。

📝今日学习小结

今天学习51单片机基础硬件与软件编程:掌握最小系统组成;理解4组GPIO口电气特性;完成LED点亮闪烁;数码管静态、动态扫描显示;按键查询检测。

学习51中断系统,掌握外部中断实现按键触发;定时器中断完成定时功能。

相关推荐
啦啦啦啦啦zzzz2 小时前
利用RAII机制进行日志的采集
linux·服务器·c++·网络编程·c++20
0+1112 小时前
Linux --线程概念与控制
linux·运维·服务器·jvm
nLif2 小时前
基于GTK的简易MFC对话框兼容层 -- MfcToGTK
linux·c++·mfc·gtk
保安大队王队长3 小时前
51单片机知识
单片机·嵌入式硬件·51单片机
Navigator_Z3 小时前
LeetCode //C - 1240. Tiling a Rectangle with the Fewest Squares
c语言·算法·leetcode
fengyehongWorld3 小时前
Linux 安装frp实现内网穿透
linux·运维·服务器
一_个前端3 小时前
[TailScale]-打通你的局域网服务器到公网电脑
linux
一条破秋裤3 小时前
20_串口通信基础与USART流程
stm32·单片机
码行山野赴时序归途3 小时前
C语言预处理指令:编译前的“幕后导演“
c语言·开发语言