别怕!51 单片机从入门到应用,小白也能轻松吃透

这篇教程专为单片机小白打造,从 51 单片机基础讲起,带你搭建开发环境,学习汇编与 C 语言编程。通过 LED 灯闪烁、按键控制、数码管显示等基础实验,以及简易电子钟、温度检测系统等应用案例,结合代码实操,助你快速掌握 51 单片机开发。

目录

一、本篇背景

二、51 单片机基础知识

2.1 什么是 51 单片机

2.2 51 单片机的硬件结构

2.3 51 单片机的引脚功能

三、开发环境搭建

3.1 硬件准备

3.2 软件准备

3.3 开发环境搭建步骤

四、编程语言

4.1 汇编语言

4.2 C 语言

五、基本实验

5.1 LED 灯闪烁实验

5.2 按键控制 LED 灯实验

5.3 数码管显示实验

六、常见应用案例

6.1 简易电子钟

6.2 温度检测系统

七、小结下:


一、本篇背景

在电子技术的广阔领域中,单片机作为一种高度集成化的计算机芯片,以其体积小、成本低、功能强等特点,在工业控制、智能家居、汽车电子等众多领域得到了广泛的应用。51 单片机作为单片机家族中的经典代表,具有结构简单、易于学习等优点,成为了众多电子爱好者和初学者入门单片机编程的首选。本文将以小白的视角,详细介绍 51 单片机的基础知识、开发环境搭建、编程语言、基本实验以及一些常见的应用案例,帮助初学者逐步掌握 51 单片机的开发技术。

二、51 单片机基础知识

2.1 什么是 51 单片机

51 单片机是指以 Intel 8051 内核为基础发展起来的一系列单片机的统称。它最早由 Intel 公司在 1980 年推出,具有 8 位 CPU、4KB 程序存储器(ROM)、128B 数据存储器(RAM)、两个 16 位定时 / 计数器、64KB 外部数据存储器和程序存储器寻址空间等基本资源。后来,许多半导体公司如 Atmel、STC 等在 8051 内核的基础上进行了改进和扩展,推出了各具特色的 51 单片机产品,这些产品在功能和性能上有所提升,但基本的指令系统和架构仍然与 8051 兼容。

2.2 51 单片机的硬件结构

51 单片机的硬件结构主要由 CPU、存储器、I/O 接口、定时 / 计数器、中断系统等部分组成。

  • CPU(中央处理器):是单片机的核心,负责执行程序指令,完成数据的运算和处理。
  • 存储器:包括程序存储器(ROM)和数据存储器(RAM)。程序存储器用于存储用户编写的程序代码,数据存储器用于存储程序运行过程中的临时数据。
  • I/O 接口:是单片机与外部设备进行数据交换的通道,51 单片机通常有 4 个 8 位的并行 I/O 口(P0、P1、P2、P3),可以实现数据的输入和输出。
  • 定时 / 计数器:用于实现定时和计数功能,可用于产生精确的定时信号、测量脉冲信号的频率等。
  • 中断系统:允许单片机在执行主程序的过程中,响应外部或内部的中断请求,暂停主程序的执行,转去执行相应的中断服务程序,处理完中断后再返回主程序继续执行。

2.3 51 单片机的引脚功能

51 单片机通常采用双列直插式(DIP)封装,具有 40 个引脚。不同的引脚具有不同的功能,下面简要介绍一些主要引脚的功能:

  • 电源引脚:VCC(+5V)和 GND(地),为单片机提供电源。
  • 时钟引脚:XTAL1 和 XTAL2,用于连接外部晶体振荡器,为单片机提供时钟信号。
  • 复位引脚:RST,当该引脚出现高电平并保持一定时间时,单片机将进行复位操作,恢复到初始状态。
  • 并行 I/O 口引脚:P0、P1、P2、P3 口的引脚,用于实现数据的输入和输出。
  • 控制引脚:如 ALE/PROG、PSEN、EA/VPP 等,用于控制单片机的工作模式和数据传输。

三、开发环境搭建

3.1 硬件准备

  • 51 单片机开发板:市面上有多种类型的 51 单片机开发板可供选择,初学者可以选择一款功能较为齐全、价格适中的开发板,如普中科技的 51 单片机开发板。
  • USB 转串口线:用于将开发板与计算机连接,实现程序的下载和调试。
  • 电源适配器:为开发板提供电源。

