单片机中,左边是高位,右边是低位。
1.LED
板子上的矩阵按键,是第二排开始1.2.3.4,第三排5.6.7.8,第一排7.10.11.12。
(因为引脚复用的缘故,点亮LED的时候数码管也会点亮。)
要想使LED_D2点亮,则在P2^0端口给个低电平。
sbit gLed1 = P2^0; 定义位变量。P2表示端口,0表示端口上的某个引脚
gLed1 = 0;//在全局变量前面都加一个小写的g,告诉我们自己这是全局变量,再首字母大写,后面的字母小写。等号两边有空格。
再在下面加个while(1);没有死循环,程序就会跑飞。
把j13跳线帽接在下面两个,第二排的LED灯才不会亮。
端口就是由若干个引脚构成的。该单片机有四个端口。
VCC是电源,GND是地。
startup code 起始代码,是用汇编语言来写的,来初始化c语言运行环境的一段代码。
单片机最后的程序一定是个死循环,第一种直接在末尾加while(1);空循环,或者在while(1)里面加内容。
LED闪烁的时候,如果延时函数时间太短暂,因为人眼视觉暂留效果,会一直发现LED是亮的,但亮度只有正常的一半。
51单片机是8位机,就是CPU处理的数据是8位的,32位的处理器是现在兴起的嵌入式系统的主流配置。8位就是char.
8位就是char,而unsigned char的范围是0~255.
char 和 unsigned char 都是8位,但是unsigned char范围是(0-255),char的范围是(-128~127)。
流水灯(跑马灯)
宏定义 #define LED_PORT P2 用整个端口操控
P2 是一个unsigned char 类型的变量,这是一个八位的变量。
宏定义具有可移植性,当端口改变的时候,直接改变代码里的宏定义就可以改变所有的。
宏定义中全部用大写字母,两个字母之间用_连接。
第一种方法分别点亮:
LED_PORT = 0xfe;//第一颗灯
延时
LED_PORT = 0xfd;
LED_PORT = 0xfb;
LED_PORT = 0xf7;
第二种方法点亮:
(1<<2-1=2)
先进行四则运算,再进行位运算。
不要把流水灯端口问题想得太复杂,
LED_PORT = (0xff & ~(1<
一个数码管有8个LED灯
数码管共阳极就是8个LED灯阳极接在了一起,且接在了VCC上。给0就亮
而共阴极就是每个引脚接到GND上,给1就亮。
板子上的数码管是共阴的
COM就是共端,就是LED的阴极,而a,b,c,d,e,f,g,dp是阳极,给1是亮,给0是不亮。此图此板COM口一定要给0,
数码管显示b只能显示小写的b,否则与8重复,d也是小写的d.
每一个数字的显示对应着一个段码。
不共的阳极端接到了74HC573芯片上。该74HC573芯片作用:增强电驱动能力。
其实P0口可以直接与LCD相接,但是驱动能力不够,显示的亮度就很弱。
COM口,共阴极端就接在了74S138上。
74S138就是典型的38译码器:3个输入决定8个输出。
然后查找74S138的真值表:
G1和G2在接线时已经接好了:
G1接VCC,G2接GND,所以直接从第三排开始看,G1是H,G2是L。八个数码管同时只能有一个数码管工作。(如图),只有为L的时候那个数码管才会亮。
板子上的晶振为12M
设置单片机小精灵,晶振为12MHz,12T。
静态数码管显示:
跳线帽,外层是绝缘塑料,内层是导线材料,插在跳线针上,将两根跳线针连接起来,当跳线帽扣在两根跳线针上时是接通状态,有电流通过,我们称之为ON;
反之不扣上跳线帽时,就说明是断开的,称之为OFF。
控制数码管是J6,点阵屏是J13.
J6控制74HC573,74HC573是高电频工作,无论接到上面或者VCC都是高电频,因为单片机,上电默认引脚是输出高电频。
J13控制点阵屏的74HC595芯片,因为数码管显示不需要点阵屏,所以就不让74HC595工作,74HC595是低点频工作,所以将跳线帽与VCC相连,接在下面,高电频不让74HC595工作。
单个数码管静态显示步骤:
先宏定义声明共阴极数码管段码经74HC573接P0端口。
#define DIG_PORT P0;
再位声明三个38译码器输入口ABC(全局变量)
sbit gA = P2^2;
sbit gB = P2^3;
sbit gC = P2^4;
再输入共阴数码管显示0-F的段码编码值,定义一个数组
unsigned char code gDuanMa[16] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
然后就可以编辑自己的显示函数
void DIgDisplay(void)
{
unsigned char i = 0;
//然后选择要显示的是哪个数码管,再根据真值表进行判断
//让右边第一个灯亮
gA = 0;
gB = 0;
gC = 0;
for(i=0;i<16;i++)
{
Dig_Port = gDuanMa[i];
Delay(800);
}
}
此处LE为高电频使能。
LE接VCC高电频时,就会始终使能。当P0变化时,LCD也在变化。
当LE接P10时,我们就可以通过软件给P10赋值,当给1的时候相当于接VCC,而当给0的时候,通路就被截断了。无论P0怎么变化,LCD也不会变化。
截断效果(因为端口复用效果,几个功能想同时存在,如果想同时驱动数码管且同时驱动点阵屏的时候,当P0端口输入,想给点阵输出时,就可以暂时关闭数码管,就将跳线帽接到P10和LE上,且将P10设置为0)
在51单片机中,引用头文件#include
crol 字符循环左移
cror 字符循环右移
动态数码管,同一时间只有一个数码管工作,但是利用人眼视觉暂留,所以就会有在较短的时间内会有多个数码管显示,并且要刷新。
动态数码管显示步骤:
先宏定义共阴极的正极端口
#define DIG_PORT P0;
重命名类型,使代码简化。
typedefine unsigned char u8;
typedefine unsigned int u16;
位声明全局变量——38译码器的输入端口
sbit gA = P2^2;
sbit gB = P2^3;
sbit gC = P2^4;
然后写共阴数码管显示0-F的段码编码值。
unsigned char code gDuanMa[16] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
两种动态显示数码管的函数
1.分别显示:
先查真值表左边第一个数码管的位置,
然后设置
gA = 1;
gB = 1;
gC = 1;
DIG_PORT = gDuanMA[1];
Delay(1);
DIG_PORT = 0x00; // 消隐,若没有消隐,则第二个数码管可能会出现不该闪烁的地方闪烁的问题。
第二个数码管设置:
gA = 0;
gB = 1;
gC = 1;
DIG_PORT = gDuanMa[2];
Delay(2);
DIG_PORT = 0x00;
2.第二种方法动态显示数码管:
u8 i = 0 ;
for(i = 0 ; i<8 ; i++)
{
swtich (i)
{
case 0:
gA = 1; gB = 1; gC = 1;
break; //显示第0位
case 1:
gA = 0; gB = 1; gC = 1;
break; //显示第1位
case 2:
gA = 1; gB = 0; gC = 1;
break; //显示第2位
case 3:
gA = 0; gB = 0; gC = 1;
break; //显示第3位
case 4:
gA = 1; gB = 1; gC = 0;
break; //显示第4位
case 5:
gA = 0; gB = 1; gC = 0;
break; //显示第5位
case 6:
gA = 1; gB = 0; gC = 0;
break; //显示第6位
case 7:
gA = 0; gB = 0; gC = 0;
break; //显示第7位
default:
break;
}
}
DIG_PORT = gDuanXuan[i+1];
Delay(1);
DIG_PORT = 0X00;
}
蜂鸣器发声原理:
蜂鸣器内部有两个平行的金属薄片,通电后,一个带正点,一个带负点,然后异性相吸,产生碰撞,碰撞之后产生振动,而振动产生声音。蜂鸣器碰撞频率很快,所以声音长。
蜂鸣器原理图:
BZ接到了ULN2003D芯片上,该芯片作用就是增强驱动。
如图:IN与OUT一一对应,所以BZ接的是OUT5,对应的就是IN5,对应的引脚就是P1^5,
所以蜂鸣器对应的引脚就是P1^5.
当给P1^5低电频的时候,两片金属就会合起来产生振动。
当连续给P1^5高低高低交换的电频时且频率很快,蜂鸣器就会产生声音。
当设置蜂鸣器开关的时候需要延时一下
延时作用:当没有延时时,开和关的速度非常快,频率太快,导致声音音调也特别高,可能会超出人耳所听到的范围,可能就听不到。所以延时1ms。
蜂鸣器开和关需要一直刷新,只有持续的开关,才能产生声音。
声音音调由频率决定,频率越高,音调也高,听起来就越刺耳
51单片机频率的计算:
板子上用的是12MHZ的晶振——外部晶振
51单片机默认的是12T的模式——时钟频率 = 晶振频率/12 = 1MHZ ,
所以CPU执行一条指令所用的时间是 1 / 12M = 1us。
人耳能听到的频率是20~20KHZ。
无源蜂鸣器就是内部没有电源(变换的电源),所以需要外部给变化的电。就是现在用的
有源蜂鸣器,内部有可以振动的电源,当外部一通电,内部就可以自己产生通断变化的电,无需再改变外部的电源,且振动频率已经定义好,不能再更改。
有源蜂鸣器一般比较无源蜂鸣器要贵一些。
蜂鸣器响的具体步骤:
引用头文件#include
#include
typedef unsigned char u8;
typedef unsigned int u16;
根据电路图,位声明
sbit gBuz = P1^5;
定义蜂鸣器振动一次的函数
void BuzzerOnOff(void)
{
gBuz = 1;
delay1ms(); //延时是为了减小金属片振动的频率,否则音调太高
gBuz = 0;
delay1ms();
}
响一次的时间是2ms,则可以根据2ms设置蜂鸣器响的时间,用for循环来实现。
8*8的LED点阵屏
分为单色和双色
单色:每一个圆点里面只有一颗LED灯,
三色:每一个圆点里面有三颗不一样颜色的LED灯,一般为红绿蓝(三原色)。
这三种颜色可以按照一定的比例组成各种的颜色,所以三色屏也就是所谓的彩色屏,可以显示各种颜色。
pitch的概念:每两个圆点之间的距离。中心之间的间距,一般以毫米为单位。
例如:两个点之间的距离为10mm,那么就叫做P10.
手机细腻程度一般比电脑高,因为电脑尺寸比手机大的多,但是很多手机也是1080p
手机pitch小于电脑pitch
显示器件的几个概念:
像素:可以成像的最小单元(元素),在单片机上也就是每个点,此单片机上就有64个像素。
分辨率:就是像素在横向和纵向的个数。像素个数 = 分辨率两个数字相乘。
清晰度:由分辨率和pitch决定。在同样尺寸的长度来说,分辨率越高就越清晰;pitch越小看起来越清晰。
字模:让屏幕显示某个字的模型。控制某些灯亮,某些灯不亮。一排就是8个,一个字节,一共有八排,就是八个字节。用八个字节来表示一个显示页面(一帧图像)。
点阵屏原理图:
P00到P07一共有八个,D0到D7一共有八个
所以P0端口代表每一个的正极,D端口代表每一个的负极
P0端口就是连接的51单片机的端口,而D端口连接的是74HC595器件
74HC595是一个移位器:给一个串行的输入会转成一个并行的输出
串行输入:
就是在一个引脚上分8个不同时间来输出高低,把所有的数放在一个引脚上放进去。
并行:
在同一时间用8个不同引脚来表示高低。
74HC595移位器
就是将串行的输入转化为并行的输出
P3^4引脚(SER——SERIAL)
单片机通过P34引脚,分不同的时间,把8个位从P34送入,再在八个输出中把它并行地输出去
P35是整个芯片工作的时钟
P36是串行的时钟
74HC595的作用:
1.节省一些引脚(如LED点阵屏,需要P0和D16个引脚,而单片机上引脚十分有限,所以只分了8个引脚给它,然后通过74HC595,用三个引脚通过串转并,得到8个引脚,所以相当于只用了11个引脚达到了16个引脚的效果)
2.驱动能力强:单片机本身引脚可以点亮LED灯,但是点亮的强度弱
用74HC595点亮LED灯
八个并行输出端口都接到了LED的负极,LED的正极通过了J14的跳线帽,接到了VCC上面,把跳线帽放上去,再让并行端口输入低电频,就可以点亮LED灯。
这个所对应的LED灯就是LED灯的下面一排
一定要将J14的跳线帽接上
74HC595:
通过P3^4引脚把一个八位的数通过串行输入再通过并行输出
SRCLK是输入数据移位时钟,时序逻辑的时钟。RCLK是储存寄存器的时钟,串行数据经过8个时钟周期变成并行一个字节。
SRCLR是复位引脚,连在VCC上是给他逻辑电平1,表示永不复位。
RCLK是数据输入存储,必须是从低电平到高电平。
SRCLK是数据移位输入控制,必须是从低电平到高电平。
用74HC595控制LED亮的具体步骤:
先位声明
sbit RCK = P3^5;//RCLK是数据输入锁存,从低电平到高电平
sbit SRCK = P3^6;//SRCLK是数据移位输入控制,从低电平到高电平
sbit SER = P3^4;//SER送字节引脚进入
NOP指令也称为空指令,NOP指令不执行操作,但占一个程序,常用于延时或者精确计时。
a<<=n的含义就是,a等于a乘以2的n次方
定义函数
函数作用:通过74HC595串行移位发送一个字节出去
void Hc595SendByte(u8 dat)
{
u8 i = 0,j=0;
SCK = 0;//将数据移位输入控制初始化
RCK = 0;//将数据存储也初始化。
for(i = 0;i<8;i++)
{
SER = dat >>7;//将左边第一位数字存入数据锁存器中;
dat <<=1;//将数字往左移移位,将第二位数字放在了第一位上
SCK = 1;//改为上升沿,将数据传进去
j++;//NOP指令延时
j++;//延时,给时间将数据送进去,如果没有延时,可能就传不进去
SCK = 0;//再打开移位控制器
}
经过八次循环后,数据存储也送完了,所以将整个数据存储打开,传进去。
RCK = 1;
j++;//也要NOP延时,防止速度太快,数据未传入
j++;
}
单片机J13跳线帽要连接上面两个——芯片才能使能,才能用。
J14也要用跳线帽接上。——让LED正极接电源
最右边的一颗灯亮
D0到D7不仅接在了LED灯上,还接在了LED点阵屏上。
所以操作LED点阵屏只需要操作P0端口和74HC595芯片就可以了
8*8LED点亮规则:
74HC595负责LED点阵正极,最低位在每列上方,最高位在每列下方。置1亮
MATRIX_PORT负责LED点阵负极,最低位在每行右方,最高位在每行左方。置0亮
8*8LED点阵屏点亮:
J13用跳线帽接上面两个,OE负极接地才能与右边的使能。
先引用两个头文件
#include
#include
typedef unsigned char u8;
typedef unsigned int u16;
#define MATRIX_PORT P0 //点阵LED负极端口,决定列,且置0亮。
所以74HC595 点阵LED正极,且置1亮。
sbit RCK = P3^5;
sbit SCR = P3^6;
sbit SER = P3^4;
void Hc595SendByte(u8 dat)
{
RCK = 0;
SCK = 0;
u8 i = 0,j=0;
for(i=0;i<8;i++)
{
SER = dat>>7;
dat <<= 1;
SCK = 1;
j++;
j++;
SCK = 0;
}
RCK = 1;
j++;
j++;
}
让右边第一个LED亮的函数:
void MatrixDisplay1(void)
{
Hc595SendByte(0x01);//D端口正极,置1亮
MATRIX_PORT(0x7f);//P0端口接负极,置0亮
}
总结:
8*8LED矩阵:
74HC595控制D口,D口连接正极,置1亮,D口控制列矩阵,最低位是第一行,最高位是第二行
所以:让第一行亮 Hc595SendByte(0x01);
P0口连接负极,置0亮,控制行矩阵,最低位是最左一列,最高位是最右一列
所以:让最右边一列亮。MATRIX_PORT = 0x7f;
让最右下角的一个LED灯亮的函数:
void MatrixDisplay2(void)
{
Hc595SendByte(0x80);
MATRIX_PORT = 0x7f;
}
让最左下角的一颗LED灯亮的函数:
void MatrixDisplay3(void)
{
Hc595SendByte(0x80);
MATRIX_PORT = 0xfe;
}
让最左上角的一颗LED灯亮的函数:
void MatrixDisplay4(void)
{
Hc595SendByte(0x01);
MATRIX_PORT = 0Xfe;
}
让全屏点亮
void MatrixDisplay5(void)
{
hc595SendByte(0xff);
MATRIX_PORT = 0X00;
}
88字模提取软件,要以管理员身份运行。
在点阵上显示数字、汉字、英语等字模:
#include
#include
typedef unsigned char u8;
typedef unsigned int u16;
#define MATRIX_PORT P0
sbit RCK = P3^5;
sbit SCK = P3^6;
sbit SER = P3^4;
十六进制最多到F——15
设置一个数组,包含每一列的点阵列选值。
u8 gLineCode[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
字模由光盘中字模提取88软件生成
字模显示方式:单行
字模提取方式:逐列
字模提取格式:C51格式
void HC595SendByte(u8 dat)
{
RCK = 0;
SCK = 0;
for(i = 0 ; i < 8 ; i ++ )
{
//不一样的地方,字模要逐行提取
SER = dat & (0x01);
dat >>= 1;
SCK = 1;
j++;
j++;
SCK = 0;
}
RCK = 1;
j++;
j++;
}
void MatrixDisplay(u8 *zimo)
{
u8 i = 0;
while(1) //点阵屏要一直显示
{
for(i = 0 ;i<8; i++)
{
MATRIX_PORT = gLineCode[i]; //选定一列
Hc595SendByte(zimo[i]);
Hc595SendByte(0x00); //每一行要消隐
}
}
}
void main(void)
{
MatrixDisplay(gZhu);
}
查询方式处理独立按键
上面是矩阵按键,下面是独立按键
虽然按键外观看上去有四个脚,但是是两个两个相连的。
所以按键从电学方面来说,仅有两个脚。当按键按下去时,两个脚就连起来了
右侧引脚全部接地,左侧引脚接RXD(P30)TXD(P31)
所以相当于左侧引脚接在了P3(0、1、2、3)
上拉电阻:就是将电源高电平引出的电阻接到输出端,
上拉就是将不确定的信号通过一个电阻钳位在高电平,电阻同时起限流作用,
P32和P33通过一个10K的上拉电阻接到VCC上,
所以平时在按键不接通的时候,P32和P33引脚为高电平。
但是一当按键按下,右侧GROUND一导通,就会变成低电平。
单片机按键,一端接在引脚上,一端接在GND上,当按键未按下时引脚是高电平,按键一按下引脚就是低电平。
按键抬起时,引脚是高电平,按键按下时,引脚是低电平。
查询方式(轮询方式)处理按键:CPU去读取引脚是高电平还是低电平,如果是高电平1,则没有人按下。
按键是一个异步事件:事件的发生无法预料。所以需要反复查询(循环中)
而同步事件,是与CPU约定好的事件
独立按键对应的引脚并不是按顺序的
是P31 P30 P32 P33
不按的时候按键的键值为0
一种抖动,外部干扰,抖动不会超过10ms
该方法不能考虑同时按下两个按键的时候
以查询方式检测独立按键的具体步骤:
实现现象:分别按下4个独立按键中的1个,点亮相应的1颗LED灯,按键松手灯灭(上一排的led灯)
J14控制下面一排LED灯亮,所以将跳线帽拔出,防止下面一排LED灯亮对实验结果造成干扰
而控制上面一排的LED灯亮,需要给P2端口低电平。
独立按键的编码是S13,S14,S15,S16。
而S13对应的端口是P3^1
S14对应的端口是P3^0
S15对应的端口是P3^2
S16对应的端口是P3^3
#include
#include
typedef unsigned char u8;
typedef unsigned int u16;
#include KEY_PORT P3 //独立按键接在P3.0到P3.3
#include LED_PORT P2 //LED灯接在P2端口
扫描4个独立按键,返回键值,有按键按下的时候返回对应的值,无按键按下的时候返回0.
u8 KeyScan(void)
{
u8 val = 0; //设置一个中间值来接受按键的值,且对中间值进行判断。
val = KEY_PORT;
val &= 0x0f ; //按键没有按下的时候低四位全是1
if(val != 0x0f) //当有按键按下的时候
{
delay10ms(); // 延时10ms软件防抖,一般外部抖动在1ms周围
//再次接收按键的值
val = KEY_PORT;
val &= 0x0f;
if(val != 0x0f) //再次判断有按键按下
{
判断哪个按键被按下
if((val & (1<<0))==0)
{
return 2;
}
if(val & (1<<1)==0)
{
return 1;
}
if(val & (1<<2)==0)
{
return 3;
}
if(val & (1<<3)==0)
{
return 4
}
}
return 0;
}
}
根据传参的值点亮相应的LED灯的函数
void LedOn(u8 n)
{
swtich (n)
{
case 0:
}
}