目录
- 一、嵌入式开发基础
-
- [1.1 51 单片机简介](#1.1 51 单片机简介)
- [1.2 开发环境搭建](#1.2 开发环境搭建)
- [1.3 C 语言与嵌入式的适配](#1.3 C 语言与嵌入式的适配)
- [二、GPIO 控制核心实现](#二、GPIO 控制核心实现)
-
- [2.1 GPIO 端口寄存器](#2.1 GPIO 端口寄存器)
- [2.2 点亮 LED 灯](#2.2 点亮 LED 灯)
-
- [2.2.1 硬件连接](#2.2.1 硬件连接)
- [2.2.2 代码实现](#2.2.2 代码实现)
- [2.2.3 实战](#2.2.3 实战)
- [2.3 按键控制 LED](#2.3 按键控制 LED)
-
- [2.3.1 硬件连接](#2.3.1 硬件连接)
- [2.3.2 代码实现](#2.3.2 代码实现)
- [2.3.3 防抖处理](#2.3.3 防抖处理)
- [三、实战项目:LED 流水灯](#三、实战项目:LED 流水灯)
-
- [3.1 需求](#3.1 需求)
- [3.2 代码实现](#3.2 代码实现)
- [3.3 烧录与测试](#3.3 烧录与测试)
一、嵌入式开发基础
1.1 51 单片机简介
51 单片机是一款经典的 8 位单片机,由美国 Intel 公司在 1980 年推出,其核心架构采用 8 位数据总线和 16 位地址总线 ,具备强大的控制能力。它的 8 位 CPU 能够高效地处理各种数据,虽然在处理速度上比不上一些现代的 32 位处理器,但对于许多简单的控制任务来说已经足够。51 单片机的内部通常包含 128 字节或 256 字节的随机存取存储器(RAM),用于存储程序运行时的临时数据和变量,比如在控制 LED 闪烁时,变量可以记录 LED 的状态。而程序存储器(ROM)的容量则从 4KB 到 64KB 不等,不同型号有所差异,它主要用于存储编写好的程序代码。
51 单片机拥有 4 个 8 位双向通用输入输出端口,分别为 P0、P1、P2 和 P3 口,每个端口都有 8 个引脚,总共 32 个引脚可用于与外部设备进行数据交互。P0 口是一个多功能端口,当作为通用 I/O 口使用时,需要外接上拉电阻,因为它的输出级是漏极开路结构;在访问外部存储器时,P0 口会复用为低 8 位地址线和数据线,实现地址和数据的分时传输。P1 口是一个准双向 I/O 口,功能相对简单,主要用于一般的输入输出操作,可直接连接外部设备,如按键、LED 等。P2 口同样是准双向 I/O 口,在访问外部存储器时,用作高 8 位地址线;平时也可作为通用 I/O 口使用,连接各种外围设备。P3 口除了具备准双向 I/O 功能外,每个引脚还具有第二功能,例如 P3.0(RXD)用于串行数据接收,P3.1(TXD)用于串行数据发送,这在实现串口通信时非常关键;P3.2(INT0)和 P3.3(INT1)分别对应外部中断 0 和外部中断 1 输入,用于处理外部的突发事件;P3.4(T0)和 P3.5(T1)用于连接定时器 / 计数器 0 和 1 的外部输入,可实现对外部脉冲信号的计数等功能;P3.6(WR)和 P3.7(RD)分别输出用于写入和读取外部存储器的脉冲,方便对外部存储器进行操作。
1.2 开发环境搭建
在进行 51 单片机开发时,我们通常使用 Keil C51 编译器来编写和编译程序。安装 Keil C51 的步骤如下:
- 首先,确保计算机满足最低配置需求,如足够的硬盘空间、内存等,并关闭所有不必要的应用程序,防止安装过程中出现冲突。然后从官方渠道或其他可信资源下载最新版 Keil C51 安装文件。
- 启动安装程序,在安装向导的欢迎界面点击 "Next" 继续。接着阅读软件许可协议条款,勾选 "我接受" 后点击 "Next"。
- 选择目标路径,软件默认会安装在 C 盘,如果想安装在其他盘,可以自行修改路径,然后点击 "Next"。这一步可以根据个人习惯和计算机磁盘空间情况进行选择,比如为了避免 C 盘过于拥挤,可以选择安装在 D 盘等空间较大的磁盘。
- 自定义组件选项时,建议保留默认设置,除非有特殊需求。这些默认设置是经过优化的,能够满足大多数开发需求。
- 注册激活产品,输入有效的序列号来注册产品。这一步至关重要,因为未授权的产品可能具有功能限制或试用期限,无法正常使用全部功能。
- 某些情况下,可能需要手动添加环境变量,以便命令行工具能够识别新安装的编译器。具体方法取决于操作系统类型,比如在 Windows 系统中,需要在系统环境变量中添加 Keil C51 的安装路径到 Path 变量中。
- 安装完成后,可以创建一个新的工程测试项目来验证安装是否成功以及基本功能能否正常使用。例如,尝试编写简单的 LED 闪烁实验代码,看是否能够正确编译和运行。
程序烧录到 51 单片机中,我们常用 STC - ISP 烧录工具。其配置要点如下:
- 用 USB 线或下载线将 51 单片机开发板与计算机连接。确保开发板的电源正常连接且处于通电状态。
- 打开 STC - ISP 烧录工具,在软件界面中,首先选择正确的单片机型号。这一步很关键,如果选择错误,可能无法正确烧录程序。比如使用的是 STC89C52 单片机,就需要在型号列表中准确选择该型号。
- 选择正确的串口号。串口号是计算机识别开发板的标识,可在计算机的设备管理器中查看。如果计算机连接了多个串口设备,要确保选择的是与开发板对应的串口号。
- 设置波特率。波特率决定了数据传输的速度,一般根据开发板的要求和实际需求进行设置,常见的波特率有 9600、115200 等。在大多数简单的 51 单片机项目中,9600 的波特率已经足够满足数据传输需求。
- 点击 "打开程序文件" 按钮,选择在 Keil C51 中编译生成的.hex 文件。这个文件包含了编译后的程序代码,是烧录到单片机中的目标文件。
- 一切设置完成后,点击 "下载 / 编程" 按钮,开始将程序烧录到 51 单片机中。在烧录过程中,要确保开发板与计算机的连接稳定,不要进行插拔等操作,以免烧录失败。烧录成功后,开发板上的单片机就会运行刚刚烧录进去的程序。
1.3 C 语言与嵌入式的适配
在 51 单片机的嵌入式开发中,通常采用无操作系统(裸机)编程方式。这种编程方式下,程序直接运行在硬件之上,没有操作系统的调度和管理,需要开发者直接控制硬件资源。这就要求开发者对硬件的工作原理和机制有深入的了解,能够精确地控制每一个硬件模块的运行。
在裸机编程中,寄存器操作是非常重要的一部分。51 单片机内部有许多特殊功能寄存器(SFR),这些寄存器用于控制单片机的各种功能和外设。通过对这些寄存器的读写操作,可以实现对硬件的控制。例如,要设置 P1 口的某个引脚为输出模式,就需要操作 P1 口对应的方向控制寄存器;要读取某个引脚的电平状态,也需要通过相应的寄存器来获取。在 C 语言中,可以通过定义特殊功能寄存器的方式来操作这些寄存器。比如:
c
sfr P1 = 0x90; // 定义P1口的特殊功能寄存器地址为0x90
这样就可以通过 P1 这个变量来操作 P1 口的寄存器了。例如,将 P1 口的第 0 位设置为高电平,可以使用以下语句:
c
P1 |= 0x01; // 将P1口的第0位置1,即设置为高电平
如果要将 P1 口的第 0 位设置为低电平,可以使用:
c
P1 &= ~0x01; // 将P1口的第0位清0,即设置为低电平
这种直接对寄存器进行操作的方式,能够让程序直接控制硬件的运行,实现高效的控制。但同时也对开发者的硬件知识和编程能力提出了较高的要求,需要仔细了解每个寄存器的功能和操作方法,避免出现错误的操作导致硬件故障或程序运行异常。
二、GPIO 控制核心实现
2.1 GPIO 端口寄存器
在 51 单片机中,P0 - P3 端口都有对应的控制寄存器,这些寄存器是控制 GPIO 引脚输入输出状态的关键。以 P1 口为例,其对应的控制寄存器就是 P1 寄存器,地址为 0x90。P1 寄存器是一个 8 位寄存器,它的每一位(bit0 - bit7)分别对应 P1 口的 8 个引脚(P1.0 - P1.7) 。当我们对 P1 寄存器进行写操作时,就可以控制 P1 口各个引脚的电平状态。比如,若要将 P1.0 引脚设置为高电平,只需要将 P1 寄存器的第 0 位置 1,即执行 P1 |= 0x01; 语句。这里的 0x01 是一个十六进制数,转换为二进制就是 00000001,通过按位或操作(|),可以将 P1 寄存器的第 0 位置 1,从而使 P1.0 引脚输出高电平。相反,若要将 P1.0 引脚设置为低电平,执行 P1 &= ~0x01; 语句,这里的 ~0x01 表示对 0x01 按位取反,得到 11111110,再通过按位与操作(&),将 P1 寄存器的第 0 位清 0,使得 P1.0 引脚输出低电平。理解这些寄存器的操作原理,是实现 GPIO 控制的基础,通过对寄存器的精准操作,我们能够灵活地控制单片机与外部设备之间的数据交互。
2.2 点亮 LED 灯
2.2.1 硬件连接
在硬件连接上,我们采用常见的方式来控制 LED 灯。将 LED 的负极连接到 51 单片机的 GPIO 引脚,比如 P1.0 引脚,而 LED 的正极则连接一个电阻后再接 VCC。这里电阻起到限流的作用,因为 LED 正常工作时需要一定的电流,如果直接将 LED 连接到电源,可能会因为电流过大而烧毁 LED。假设我们使用的是普通的 5mm 直插式 LED,其额定工作电流一般在 10 - 20mA 左右,我们可以选择一个合适阻值的电阻,如 330Ω 或 470Ω,根据欧姆定律(I = V / R),在 5V 的电源电压下,通过电阻限流后,能够使通过 LED 的电流在安全工作范围内。这样的连接方式,当 GPIO 引脚输出低电平时,LED 两端形成电压差,有电流通过 LED,从而点亮 LED;当 GPIO 引脚输出高电平时,LED 两端没有电压差,LED 熄灭。
2.2.2 代码实现
在 C 语言代码中,实现点亮和熄灭 LED 的操作非常直观。假设我们使用 P1.0 引脚来控制 LED,代码如下:
c
#include <reg52.h> // 包含51单片机的头文件,定义了特殊功能寄存器
sbit LED = P1^0; // 定义LED连接在P1.0引脚
void main() {
// 点亮LED
LED = 0; // 将P1.0引脚设置为低电平,点亮LED
// 这里可以添加其他代码,比如延时等操作
// 熄灭LED
LED = 1; // 将P1.0引脚设置为高电平,熄灭LED
// 同样可以添加其他相关代码
while(1); // 无限循环,防止程序跑飞
}
在这段代码中,首先通过 sbit 关键字定义了 LED 连接在 P1.0 引脚,然后在 main 函数中,通过对 LED 变量的赋值来控制 P1.0 引脚的电平,进而实现 LED 的点亮和熄灭。
2.2.3 实战
为了让 LED 实现闪烁效果,我们需要在点亮和熄灭 LED 之间添加延时函数,通过循环不断切换引脚电平。下面是实现 LED 闪烁的代码:
c
#include <reg52.h> // 包含51单片机的头文件
#include <intrins.h> // 包含_nop_()函数的头文件,用于简单延时
sbit LED = P1^0; // 定义LED连接在P1.0引脚
// 简单延时函数,这里的延时时间与系统时钟频率有关
void delay(unsigned int time) {
unsigned int i, j;
for(i = 0; i < time; i++) {
for(j = 0; j < 123; j++) {
_nop_(); // 执行一个空操作,产生一定的延时
}
}
}
void main() {
while(1) {
LED = 0; // 点亮LED
delay(500); // 延时一段时间,这里的500表示延时约500ms(具体时间根据系统时钟计算)
LED = 1; // 熄灭LED
delay(500); // 再延时一段时间
}
}
在这段代码中,定义了一个 delay 函数,通过双重循环和 nop() 函数来实现简单的延时。在 main 函数中,通过循环不断点亮和熄灭 LED,并在每次操作后调用 delay 函数进行延时,从而实现 LED 的闪烁效果。用户可以根据实际需求调整 delay 函数中的参数,来改变 LED 的闪烁频率。
2.3 按键控制 LED
2.3.1 硬件连接
在硬件连接上,按键一端连接到 51 单片机的 GPIO 引脚,比如 P3.2 引脚,另一端接地。同时,为了确保在按键未按下时,引脚处于高电平状态,我们需要将引脚通过一个上拉电阻连接到 VCC。通常上拉电阻的阻值可以选择 10kΩ 左右,这样在按键未按下时,由于上拉电阻的作用,P3.2 引脚被拉高到 VCC 电平,即高电平;当按键按下时,P3.2 引脚与地连通,电平被拉低,变为低电平。通过检测 P3.2 引脚的电平变化,我们就可以判断按键是否被按下,从而实现按键控制 LED 的功能。
2.3.2 代码实现
下面是检测按键引脚电平,按下时切换 LED 状态的代码:
c
#include <reg52.h> // 包含51单片机的头文件
sbit LED = P1^0; // 定义LED连接在P1.0引脚
sbit KEY = P3^2; // 定义按键连接在P3.2引脚
void main() {
while(1) {
if(KEY == 0) { // 检测按键是否按下,按键按下时P3.2引脚为低电平
// 按键按下,切换LED状态
if(LED == 0) {
LED = 1; // LED原来为亮,熄灭LED
} else {
LED = 0; // LED原来为灭,点亮LED
}
while(KEY == 0); // 等待按键松开,防止一次按下多次触发
}
}
}
在这段代码中,通过 sbit 分别定义了 LED 和按键连接的引脚。在 main 函数的循环中,通过 if(KEY == 0) 来检测按键是否按下,当按键按下时,通过判断 LED 的当前状态,来切换 LED 的状态。同时,使用 while(KEY == 0); 语句来等待按键松开,避免一次按下产生多次触发的情况。
2.3.3 防抖处理
由于机械按键在按下和松开的瞬间会产生抖动,可能会导致误检测。为了解决这个问题,我们可以采用软件防抖的方法。即在检测到按键按下后,延时 10ms 左右,再检测按键的电平状态,如果仍然为低电平,则认为按键确实被按下。以下是添加软件防抖的代码:
c
#include <reg52.h> // 包含51单片机的头文件
#include <intrins.h> // 包含_nop_()函数的头文件,用于简单延时
sbit LED = P1^0; // 定义LED连接在P1.0引脚
sbit KEY = P3^2; // 定义按键连接在P3.2引脚
// 简单延时函数,这里的延时时间与系统时钟频率有关
void delay(unsigned int time) {
unsigned int i, j;
for(i = 0; i < time; i++) {
for(j = 0; j < 123; j++) {
_nop_(); // 执行一个空操作,产生一定的延时
}
}
}
void main() {
while(1) {
if(KEY == 0) { // 检测按键是否按下,按键按下时P3.2引脚为低电平
delay(10); // 延时10ms,进行软件防抖
if(KEY == 0) { // 再次检测按键电平,如果仍然为低电平,说明按键确实按下
// 按键按下,切换LED状态
if(LED == 0) {
LED = 1; // LED原来为亮,熄灭LED
} else {
LED = 0; // LED原来为灭,点亮LED
}
while(KEY == 0); // 等待按键松开,防止一次按下多次触发
}
}
}
}
在这段代码中,在检测到按键按下后,先调用 delay(10) 函数延时 10ms,然后再次检测按键电平,只有当再次检测到按键仍然为低电平时,才认为按键真正被按下,从而切换 LED 状态,有效地避免了按键抖动带来的误检测问题。
三、实战项目:LED 流水灯
3.1 需求
在这个实战项目中,我们要实现一个有趣的 LED 流水灯效果。具体需求是使用 8 个 LED 灯,让它们依次点亮,并且形成循环流动的视觉效果。例如,首先点亮 P1.0 引脚连接的 LED,接着点亮 P1.1 引脚连接的 LED,以此类推,直到 P1.7 引脚连接的 LED 点亮,然后再从 P1.0 引脚连接的 LED 重新开始循环。这种流水灯效果在许多场合都有应用,比如装饰彩灯、状态指示等。通过实现这个项目,我们可以进一步加深对 51 单片机 GPIO 控制的理解和应用能力。
3.2 代码实现
为了实现 LED 流水灯效果,我们可以使用 C 语言中的循环移位函数_crol_来控制 P1 口的输出。_crol_函数是 C51 编译器提供的内部函数,包含在intrins.h头文件中,它能够实现无符号字符型数据的循环左移操作 。下面是具体的代码实现:
c
#include <reg52.h> // 包含51单片机的头文件
#include <intrins.h> // 包含循环移位函数_crol_的头文件
void delay(unsigned int time); // 延时函数声明
void main() {
unsigned char led = 0xfe; // 初始值,11111110,点亮第一个LED
while(1) {
P1 = led; // 将led的值赋给P1口,点亮相应的LED
delay(500); // 延时,控制流水灯流动速度,这里延时约500ms
led = _crol_(led, 1); // 循环左移一位,准备点亮下一个LED
}
}
// 简单延时函数,这里的延时时间与系统时钟频率有关
void delay(unsigned int time) {
unsigned int i, j;
for(i = 0; i < time; i++) {
for(j = 0; j < 123; j++) {
_nop_(); // 执行一个空操作,产生一定的延时
}
}
}
在这段代码中,首先定义了一个led变量,初始值为0xfe,即二进制的11111110,这样 P1.0 引脚输出低电平,第一个 LED 点亮。然后在while(1)无限循环中,将led的值赋给 P1 口,点亮对应的 LED。接着调用delay函数进行延时,延时时间通过time参数控制,这里设置为500,可以根据实际需求调整延时时间来改变流水灯的流动速度。之后,使用_crol_(led, 1)函数将led的值循环左移一位,例如11111110左移一位后变为11111101,这样下一次循环时,P1.1 引脚输出低电平,第二个 LED 点亮,从而实现了 LED 依次点亮的流水灯效果。
3.3 烧录与测试
完成代码编写后,我们需要将程序烧录到 51 单片机中进行测试。烧录步骤如下:
- 确保已经正确安装了 Keil C51 编译器和 STC - ISP 烧录工具,并且 51 单片机开发板已通过 USB 线或下载线与计算机连接,开发板电源正常连接且通电。
- 在 Keil C51 中对编写好的程序进行编译,检查是否存在语法错误等问题。如果编译成功,会生成一个.hex 文件,这个文件包含了可烧录到单片机中的程序代码。
- 打开 STC - ISP 烧录工具,在软件界面中进行如下设置:选择正确的单片机型号,确保与实际使用的 51 单片机型号一致;选择正确的串口号,可在计算机的设备管理器中查看与开发板连接的串口号;设置波特率,一般可采用默认的 9600 波特率。
- 点击 "打开程序文件" 按钮,在弹出的文件选择窗口中找到刚才在 Keil C51 中生成的.hex 文件,选中并打开。
- 一切设置完成后,点击 "下载 / 编程" 按钮,开始将程序烧录到 51 单片机中。在烧录过程中,要确保开发板与计算机的连接稳定,不要进行插拔等操作,以免烧录失败。
- 烧录成功后,观察 51 单片机开发板上的 8 个 LED 灯。可以看到它们按照程序设定的方式依次点亮,循环流动,实现了预期的 LED 流水灯效果。如果发现 LED 灯没有按照预期点亮,或者出现其他异常情况,可以检查硬件连接是否正确,程序代码是否存在逻辑错误,以及烧录过程是否成功等。通过这样的烧录与测试过程,我们能够将理论上的程序转化为实际的硬件运行效果,验证我们的设计是否正确,也能在实践中不断积累经验,提高嵌入式开发的能力。