3.2 软件准备

  • Keil C51:是一款专门用于 51 单片机编程的集成开发环境(IDE),支持 C 语言和汇编语言编程,具有代码编辑、编译、调试等功能。
  • STC-ISP 下载软件:用于将 Keil C51 编译生成的程序代码下载到 STC 系列 51 单片机中。

3.3 开发环境搭建步骤

  1. 安装 Keil C51:下载 Keil C51 安装程序,按照安装向导的提示进行安装。安装完成后,打开 Keil C51,创建一个新的工程,选择合适的单片机型号。
  2. 安装 STC-ISP 下载软件:下载 STC-ISP 下载软件,解压后运行安装程序。安装完成后,打开 STC-ISP 下载软件,设置好单片机型号、串口波特率等参数。
  3. 连接开发板:使用 USB 转串口线将开发板与计算机连接,确保开发板的电源开关处于打开状态。
  4. 编写并编译程序:在 Keil C51 中编写 51 单片机程序代码,点击编译按钮进行编译。如果代码没有错误,编译器将生成一个十六进制(.hex)文件。
  5. 下载程序:打开 STC-ISP 下载软件,选择刚才生成的.hex 文件,点击下载按钮,将程序下载到单片机中。

四、编程语言

4.1 汇编语言

汇编语言是一种面向机器的低级语言,它使用助记符来表示机器指令,与单片机的硬件结构密切相关。汇编语言的优点是执行效率高、代码简洁,适合对程序执行时间和内存空间要求较高的场合。下面是一个简单的 51 单片机汇编语言程序示例,实现 LED 灯的闪烁:

ORG 0000H ; 程序起始地址
LJMP MAIN ; 跳转到主程序
ORG 0030H ; 主程序起始地址
MAIN:
    SETB P1.0 ; 将 P1.0 引脚置高电平,点亮 LED 灯
    LCALL DELAY ; 调用延时子程序
    CLR P1.0 ; 将 P1.0 引脚置低电平,熄灭 LED 灯
    LCALL DELAY ; 调用延时子程序
    SJMP MAIN ; 跳转到主程序开头,循环执行
DELAY: ; 延时子程序
    MOV R7, #200 ; 设置外层循环次数
D1: MOV R6, #250 ; 设置内层循环次数
D2: DJNZ R6, D2 ; 内层循环,R6 减 1,不为 0 则继续循环
    DJNZ R7, D1 ; 外层循环,R7 减 1,不为 0 则继续循环
    RET ; 子程序返回
END ; 程序结束

4.2 C 语言

C 语言是一种高级编程语言,具有结构化、模块化、可读性强等优点,易于学习和掌握。在 51 单片机开发中,使用 C 语言可以大大提高开发效率,减少开发周期。下面是一个使用 C 语言实现 LED 灯闪烁的程序示例:

#include <reg51.h> // 包含 51 单片机的头文件
#define uchar unsigned char // 定义无符号字符型变量
#define uint unsigned int // 定义无符号整型变量
sbit LED = P1^0; // 定义 P1.0 引脚为 LED 控制引脚
// 延时函数
void delay(uint time) {
    uint i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 123; j++);
}
void main() {
    while(1) {
        LED = 1; // 点亮 LED 灯
        delay(500); // 延时 500ms
        LED = 0; // 熄灭 LED 灯
        delay(500); // 延时 500ms
    }
}

五、基本实验

5.1 LED 灯闪烁实验

LED 灯闪烁实验是 51 单片机开发中最基础的实验之一,通过该实验可以熟悉 51 单片机的 I/O 口操作和延时函数的使用。实验步骤如下:

  1. 硬件连接:将开发板上的 LED 灯连接到 P1.0 引脚。
  2. 编写程序:使用 C 语言编写 LED 灯闪烁的程序代码,如上述示例所示。
  3. 编译并下载程序:在 Keil C51 中编译程序,生成.hex 文件,然后使用 STC-ISP 下载软件将程序下载到单片机中。
  4. 观察实验现象:下载完成后,开发板上的 LED 灯将开始闪烁。

5.2 按键控制 LED 灯实验

