第一部分:数码管
一、基础知识
1、数码管引脚定义(单个数码管)
上方 的为 共阴极连接
下方 的为 共阳极连接
把 “8”分为 8个段,分别为“A、B、C、D、E、F、G、DP(顺时针)" ,正好对应寄存器的 8个字节 。
引脚定义
1.1引脚类型
若其为共阴极连接
2、数码管引脚定义(多个数码管)(也称:静态数码管显示)
拥有 12 个引脚
上方 为 共阴极 ,下方 为共阳极
二、操作分析
1、点亮数字 6 (单个数码管)
点亮 A、C、D、E、F、G段
因为是共阴极 ,设阴极接地 ,为低电平 (数字 0)。要点亮 数字 6 ,则要改变另一端 为高电平 。其段码 :1011 1110; 若是 共阳极 ,则阳极接Vcc ,为高电平 (数字 1)。段码 与共阴极相反 。
2、点亮数字 1(多个数码管)(也称:静态数码管显示)
点亮第三块数码管的 B、C段
因为要使第三块数码管亮 数字1 ,又因为是共阴极 ,所以可把 12、9、6号引脚 都接高电平 ,8号引脚 接低电平 。【 这样接的话,第一、二、四块数码管不会亮,二极管不导通 嘛!】
再使得第三块数码管的 B、C段 的7,4号引脚 都接高电平 【二极管可以导通 ,就可点亮 第三块数码管的 A、B段,即点亮数字 1】
但是这种数码管只能 **几个数码管同时显示相同是数字。**要同时显示不同数字的,需要用到 动态数码管显示。
3、我的数据手册中的电路图
图中是一个共阴极 的数码管。所以下方要配 阳码
图中 LED8、LED7、LED6、LED5、LED4、LED3、LED2、LED1 是接在 138译码器 上的输出端 的。
此时,我们需要让其中一个 端口输入为 0 (低电平 ),其余 的均为 1(高电平 )。
138译码器 可以完成上面的功能。它也可以用 左边的 3个输入端 来控制 右边的 8个输出端 。【8条线减少到了3条线,可以减少单片机的 IO口】- 左下角的三个引脚,称为
“使能端” 。(相当于一种电源开关,若是电平有效 则其会工作,否则不工作。)
6号引脚 接Vcc(高电平 ),4、5号引脚 接 0(低电平 ),其即可工作。
把 C、B、A 高低位排序 后,转化为 10进制 ,转化完的数 就对应着输出 。
可以把各个位权 写出来,如下:
| C(4) | B(2) | A(1) | 转化完后的结果 | Y0 | Y1 | Y2 | Y3 | Y4 | Y5 | Y6 | Y7 |
---|
电平 | 0 | 0 | 0 | 0×4+0×2+0×1=0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 电平 | 0 | 0 | 1 | 0×4+0×2+1×1=1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 电平 | 1 | 0 | 1 | 1×4+0×2+1×1=5 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
注意:Y0接的是 LED1、Y1接的是 LED2…以此类推。
三、实际操作
1、静态数码管显示
要让 LED6 显示 6
点亮A、C、D、E、F、G段 ,阳码(从下往上读 )为 0111 1101 (十六进制为 7D )
#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)
{
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);
while(1)
{
}
}
2、动态数码管显示
虽然它有显示 1 2 3 但是有叠影
所以,需要加:
消影
因为它是 这样的一个过程:位选 段选 位选 段选 位选 段选 会串位
所以 我们要添加 清零
结果为:
四、数码管的驱动方式
1、单片机直接扫描
硬件设备简单,但会消耗大量单片机CPU时间 。
2、专用驱动芯片
内部自带显存、扫描电路,单片机只需告诉它显示什么即可 。
TM1640 两根线可以控制 16个IO口
第二部分:模块化编程
一、基础知识
1、传统方式编程
所有的函数均放在main.c里 ,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路 。
2、模块化编程
把各个模块 的代码放在不同的.c 文件 里,在.h文件 里提供外部可调用函数的声明 。对于其它.c文件,当我们想使用其中的代码时,只需要 #include"XXX.h:" 即可。使用模块化编程可极大的提高代码的可阅读性、可维护性和可移植性 等。
2.1注意事项
- .c文件:函数、变量的定义;
- .h文件:可被外部调用的函数、变量的声明;
- 任何自定义的变量、函数在调用前必须有定义或声明(同一个.c);
- 使用到的自定义函数的 .c文件必须添加到工程树中参与编译;
- 使用到的.h文件必须要放在编译器可寻找到的地方(工程文件夹根目录、安装目录、自定义)。
3、C_预编译
C语言的预编译以#开头 ,作用是在真正的编译开始之前,对代码做一些处理(预编译)。
此外还有 #ifdef, #if, #else, #elif, #undef等
第三部分:LCD1602调试工具
一、基本介绍
- 使用
LCD1602液晶屏 作为调试窗口 ,提供类似printf函数 的功能,可实时观察 单片机内部数据的变换情况,便于调试和演示; - 本课中的LCD1602代码属于模块化的代码,我们只需要知道所提供
函数的作用 和使用方法 就可以很容易的使用LCD1602。
1、函数尝试
#include <REGX52.H>
#include "LCD1602.h"
void main()
{
LCD_Init();
LCD_ShowChar(1,1,'A');
LCD_ShowString(1,3,"Hello");
LCD_ShowNum(1,9,123,3);
LCD_ShowSignedNum(1,13,-66,2);
LCD_ShowHexNum(2,1,0xA8,2);
LCD_ShowBinNum(2,4,0xAA,8);
while(1)
{
}
}
结果如下:
2、秒表
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
int Result=0;
void main()
{
LCD_Init();
while(1)
{
Result++;
Delay(1000);
LCD_ShowNum(1,1,Result,3);
}
}
结果如下:
二、矩阵键盘_介绍
- 在键盘中按键数量较多时,为了
减少I/O口的占用 ,通常将按键排列成矩阵形式; - 采用
逐行或逐列 的“扫描”,就可以读出任何位置 按键的状态。
1、扫描的概念
数码管扫描 (输出扫描) 原理 :显示第1位→显示第2位→显示第3位→…,然后快速循环这个过程, 最终实现所有数码管同时显示的效果。矩阵键盘扫描 (输入扫描) 原理 :读取第1行(列)→读取第2行(列) →读取第3行(列),→…,然后快速循环这个过程,最终实现所有按键同时检测的效果。- 以上两种扫描方式的
共性 :节省I/O口。
1.1 逐行扫描(会与其它的IO口 有冲突)
- 以扫描第 2行为例,扫描前,上面的P17、15、14 都给1,P16 给0
- 当按下的按键 S5的时候,给了 P13为 0;按下 S6的,给了 P12 为0…以此类推。
| 扫描的行 | P17 | P16 | P15 | P14 | 按下X键,相应电平的变化 | P13 | P12 | P11 | P10 |
---|
电平 | 第2行 | 1 | 0 | 1 | 1 | S5 | 0 | 1(不变) | 1(不变) | 1(不变) | 电平 | 第3行 | 1 | 1 | 0 | 1 | S11 | 1(不变) | 1(不变) | 0 | 1(不变) |
1.2 逐列扫描
- 以扫描第 2列为例,扫描前,下面的P13、11、10 都给1,P12 给0
- 当按下的按键 S5的时候,给了 P16为 0;按下 S9的,给了 P15 为0…以此类推。
| 扫描的列 | P13 | P12 | P11 | P10 | 按下X键,相应电平的变化 | P17 | P16 | P15 | P14 |
---|
电平 | 第1列 | 0 | 1 | 1 | 1 | S5 | 1(不变) | 0 | 1(不变) | 1(不变) | 电平 | 第3列 | 1 | 1 | 0 | 1 | S11 | 1(不变) | 1(不变) | 0 | 1(不变) |
实现代码如下:
#include <REGX52.H>
#include "Delay.h"
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 <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKey.h"
unsigned char KeyNum;
void main()
{
LCD_Init();
LCD_ShowString(1,1,"Hello,World");
while(1)
{
KeyNum=MatrixKey();
if(KeyNum)
{
LCD_ShowNum(2,1,KeyNum,2);
}
}
}
三、矩阵键盘电子密码锁
#include <REGX52.H>
#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)
{
if(count<4)
{
Password*=10;
Password+=KeyNum%10;
count++;
}
}
LCD_ShowNum(2,1,Password,4);
}
if(KeyNum==11)
{
if(Password==2345)
{
LCD_ShowString(1,14,"OK ");
Password=0;
count=0;
LCD_ShowNum(2,1,Password,4);
}
else
{
LCD_ShowString(1,14,"ERR");
Password=0;
count=0;
LCD_ShowNum(2,1,Password,4);
}
}
if(KeyNum==12)
{
Password=0;
count=0;
LCD_ShowNum(2,1,Password,4);
}
}
}
编译器小技巧
添加快捷方式
加快写代码的效率
“|”在哪里,双击添加后的 光标就会出现在哪里
心得:
关键不是那些已封装好的函数,而是要理清楚代码的逻辑结构。
|