Contents
[TOC]
Part1–初识MCU
一、Preparation
1. Two applications
Keil5(编写程序)
(1)从官网下载Keil5
(2)从License Magement中复制你的CID并将其输入keygen中激活
注意,以管理员身份运行程序,否则权限不够。 AND 在打开Keygen或者下载前,关闭防火墙,否则有一定概率误杀 |
(3)点击Generate,完成激活
Stc-isp(下载程序)
这个自行搜索下载
在成功下载之后,一般来说无法直接读取或识别外接的C51,这个时候需要安装相应的driver:
这时候如果在任务管理器中能够能看到USB-SERIAL,说明驱动安装成功,C51已被读取。
x def phase5(n1, c1, n2, c2, n3, c3): r = CRT([c1, c2, c3], [n1, n2, n3]) m = int(r)^(1/3) print(hex(m)[2:]) n1 = 78642188663937191491235684351005990853149481644703243255021321296087539054265733392095095639539412823093600710316645130404423641473150336492175402885270861906530337207734106926328737198871118125840680572148601743121884788919989184318198417654263598170932154428514561079675550090698019678767738203477097731989c1 = 23419685303892339080979695469481275906709035609088426118328601771163101123641599051556995351678670765521269546319724616458499631461037359417701720430452076029312714313804716888119910334476982840024696320503747736428099717113471541651211596481005191146454458591558743268791485623924245960696651150688621664860n2 = 98174485544103863705821086588292917749386955237408645745685476234349659452606822650329076955303471252833860010724515777826660887118742978051231030080666542833950748806944312437614585352818344599399156268450521239843157288915059003487783576003027303399985723834248634230998110618288843582573006048070816520647c2 = 72080679612442543693944655041130370753964497034378634203383617624269927191363529233872659451561571441107920350406295389613006330637565645758727103723546610079332161151567096389071050158035757745766399510575237344950873632114050632573903701015749830874081198250578516967517980592506626547273178363503100507676n3 = 91638855323231795590642755267985988356764327384001022396221901964430032527111968159623063760057482761918901490239790230176524505469897183382928646349163030620342744192731246392941227433195249399795012672172947919435254998997253131826888070173526892674308708289629739522194864912899817994807268945141349669311c3 = 22149989692509889061584875630258740744292355239822482581889060656197919681655781672277545701325284646570773490123892626601106871432216449814891757715588851851459306683123591338089745675044763551335899599807235257516935037356212345033087798267959242561085752109746935300735969972249665700075907145744305255616phase5(n1,c1,n2,c2,n3,c3)# 464c41477b325e3872736138633566336366663462633039353334396665633635666332323633653837387dpython
二、About MCU
1. Brief introduction
单片机,英文Micro ControIIer Unit , 简称MCU
- 内部集成了CPU 、RAM 、ROM 、定时器、中断系统、通讯接口等一系列电脑的常用硬件功能
- 单片机的任务是信息采集( 依靠传感器) 、处理( 依靠CPU) 和硬件设备( 例如电机, LED 等) 的控制
- 单片机跟计算机相比, 单片机算是一个袖珍版计算机, 一个芯片就能构成完整的计算机系统。但在性能上, 与计算机相差甚远, 但单片机成本低、体积小、结构简单, 在生活和工业控制领域大有所用。同时, 学习使用单片机是了解计算机原理与结构的最佳选择
51单片机:指80年代Intel开发的8051内核的单片机的统称。
*类比电脑内存条
*:RAM(Random Access Memory,随机存取存储器)是计算机中的一种主要存储设备,用于临时存储和快速访问数据。它是计算机的一个重要组成部分,用于存储正在执行的程序和数据。
RAM与计算机的硬盘驱动器(或固态驱动器)不同,后者用于长期存储数据,而RAM是一种易失性存储器,意味着其存储的数据在断电或重新启动后会被清除。这是因为RAM是基于电子器件和电路构建的,需要持续的电源供应来保持存储的数据。
RAM被分为主存储器(Main Memory)和高速缓存(Cache Memory)。主存储器是计算机直接访问的存储区域,用于存储正在运行的程序和数据。它的存取速度比硬盘驱动器等次要存储设备要快得多。高速缓存是位于CPU内部的一种更快速的存储器,用于临时存储处理器频繁访问的数据,以提高计算机的性能。
RAM的容量通常以兆字节(MB)或千兆字节(GB)进行衡量。较大容量的RAM可以容纳更多的程序和数据,从而提供更好的性能和多任务处理能力。计算机的RAM容量可以根据需要进行扩展或升级。
总之,RAM是一种临时存储设备,用于存储正在执行的程序和数据,提供计算机的实时访问能力。它在计算机的性能和多任务处理方面起着重要的作用。
*类比电脑硬盘
*:ROM(Read-Only Memory)是一种计算机芯片或存储设备中的一种存储器类型。与随机访问存储器(RAM)相比,ROM在断电后能够保持其存储的数据不变,因此也被称为非易失性存储器。ROM的数据一般是由制造厂商在生产过程中预先写入的,并且用户无法对其进行修改。这意味着ROM中存储的内容是只读的,无法被擦除或重写。
ROM有多种不同类型,包括:
- PROM(Programmable Read-Only Memory,可编程只读存储器):这种ROM允许用户在一次性编程之前将数据写入其中。一旦编程完成,数据将永久固化在芯片中,不可更改。
- EPROM(Erasable Programmable Read-Only Memory,可擦除可编程只读存储器):这种ROM允许用户通过使用特定设备将其数据擦除,然后再次编程。擦除通常通过使用紫外线光线或电子擦除器来进行。
- EEPROM(Electrically Erasable Programmable Read-Only Memory,电可擦除可编程只读存储器):EEPROM与EPROM类似,但是擦除操作可以通过电源供电而不需要其他特殊设备。EEPROM的擦除和编程可以在特定的操作条件下进行。
ROM的应用非常广泛,它被用于存储启动引导程序、固件、芯片内的固定数据以及其他一些需要在断电后保持不变的数据。有些游戏机、手机和电脑主板中也使用ROM存储固件或操作系统。总之,ROM在计算机和电子设备中起到了重要的作用,并且因其稳定性和数据不易丢失的特点而受到广泛使用。
2. Application field
单片机的使用领域已十分广泛, 如智能仪表、实时工控、通讯设备、导航系统、家用电器等。各种产品一旦用上了单片机, 就能起到使
产品升级换代的功效, 常在产品名称前冠以形容词——“智能型”,如智能型洗衣机等。
3. Naming rules
4. Structure
单片机内部结构图:
单片机管脚图:
例如 Vcc代表 + 极,Gnd代表 - 极
单片机的最小系统:
电容——过滤因为电源不稳定产生的电磁波
复位电路(中间的)——复位电路通常用来清除存储器中的数据、关闭所有开关和重置相关的逻辑电路。它可以在系统启动时,或在错误发生时,通过将相关电路重新初始化,确保系统处于可控状态。
晶振(左下角)——用于时钟电路和振荡电路中,以稳定电子设备的工作频率。晶振的主要原理是利用压电效应,在晶体振荡器中产生稳定的振荡信号。推动程序往下进行的关键。
Part2–编写MCU
一、Light up LED
LED,全称为Light Emitting Diode(发光二极管),是一种固体电子器件,可以将电能直接转化为光能。相对于传统的光源,如白炽灯和荧光灯,LED具有更高的能效、更长的使用寿命和更大的可靠性。
LED的工作原理是基于半导体材料的特性。当电流通过LED时,电子和空穴在半导体材料中重新组合,产生能量释放,从而产生可见光。不同半导体材料的能带结构决定了LED发出的光的颜色。
LED具有许多优点。首先,LED保存能量且效率高,相比传统光源,LED产生的光功率更大,但消耗的电能更少。其次,LED寿命长,通常可以达到数万个小时,远远超过传统光源。此外,LED具有快速开启和关闭的特性,并且可以根据需要调节亮度,因此被广泛应用于照明和显示领域。
LED在各个领域有广泛的应用。在照明领域,LED被用于家庭照明、商业照明和街道照明等。由于其高效节能的特性,LED也被用于太阳能照明和绿色建筑项目中。在电子显示领域,LED用于制造各种显示屏,如电视、计算机显示器和手机屏幕。此外,LED还常用于车辆照明、室内装饰、电子设备指示灯等。
1. 点亮一个LED
首先观察MCU里的LED模块
右边图片中的RP9、RP10是两个电阻 102=1k(10*10^2)元器件上的标号以此类推
P2口下的寄存器控制LED: 1–5V 不亮 0–0V 亮
- 创建一个Project,在列表里的Atmel下选择AT89C52
- 在该文件夹下创建一个C的文件,以便进一步编写程序
3.编写控制LED的代码
1 2 3 4 5 6
| #include <REGX52.H>
void main() { P2=0xFE; }
|
4.将文件保存为hex格式,以便烧入
5.用stc-isp将程序烧入进MCU中,尝试运行
选择相应的单片机型号与串口号,并打开已写好的程序文件
然后点击下载,重启单片机,发现成功点亮LED
2. LED闪烁
重复(1)中的操作步骤,新建一个Project
其实要完成闪烁只需要写一个while循环就可以了
1 2 3 4 5 6 7 8 9 10 11 12
| #include <REGX52.H>
void main() { while(1) { P2=0xFE; P2=0xFF; }
}
|
但这个时候发现LED闪烁周期过快,肉眼无法观察到明显现象,这个时候需要加大周期,即加入延时函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <REGX52.H> #include <INTRINS.H>
void Delay500ms() { 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; Delay500ms(); P2=0xFF; Delay500ms(); }
}
|
3. LED流水灯
有了“前车之鉴”,再写流水灯其实就很简单了,逻辑是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include <REGX52.H> #include <INTRINS.H>
void Delay250ms() { unsigned char i, j, k;
_nop_(); i = 2; j = 231; k = 91; do { do { while (--k); } while (--j); } while (--i); }
void main() { while(1) { P2=0xFE; Delay250ms(); P2=0xFD; Delay250ms(); P2=0xFB; Delay250ms(); P2=0xF7; Delay250ms(); P2=0xEF; Delay250ms(); P2=0xDF; Delay250ms(); P2=0xBF; Delay250ms(); P2=0x7F; Delay250ms(); } }
|
Pro版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include <REGX52.H>
int n=50; void Delay1ms(unsigned int xms) { unsigned char i,j; while(xms) { i=2; j=239; do { while(--j); } while(--i); xms--; } }
void main() { while(1) { P2=0xFE; Delay1ms(n); P2=0xFD; Delay1ms(n); P2=0xFB; Delay1ms(n); P2=0xF7; Delay1ms(n); P2=0xEF; Delay1ms(n); P2=0xDF; Delay1ms(n); P2=0xBF; Delay1ms(n); P2=0x7F; Delay1ms(n); } }
|
二、Individual key
C51上的独立按键:底座+金属弹片+ +
不按下时4个引脚两两连接,按下去时4个引脚同时连接
独立按键:
4个独立按键一端接 -极,另一端接I/O口(I/O口默认高电平)
寄存器检测I/O口电平
1. 独立按键控制LED亮灭
sfr:整个8位的寄存器
寄存器中P2表示8位一体,不可单独控制单独位次
发现sbit可控制单独位次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <REGX52.H>
void main() { while(1) { if(P3_1==0) { P2_0=0; } else { P2_0=1; } }
}
|
2. 独立按键控制LED状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <REGX52.H>
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) { Delay(20); while(P3_1==0); Delay(20); P2_0=~P2_0; } } }
|
3. 独立按键控制LED显示二进制
项目的创建以及准备工作就不再赘述
主要是利用 unsigned char 类型(0—255)与寄存器同样为8位的特性,来间接表示LED二进制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <REGX52.H>
void Delay(unsigned int xms) { unsigned char i, j; while(xms--) { i = 12; j = 169; do { while (--j); } while (--i); } }
void main() { unsigned char LEDNum=0; while(1) { if(P3_1==0) { Delay(20); while(P3_1==0); Delay(20); LEDNum++; P2=~LEDNum; } } }
|
P2=1111 1111 //P2口默认高电平
P2++ 会使端口溢出,P2变成 0000 0000
再次取反P2将保持原样,即P2=1111 1111 ,表现为D1—D8全灭
4. 独立按键控制LED移位
项目预期:按下P3_1(左1),实现LED从左向右移位1
按下P3_0(左2),实现LED从右向左移位1
[^如图所示]:
实现移位的代码如下,主要使用C语言中的位运算:**<< 按位左移** 与 >>按位右移
P3_1口闭合,左移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include <REGX52.H>
void Delay(unsigned int xms); unsigned char LEDNum=0;
void main() { while(1) { if(P3_1==0) { Delay(20); while(P3_1==0); Delay(20); if(LEDNum>7) { LEDNum=0; } P2=~(0x01<<LEDNum); LEDNum++; } } }
void Delay(unsigned int xms) { unsigned char i, j; while(xms--) { i = 12; j = 169; do { while (--j); } while (--i); } }
|
与左移逻辑一样,加上右移,完善代码,实现项目预期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include <REGX52.H>
void Delay(unsigned int xms); unsigned char LEDNum=0;
void main() { P2=~0x01; while(1) { if(P3_1==0) { Delay(20); while(P3_1==0); Delay(20); LEDNum++; if(LEDNum>=8) { LEDNum=0; } P2=~(0x01<<LEDNum); } if(P3_0==0) { Delay(20); while(P3_0==0); Delay(20); if(LEDNum==0) { LEDNum=7; } else { LEDNum--; } P2=~(0x01<<LEDNum); } } }
void Delay(unsigned int xms) { unsigned char i, j; while(xms--) { i = 12; j = 169; do { while (--j); } while (--i); } }
|
三、Static digitron display
(1)数码管介绍
控制数码管显示:
138译码器和数码管,两者共同作用下控制数码管的显示
(2)数码管引脚定义:
1. 静态数码管显示
目标是使数码管的第三位单独显示数字“6”
步骤为:
1.控制138译码器选中LED6(标号从右到左) Y5—101
2.给予P0口段码数据,经过信号缓冲,再传输至公共段码端
总共有以下几种实现方式:
1.共阴极连接
2.共阳极连接:
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <REGX52.H>
void main() { P2_4=1; P2_3=0; P2_2=1; P0=0x7D; while(1) { }
}
|
完整的静态显示代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <REGX52.H>
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
void Nixie(unsigned char Location,Number); void main() { Nixie(2,3); while(1) { } }
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]; }
|
2. 动态数码管显示
利用辉光现象造成的人眼视觉残留,不断扫描数码管,来动态显示多位数字。
可看成静态数码管的快随迭代显示。
控制原理
LED1–8的一端直接连在138译码器的输出端
(1) 138译码器:
实际上就是用3个输入口控制8个输出口,以达到减少I/O口的目的,进而提高工作效率
P2_3口,P2_4 口,P2_5口,是”二进制“
(2) 74HC245:
74HC245(双向数据缓冲器)其实是芯片,筛选弱信号通过芯片,过滤掉强信号
因为低电平的驱动能力强(LED在低电平驱动时远亮与被高电平驱动时)
排阻:
动态显示原理
控制数码管单独静态显示:
动态数码管显示就是轮流“静态”显示,利用人的视觉残留从而实现动态效果
消影
即使能够利用视觉残留达成动态显示的效果,实际上依旧存在”窜位“问题
视觉上”1、2、3“,依旧在闪烁
这时就需要消影
综述
单片机直接扫描数码管:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include <REGX52.H>
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
void Nixie(unsigned char Location,Number);
void Delay(unsigned int xms);
void main() { while(1) { Nixie(1,1); Nixie(2,2); Nixie(3,3); } }
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; }
void Delay(unsigned int xms) { unsigned char i, j; while(xms--) { i = 2; j = 239; do { while (--j); } while (--i); } }
|
四、模块化编程及LCD1602调试工具
1.模块化编程
(1) 介绍
模块化的编程可以提升效率,避免主函数显得冗长,使代码容易阅读。
(2) 框图表示
模块化编程即使就是一个预编译的过程,例如在C语言中经常使用的
头文件
实际上,就是把函数的定义与声明分开来,使接口和实现分离开来
(3) 预编译
一般来说,预编译的实现如下:
有两个文件
一个是以结尾的头文件(用于声明函数)
还有一个是以结尾的C语言文件(用于定义函数)
.h
1 2 3 4 5 6 7
| #ifndef __DELAY_H__ #define __DELAY_H__
void Delay(unsigned int xms);
#endif
|
.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void Delay(unsigned int xms) { unsigned char i, j; while(xms--) { i = 2; j = 239; do { while (--j); } while (--i); } }
|
(4) 注意事项
例如:
调用的函数一定要声明,否则会报错
1 2 3 4 5 6 7
| #ifndef __NIXIE_H__ #define __NIXIE_H__
void Nixie(unsigned char Location,Number);
#endif
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <REGX52.H> #include "Delay.h"
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
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; }
|
2. LCD1602调试工具
在插入LCD显示屏后由于引脚的冲突,数码管将不再亮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <REGX52.H> #include "LCD1602.h"
void main() { LCD_Init(); LCD_ShowChar(1,1,'A'); LCD_ShowString(1,3,"Hello"); LCD_ShowString(1,9,"ZZH"); LCD_ShowNum(2,1,123,3); LCD_ShowSignedNum(2,5,-66,2); while(1) { }
}
|
每秒加1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <REGX52.H> #include "LCD1602.h" #include "Delay.h"
int a=0;
void main() { LCD_Init(); while(1) { a++; Delay(1000); LCD_ShowNum(1,1,a,3); } }
|
五、 矩阵键盘
1. MatrixKey简介
(1) 矩阵按键
这种矩阵按键能很好的节省I/O口,提升工作效率。
工作方式
若有多行/列会无法判断
I/O口原理
C51的I/O口是“弱上拉,强下拉”模式
手绘简图介绍:
(2) 扫描
数码管快速扫描,但是会占用CPU的时间
显示器/手机屏幕:(利用矩阵的方式)对横竖交错的像素点进行扫描 |
(3) MatrixKey代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <REGX52.H> #include "Delay.h"
unsigned char MatrixKey() { unsigned char KeNumber=0; P1=0xFF; P1_3=0; if(P1_7==0) { Delay(20); while(P1_7==0) { } Delay(20); KeyNumber=1; } return KeyNumber; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <REGX52.H> #include "Delay.h" #include "LCD1602.h" #include "MatrixKey.h"
unsigned char KeyNumber;
void main() { LCD_Init(); LCD_ShowString(1,1,"Hello ZZH"); while(1) { KeyNumber=MatrixKey(); if(KeyNumber) { LCD_ShowNum(2,1,KeyNumber,2); } }
}
|
2. 矩阵键盘密码锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void main() { LCD_Init(); LCD_ShowString(1,1,"Password:"); while(1) { KeyNumber=MatrixKey(); if(KeyNumber) { if(KeyNumber<=10) { Password*=10; Password+=KeyNumber%10; } LCD_ShowNum(2,1,Password,4); } }
}
|
简易的密码锁代码L:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include <REGX52.H> #include "Delay.h" #include "LCD1602.h" #include "MatrixKey.h"
unsigned char KeyNumber; unsigned int Password,Count;
void main() { LCD_Init(); LCD_ShowString(1,1,"Password:"); while(1) { KeyNumber=MatrixKey(); if(KeyNumber) { if(KeyNumber<=10&&Count<4) { Password*=10; Password+=KeyNumber%10; Count++; } LCD_ShowNum(2,1,Password,4); if(KeyNumber==11) { if(Password==521) { LCD_ShowString(1,14,"OK "); } else{LCD_ShowString(1,14,"ERR");} Password=0; Count=0; LCD_ShowNum(2,1,Password,4); } if(KeyNumber==12) { Password=0; Count=0; LCD_ShowNum(2,1,Password,4); } } }
}
|
六、 定时器
1. 定时器
(1)简介
(2)工作原理及模块
工作模式
工作模块
时钟
计数系统和中断系统
整体
定时器时钟
定时器0的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
| void Timer0Init(void) { AUXR &= 0xF0; TMOD |= 0x01; TL0 = 0x18; TH0 = 0xFC; TF0 = 0; TR0 = 1; ET0=1; EA=1; PT0=0; }
|
中断系统
流程图如下:
单片机中的中断资源
注意:
1.中断函数一般都比较“短”,执行的都是一些简单的自增或是赋值操作,因为时间较长的话会影响main函数,即不分主次
2.中断函数不能有形参和返回值
1 2 3 4 5
| void Timer0_Routine() interrupt 1 {
}
|
2. 按键控制LED流水灯模式&定时器时钟
按键控制LED流水灯模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <REGX52.H> #include "Timer0.h" #include "Key.h" #include <INTRINS.H>
unsigned char KeyNum,LEDMode;
void main() { P2=0xFE; Timer0Init(); while(1) { KeyNum=Key(); if(KeyNum) { if(KeyNum==1) { LEDMode++; if(LEDMode>=2)LEDMode=0; } } } }
void Timer0_Routine() interrupt 1 { static unsigned int T0Count; TL0 = 0x18; TH0 = 0xFC; T0Count++; if(T0Count>=500) { T0Count=0; if(LEDMode==0) P2=_crol_(P2,1); if(LEDMode==1) P2=_cror_(P2,1); } }
|
注意
1 2
| _crol_( , ) 这两个函数均为循环移位函数,可循环(左/右)移动二进制位 0x01 》0x02》0x04 若移到最高位则循环
|
定时器时钟代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include <REGX52.H> #include "Delay.h" #include "LCD1602.h" #include "Timer0.h"
unsigned char Sec=58,Min=56,Hour=22;
void main() { LCD_Init(); Timer0Init(); LCD_ShowString(1,1,"Clock:"); LCD_ShowString(2,1," : :"); while(1) { LCD_ShowNum(2,7,Sec,2); LCD_ShowNum(2,4,Min,2); LCD_ShowNum(2,1,Hour,2); }
}
void Timer0_Routine() interrupt 1 { static unsigned int T0Count; TL0 = 0x18; TH0 = 0xFC; T0Count++; if(T0Count>=1000) { T0Count=0; Sec++; if(Sec>=60) { Sec=0; Min++; } if(Min>=60) { Min=0; Hour++; } if(Hour>=24) { Hour=0; } } }
|
七、串口
1.串口通信
2.串口向电脑发送数据&电脑通过串口控制LED
串口每隔一秒向电脑发送数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <REGX52.H>
void UART_Init() { SCON=0x40; PCON|=0x80; TMOD &= 0x0F; TMOD |= 0x20; TL1 = 0xF4; TH1 = 0xF4; ET1= 0; TR1=1;
}
void UART_SendByte(unsigned char Byte) { SBUF=Byte; while(TI==0); TI=0; }
|
主函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <REGX52.H> #include "Delay.h" #include "UART.h"
unsigned char Sec;
void main() { UART_Init(); while(1) { UART_SendByte(Sec); Sec++; Delay(1000); }
}
|
电脑通过串口控制LED
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <REGX52.H> #include "Delay.h" #include "UART.h"
void main() { UART_Init(); while(1) {
}
}
void UART_Routine() interrupt 4 { if(RI==1) { P2=~SBUF; UART_SendByte(SBUF); RI=0; }
}
|
八、LED点阵屏
点阵屏显示笑脸:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <REGX52.H> #include "Delay.h"
sbit RCK=P3^5; sbit SCK=P3^6; sbit SER=P3^4;
#define MATRIX_LED_PORT P0
void _74HC595_WriteByte(unsigned char Byte) { unsigned char i; for(i=0;i<8;i++) { SER=Byte&(0x80>>i); SCK=1; SCK=0; } RCK=1; RCK=0;
}
void MatrixLED_ShowColumn(unsigned char Column,Date) { _74HC595_WriteByte(Date); MATRIX_LED_PORT=~(0x80>>Column); Delay(1); MATRIX_LED_PORT=0xFF; }
int main() { SCK=0; RCK=0; while(1) { MatrixLED_ShowColumn(0,0x3C); MatrixLED_ShowColumn(1,0x42); MatrixLED_ShowColumn(2,0xA9); MatrixLED_ShowColumn(3,0x85); MatrixLED_ShowColumn(4,0x85); MatrixLED_ShowColumn(5,0xA9); MatrixLED_ShowColumn(6,0x42); MatrixLED_ShowColumn(7,0x3C); }
}
|
使用文字取模软件来读取点阵数据
横向取模,高位在上(不用选取字节倒序)
C51自动生成
动态动画显示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <REGX52.H> #include "Delay.h" #include "MatrixLED.h"
unsigned char Animation[]={ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xFF,0x18,0x18,0x18,0xFF,0x00,0x0E,0x15, 0x15,0x15,0x09,0x00,0x7E,0x01,0x02,0x00, 0x7E,0x01,0x02,0x00,0x0E,0x11,0x11,0x0E, 0x00,0x7D,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, };
int main() { unsigned char i,Offset,Count=0; MatrixLED_Init(); while(1) { for(i=0;i<8;i++) { MatrixLED_ShowColumn(i,Animation[i+Offset]); } Count++; if(Count>10) { Count=0; Offset++; if(Offset>40) { Offset=0; } } } }
|
九、DS1302实时时钟