单片机管脚,P3口有两个功能,上电默认情况下就使用P3口的普通IO口,当对单片机内部的某些寄存器设置时就启用P3口的第二个功能
P3^4和 P3^5可以作为计时器或定时器使用,给这两个口输入方波,进行计数
做一个频率计数器时,当是正弦波时加一个比较器变成方波,送给单片机的计数器端口,由程序来计数,当三角波通过积分变成方波,送给P3^4和 P3^5
XTAL1和XTAL2是外部晶振输入端
TX-1C型单片机实验板原理图中复位电路在右边晶振电路的下面,电容是通交隔直的,当S22按下,上边电路接通,10k和1k电阻分压,5V*10/11,接近5V,所以S22按下后复位;开始上电时也可以复位,通过电容充电,下边电路出现电流,RST接10K电阻会分到电压,上电结束,电容放电,将电通过10K电阻,10K电阻接地,将电流放掉
晶振在复位电路的上面,30P的电容指的是30PF,两个电容是帮助晶振起振;11.0592M的晶振用30P的电容,6M的晶振用20P的电容
20管脚是Vss接地
29管脚是PSEN,一般不用
30管脚是ALE/PROG,PROG编程时用,编程时给31 管脚Vpp端加12V电压才能写入程序;ALE可以输出1/6时钟周期的方波,检测单片机是否正常工作,就用示波器接ALE,看输出是否是1/6个晶振频率的方波
31管脚的EA,见书P11,选择内部存储器或外部存储器;一般EA接高电平,从内部存储器执行
见书P11的2.3.3,P0口是8位三态双向IO口,P1、P2和P3是8位准双向IO口;三态是高电平,低电平和高阻态;P0口没有上拉电阻,P1、P2和P3有上拉电阻;P1、P2和P3是8位准双向IO口,是在输入时要先写个1,然后才能输入,即准备一下才能输入
见书P16,P26,复位时,P0口、P1、P2和P3口是0xff,其它寄存器全部清零
LED、LCD显示器有两种显示结构:段显示(7段、米字型等)和点阵显示(5×8、8×8点阵等)
设计单片机时,一定要给P0口加上拉电阻,电阻值是10K,接法见课件PPT的LESSON3_数码管静态显示及定时器和中断应用的P11
数码管内部由8个发光二极管构成
单片机P26连接段锁存端(DULA),P27连接位锁存端(WELA),见电路设计图
快速复制的小技巧:一直按住SHIFT,按下HOME,然后按下DELETE,再按两次INSERT
晶振6MHz对应20pF,晶振12MHz对应30pF
LED显示器分为“共阴极和共阳极两类,见上图,左端为共阴极,右端为共阳极
位选(WE)(程序中为wela):选中一位,单片机上共六个数码管,每一个数码管即一位
段选(程序中为dula):控制一个数码管哪几段亮
见上图共阴极a图,简单的说位选就是位选是右边的接地(第二个锁存器给相应的位WE赋给低电平),段选就是左边的八个管脚(第一个锁存器给相应的段DU赋给低电平)
本教程用共阴极,共阴极就是发光二极管的共阴极接在一起,共阴极的地端就是位选线(对应TX-1C型单片机实验板原理图中的WE),比如给共阴极高电平就不会让该数码管亮,即控制哪个数码管亮,也可以理解成控制哪个位的数码管亮;a到g和dp是段选线,即控制这个数码管的哪几段亮,来显示相应的数字
TX-1C型单片机实验板原理图中的六个数码管的管脚不必管,只要看要显示的数字和锁存器74HC573的位,因为要用单片机直接控制锁存器74HC573
让发光二极管全亮,就给八个二极管高电平,二极管的右边送低电平就可以
一个char型变量是8位,因为要写一个字需要8个位,一个int型的变量是16位
TX-1C型单片机实验板原理图中的六个LED就是数码管,LED图中的内部管脚标错了,外部管脚标的是对的,WE1是共阴极端,即段选端,六个数码管所有的段选都是连在一起的,位选是独立开的,通过位选控制某一个数码管亮,通过段选控制该数码管亮什么;见单片机左边的两个锁存器74HC573,第一个锁存器控制数码管的段选,第二个控制数码管的位选,锁存器的输入端是D0到D7,单片机左边紧靠的是10K的上拉电阻,因为P0口是8位三态双向IO口,无法进行低电平或高电平的操作,加个上拉电阻之后一上电就是高电平,然后单片机的P0口通过上拉电阻输入到锁存器;锁存端是高电平时,输入端和输出端是直通的,锁存端是低电平时,输入端和输出端是断开的,输出端保持原来的值,所以先给位选的锁存器的锁存端(WELA)高电平,并控制选择要亮的数码管,然后拉低锁存端,保持住选择的数码管,再把段选的锁存器(DULA)打开,将其锁存端拉高,输入要显示的段,再将锁存端拉低,保持住要显示
锁存器的锁存端一个下降沿将数据保持在锁存器的输出端
两个锁存器的锁存端DULA和WELA分别连接单片机的P2^6口和P2^7口,
CSAD是锁存器的片选,高电平有效
共阴数码管码表:
- 0x3f , 0x06 , 0x5b , 0x4f , 0x66 , 0x6d ,
- 0 1 2 3 4 5
- 0x7d , 0x07 , 0x7f , 0x6f , 0x77 , 0x7c ,
- 6 7 8 9 A B
- 0x39 , 0x5e , 0x79 , 0x71 , 0x00
- C D E F 无显示
见实验板原理图,LED数码管的管脚8连接WE,就是控制该数码管是否使用
看U2:WELA控制“位”,
想让第一个数码管亮,让WE1=0(共阴极的地端就是位选线,选择哪个数码管,对应位选应该赋给低电平),WE2到WE6和13、12口都为1,即十六进制的fe
#include<reg52.h>
sbit dula=P2^6;
sbit wela=P2^7;
void main()
{
wela=1;//位选锁存器打开
P0=0xfe;//第一个数码管亮, 如果要显示两个就P0=0xfc
wela=0;//位选锁存器关闭,数据保持
dula=1;//段选锁存器打开
P0=0x06;//显示数字1,如果要显示0就,P0=0x3f
dula=0;//段选锁存器关闭,数据保持
while(1);
}
见课件PPT的LESSON3_数码管静态显示及定时器和中断应用,单片机内部的电流很弱,不足以点亮发光二极管,所以要加个上拉电阻用5V电压,才能点亮发光二极管
程序0到F的变化:
#include<reg52.h>
sbit dula=P2^6;
sbit wela=P2^7;
#define uint unsigned int
#define uchar unsigned char
uchar num;//用多大的变量就定义相应的类型
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
void delay(uint z);
void main()
{
while(1)
{
wela=1;
P0=0xc0;//所有数码管全亮
wela=0;
for(num=0;num<16;num++)
{
dula=1;
P0=table[num];
dula=0;
delay(1000);
}
}
}
void delay(uint z)//经测试得:z=1时是一毫秒
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
第一三五数码管亮:
#include<reg52.h>
sbit dula=P2^6;
sbit wela=P2^7;
#define uint unsigned int
#define uchar unsigned char
uchar num;
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
void delay(uint z);
void main()
{
wela=1;
P0=0xc0;//所有数码管全亮
wela=0;
while(1)
{
for(num=0;num<16;num++)
{
dula=1;
P0=table[num];
dula=0;
delay(1000);
}
}
}
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
中断:见书P54P55
单片机的核心就是中断,定时器,串口通信
P3.2口控制外部中断0,给低电平时进行中断0
置位表示置1
中断优先级:
中断序号:0,1,2,3,4,对应上表中的从高到低的顺序中断,外部中断0中断序号是0
中断响应条件:
中断源有中断请求;例如设定中断函数void exter0() interrupt 0
此中断源的中断允许位为1;例如开外部中断0,让IE0=1
CPU开中断(即EA=1)。
以上三条同时满足时,CPU才有可能响应中断。
寄存器的位数能被8整除的寄存器都可以直接进行位寻址,位寻址就是可以直接操作某一位
用杜邦线一端接地一端接P3.2口,即给P3.2口低电平,小灯亮,产生中断,数码管显示数字不变,拔掉杜邦线数码管数字变化:
#include<reg52.h>
sbit dula=P2^6;
sbit wela=P2^7;
sbit d1=P1^0;
#define uint unsigned int
#define uchar unsigned char
uchar num;
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
void delay(uint z);
void main()
{
EA=1;//开总中断
IT0=0;//IT0=0为电平触发方式,IT0=1为跳沿触发方式
//IT0=1也可以设置为TCON=0x01;
EX0=1;//开外部中断0,P3.2口有低电平时进行中断0,中断设置好就跳到中断函数处执//行中断函数
wela=1;
P0=0xea;//所有数码管全亮
wela=0;
while(1)
{
for(num=0;num<16;num++)
{
d1=1;
dula=1;
P0=table[num];
dula=0;
delay(1000);
}
}
}
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
IT0=0为电平触发方式,用杜邦线的两端连接地和P3.2口发光二极管一直亮,数码管一直不变,即一直在中断程序中,直到杜邦线断开;IT0=1为跳沿触发方式,用杜邦线连上,发光二极管亮一下就熄灭,即使杜邦线一直连着二极管也不亮,数码管继续变化,即中断一下跳变沿结束中断就结束,返回主程序
一般中断是设置成跳变沿触发方式,因为如果设置成电平触发方式容易忘记在程序中再给P2.3口高电平,回不去主程序,结束不了中断
void exter0() interrupt 0//interrupt表示中断服务程序,
//interrupt后面的0表示中断序号,中断序号:0,1,2,3,4,中断序号是几就对应哪个中//断;中断函数不需要声明
{
d1=0;
}
由溢出时计数器的值减去计数初值才是加1计数器的计数值。
计数器就是连接P3.4口或P3.5口就自动计数
计数器来源于T0或T1引脚输入的外部脉冲源,定时器来源是由系统的时钟振荡器输出脉冲经12分频后送来(即每12个振荡周期即一个机器周期,计数器加1)
TCON的低4位用于控制外部中断,已在前面介绍。TCON的高4位用于控制定时/计数器的启动和中断申请。其格式如下:
其中TF1和TF0是由单片机自动控制,这里不用管它,我们只需考虑TR1和TR0,这里我们只用TR0控制定时器T0,TR0置1时,T0开始工作,TR0置0时,T0停止工作
两个十六位定时器最大定时65ms(65536us约等于65ms)
设置为定时器模式时,加1计数器是对内部机器周期计数(1个机器周期等于12个振荡周期,即计数频率为晶振频率的1/12)。计数值N乘以机器周期Tcy就是定时时间t 。
初始化程序应完成如下工作:
- 对TMOD赋值,以确定T0和T1的工作方式。
- 计算初值,并将其写入TH0、TL0或TH1、TL1。
- 中断方式时,则对IE赋值,开放中断。
- 使TR0或TR1置位,启动定时/计数器定时或计数。
思路:定时1s:进行20次中断,每次中断进行的时间是50ms并且对一个变量加1,进入一次中断就加1,一直加到20次,在主程序中进行判断,一旦到20让数码管变化一次
#include<reg52.h>
sbit dula=P2^6;
sbit wela=P2^7;
sbit d1=P1^0;
#define uint unsigned int
#define uchar unsigned char
uchar num,tt;
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
void main()
{
num=0;
tt=0;
TMOD=0x01;//设置定时器0为工作方式1,C/T=0为定时模式, GATE=0用软件控制
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;//开总中断
ET0=1;//开定时器0中断
TR0=1;//启动定时器0,定时器就开始工作,计数器就开始计数,50ms一到就进入中断
//程序中
wela=1;
P0=0xea;//所有数码管全亮
wela=0;
dula=1;
P0=0x3f;//开始时要给数码管一个要显示的数字,否则会出现乱码
dula=0;
while(1)
{
if(tt==20)
{
tt=0;
num++;
if(num==16) //因为num计数到16就找不到值了
num=0;
dula=1;
P0=table[num];
dula=0;
}
}
}
void exter0() interrupt 1//定时器中断0中断序号为1
{
TH0=(65536-50000)/256;//50000表示50毫秒,即经过50毫秒中断一次,TH0和TL0
//是设定的初值,从初值开始计数到65536就经过50毫秒
TL0=(65536-50000)%256;//因为当定时器计数值满再加1变成0才能从主程序进入中断//后,此时定时器的计数值为0,这里要重新装一次值,再进行计数,下一次50ms后才能再//次进入中断,否则定时器的计数值会从0开始,一直计数到65536才再次进入中断
tt++;
}
课件作业:
利用定时/计数器T0从P1.0输出周期为1s的方波,让发光二极管以1HZ闪烁,设晶振频率为12MHz:重点就是让P1.0口正负正负不断变化
我的方法:
#include<reg52.h>
#define uchar unsigned char
sbit D1=P1^0;
uchar flag,tt;
void main()
{
tt=0;
//D1=1;
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
while(1)
{
if(tt==10)
{
tt=0;
D1=~D1;
}
}
}
void inter0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
tt++;
}
课件的方法:
#include<reg52.h> //52单片机头文件
#define uchar unsigned char //宏定义
sbit P1_0=P1^0;
uchar tt;
void main() //主函数
{
TMOD=0x01;//设置定时器0为工作方式1
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;//开总中断
ET0=1;//开定时器0中断
TR0=1;//启动定时器0
while(1);//等待中断产生
}
void timer0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
tt++;
if(tt==20)//直接在中断函数里进行方波程序
{
tt=0;
P1_0=~P1_0;
}
}
利用定时/计数器T1产生定时时钟,由P1口控制8个发光二极管,使8个指示灯依次一个一个闪动,闪动频率为10次/秒(8个灯依次亮一遍为一个周期),循环:
我的方法:
#include<reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
uint num,tt;
void main()
{
uchar a;
TMOD=0x10;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET1=1;
TR1=1;
a=0xfe;
while(1)
{
if(tt==2)
{
tt=0;
P1=a;
a=_crol_(a,1);
}
}
}
void inter1() interrupt 3
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
tt++;
}
课件的方法:
#include<reg52.h> //52单片机头文件
#include <intrins.h> //包含有左右循环移位子函数的库
#define uint unsigned int //宏定义
#define uchar unsigned char //宏定义
sbit P1_0=P1^0;
uchar tt,a;
void main() //主函数
{
TMOD=0x01;//设置定时器0为工作方式1
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;//开总中断
ET0=1;//开定时器0中断
TR0=1;//启动定时器0
a=0xfe;
while(1);//等待中断产生
}
void timer0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
tt++;
if(tt==2)
{
tt=0;
P1=a;
a=_crol_(a,1);
}
}
同时用两个定时器控制蜂鸣器发声,定时器0控制频率,定时器1控制同个频率持续的时间,间隔2s依次输出1,10,50,100,200,400,800,1k(hz)的方波:
我的方法:先做一下中断0控制一下蜂鸣器在300ms内以频率1响一下,再控制蜂鸣器以频率1和10响,再依次做下面的频率,思想是从简单到复杂,一步一步完成
在中断1中控制同个频率持续的时间,再用一个变量a来控制是以何种频率进行,为中断0做准备
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit beep=P2^3;
uint num,a,b;
void main()
{
TMOD=0x11;//注意在中断0和中断1同时使用时要在TMOD中一起设置不要分开设置
TH1=(65536-50000)/256;
TL1=(65536-50000)%256;
EA=1;
ET1=1;
TR1=1;
//TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
//EA=1;
ET0=1;
TR0=1;
beep=0;
while(1);
}
void inter1() interrupt 3
{
TH1=(65536-50000)/256;
TL1=(65536-50000)%256;
num++;
if(num==40)
{
num=0;
a++;
if(a==8)
{
a=0;
}
}
}
void inter0() interrupt 1
{
if(a==0)
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
b++;
if(b==20)
{
b=0;
beep=~beep;
}
}
if(a==1)
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
beep=~beep;
b++;
if(b==2)
{
b=0;
beep=~beep;
}
}
if(a==2)
{
TH0=(65536-20000)/256;
TL0=(65536-20000)%256;
beep=~beep;
}
if(a==3)
{
TH0=(65536-10000)/256;
TL0=(65536-10000)%256;
beep=~beep;
}
if(a==4)
{
TH0=(65536-5000)/256;
TL0=(65536-5000)%256;
beep=~beep;
}
if(a==5)
{
TH0=(65536-2500)/256;
TL0=(65536-2500)%256;
beep=~beep;
}
if(a==6)
{
TH0=(65536-1250)/256;
TL0=(65536-1250)%256;
beep=~beep;
}
if(a==7)
{
TH0=(65536-1000)/256;
TL0=(65536-1000)%256;
beep=~beep;
}
b=0;
}
课件的程序:
#include<reg52.h> //52单片机头文件
#include <intrins.h> //包含有左右循环移位子函数的库
#define uint unsigned int //宏定义
#define uchar unsigned char //宏定义
sbit beep=P2^3;
uchar tt;
uint fre,flag;
void main() //主函数
{
fre=50000;
beep=0;
TMOD=0x11;//设置定时器0,定时器1为工作方式1
TH0=(65536-fre)/256;
TL0=(65536-fre)%256;
TH1=(65536-50000)/256;
TL1=(65536-50000)%256;
EA=1;//开总中断
ET0=1;//开定时器0中断
ET1=1;
TR1=1;
TR0=1;//启动定时器0
while(1);//等待中断产生
}
void timer0() interrupt 1 //定时器0中断
{
TR0=0; //进中断后先把定时器0中断关闭,防止内部程序过多而造成中断丢失
TH0=(65536-fre)/256;
TL0=(65536-fre)%256;
tt++;
if(flag<40) //以下几个if分别用来选取不同的频率,flag<40即第一个2s时间内
if(tt==10)
{
tt=0;
fre=50000;
beep=~beep;
}
if(flag>=40&&flag<80)//flag>=40&&flag<80即第二个2s时间内
{
tt=0;
fre=50000;
beep=~beep;
}
if(flag>=80&&flag<120)
{
tt=0;
fre=10000;
beep=~beep;
}
if(flag>=120&&flag<160)
{
tt=0;
fre=5000;
beep=~beep;
}
if(flag>=160&&flag<200)
{
tt=0;
fre=2500;
beep=~beep;
}
if(flag>=200&&flag<240)
{
tt=0;
fre=1250;
beep=~beep;
}
if(flag>=240&&flag<280)
{
tt=0;
fre=625;
beep=~beep;
}
if(flag>=280&&flag<320)
{
tt=0;
fre=312;
beep=~beep;
}
if(flag>=320&&flag<360)
{
tt=0;
fre=156;
beep=~beep;
}
TR0=1;
}
void timer1() interrupt 3 //定时器1中断用来产生2秒时间定时
{
TH1=(65536-50000)/256;
TL1=(65536-50000)%256;
flag++;
if(flag==360)
{
flag=0;
fre=50000;//因为到if(flag>=320&&flag<360)这步时fre回不到50000,所以这里还
//原一下fre
}
}
用定时器以间隔500MS在6位数码管上依次显示0、1、2、3….C、D、E、F,重复:
我的程序:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit dula=P2^6;
sbit wela=P2^7;
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
uint num,a;
void main()
{
num=0;
a=0;
wela=1;
P0=0xc0;
wela=0;
TMOD=0x01;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
dula=1;
P0=0x3f;
dula=0;
while(1);
}
void inter0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
num++;
if(num==10)
{
a++;
if(a==16)
{
a=0;
}
num=0;
dula=1;
P0=table[a];
dula=0;
}
}
课件的程序:
#include<reg52.h> //52单片机头文件
#include <intrins.h> //包含有左右循环移位子函数的库
#define uint unsigned int //宏定义
#define uchar unsigned char //宏定义
sbit dula=P2^6; //数码管段选锁存端
sbit wela=P2^7; 数码管位选锁存端
uchar num,tt;
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
void main()
{
num=0;
tt=0;
TMOD=0x01;//设置定时器0为工作方式1
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;//开总中断
ET0=1;//开定时器0中断
TR0=1;//启动定时器0
dula=1;
P0=0x3f; //给段开始送显示0。
dula=0;//关闭段选锁存端,防止开始时出现乱码。
wela=1;//11101010
P0=0xc0; // 打开六个数码管位选
wela=0;
while(1)//把数码管显示放到主程序里执行
{
if(tt==10) //每进入10次中断即为500ms,执行一次显示变化。
{
tt=0;
num++;
if(num==16)
num=0;
dula=1;
P0=table[num];
dula=0;
}
}
}
void exter0() interrupt 1 // 定时器0中断
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
tt++;
}