单片机学习笔记(一)——STC89C52的GPIO、LED、数码管、定时器和中断

一、STC89C52单片机

STC89C52是一款常见的51系列单片机。

使用Keil进行开发时,通常通过:

复制代码
#include <reg52.h>

来使用单片机内部的寄存器和IO端口。

单片机程序和Linux C程序最大的区别之一是:

Linux程序主要操作操作系统提供的资源,而单片机程序主要直接控制硬件。


二、GPIO

1. 什么是GPIO

GPIO就是通用输入输出接口(General Purpose Input/Output)。

STC89C52主要有4组IO口:

复制代码
P0
P1
P2
P3

每组有8个引脚:

复制代码
P0.0 ~ P0.7
P1.0 ~ P1.7
P2.0 ~ P2.7
P3.0 ~ P3.7

因此可以通过操作这些IO口控制LED、数码管、按键、蜂鸣器等外围设备。


2. 使用寄存器控制IO

例如:

复制代码
P2 = 0xFF;

表示给P2口写入:

复制代码
1111 1111

也可以单独控制某一位:

复制代码
P2^0 = 0;

表示控制P2.0。


三、LED控制

1. LED的基本原理

LED本质上是发光二极管。

单片机通过GPIO输出高低电平控制LED。

在目前使用的开发板中,LED通常采用低电平有效

复制代码
P2.x = 0  → LED亮
P2.x = 1  → LED灭

因此不能简单地认为:

复制代码
输出1 = LED亮

具体还要看开发板的硬件连接方式。


2. LED初始化

为了让LED上电后默认关闭,可以:

复制代码
void led_init(void)
{
    P2 = 0xFF;
}

因为当前实验板是低电平点亮,所以:

复制代码
P2 = 0xFF
      ↓
1111 1111
      ↓
所有LED关闭

3. 控制LED亮灭

可以封装成函数:

复制代码
void led_on(unsigned char on)
{
    P2 = ~on;
}

例如:

复制代码
led_on(0x01);

表示点亮第1个LED。

如果:

复制代码
led_on(0x05);

二进制为:

复制代码
0000 0101

取反后输出:

复制代码
1111 1010

对应相应的LED亮起。


四、LED流水灯

LED最常见的练习就是流水灯。

例如:

复制代码
LED1 → LED2 → LED3 → LED4
→ LED5 → LED6 → LED7 → LED8

程序的核心就是不断改变P2的输出值。

例如:

复制代码
P2 = 0xFE;
P2 = 0xFD;
P2 = 0xFB;
P2 = 0xF7;

由于LED采用低电平有效,因此可以实现依次点亮。


1. 延时函数

LED流水灯需要加入延时,否则变化速度太快,人眼无法观察。

例如:

复制代码
void delay(unsigned int t)
{
    while(t--);
}

使用:

复制代码
delay(0x3FFF);

让程序暂停一段时间。


五、数码管

1. 什么是数码管

数码管是一种用于显示数字的器件。

常见的数码管由:

复制代码
a
b
c
d
e
f
g
dp

组成。

其中:

  • a~g:组成数字

  • dp:小数点

例如显示数字:

复制代码
   a
  ---
f|   |b
  -g-
e|   |c
  ---
   d

六、数码管段选

显示不同数字,本质上就是控制a~g这些段的亮灭。

例如常见的段码表:

复制代码
unsigned char code seg_table[] =
{
    0x3F,   // 0
    0x06,   // 1
    0x5B,   // 2
    0x4F,   // 3
    0x66,   // 4
    0x6D,   // 5
    0x7D,   // 6
    0x07,   // 7
    0x7F,   // 8
    0x6F    // 9
};

例如:

复制代码
P0 = 0x06;

就可以让数码管显示:

复制代码
1

七、数码管的位选和段选

如果开发板上有多个数码管,就需要解决两个问题:

段选

决定:

当前显示什么数字。

例如:

复制代码
seg_select(1);

可以理解为选择数字1对应的段码。

位选

决定:

哪一个数码管显示。

例如:

复制代码
bit_select(1);

表示选择第1位。

因此可以记成:

复制代码
位选 → 选择第几个数码管

段选 → 选择显示什么数字

八、数码管动态显示

多个数码管通常不是同时真正点亮,而是采用动态扫描

例如有4位数码管,要显示:

复制代码
1 2 3 4

程序快速执行:

复制代码
选择第1位 → 显示1
      ↓
选择第2位 → 显示2
      ↓
选择第3位 → 显示3
      ↓
选择第4位 → 显示4
      ↓