按键控制 LED 灯实验可以让我们了解 51 单片机的输入输出控制原理。实验步骤如下:

  1. 硬件连接:将开发板上的按键连接到 P3.2 引脚,LED 灯连接到 P1.0 引脚。
  2. 编写程序:使用 C 语言编写按键控制 LED 灯的程序代码,示例代码如下:
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit KEY = P3^2; // 定义按键连接的引脚
sbit LED = P1^0; // 定义 LED 连接的引脚
void main() {
    while(1) {
        if(KEY == 0) { // 检测按键是否按下
            while(KEY == 0); // 等待按键释放
            LED = ~LED; // 取反 LED 灯的状态
        }
    }
}
  1. 编译并下载程序:同 LED 灯闪烁实验。
  2. 观察实验现象:按下按键,LED 灯的状态将发生改变。

5.3 数码管显示实验

数码管显示实验可以让我们学习 51 单片机的数码管驱动原理和段码编码方法。实验步骤如下:

  1. 硬件连接:将开发板上的数码管连接到相应的 I/O 口。
  2. 编写程序:使用 C 语言编写数码管显示的程序代码,示例代码如下:
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
uchar code SEG_CODE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; // 数码管段码表
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
void main() {
    uchar i;
    while(1) {
        for(i = 0; i < 10; i++) {
            LSA = 0; LSB = 0; LSC = 0; // 选择第一个数码管
            P0 = SEG_CODE[i]; // 显示数字 i
            for(uint j = 0; j < 50000; j++); // 延时
        }
    }
}
  1. 编译并下载程序:同前两个实验。
  2. 观察实验现象:数码管将依次显示 0 - 9 的数字。

六、常见应用案例

6.1 简易电子钟

简易电子钟可以实现时间的显示和调整功能,是一个比较综合的 51 单片机应用案例。实现步骤如下:

  1. 硬件连接:使用数码管显示时间,按键用于时间调整,同时可以使用定时器来实现精确的计时。
  2. 编写程序:使用 C 语言编写电子钟的程序代码,包括时间计时、数码管显示、按键扫描等功能。示例代码如下:
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
uchar code SEG_CODE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; // 数码管段码表
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
sbit KEY1 = P3^2; // 调整小时按键
sbit KEY2 = P3^3; // 调整分钟按键
uchar hour = 0, minute = 0, second = 0; // 初始化时间
// 延时函数
void delay(uint time) {
    uint i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 123; j++);
}
// 数码管显示函数
void display_time() {
    uchar h1, h2, m1, m2;
    h1 = hour / 10;
    h2 = hour % 10;
    m1 = minute / 10;
    m2 = minute % 10;
    LSA = 0; LSB = 0; LSC = 0; P0 = SEG_CODE[h1]; delay(5); // 显示小时十位
    LSA = 1; LSB = 0; LSC = 0; P0 = SEG_CODE[h2]; delay(5); // 显示小时个位
    LSA = 0; LSB = 1; LSC = 0; P0 = 0x40; delay(5); // 显示冒号
    LSA = 1; LSB = 1; LSC = 0; P0 = SEG_CODE[m1]; delay(5); // 显示分钟十位
    LSA = 0; LSB = 0; LSC = 1; P0 = SEG_CODE[m2]; delay(5); // 显示分钟个位
}
// 定时器初始化函数
void timer_init() {
    TMOD = 0x01; // 设置定时器 0 为模式 1
    TH0 = (65536 - 50000) / 256; // 定时器初值
    TL0 = (65536 - 50000) % 256;
    ET0 = 1; // 使能定时器 0 中断
    EA = 1; // 全局中断使能
    TR0 = 1; // 启动定时器 0
}
// 定时器中断服务函数
void timer0_isr() interrupt 1 {
    TH0 = (65536 - 50000) / 256; // 重新加载初值
    TL0 = (65536 - 50000) % 256;
    static uint count = 0;
    count++;
    if(count >= 20) { // 每 1 秒
        count = 0;
        second++;
        if(second >= 60) {
            second = 0;
            minute++;
            if(minute >= 60) {
                minute = 0;
                hour++;
                if(hour >= 24) {
                    hour = 0;
                }
            }
        }
    }
}
// 按键扫描函数
void key_scan() {
    if(KEY1 == 0) {
        delay(20); // 消抖
        if(KEY1 == 0) {
            while(KEY1 == 0);
            hour = (hour + 1) % 24;
        }
    }
    if(KEY2 == 0) {
        delay(20); // 消抖
        if(KEY2 == 0) {
            while(KEY2 == 0);
            minute = (minute + 1) % 60;
        }
    }
}
void main() {
    timer_init();
    while(1) {
        display_time();
        key_scan();
    }
}
  1. 编译并下载程序:同前面的实验。
  2. 观察实验现象:数码管将显示当前的时间,按下按键可以调整时间。

