参考
- 51单片机入门教程
STC 公司 51 单片机系列,8 位,RAM(512 字节),ROM(8K,Flash),工作频率 12MHz
51 单片机为什么叫 51?
- 是因为这种单片机最初采用的是英特尔公司的 8051 指令系统。随着时间的推移,有多家公司生产了兼容 8051 指令系统的单片机,它们虽然功能各异,但是核心架构相同,因此都被统称为 51 单片机
- “51” 在这里代表了 8051 架构,也就是 Intel 公司发明的一种早期的单片控制器架构
单片机 TTL 电平:高电平 5V,低电平 0V,LED 具有单向导电性,当 LED 的正端接了高电位,负端连接了低电位,且正负端电位差超过 1.8V 以上时,LED 就会亮起来
#include // 定义寄存器和端口(识别 P2 口)
void main() {
P2 = 0xFE; // 1111 1110,0x 前缀表示 十六进制,引脚配置为 “低电平有效”
// P2 = 0x55; // 0101 0101,8 个 LED 灯间隔点亮
while (1) {
}
}
#include
#include // _nop_(); 需要的头文件
void Delay500ms() { // @12.000 MHz
unsigned char i, j, k;
_nop_(); // 空操作命令,确保编译器不会对后续的循环优化
i = 4;
j = 205;
k = 187;
// 外层循环的条件是 i != 0
// 内层两层循环的条件分别是 j != 0 && k != 0
do {
do {
while (--k); // 循环耗时操作
} while (--j); // 嵌套循环耗时操作
} while (--i);
}
void main() {
while(1) {
P2 = 0xFE; // 1111 1110
Delay500ms(); // 单片机当中每次都是以 MHZ 速度运行,闪烁太快人眼看不出,因此要加延迟函数
P2 = 0xFF; // 1111 1111
Delay500ms();
}
}
#include
#include
void Delay500ms() { // @12.000 MHz
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do {
do {
while (--k);
} while (--j);
} while (--i);
}
void main() {
while (1) {
P2 = 0xFE; // 1111 1110
Delay500ms();
P2 = 0xFD; // 1111 1101
Delay500ms();
P2 = 0xFB; // 1111 1011
Delay500ms();
P2 = 0xF7; // 1111 0111
Delay500ms();
P2 = 0xEF; // 1110 1111
Delay500ms();
P2 = 0xDF; // 1101 1111
Delay500ms();
P2 = 0xBF; // 1011 1111
Delay500ms();
P2 = 0x7F; // 0111 1111
Delay500ms();
}
}
#include
void Delay1ms(unsigned int xms); // @12.000MHz
void main() {
while (1) {
P2 = 0xFE; // 1111 1110
Delay1ms(100);
P2 = 0xFD; // 1111 1101
Delay1ms(100);
P2 = 0xFB; // 1111 1011
Delay1ms(100);
P2 = 0xF7; // 1111 0111
Delay1ms(100);
P2 = 0xEF; // 1110 1111
Delay1ms(100);
P2 = 0xDF; // 1101 1111
Delay1ms(100);
P2 = 0xBF; // 1011 1111
Delay1ms(100);
P2 = 0x7F; // 0111 1111
Delay1ms(100);
}
}
void Delay1ms(unsigned int xms) { // @12.000MHz
unsigned char i, j;
while (xms) {
i = 2;
j = 239;
do {
while (--j);
} while (--i);
xms--;
}
}
#include
void main() {
while (1) {
if (P3_1 == 0 || P3_0 == 0) { // 如果 K1 按键(RXD,P3_1)或 K2 按键(TXD,P3_0)按下
P2_0 = 0; // LED1 输出 0,点亮
} else {
P2_0 = 1; // LED1 输出 1,熄灭
}
}
}
#include
void Delay(unsigned int xms) {
unsigned char i, j;
while (xms) {
i = 2;
j = 239;
do {
while (--j);
} while (--i);
xms--;
}
}
void main() {
while (1) {
if (P3_1 == 0) { // 如果 K1 按键按下
Delay(20); // 延时,以消除按键抖动带来的影响
while (P3_1 == 0); // 判断 K1 按键是否仍处于按下状态,松手检测
Delay(20); // 延时,以消除按键抖动带来的影响
P2_0 = ~P2_0; // LED1 取反
}
}
}
#include
void Delay(unsigned int xms) {
unsigned char i, j;
while (xms--) {
i = 2;
j = 239;
do {
while (--j);
} while (--i);
}
}
void main() {
unsigned char LEDNum = 0; // 无符号字符型(所占1字节 = 8bit位)刚好对应着 8 位二进制的数据
while (1) {
if (P3_1 == 0) { // 如果 K1 按键按下
Delay(20); // 延时消抖
while (P3_1 == 0); // 判断 K1 按键是否仍处于按下状态,松手检测
Delay(20); // 延时消抖
LEDNum++; // 变量自增,用于切换 LED 灯的状态
// P2 口上电之后和单片机的 IO 上电一样都是默认的是高电平:1111 1111
P2 = ~LEDNum; // 变量取反输出给 LED,控制 LED 灯的亮灭
}
}
}
// K1 = P3_1;K2 = P3_0;K3 = P3_2;K4 = P3_3
#include
void Delay(unsigned int xms);
unsigned char LEDNum; // 全局变量定义默认为 0
void main() {
P2 = ~0x01; // 上电默认 LED1 点亮
while (1) {
if (P3_1 == 0) { // 如果 K1 按键按下
Delay(20);
while (P3_1 == 0);
Delay(20);
LEDNum++; // LEDNum 自增
if (LEDNum >= 8) // 限制 LEDNum 自增范围
LEDNum = 0;
P2 = ~(0x01 << LEDNum); // LED 的第 LEDNum 位点亮
}
if (P3_0 == 0) { // 如果 K2 按键按下
Delay(20);
while (P3_0 == 0);
Delay(20);
if (LEDNum == 0) // LEDNum 减到 0 后变为 7
LEDNum = 7;
else // LEDNum 未减到 0,自减
LEDNum--;
P2 = ~(0x01 << LEDNum); // LED 的第 LEDNum 位点亮
}
}
}
void Delay(unsigned int xms) {
unsigned char i, j;
while (xms--) {
i = 2;
j = 239;
do {
while (--j);
} while (--i);
}
}
以共阴极为例(下图右上),若要显示数字 6
以共阴极为例(下图右上),若要在第三位数码管显示数字 1
LED1~LED8 都是接到 138 译码器上的输出端,138 译码器原理如下
74HC245 芯片作用:也称双向数据缓冲器,用来提高芯片驱动能力
电容 CC2 作用:起到电源滤波作用,使得芯片的供电更加稳定
位选
段选
数组:把相同类型的一系列数据统一编制到某一个组别中,可以通过数组名 + 索引号简单快捷的操作大量数据
int x[3]; // 定义一组变量(3个)
int x[]={1,2,3}; // 定义一组变量并初始化
x[0]; //引用数组的第0个变量
x[1]; //引用数组的第1个变量
x[2]; //引用数组的第2个变量
// 引用 x[3] 时,数组越界,读出的数值不确定,应避免这种操作
子函数:将完成某一种功能的程序代码单独抽取出来形成一个模块,在其它函数中可随时调用此模块,以达到代码的复用和优化程序结构的目的
void Function(unsigned char x, y) {
}
返回值 函数名(形参){
函数体
}
#include
// 数码管段码表
unsigned char NixieTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
// Location:数码管的位置,Number:显示数码管的数字
void Nixie(unsigned char Location, Number) {
switch (Location) { // 位码输出
case 1:
P2_4 = 1; P2_3 = 1; P2_2 = 1;
break;
case 2:
P2_4 = 1; P2_3 = 1; P2_2 = 0;
break;
case 3:
P2_4 = 1; P2_3 = 0; P2_2 = 1;
break;
case 4:
P2_4 = 1; P2_3 = 0; P2_2 = 0;
break;
case 5:
P2_4 = 0; P2_3 = 1; P2_2 = 1;
break;
case 6:
P2_4 = 0; P2_3 = 1; P2_2 = 0;
break;
case 7:
P2_4 = 0; P2_3 = 0; P2_2 = 1;
break;
case 8:
P2_4 = 0; P2_3 = 0; P2_2 = 0;
break;
}
P0 = NixieTable[Number]; // 段码输出
}
void main() {
Nixie (2, 3); // 在数码管的第 2 位置显示 3
while (1) {}
}
#include
// 数码管段码表
unsigned char NixieTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
//延时子函数
void Delay(unsigned int xms) {
unsigned char i, j;
while (xms--) {
i = 2;
j = 239;
do {
while (--j);
} while (--i);
}
}
// Location:数码管的位置,Number:显示数码管的数字
void Nixie(unsigned char Location, Number) {
switch (Location) { // 位码输出
case 1:
P2_4 = 1; P2_3 = 1; P2_2 = 1;
break;
case 2:
P2_4 = 1; P2_3 = 1; P2_2 = 0;
break;
case 3:
P2_4 = 1; P2_3 = 0; P2_2 = 1;
break;
case 4:
P2_4 = 1; P2_3 = 0; P2_2 = 0;
break;
case 5:
P2_4 = 0; P2_3 = 1; P2_2 = 1;
break;
case 6:
P2_4 = 0; P2_3 = 1; P2_2 = 0;
break;
case 7:
P2_4 = 0; P2_3 = 0; P2_2 = 1;
break;
case 8:
P2_4 = 0; P2_3 = 0; P2_2 = 0;
break;
}
P0 = NixieTable[Number]; // 段码输出
Delay(1); // 显示一段时间
P0 = 0x00; // 段码清 0,消影
}
void main() {
while (1) {
Nixie(1, 1); // 在数码管的第 1 位置显示 1
// Delay(20);
Nixie(2, 2); // 在数码管的第 2 位置显示 2
// Delay(20);
Nixie(3, 3); // 在数码管的第 3 位置显示 3
// Delay(20);
}
}
#include
#include
int main(void) {
unsigned int Number = 51;
signed int negative = -1;
LCD_Init();
while (1) {
LCD_ShowChar(1, 1, 'W');
LCD_ShowString(1, 2, "XH");
LCD_ShowNum(1, 4, Number,2);
LCD_ShowSignedNum(1, 7, negative, 1);
LCD_ShowHexNum(2, 1, 0xFF, 2);
LCD_ShowBinNum(2, 4, 0x00, 8);
}
}
在键盘中按键数量较多时,为了减少 I/O 口的占用,通常将按键排列成矩阵形式,采用逐行或逐列的 “扫描”,就可以读出任何位置按键的状态
扫描
以上两种扫描方式的共性:节省 I/O 口
单片机 IO 口模式
为什么单片机它的 IO 口是默认为高电平呢?
#ifndef __MATRIXKEY_H__
#define __MATRIXKEY_H__
unsigned char MatrixKey();
#endif
#include
#include "Delay.h"
/**
* @brief 矩阵键盘读取按键键码
* @param 无
* @retval KeyNumber 按下按键的键码值
如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回 0
*/
unsigned char MatrixKey() {
unsigned char KeyNumber = 0;
P1 = 0xFF;
P1_3 = 0;
if (P1_7 == 0) {Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 1;}
if (P1_6 == 0) {Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 5;}
if (P1_5 == 0) {Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 9;}
if (P1_4 == 0) {Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 13;}
P1 = 0xFF;
P1_2 = 0;
if (P1_7 == 0) {Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 2;}
if (P1_6 == 0) {Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 6;}
if (P1_5 == 0) {Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 10;}
if (P1_4 == 0) {Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 14;}
P1 = 0xFF;
P1_1 = 0;
if (P1_7 == 0) {Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 3;}
if (P1_6 == 0) {Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 7;}
if (P1_5 == 0) {Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 11;}
if (P1_4 == 0) {Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 15;}
P1 = 0xFF;
P1_0 = 0;
if (P1_7 == 0) {Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 4;}
if (P1_6 == 0) {Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 8;}
if (P1_5 == 0) {Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 12;}
if (P1_4 == 0) {Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 16;}
return KeyNumber;
}
#include
#include "Delay.h" // 包含 Delay 头文件
#include "LCD1602.h" // 包含 LCD1602 头文件
#include "MatrixKey.h" // 包含矩阵键盘头文件
unsigned char KeyNum;
void main() {
LCD_Init(); // LCD 初始化
LCD_ShowString(1, 1, "MatrixKey:"); // LCD 显示字符串
while (1) {
KeyNum = MatrixKey(); // 获取矩阵键盘键码
if (KeyNum) { // 如果有按键按下
LCD_ShowNum(2, 1, KeyNum, 2); // LCD 显示键码
}
}
}
#include
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKey.h"
unsigned char KeyNum;
unsigned int Password, Count;
void main() {
LCD_Init();
LCD_ShowString(1, 1, "Password:");
while (1) {
KeyNum = MatrixKey();
if (KeyNum) {
if (KeyNum <= 10) { // 如果 S1~S10 按键按下,输入密码
if (Count<4) { // 如果输入次数小于 4
Password *= 10; // 密码左移一位
Password += KeyNum % 10; // 获取一位密码
Count++; // 计次加一
}
LCD_ShowNum(2, 1, Password, 4); // 更新显示
}
if (KeyNum == 11) { // 如果 S11 按键按下,确认
if (Password == 2345) { // 如果密码等于正确密码
LCD_ShowString(1, 14, "OK "); // 显示 OK
Password = 0; // 密码清零
Count = 0; // 计次清零
LCD_ShowNum(2, 1, Password, 4); // 更新显示
} else {
LCD_ShowString(1, 14, "ERR"); // 显示 ERR
Password = 0; // 密码清零
Count = 0; // 计次清零
LCD_ShowNum(2, 1, Password, 4); // 更新显示
}
}
if (KeyNum == 12) { // 如果 S12 按键按下,取消
Password = 0; // 密码清零
Count = 0; // 计次清零
LCD_ShowNum(2, 1, Password, 4); // 更新显示
}
}
}
}