学习引脚的功能
9引脚
RXD:串行输入口
TXD:串行输出口
单片机通过电脑下载程序就是通过这两个IO口,单片机内部有固件程序,上电后先和计算机通信一次,确定计算机是否有发送下载命令,如果没有,就执行内部程序,如果有,就进行交互,把要下载的程序下载进去。所以需要冷启动,单片机只有启动的时候才会检测是否下载。
INT0、INT1:外部中断0、外部中断1
T0、T1:定时器0外部计数输入,定时器1外部计数输入(有定时器和计数器功能),定时时内部自动定时间,与管脚无关。外部计数器的输入端:给端口加一个方波(高低电平变化的波形)设置内部寄存器设置为计数器后,它就可以数数,数你输入了多少方波,即一共有多少次高低电平变化。做频率计,测一个信号源频率为多少,若为正弦波,通过比较器变为方波,然后输入到这个端口,写程序控制单片机进行计数,就可以做出计数器。三角波通过积分也可以变成方波。方波就可以直接读取频率了。
WR、RD:外部数据存储器的写选通、外部数据存储器的读选通(暂时不用,用的是片内存储器,等对单片机了解加深后,自己就会明白)
P3.0~P3.7每一个都有相应的寄存器设置,并不是一个寄存器设置了7个。
XTAL1、2:晶振输入端,外部加晶振时用
复位电路
开关按下后,VCC将于1K电阻这里接通,电容隔直通交,根据分压(1/(10+1) *5),RST这里电压接近5V,按下去的时候肯定大于24个时钟周期(24个时钟周期很短)将复位。上电时也是自动复位,上电时电容充电,两个极板就会有电压,然后会复位,充完点后会慢慢放电,通过10K电阻流到地。放电时间:τ(tao)=根号下(RC)
晶振电路
两个电容的作用是上电时帮助晶振Y1起振,晶振正常工作时是输出正弦波,有时不加电容可能起不来,上电后给电容充电,电容放电帮助起振。一般12MHz左右用30P电容,6MHz一般用20P。
29引脚(PSEN):一般不用,空着就可以
29-31,9一般都是用于编程的,at公司的的89c51必须要专门的编程器编程,编程时VPP要加12V电压才能把程序写进去,PROG(program),ALE:单片机正常工作时,可以输出1/6个时钟周期的脉冲(方波),若想检测单片机是否正常工作,这里放一个示波器,检测是否是晶振的1/6频率输出方波。
EA:内部程序存储器选择控制端,高电平时访问内部存储器,但在程序计数器值超过0xff时,即51单片机4KB,记值范围0~0xfffh,将自动转为执行外部程序,即超过内部程序时会自动转为执行外部存储器的程序。低电平时只访问外部存储器的程序,不论是否有内部程序存储器,对于8031来说,因为没有内部存储器,该脚必须接地,只能选择外部存储器。现在很少用外接的程序存储器,科技发展快,单片机内的存储空间越来越大。所以一般EA接高即可。
单片机这里EA直接高电平,内部执行程序。
P0是双向8位三态(高电平,低电平,高阻态)IO口,与P1~P3不同,他们是8位准双向。P1~3线内均有固定的上拉电阻,P0没有,当P1~3做输入使用时,要向该口先写1,准双向,即要准备一下才能成为双向口,输出时可以直接用,准双向IO口没有高阻的浮空状态,即无高阻状态,P0线内无固定上拉电阻,由两个MOS管串接,既可开路输出,又可以处于高阻的浮空状态,故称之为双向三态IO口。
至此管脚介绍完毕,29PSEN不用记,30ALE正常工作时1/6晶振频率的方波,31EA程序从哪里执行的标志,30、31的第二功能VPP、PROG编程用的,10~17第二功能边学边记,没必要一次记住。学单片机就是通过学程序把这32个IO随意控制,设计电路要不断的积累经验(网上查资料,找书看),调试。
特殊功能寄存器有P0~P3,PSW、IP、IE,实际上对单片机本身来说开放的IO口,P0~P3就是4个寄存器,对他们操作能直接体现出高低电平变化,每个寄存器都占有一个地址。 (之前已经分散的说过)
点阵型发光二极管,其中一个管有的是能发三种颜色(三原色)可以控制其中的一个两个或者三个发出不同的颜色。这种不同的颜色变化是一个点一个点构成的,会形成真彩,所有颜色都出现了,电视信号接收到后经过信号分析,送到屏幕就成了电视。所有的电子设备都可以做,空调也可用单片机做出,内部有变频器,单片机控制它的频率变化,然后运行空气压缩器,空气压缩器运行压缩空气产生空气变化,这是制冷,加热,内部有温度传感器,有加热管,温度到某一个温度自动把它停掉,用继电器切换加热管是否加热。功能强的单片机也可以直接控制CRT显示器,也是三原色的三根线控制,不过是输出的模拟信号,模拟量不同,输出的亮度不同,还有两根数据线X场、Y场,控制在屏幕显示的位置,再三根线控制颜色,不停地扫描就出现图形。(..基本废话,就当了解吧)
数码管是使用7段或者8段LED发光二极管显示的,七段就是不带“点”(dp)
共阴极就是发光二极管阴极接在一起,共阳极就是发光二极管阳极接在一起。接在一起的地方叫做公共端,公共端是接地还是接电源就是高低电平,是由单片机IO口决定的。
举例,若显示1,就是数码管bc两个led亮
然后高低位从高位dp到单位a,共阴极就是0x06,共阳极为0xf9
什么是段选,什么是位选?
总线形式画出来的,P20~P23位选,P00~P07段选,段选加上拉电阻,单片机IO口输出的电流很小,可能不到1mA,发光二极管点亮需要5~10mA电流,所以需要上拉电阻。当P00位高,P20位低时,电流会从IO口和VCC一起流经a然后到P20,这种是最简单的接法,我们电路板不同,但原理类似。疑问:如果P00位低,P20位低,那么电压不就直接从VCC进入IO口和LED了吗?
郭天祥教学实验板,图中有错误,红线划掉了,每个管脚控制一个段,WE是公共端阴极,所有段选全部连在一起,图中看到每个数码管的e都是1,位选独立的。
此处位选寄存器的12是AD芯片的片选chip select,低电平有效,模数转换时才用,这里一直给高电平
段选和位分别由锁存器控制。第一个控制段选,第二个位选,锁存器的输入都接在了D0~D7,即接在了P0,都有10K上拉电阻,P0位三态状态,无上拉电阻,所以无法给高低电平操作,加上拉电阻后,一上电就是高电平,此为原因,记得以后设计电路时单片机P0要加上拉电阻,大小10K,接法如此。看到这里为什么两个锁存器都接在了P0?原因锁存器可以利用11引脚来控制是否使用,高电平,输入输出直通,低电平,输入输出断开,输出保持原来的值(其实就是高电平变低电平的这个下降沿使其锁存的)。忘接了就回去看。例如,先让第二个锁存器11为高,控制位选选择控制某一个数码管,然后11位低,保持输出不变,然后打开段选的锁存器即可,最后再锁住。
这就是用一组IO口控制6个数码管,最多可以八个,位选(12、13还没放数码管)那里可以放8个,而之前那种是用了12个IO口控制4个数码管,浪费IO口,占用资源。
编程开始
如何让第一个数码管亮1,其它不亮?思考下
首先其它不数码管不亮,单片机上电后IO口为高电平,那么所有的位选和段选都是高电平,所以所有的LED都是不亮的,不亮的原因当然就是数码管的LED阳极和阴极都是高电平喽。接下来我们要让第一个数码管亮,那么首先要打开位选锁存器来选择某一个数码管,先让位选锁存器11引脚为1(P2^7=1,当然肯定不能这样用,毕竟要先定义),这样就能控制位选锁存器了,然后是让第一个亮,其他的不用管,那么第一个数码管的位选为0(位选是数码管LED的阴极嘛,忘了就会取看看),其它的位选为1,那么就是0xfe,这样数码管就选择完了,那么就要关闭这个锁存器,11阴极为低。接下来是控制段选了,来让数码管亮1,首先要打开段选锁存器,让他的11引脚为1(P2^6=1),这样就打开了,亮1,那么就是bc为1,其它为0,那么就是0x06,控制完段选后就关掉锁存器,11位低,这样就全都操作完了。
这里我是用我单片机的电路图
1 #include
2
3 sbit DUAN=P2^6;
4 sbit WEI=P2^7;
5
6 void main()
7 {
8 while(1)
9 {
10 //控制位选
11 WEI=1;
12 P0=0xfe;
13 WEI=0;
14
15 //控制段选
16 DUAN=1;
17 P0=0x06;
18 DUAN=0;
19 }
20 }
1 #include
2
3 sbit DUAN=P2^6;
4 sbit WEI=P2^7;
5
6 void main()
7 {
8 while(1)
9 {
10 //控制位选
11 WEI=1;
12 P0=0xf0;
13 WEI=0;
14
15 //控制段选
16 DUAN=1;
17 P0=0x06;
18 DUAN=0;
19 while(1);
20 }
21 }
这样呢就显示了4个1,先看下我代码里最后多了一个while(1),如果没有这条语句,在最外面的while(1)循环中,执行到WEI=0后,P0一开始是有值的,那么在DUAN=1时,P0此时还是0xfe,然后才会变成0x06,同样的在DUAN=0结束后P0=0x06,然后再一次循环开始,WEI=1,这时候P0也是有值的,是0x06,也就是出了第二三两个数码管都亮。这样在这种一直闪烁的情况下,由于执行的很快,会有一些错误,如下:
因此要加上while(1),不知道实体单片机会不会这样。
接下来让8个数码管从0计数到F
1 #include
2 #define uchar unsigned char
3 #define uint unsigned int
4
5 sbit WEI=P2^7;
6 sbit DUAN=P2^6;
7
8 void Delay1ms();
9 void delay(int n);
10
11 uchar code Table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
12 // 0 1 2 3 4 5 6 7 8 9 A B C D E F 无显示
13 //[]括号内可以不写,编译时会自动数元素数然后分配内存
14
15 uchar num;
16 void main()
17 {
18 while(1)
19 {
20 WEI=1;
21 P0=0x00;
22 WEI=0;
23
24 // DUAN=1;
25 for(num=0;num<15;num++)
26 {
27 DUAN=1;
28 P0=Table[num];
29 delay(1000);
30 DUAN=0;
31 }
32 // DUAN=0;
33 }
34 }
35
36 void delay(int n)
37 {
38 while(n--)
39 {
40 Delay1ms();
41 }
42 }
43 void Delay1ms() //@12.000MHz
44 {
45 unsigned char i, j;
46
47 i = 2;
48 j = 239;
49 do
50 {
51 while (--j);
52 } while (--i);
53 }
code表示编码表,写它编译完会放在程序存储器中,如果不写就放在了随机存储器,随机存储器是有限的,也就是数据存储器,51单片机是128字节,每定义一个char变量,例如char num;,这将占用一个字节,如果没用code那么这个char数组里面有多少个数就占用多少字节,而若是int数组,那么就占用2*元素数的字节。如果程序大了,变量多了,就不够用了,数据存储器很宝贵,要省着用,所以用多大的数据量就用多大的变量,例如能用char就不用int。
中断系统
所有微处理器最有用的就是中断,而且经常用到
51单片机有5个中断源,可以嵌套,就是A发生中,B事件来了,那么去执行B,结果执行B的时候,C事件发生了,那么就去执行C,当C执行完,接着执行B,执行完B,再去执行A。这里不讲嵌套,之后慢慢就会了,当然51单片机只有两级嵌套,嵌入式系统可以嵌套4、5级。
汇编中RETI是中断的返回条件,c语言没有,c语言执行完中断函数就自己回去了。
计算机的键盘、鼠标等都是有中断的,键盘通过发送一段扫描码,按一下发送一段,扫描码是有两个8位的数据过去,单片机检测到扫描码再分析判断出按得哪个键。单片机可以驱动键盘,也可以驱动显示器,可以用单片机做一个电脑,当然要求单片机性能高点。
可以看到1(中断号)是键盘,3是红外.....共23个
串行输入和串行输出是一个中断源ES,所以是5个中断源(EX0,ET0,EX1,ET1,ES)。串口暂时不用,这一位先不考虑。
下面第二个和第三张图片先不用看。
EA总中断,要想要有中断就要打开EA,EA=0关闭,CPU屏蔽所有中断请求,也称为CPU关中断,所以如果EA=0,那么P3口只能作为普通IO口,EA=1打开总中断,启用了第二功能。
要想启动外部中断0,那么要开启总中断(EA=1)和外部中断源(EX0=1),并且选择电平触发方式(IT0=0,IT0=1为从高到低负跳变沿触发),当P3^2有低电平输入,就去执行中断程序。
下面第一张图是串口的,先不用看
下面这个也看不懂,可以先不看
嵌套时,高优先级的可以在低优先级中断中继续中断
下面这个必须记住,记住优先级顺序就好,程序入口是汇编用的。
例如当这五个中断同时发生,那么先发生外部中断0,然后定时计数器0,外部中断1,以此类推。
他们的序号分别为0~4,等会写程序会用。
中断允许控制寄存器,字节地址A8H,所有能被8整除的寄存器都可以进行位寻址,就是可以直接操作某一位,例如在操纵P2时,我们就不能P2^3=0,而这里就可以EA(IE寄存器的最高位)=1。
打开头文件REG51.H或者52,可以看到定义了IE,然后又定义了IE的各个位
所有寄存器上电后,默认为0,所有默认为电平触发。(IT0=0)
下面是当P3^2变为低电平时会触发中断函数的代码。
1 #include
2 #define uchar unsigned char
3 #define uint unsigned int
4
5 sbit WEI=P2^7;
6 sbit DUAN=P2^6;
7 sbit LED0=P1^0;
8
9 void Delay1ms();
10 void delay(int n);
11
12 uchar code Table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
13 // 0 1 2 3 4 5 6 7 8 9 A B C D E F 无显示
14 //[]括号内可以不写,编译时会自动数元素数然后分配内存
15
16 uchar num;
17 void main()
18 {
19 EA=1;
20 EX0=1;
21 while(1)
22 {
23 WEI=1;
24 P0=0x00;
25 WEI=0;
26
27 // DUAN=1;
28 for(num=0;num<15;num++)
29 {
30 DUAN=1;
31 P0=Table[num];
32 delay(1000);
33 DUAN=0;
34 }
35 // DUAN=0;
36 }
37 }
38
39 void exter0() interrupt 0
40 {
41 LED0=~LED0;
42 }
43
44 void delay(int n)
45 {
46 while(n--)
47 {
48 Delay1ms();
49 }
50 }
51 void Delay1ms() //@12.000MHz
52 {
53 unsigned char i, j;
54
55 i = 2;
56 j = 239;
57 do
58 {
59 while (--j);
60 } while (--i);
61 }
可以看到,中断函数不需要声明,中断函数返回值为空(void),中断函数后面有一个interrupt,表示是中断服务程序,还要有一个标号,这里我们用的是外部中断0,所以为interrupt0,上面提到过。上面代码,如果P3^2一直为低电平,将会一直在中断函数中,总程序将不执行了。
若为电平触发方式(就是上面这种),在中断服务返回之前,外部中断请求输入必须无效,即变为高电平,否则CPU返回主程序后就会再次返回中断,就会发生上面总程序不执行的现象。所以电平触发方式适合于外部中断以低电平输入,而且中断服务程序能清除外部中断请求源,即外部中断请求输入变为高电平。
所以我们这里我们该用跳边沿触发方式,也就是在代码EX0=1;后面加上一句IT0=1;或者TCON=0x01;这样中断程序只有在我们将电平从1变为0才会触发(负跳变沿)
接下来讲定时计数器
两个功能:定时,计数(就是P3^4,P3^5两个IO口,接了东西会自动计数),我们现在只讲定时。
我们用的delay函数就是软件定时,而定时器是单片机内另一个硬件,和单片机主CPU是隔离开的,设置好自动运行,时间一到只是告诉CPU我触发中断。
TMOD的高四位控制T1定时器的工作方式,第四位控制T2定时器的工作方式。
作为计数器时,是由T0或T1引脚输入的外部脉冲源,作为定时器,是由时钟周期的输出脉冲经12分频,即12个振荡周期,每12个振荡周期,计数器加1,12个振荡周期就是一个机器周期。
我们一般让GATE=0即可,工作方式我们主要讲解方式1,会了这个其它也就会了。GATE=1一般用于方波检测。
TCON的低四位是用于控制外部中断的,忘了就回去看看,TF1一般由硬件自动置1清0,所以不需要控制。
下面两张图是方式0的,方式0是13位计数,所以不看了。
下面讲方式1
一共16位,满的话就是全1,也就是2的16次方,初值可由自己设定,比如全0或其它,设为N,每一个机器周期则+1,那么计数个数就如上所示。
如果我们定时50ms,那么就需要加50000次,那么就不能从0加到65536,初值就需要为15536(65536-50000),但是要把它分给TH0和TL0(高八位和低八位),那么TH0=(65536-50000)/256,TL0=(65536-50000)%256,2的8次方是256,/256,说明有多少个256,那么进位到高位多少,%256就是不能进位的了。
由此可以看出最多定时65ms,要定时1s怎么办?那么可以进入20次中断,每次中断50ms,就达到了1s。每进一次中断就在中断中对一个变量+1,加到20,主程序中判断什么时候到20,到了20就是1s。
方式2用于串口通信,现在不用
方式3也不用
这样就开始定时了,满后就进入定时中断函数(后面是interrupt 1),这时要重新赋值,不然就从0开始定时了,就是65ms了。
下面是用定时器完成的数码管计数。
1 #include
2 #define uchar unsigned char
3 #define uint unsigned int
4
5 void Delay1ms();
6 void delay(int n);
7
8 sbit WEI=P2^7;
9 sbit DUAN=P2^6;
10 sbit LED0=P1^0;
11
12 void Delay1ms();
13 void delay(int n);
14
15 uchar code Table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
16 // 0 1 2 3 4 5 6 7 8 9 A B C D E F 无显示
17 //[]括号内可以不写,编译时会自动数元素数然后分配内存
18
19 uchar num,tt;
20 void main()
21 {
22 tt=0,
23 num=0;
24 TMOD=0x01;//设置定时器0为工作方式1
25 TH0=(65536-50000)/256;
26 TL0=(65536-50000)%256;
27 EA=1;//开总中断
28 ET0=1;//开启定时器中断
29 TR0=1;//开启定时器
30 WEI=1;
31 P0=0x00;
32 WEI=0;
33
34 // DUAN=1;
35 // P0=Table[num];
36 // DUAN=0;
37 while(1)
38 {
39 if(tt==20)
40 {
41 tt=0;
42 num++;
43 if(num==16)
44 {
45 num=0;
46 }
47 DUAN=1;
48 P0=Table[num];
49 // delay(1000);
50 DUAN=0;
51 }
52 }
53 }
54
55 void exter0() interrupt 1
56 {
57 TH0=(65536-50000)/256;
58 TL0=(65536-50000)%256;
59 tt++;
60 }
61
62 void delay(int n)
63 {
64 while(n--)
65 {
66 Delay1ms();
67 }
68 }
69 void Delay1ms() //@12.000MHz
70 {
71 unsigned char i, j;
72
73 i = 2;
74 j = 239;
75 do
76 {
77 while (--j);
78 } while (--i);
79 }
会看到数码管没有从0开始或者第一秒会不是0是一个乱码,因为一开始段选的P0没给0的值,只有到了1s后才会给段选的P0显示1的值,所以需要把注释的给段选P0赋值的语句加上。还有记住绝对不能再加上那句delay,因为当程序进入if开始执行delay后,delay延时执行时,会被中断去执行定时中断程序,因此若加上delay,那么延时的时间会非常长。
最后再总结一下中断这里
从图中可以看到有这几个寄存器,TCON、IE、IP、SCON还有一个图中没有的IPH,这里我在把图放出来,其中SCON是串口的,这里还不学,所以先不放了。
看出来IP、IPH是管理优先级的,你只需要知道默认的优先级顺序是:外部中断0、定时/计数器0、外部中断1、定时/计数器1、串行口、定时/计数器2 其他的就不用管了。
接下来就是梳理中断了:
下一课开始是这节课的作业!!!
动态扫描这里说一下,下节课讲
让分别显示1234(动态扫描)
就要用人眼的视觉暂留效应和数码管的余辉,先让第一个数码管亮,显示1,其它不亮,然后快速的让第二个显示2,其他的不亮,以此类推,这就是动态扫描。(人眼只能分别20ms以内的变化,那么让他20ms以内变化就可以了,这样人就看不出来了,就相当于四个数码管一直亮着1234)