6.2 温度检测系统

温度检测系统可以实时检测环境温度,并将温度值显示在数码管上。实现步骤如下:

  1. 硬件连接:使用温度传感器(如 DS18B20)采集温度数据,数码管显示温度值。
  2. 编写程序:使用 C 语言编写温度检测系统的程序代码,包括温度传感器驱动、数码管显示等功能。示例代码如下:
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit DQ = P3^7; // DS18B20 数据引脚
uchar code SEG_CODE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F}; // 数码管段码表
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
// 延时函数
void delay(uint time) {
    uint i, j;
    for(i = 0; i < time; i++)
        for(j = 0; j < 123; j++);
}
// DS18B20 初始化函数
bit ds18b20_init() {
    bit presence;
    DQ = 1;
    _nop_();
    DQ = 0;
    delay(500); // 延时 480 - 960us
    DQ = 1;
    delay(60); // 延时 60 - 240us
    presence = DQ;
    delay(440); // 延时 480 - 960us
    return presence;
}
// DS18B20 写一个字节函数
void ds18b20_write_byte(uchar dat) {
    uchar i;
    for(i = 0; i < 8; i++) {
        DQ = 0;
        _nop_();
        DQ = dat & 0x01;
        delay(60); // 延时 60 - 120us
        DQ = 1;
        dat >>= 1;
    }
}
// DS18B20 读一个字节函数
uchar ds18b20_read_byte() {
    uchar i, dat = 0;
    for(i = 0; i < 8; i++) {
        DQ = 0;
        _nop_();
        dat >>= 1;
        DQ = 1;
        if(DQ)
            dat |= 0x80;
        delay(60); // 延时 60 - 120us
    }
    return dat;
}
// 读取 DS18B20 温度函数
int ds18b20_read_temp() {
    uchar LSB, MSB;
    int temp;
    ds18b20_init();
    ds18b20_write_byte(0xCC); // 跳过 ROM 操作
    ds18b20_write_byte(0x44); // 启动温度转换
    delay(750); // 等待转换完成
    ds18b20_init();
    ds18b20_write_byte(0xCC); // 跳过 ROM 操作
    ds18b20_write_byte(0xBE); // 读取温度寄存器
    LSB = ds18b20_read_byte();
    MSB = ds18b20_read_byte();
    temp = (MSB << 8) | LSB;
    temp = temp * 0.0625 * 10 + 0.5; // 转换为实际温度值
    return temp;
}
// 数码管显示函数
void display_temp(int temp) {
    uchar t1, t2, t3;
    t1 = temp / 100;
    t2 = (temp % 100) / 10;
    t3 = temp % 10;
    LSA = 0; LSB = 0; LSC = 0; P0 = SEG_CODE[t1]; delay(5); // 显示百位
    LSA = 1; LSB = 0; LSC = 0; P0 = SEG_CODE[t2] | 0x80; delay(5); // 显示十位,带小数点
    LSA = 0; LSB = 1; LSC = 0; P0 = SEG_CODE[t3]; delay(5); // 显示个位
}
void main() {
    int temp;
    while(1) {
        temp = ds18b20_read_temp();
        display_temp(temp);
    }
}
  1. 编译并下载程序:同前面的实验。
  2. 观察实验现象:数码管将显示当前的环境温度值。

七、小结下:

通过本文的学习,我们从 51 单片机的基础知识入手,逐步了解了开发环境的搭建、编程语言的使用、基本实验的实现以及常见应用案例的开发。对于初学者来说,掌握 51 单片机的开发技术需要不断地实践和积累,通过完成更多的实验和项目,加深对单片机原理和编程方法的理解。希望本文能够帮助广大电子爱好者和初学者快速入门 51 单片机开发,开启电子技术的探索之旅。