重新开始

扫描速度足够快时,人眼看到的就是:

复制代码
1234

1. 动态扫描的核心

可以封装:

复制代码
void display_num(unsigned int num)
{
    // 依次进行位选和段选
}

例如:

复制代码
display_num(1234);

就可以让数码管显示:

复制代码
1234

九、数码管模块化编程

之前练习中使用了:

复制代码
digitaltube.h
digitaltube.c
delay.h
delay.c
main.c

这种方式比较适合单片机项目。

例如:

digitaltube.h

复制代码
#ifndef __DIGITALTUBE_H__
#define __DIGITALTUBE_H__

void digitaltube_init(void);
void bit_select(unsigned char b);
void seg_select(unsigned char s);
void display_num(unsigned int num);

#endif

主函数中:

复制代码
#include <reg52.h>
#include "digitaltube.h"
#include "delay.h"

void main(void)
{
    unsigned int num = 0;

    digitaltube_init();

    while(1)
    {
        display_num(num++);
        delay(0x3FFF);
    }
}

这样主函数只负责:

调用功能。

具体的数码管控制代码放到对应的 .c 文件中。


十、定时器

1. 为什么需要定时器

前面的延时函数:

复制代码
while(t--);

属于软件延时。

这种方法简单,但是CPU一直在执行循环。

定时器则可以利用单片机内部的硬件计时功能。

常见用途:

  • 定时

  • 延时

  • LED闪烁

  • 数码管扫描

  • 产生周期信号

  • 串口波特率产生


十一、STC89C52定时器

STC89C52中常用:

复制代码
Timer0
Timer1

也就是:

复制代码
定时器0
定时器1

定时器工作时会不断计数。

当计数达到溢出条件后,可以产生中断。

基本过程:

复制代码
设置定时器
    ↓
设置初值
    ↓
启动定时器
    ↓
不断计数
    ↓
溢出
    ↓
产生中断
    ↓
执行中断函数

十二、定时器相关寄存器

学习定时器时经常遇到:

复制代码
TMOD
TH0
TL0
TH1
TL1
TR0
TR1
TF0
TF1

其中:

TMOD

用于设置定时器的工作模式。

TH0、TL0

定时器0的高8位和低8位。

TH1、TL1

定时器1的高8位和低8位。

TR0

控制定时器0启动。

复制代码
TR0 = 1;

表示启动定时器0。

TR1

控制定时器1启动。

TF0

定时器0溢出标志。

TF1

定时器1溢出标志。


十三、16位定时器

定时器0可以工作在16位模式。

此时:

复制代码
TH0 + TL0

共同组成16位计数器。

可以理解为:

复制代码
TH0          TL0
高8位        低8位
  ↓            ↓
┌────────┬────────┐
│ 8 bit  │ 8 bit  │
└────────┴────────┘
       16 bit

例如之前练习中使用:

复制代码
TH0 = 0x4C;
TL0 = 0x00;

设置定时器初值。


十四、定时器中断

如果只使用定时器而不使用中断,可以一直查询:

复制代码
while(TF0 == 0);

但是使用中断以后,程序可以:

复制代码
主程序
  ↓
正常运行
  ↓
定时器溢出
  ↓
CPU暂停当前程序
  ↓
执行定时器中断函数
  ↓
中断结束
  ↓
返回主程序继续运行

这就是中断机制


十五、什么是中断

中断可以理解为:

CPU正在执行当前程序时,突然有一个事件需要处理,于是CPU暂时停止当前程序,去处理这个事件,处理完成后再回来继续执行。

例如:

复制代码
main()
 ↓
LED正常运行
 ↓
按键按下
 ↓
产生中断
 ↓
执行中断函数
 ↓
返回main()

十六、外部中断

STC89C52常见的外部中断包括:

复制代码
INT0
INT1

其中:

复制代码
INT0 → P3.2
INT1 → P3.3

例如使用INT0:

复制代码
按键
 ↓
INT0
 ↓
CPU产生中断
 ↓
执行中断函数

十七、中断相关寄存器

学习中断时需要重点掌握:

复制代码
IE
TCON

IE------中断允许寄存器

IE用于控制中断是否开启。

其中:

复制代码
EA

是总中断允许位。

可以理解为:

复制代码
EA = 0 → 关闭总中断
EA = 1 → 开启总中断

所以使用中断时通常需要:

复制代码
EA = 1;

十八、外部中断初始化

例如使用INT0,可以进行相应初始化:

