上一节博客给大家基本介绍了51单片机
51单片机从入门到精通:理论与实践指南入门篇(二)
https://blog.csdn.net/speaking_me/article/details/144068159?spm=1001.2014.3001.5501
那么这节课就来介绍——花样流水灯的实现
花样流水灯电路设计
1. 所需材料
- 51系列单片机(如AT89S51)
- LED灯若干(根据需要的数量选择)
- 限流电阻若干(每个LED灯都需要一个)
- 开关电源或电池
- 连接线
- 实验板或PCB板
2. 电路连接
- 将每个LED灯的一端通过一个限流电阻连接到51单片机的一个I/O口(例如P1.0到P1.7),另一端连接到地(GND)。这样,当对应的I/O口输出高电平时,LED灯就会亮起;当输出低电平时,LED灯熄灭。
- 确保电源电压适合51单片机的工作电压(通常是5V)。
C51语言知识精讲
C51语言是专门为51系列单片机设计的一种C语言变种,它在标准C语言的基础上增加了一些特定于51单片机的特性和关键字,以便更好地适应嵌入式系统的开发需求。以下是C51语言的一些关键特性和概念,这些特性使得C51语言成为51单片机编程的理想选择。
C51语言的关键特性
1. 特殊功能寄存器(SFR)和位操作
- sfr:用于定义特殊功能寄存器(Special Function Register)。这些寄存器用于控制单片机的各种功能,如I/O端口、定时器、中断等。
sfr P1 = 0x90; // 定义P1端口寄存器,地址为0x90
- sfr16:用于定义16位的特殊功能寄存器。
sfr16 DPTR = 0x82; // 定义数据指针寄存器,地址为0x82
- sbit:用于定义特殊功能寄存器中的某一位。
sbit LED = P1^0; // 定义P1端口的第0位为LED
2. 存储器类型
C51语言支持多种存储器类型,这些类型决定了变量或函数在单片机内存中的存储位置。
- code:程序存储器,用于存放常量和函数。
const char str[] code = "Hello, World!"; // 存储在程序存储器中的字符串
- data:内部RAM的直接寻址区域,用于存放小型变量。
unsigned char var1 data; // 存储在内部RAM中的变量
- xdata:外部RAM,用于存放大型变量。
unsigned int var2 xdata; // 存储在外部RAM中的变量
- idata:内部RAM的间接寻址区域。
unsigned char var3 idata; // 存储在内部RAM间接寻址区域的变量
- pdata:外部RAM的256字节区域,使用间接寻址。
unsigned char var4 pdata; // 存储在外部RAM间接寻址区域的变量
- bdata:内部RAM的位寻址区域。
unsigned char var5 bdata; // 存储在内部RAM位寻址区域的变量
3. 位变量和位操作
- bit:用于定义位变量,值只能是0或1。
bit flag; // 定义一个位变量 flag = 1; // 设置位变量为1
- 位操作符:C51支持位操作符,如
&
(按位与)、|
(按位或)、^
(按位异或)、~
(按位取反)等。unsigned char a = 0x0F; unsigned char b = 0xF0; unsigned char c = a | b; // c = 0xFF
4. 中断函数
C51语言支持中断函数的定义,这些函数用于处理单片机的中断事件。
- interrupt:用于定义中断函数。
void Timer0_ISR() interrupt 1 { // 中断服务例程 }
5. 数据类型
C51语言支持多种数据类型,包括标准C语言中的数据类型以及一些扩展的数据类型。
- 基本数据类型:
char
、int
、long
、float
等。 - 扩展数据类型:
bit
、sfr
、sfr16
、sbit
等。
与C语言相比
C51语言在很多方面与标准C语言是非常相似的。然而,也有一些细微的差别和额外的功能,这些是为了更好地适应嵌入式系统开发的需求。以下是一些主要的相似之处和差异:
相似之处
基本语法:
- 变量声明:变量的声明和初始化方式与标准C语言相同。
int a = 10; char b = 'A'; float c = 3.14;
- 控制结构:
if
、else
、for
、while
、do-while
等控制结构的使用方式与标准C语言相同。if (a > 0) { // 执行某些操作 } else { // 执行其他操作 } for (int i = 0; i < 10; i++) { // 循环体 } while (condition) { // 循环体 }
- 变量声明:变量的声明和初始化方式与标准C语言相同。
函数定义:
- 函数的定义和调用方式与标准C语言相同。
void myFunction(int param) { // 函数体 } int main() { myFunction(5); return 0; }
- 函数的定义和调用方式与标准C语言相同。
运算符:
- 基本的算术运算符、关系运算符、逻辑运算符等与标准C语言相同。
int sum = a + b; int product = a * b; int result = (a > b) ? a : b;
- 基本的算术运算符、关系运算符、逻辑运算符等与标准C语言相同。
数组和指针:
- 数组和指针的使用方式与标准C语言相同。
int array[5] = {1, 2, 3, 4, 5}; int *ptr = array; int value = *ptr;
- 数组和指针的使用方式与标准C语言相同。
差异之处
存储器类型:
- C51语言引入了特定的存储器类型关键字,如
data
、xdata
、idata
、pdata
、bdata
、code
等,这些关键字用于指定变量或函数在单片机内存中的存储位置。unsigned char data var1; // 存储在内部RAM中的变量 unsigned int xdata var2; // 存储在外部RAM中的变量 const char str[] code = "Hello, World!"; // 存储在程序存储器中的字符串
- C51语言引入了特定的存储器类型关键字,如
特殊功能寄存器(SFR):
- C51语言提供了
sfr
和sfr16
关键字,用于定义特殊功能寄存器。sfr P1 = 0x90; // 定义P1端口寄存器,地址为0x90 sfr16 DPTR = 0x82; // 定义数据指针寄存器,地址为0x82
- C51语言提供了
位操作:
- C51语言提供了
bit
和sbit
关键字,用于定义位变量和特殊功能寄存器中的位。bit flag; // 定义一个位变量 sbit LED = P1^0; // 定义P1端口的第0位为LED
- C51语言提供了
中断函数:
- C51语言支持中断函数的定义,这些函数用于处理单片机的中断事件。
void Timer0_ISR() interrupt 1 { // 中断服务例程 }
- C51语言支持中断函数的定义,这些函数用于处理单片机的中断事件。
库函数:
- C51语言的库函数与标准C语言有所不同,许多标准C库函数可能不适用于嵌入式系统,而C51库函数则专门针对51单片机进行了优化。
#include <intrins.h> // 包含内建函数头文件 _nop_(); // 插入一个空操作指令
- C51语言的库函数与标准C语言有所不同,许多标准C库函数可能不适用于嵌入式系统,而C51库函数则专门针对51单片机进行了优化。
存储模式:
- C51语言支持不同的存储模式,如
SMALL
、COMPACT
和LARGE
,这些模式影响变量和指针的默认存储位置。#pragma small // 设置存储模式为SMALL
- C51语言支持不同的存储模式,如
示例程序:51单片机控制LED流水灯
以下是一个使用C51语言编写的示例程序,该程序控制51单片机的P1端口上的8个LED灯以流水灯的形式依次点亮。
#include <reg51.h> // 定义延时函数 void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) for(j = 0; j < 1275; j++); } void main() { // 设置P1口为输出模式 P1 = 0xFF; // 初始状态下所有LED灯都关闭 while(1) { // 无限循环 P1 = 0xFE; // 第一个LED灯亮,其余熄灭 delay(500); P1 = 0xFD; // 第二个LED灯亮,其余熄灭 delay(500); P1 = 0xFB; // 第三个LED灯亮,其余熄灭 delay(500); P1 = 0xF7; // 第四个LED灯亮,其余熄灭 delay(500); P1 = 0xEF; // 第五个LED灯亮,其余熄灭 delay(500); P1 = 0xDF; // 第六个LED灯亮,其余熄灭 delay(500); P1 = 0xBF; // 第七个LED灯亮,其余熄灭 delay(500); P1 = 0x7F; // 第八个LED灯亮,其余熄灭 delay(500); P1 = 0xFF; // 所有LED灯熄灭 delay(500); } }
学习其他方法控制流水灯
1. 位操作控制流水灯
思路
通过直接操作寄存器的某一位来控制LED灯的亮灭。使用sbit
关键字定义特定的位。
代码
#include <reg51.h> sbit LED1 = P1^0; sbit LED2 = P1^1; sbit LED3 = P1^2; sbit LED4 = P1^3; sbit LED5 = P1^4; sbit LED6 = P1^5; sbit LED7 = P1^6; sbit LED8 = P1^7; void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) for(j = 0; j < 1275; j++); } void main() { while(1) { LED1 = 0; delay(500); LED1 = 1; LED2 = 0; delay(500); LED2 = 1; LED3 = 0; delay(500); LED3 = 1; LED4 = 0; delay(500); LED4 = 1; LED5 = 0; delay(500); LED5 = 1; LED6 = 0; delay(500); LED6 = 1; LED7 = 0; delay(500); LED7 = 1; LED8 = 0; delay(500); LED8 = 1; } }
2. 字节操作控制流水灯
思路
通过操作整个字节来控制一组LED灯。使用字节操作符来改变P1端口的值。
代码
#include <reg51.h> void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) for(j = 0; j < 1275; j++); } void main() { while(1) { P1 = 0xFE; delay(500); // 第一个LED灯亮 P1 = 0xFD; delay(500); // 第二个LED灯亮 P1 = 0xFB; delay(500); // 第三个LED灯亮 P1 = 0xF7; delay(500); // 第四个LED灯亮 P1 = 0xEF; delay(500); // 第五个LED灯亮 P1 = 0xDF; delay(500); // 第六个LED灯亮 P1 = 0xBF; delay(500); // 第七个LED灯亮 P1 = 0x7F; delay(500); // 第八个LED灯亮 } }
3. 移位运算符控制流水灯
思路
通过移位运算符来改变字节的值,从而控制LED灯的亮灭。
代码
#include <reg51.h> void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) for(j = 0; j < 1275; j++); } void main() { unsigned char mask = 0x01; while(1) { for(int i = 0; i < 8; i++) { P1 = ~mask; // 取反,使LED灯亮 delay(500); mask <<= 1; // 左移一位 } mask = 0x01; // 重置mask } }
4. 库函数控制流水灯
思路
使用51单片机的内置库函数来实现循环移位,简化代码。
代码
#include <reg51.h> #include <intrins.h> void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) for(j = 0; j < 1275; j++); } void main() { unsigned char mask = 0x01; while(1) { for(int i = 0; i < 8; i++) { P1 = ~mask; // 取反,使LED灯亮 delay(500); mask = _crol_(mask, 1); // 循环左移一位 } mask = 0x01; // 重置mask } }
5. 条件控制语句控制流水灯
思路
使用if
语句来控制LED灯的亮灭。
代码
#include <reg51.h> void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) for(j = 0; j < 1275; j++); } void main() { unsigned char state = 0x01; while(1) { if (state == 0x01) { P1 = 0xFE; delay(500); state = 0x02; } else if (state == 0x02) { P1 = 0xFD; delay(500); state = 0x04; } else if (state == 0x04) { P1 = 0xFB; delay(500); state = 0x08; } else if (state == 0x08) { P1 = 0xF7; delay(500); state = 0x10; } else if (state == 0x10) { P1 = 0xEF; delay(500); state = 0x20; } else if (state == 0x20) { P1 = 0xDF; delay(500); state = 0x40; } else if (state == 0x40) { P1 = 0xBF; delay(500); state = 0x80; } else if (state == 0x80) { P1 = 0x7F; delay(500); state = 0x01; } } }
6. switch
语句控制流水灯
思路
使用switch
语句来控制LED灯的亮灭。
代码
#include <reg51.h> void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) for(j = 0; j < 1275; j++); } void main() { unsigned char state = 0x01; while(1) { switch(state) { case 0x01: P1 = 0xFE; delay(500); state = 0x02; break; case 0x02: P1 = 0xFD; delay(500); state = 0x04; break; case 0x04: P1 = 0xFB; delay(500); state = 0x08; break; case 0x08: P1 = 0xF7; delay(500); state = 0x10; break; case 0x10: P1 = 0xEF; delay(500); state = 0x20; break; case 0x20: P1 = 0xDF; delay(500); state = 0x40; break; case 0x40: P1 = 0xBF; delay(500); state = 0x80; break; case 0x80: P1 = 0x7F; delay(500); state = 0x01; break; } } }
7. 数组控制流水灯
思路
使用数组来存储每个LED灯的状态,通过索引来控制LED灯的亮灭。
代码
#include <reg51.h> void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) for(j = 0; j < 1275; j++); } void main() { unsigned char led_states[] = {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F}; while(1) { for(int i = 0; i < 8; i++) { P1 = led_states[i]; delay(500); } } }
8. 指针控制流水灯
思路
使用指针来遍历数组,通过指针来控制LED灯的亮灭。
代码
#include <reg51.h> void delay(unsigned int time) { unsigned int i, j; for(i = 0; i < time; i++) for(j = 0; j < 1275; j++); } void main() { unsigned char led_states[] = {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F}; unsigned char *ptr = led_states; while(1) { for(int i = 0; i < 8; i++) { P1 = *ptr; delay(500); ptr++; } ptr = led_states; // 重置指针 } }
总结
以上是使用位操作、字节操作、移位运算符、库函数、条件控制语句、switch
语句、数组和指针这8种方法来控制51单片机上的流水灯的详细讲解和代码示例。每种方法都有其独特的优点和适用场景,通过学习这些方法,你可以更灵活地控制51单片机的I/O端口,实现各种复杂的功能。