复制代码
void eint0_init(void)
{
    EA = 1;       // 开启总中断
    EX0 = 1;      // 开启外部中断0
    IT0 = 1;      // 设置INT0触发方式
}

之后等待外部中断发生。


十九、中断服务函数

当INT0产生中断后,需要执行中断服务函数。

Keil C51中可以使用:

复制代码
void eint0_isr(void) interrupt 0
{
    // 中断处理代码
}

这里:

复制代码
interrupt 0

表示这个函数对应外部中断0。

例如:

复制代码
unsigned int count = 0;

void eint0_isr(void) interrupt 0
{
    count++;
}

每产生一次INT0中断:

复制代码
count = count + 1

二十、数码管显示中断计数

可以把前面学习的内容结合起来。

例如:

复制代码
按键
 ↓
INT0中断
 ↓
count++
 ↓
数码管显示count

主程序:

复制代码
void main(void)
{
    unsigned int count = 0;

    digitaltube_init();
    eint0_init();

    while(1)
    {
        display_num(count);
    }
}

中断函数:

复制代码
void eint0_isr(void) interrupt 0
{
    count++;
}

这样每按一次按键:

复制代码
0
 ↓
1
 ↓
2
 ↓
3
 ↓
4
...

数码管上的数字也会随之变化。


二十一、定时器与中断的区别

这两个概念很容易混淆。

定时器

负责:

计时、计数。

中断

负责:

通知CPU立即处理某个事件。

两者可以结合:

复制代码
定时器
 ↓
计数
 ↓
溢出
 ↓
产生中断
 ↓
执行中断函数

所以:

定时器不等于中断,但是定时器可以通过溢出产生中断。


二十二、GPIO、LED、数码管、定时器、中断之间的关系

把本篇内容串起来:

复制代码
                 STC89C52
                    │
        ┌───────────┼───────────┐
        ↓           ↓           ↓
       GPIO       定时器       外部中断
        │           │           │
     ┌──┴──┐        │         按键
     ↓     ↓        ↓           │
    LED   数码管   定时溢出      ↓
     │     │        │        中断函数
     │     │        ↓           │
     └─────┴──────→ 显示 ←──────┘

通过这些模块,就可以完成比较完整的单片机控制程序。


二十三、本篇重点总结

1. GPIO

复制代码
P0
P1
P2
P3

用于控制外部硬件。


2. LED

重点理解:

复制代码
低电平有效
0 → 亮
1 → 灭

通过P2可以控制LED。


3. 数码管

记住两个概念:

复制代码
位选 → 选择第几位

段选 → 显示什么数字

多个数码管一般采用动态扫描。


4. 定时器

重点:

复制代码
Timer0
Timer1
TMOD
TH0
TL0
TH1
TL1
TR0
TR1

作用:

实现硬件计时。


5. 中断

重点:

复制代码
IE
TCON
EA
EX0
IT0

基本流程:

复制代码
开启中断
   ↓
等待事件
   ↓
产生中断
   ↓
执行中断服务函数
   ↓
返回主程序

GPIO负责控制硬件,LED和数码管是GPIO的具体应用;定时器负责计时,中断负责及时响应事件。

相关推荐
开发笔记-阿牛2 小时前
做工业报警器语音提示,CK6159A 为什么更合适?
人工智能·stm32·单片机·嵌入式硬件·音频
恶魔泡泡糖3 小时前
stm32F103C8T6标准库串口接收轮询方式1
stm32·单片机·嵌入式硬件
艾芯微科技4 小时前
B16WS SOD-323 肖特基二极管参数、应用电路与选型踩坑指南
网络·单片机·嵌入式硬件·集成测试·51单片机
天空'之城4 小时前
单片机基础核心知识点汇总(三十七)
单片机·嵌入式硬件
Groundwork Explorer4 小时前
ESP32-C3 SuperMini 排查WIFI收发故障
python·单片机·嵌入式硬件·mcu
wixzjsh4 小时前
基于STC89C52的温度采集与监控系统设计与实现
单片机
H_oRIZoN_5 小时前
Linux入门DAY42【51 单片机】DS18B20 数字温度传感器完整解析(单总线原理 + 时序 + 读取流程)
linux·单片机·linux应用编程
智购无人售货机厂家5 小时前
2026自动售货机品类拓展逻辑:从饮料零食到鲜食药品文创的工程实践~YH
服务器·人工智能·单片机·线性代数·矩阵
ElfBoard6 小时前
作品展示|基于RK3588的复杂空间下自主导航无人机与多传感器 AI 融合环境监测分析
大数据·人工智能·单片机·嵌入式硬件·团队开发