郭天祥的10天学会51单片机_第七节
串行通信:从D0位一位一位发发到D7位
十个位为一帧如:0101000100,表示一个字符
起始位低电平有效,停止位高电平有效,单片机是异步通信
宽带网线是八根线,组成两队差分信号,分别是TXD1(发送),RXD1(接收),TXD0,RXD0,电源线,地线,询问线,应答线
电话线上网是由宽带线接出来,连接调制解调器(把数字信号调制成模拟信号),发送到电话线(只能传送模拟信号)上(传出DTMF编码),在网络另外一端接调制解调器(解调DTMF编码,模拟信号解调成数字信号)
异步通信的数据格式:
起始位为低电平,停止位为高电平
串行通信的传输方向
1、单工
单工是指数据传输仅能沿一个方向,不能实现反向传输。
2、半双工
半双工是指数据传输可以沿两个方向,但需要分时进行。
3、全双工
全双工是指数据可以同时进行双向传输。
串口公头:带针的头;串口母头:带孔的头。
这里只用TXD,RXD,SGND
开发板的左下角的MAX232既可以将RS232电平转换成TTL电平,又可以将TTL电平转换成RS232电平
RS-232C接口传输距离短,传输速率低
485总线传输距离长,抵抗静电超过八千伏
A是寄存器,SBUF是串行口寄存器,TXD(发送)接单片机的P3.1口,RXD(接收)接单片机的P3.0口,T1是定时器1,除以16表示十六分频,T1每溢出一次(一次计数满)就发送一次,每一个字节发送完就用TI申请中断,每一个字节接收完就用RI申请中断,接收完通过移位寄存器送到下面的SBUF当中,产生中断通知单片机将SBUF中的数据取走
PPT的P25中T1是计数器1,T1溢出即计时时间到,发送数据,TI见书P84
TI是为发送数据服务的(单片机向串口发送数据),RI是为接收数据服务的(单片机从串口接收数据)
发送一个字节的同时会添加上起始位和终止位,可能还添加奇偶校验位
串口通信体现在程序上就两句:发送数据a=sbuf;接收数据sbuf=a
SCON 是一个特殊功能寄存器,用以设定串行口的工作方式、接收/发送控制以及设置状态标志:
因为98H是8的倍数,所以可以直接对SCON进行位操作
学会方式1,其它方式也自然会了
这里设置为方式1,SM0=0,SM1=1,只用一个单片机让SM2=0,REN=1接收数据,方式1中不用TB8,这里TB8=0或不设置(因为单片机上电复位时这些标志位都是0),RB8=0;TI和RI是发完一个字节后由硬件置1,我们不能用软件控制,只能用软件在程序中清0,这里TI=0,RI=0;这里需要我们设置的就是SM0=0,SM1=1,SM2=0,REN=1
PCON是电源设置,PCON的地址位是97H,所以不能进行位选址
这里设置PCON的SMOD=0,初始化时寄存器自动设置为0,这里可以不在程序中置零
以后使用芯片时,会有很多标志位要设置,只要把资料中每一位搞明白了就会进行设置了
方式1输出输入见LESSON7_串口通信的PPT的P33和P34
方式1的波特率是可变的,由定时器T1的溢出率(定时器1计数满了就溢出)来决定:
T1 溢出率 = fosc /{12×[256 -(TH1)]},T1一秒钟溢出多少次
在单片机应用中,常用的晶振频率为:12MHz和11.0592MHz;串口通信中必须用11.0592MHz
先给SBUF0x01,箭头包起来的有效数据位,为0为1都可以
定时器的工作方式2:例如将TH1中装入初始值,当TL1计数溢出时给TF1置1,再将TH1中的初始值装入TL1中继续计数,TH1不参与计数,这里用定时器方式2
串行口工作之前,应对其进行初始化,主要是设置产生波特率的定时器1、串行口控制和中断控制。具体步骤如下:
- 确定T1的工作方式(编程TMOD寄存器);
- 计算T1的初值,装载TH1、TL1;
- 启动T1(编程TCON中的TR1位);
- 确定串行口控制(编程SCON寄存器);
串行口在中断方式工作时,要进行中断设置(编程IE(中断寄存器)、IP寄存器(中断优先级寄存器))。
波特率不同,串口传送的数据之间的间隔就不同,排队就不同。
打开串口调试助手软件,串口调试助手软件不能和STC_ISP软件使用同一个COM口,相当于一个COM口用来传送数据,一个COM口用来传程序;可以在下载程序时将串口调试助手软件关闭,下载完要传送数据时再把串口调试助手软件打开
先用串口调试助手软件发送一个数,然后传给单片机,让单片机点亮一个发光二极管:
查询法:收到一帧数据后SCON的RI位会由硬件置1,查询RI是否置位,如果置位说明收到数据
#include<reg52.h>
void main()
{
TMOD=0x20;//设置定时器1为工作方式2
TH1=0xfd;//定时器的工作方式2:例如将TH1中装入初始值,当TL1计数溢出时给TF1
//置1,再将TH1中的初始值装入TL1中继续计数
TL1=0xfd;//装入0xfd对应波特率9600
TR1=1;//只有打开定时器,没有波特率没法检测数据,有了波特率才能不断检测数据
REN=1;
SM0=0;
SM1=1;
while(1)//一直检测有没有收到数据
{//串口收到数据,RI由硬件自动置1,软件不能控制
if(RI==1)//当RI等于0说明串口没送数据,等于1说明数据到了退出while循环
RI=0;//RI要由软件清零
P1=SBUF;//P1=SBUF即将串口收到的数据SBUF赋给P1,让相应的发光二极管亮
}
}
使用时,先编译再使用STC-ISP软件,再使用串口调试助手软件,波特率选择9600,发送一个任意字符或字符串(可以点击串口调试助手软件的16位进制发送)
串行口板块:调试模式下,菜单栏的Peripherals选项下的Serial,这里看RI,将RI划勾
串口中断法:收到一帧数据后SCON的RI位会由硬件置1,RI置位后会申请中断,我们写一个中断函数,在中断函数里处理
程序中先将中断打开,即先将总中断打开EA=1,然后将串口中断打开ES=1,写串口中断函数(interrupt 标号为4),一旦进入中断说明收到数据,在中断函数中先将RI清零(才能进入下一次中断),再将SBUF读走
中断共5个,内部中断0和1,外部中断0和1,串口中断标号4
#include<reg52.h>
unsigned char a;
void main()
{
TMOD=0x20;//设置定时器1为工作方式2,自动装初始值,所以下面不用在中断中再
//设置初始值
TH1=0xfd;//见书P91
TL1=0xfd;//工作方式2,TH1的值传给TL1
TR1=1;//T1计数器启动
REN=1;//工作方式2
SM0=0;
SM1=1;
EA=1;
ES=1;//设置中断后,当有中断产生,这里是串口传送数据,即中断方式4串口中断,
//程序走到中断那
while(1);
}
void ser() interrupt 4
{
RI=0;
a=SBUF;//此时的SBUF是发送缓冲器
P1=a;
}
单片机收到计算机发送的数据,再由单片机把收到的数据发送回计算机:
#include<reg52.h>
unsigned char flag,a;
void main()
{
TMOD=0x20;//设置定时器1为工作方式2
TH1=0xfd;//见书P91
TL1=0xfd;//工作方式2,TH1的值传给TL1
TR1=1;//T1计数器启动
REN=1;//串口允许接收
SM0=0;
SM1=1;//方式1
EA=1;
ES=1;//设置中断后,当有中断产生,这里是串口传送数据,即中断方式4串口中断,程序走到中断那
while(1)
{
if(flag==1)//收到数据
{
ES=0;//关闭串口中断,防止单片机发送给计算机数据时产生中断,不能继续中断
flag=0;
SBUF=a;// SBUF是收到的串口数据,此时的SBUF是发送缓冲器;这里发送完数据就会进入中断,由硬件让TI=1,进入中断,所以要停止中断就要用软件将TI清零,见LESSON7_串口通信的PPT的P25的图,SBUF给数据后,只要T1作为波特率发生器,数据就由控制门传送出去
while(!TI);
TI=0;//T1必须由软件清零,否则会不断进入中断
ES=1;
}
}
}
void ser() interrupt 4
{
RI=0;
a=SBUF;//此时的SBUF是接收缓冲器
flag=1;
}
使用时,先编译再使用STC-ISP软件,再使用串口调试助手软件,波特率选择9600,发送一个任意字符或字符串(可以点击串口调试助手软件的16位进制发送),显示英文例如Hello Everyone时,要把十六进制显示的勾去掉
十六进制发送也要十六进制显示,例如发送55aa,发送过去并显示是55aa,发送55a,发送过去并显示是55a,因为a不够一个字节
发送字符串:
#include<reg52.h>
unsigned char flag,a;
void main()
{
TMOD=0x20;//设置定时器1为工作方式2
TH1=0xfd;//见书P91
TL1=0xfd;//工作方式2,TH1的值传给TL1
TR1=1;//定时器1启动,T1作为波特率发生器,所以不用ET1=1,即不用启动定时器1
//的中断允许位
REN=1;
SM0=0;
SM1=1;
EA=1;
ES=1;//设置中断后,当有中断产生,这里是串口传送数据,即中断方式4串口中断,
//程序走到中断那
while(1)
{
if(flag==1)//收到数据
{
ES=0;
flag=0;
SBUF='1';//发送128(十进制),要一个一个发,如果直接发128,发//过去是十六进制的数
while(!TI);
TI=0;//T1必须由软件清零
SBUF='2';//发的数的位数多时,可以用一个循环,把这个数分离开
while(!TI);
TI=0;
SBUF='8';
while(!TI);
TI=0;
ES=1;
}
}
}
void ser() interrupt 4
{
RI=0;
a=SBUF;//此时的SBUF是发送缓冲器
flag=1;
}
课件练习:
由上位机发送1给单片机时,蜂鸣器以400ms频率发声,发2时以200ms频率发声,发3时以100ms频率发声,发4时关闲蜂鸣器:
我的思路,先做由上位机发送1给单片机时,蜂鸣器以400ms频率发声
先测试可以收到数据,用简单的启动停止蜂鸣器实现,证明可以收到数据:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar flag;
uint temp,a;
sbit feng=P2^3;
void init()
{
TMOD=0x21;
TH1=0xfd;
TL1=0xfd;
TR1=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
}
void delay(uint z)
{
uchar x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void main()
{
init();
while(1)
{
if(flag==1)
{
flag=0;
ES=0;
feng=temp;//使用串口调试助手软件用十六进制发送,发送0时发送的字符数据栏中填入00才能发送0,十六进制要写两个数才行
delay(500);
feng=1;
ES=1;
}
}
}
void ser() interrupt 4
{
RI=0;
temp=SBUF;
flag=1;
}
一个收获:定时器0用来控制时间,设置标志位,只对标志位操作,进行控制时间,将主要控制蜂鸣器的程序放在主函数中,见下面程序
我的程序:
由上位机发送1给单片机时,蜂鸣器以400ms频率发一次声,发2时以200ms频率发一次声,发3时以100ms频率发一次声,发4时关闲蜂鸣器:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar flag,flag1,flag2,flag3;
uint temp,a;
sbit feng=P2^3;
void init()
{
TMOD=0x21;
TH1=0xfd;
TL1=0xfd;
TR1=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
}
void main()
{
init();
while(1)
{
if(flag==1)//使用串口调试助手软件用十六进制发送,发送的字数据栏中填入01
{
flag=0;
ES=0;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
ET0=1;
TR0=1;
if(temp==1)
{
feng=0;
while(!flag1);
flag1=0;
a=0;
feng=1;
TR0=0;
}
if(temp==2)
{
feng=0;
while(!flag2);
flag2=0;
a=0;
feng=1;
TR0=0;
}
if(temp==3)
{
feng=0;
while(!flag3);
flag3=0;
a=0;
feng=1;
TR0=0;
}
if(temp==4)
{
feng=1;
TR0=0;
}
ES=1;
}
}
}
void ser() interrupt 4
{
RI=0;
temp=SBUF;
flag=1;
}
void timer0() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
a++;
if(a==8)//定时器0用来控制时间,设置标志位,只对标志位操作,进行控制时间,将主要控制蜂鸣器的程序放在主函数中
{
flag1=1;
}
if(a==4)
{
flag2=1;
}
if(a==2)
{
flag3=1;
}
}
使用时有时要点两下发送才能发送出去,第一次按发送按键是选择上发送的按键,第二次按发送按键才是发送出去
正式程序针对: 由上位机发送1给单片机时,蜂鸣器以400ms频率发声,发2时以200ms频率发声,发3时以100ms频率发声,发4时关闲蜂鸣器:
我的程序:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar flag,flag1,flag2,flag3,num;
uint temp,a;
sbit feng=P2^3;
void init()
{
TMOD=0x21;
TH1=0xfd;
TL1=0xfd;
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
TR1=1;
TR0=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ET0=1;
ES=1;
}
void main()
{
init();
while(1)
{
if(flag==1)//使用串口调试助手软件用十六进制发送,发送的字符/数据栏中填入01
{
flag=0;
ES=0;
if(temp==1)
{
a=0;//定时器0一直打开,上面的语句会对时间间隔造成影响,所以a=0
num=4;
}
if(temp==2)
{
a=0;
num=8;
}if(temp==3)
{
a=0;
num=2;
}
if(temp==4)
{
feng=1;
TR0=0;
}
ES=1;
}
}
}
void ser() interrupt 4
{
RI=0;
temp=SBUF;
flag=1;
}
void timer0() interrupt 1//如果蜂鸣器响的程序放在主函数中会不容易控制时间,这里放在定时器里,主程序该怎么走怎么走
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
a++;
if(a==num)
{
a=0;
feng=~feng;//蜂鸣器不断响、停之间变换
}
}
课件程序:
#include<reg52.h>
unsigned char flag,a,num,benum;
sbit beep=P2^3;
void main()
{
TMOD=0x21;//设置定时器1为工作方式2
TH1=0xfd;
TL1=0xfd;
TH0=(65536-50000)/255;
TL0=(65536-50000)%255;
TR1=1;
ET0=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
while(1)
{
if(flag==1)
{
EA=0;
flag=0;
TR0=1;
if(a==1)
benum=4;
if(a==2)
benum=2;
if(a==3)
benum=1;
if(a==4)
{
TR0=0;
beep=1;
}
EA=1;
}
}
}
void ser() interrupt 4
{
RI=0;
a=SBUF;
flag=1;
}
void time0() interrupt 1
{
TH0=(65536-50000)/255;
TL0=(65536-50000)%255;
num++;
if(num>=benum)
{
num=0;
beep=~beep;
}
}
以2400bps从计算机发送任一字节数据,当单片机收到该数据后,在此数据前加上一序号然后连同此数据一起发送至计算机,当序号超过255时归零:
我的思路:见LESSON7_串口通信的PPT的P39的图可知2400bps对应的初值为f4H
我的程序:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uint temp,num;
uchar flag;
void init()
{
TMOD=0x20;
TH1=0xf4;
TL1=0xf4;
TR1=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
}
void main()
{
init();
while(1)
{
if(flag==1)
{
flag=0;
ES=0;
SBUF=num;
while(!TI);
TI=0;
SBUF=temp;
while(!TI);
TI=0;
ES=1;
}
}
}
void ser() interrupt 4
{
RI=0;
temp=SBUF;
flag=1;
num++;
if(num==256)
num=0;
}
课件程序:第一次发送序号,第二次发送数据,因为一次只能发送一个字节,一个序号在0到255之间就够一个字节,所以数据用另一个字节发送
#include<reg52.h>
unsigned char flag,a,num,num1;
sbit beep=P2^3;
void main()
{
TMOD=0x20;//设置定时器1为工作方式2
TH1=0xf4;
TL1=0xf4;
TR1=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
while(1)
{
if(flag==1)
{
ES=0;
flag=0;
num1++;
if(num1==255)
num1=0;
SBUF=num1;//串口一直开着,当发现SBUF有数据时,说明单片机往串口写
数据,当写满一帧数据就发送
while(!TI);
TI=0;
SBUF=a;
while(!TI);
TI=0;
ES=1;
}
}
}
void ser() interrupt 4
{
RI=0;
a=SBUF;
flag=1;
}
以16进制发送一个0-65536之间的任一数,当单片机收到后在数码管上动态显示出来,波特率自定:
我的程序:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar flag,aa,bb,cc,dd,ee;
uint a;
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
};
void delay(uchar z)
{
uchar x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void init()
{
TMOD=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
SM0=0;//先赋值SM0,再赋值SM1
SM1=1;
REN=1;
EA=1;
ES=1;
dula=1;
P0=table[0];
dula=0;
P0=0xff;
wela=1;
P0=0;
wela=0;
}
void display(uint kk)
{
aa=kk/10000;
bb=kk%10000/1000;
cc=kk%1000/100;
dd=kk%100/10;
ee=kk%10;
dula=1;
P0=table[aa];
dula=0;
P0=0xff;
wela=1;
P0=0xfe;
wela=0;
delay(1);
dula=1;
P0=table[bb];
dula=0;
P0=0xff;
wela=1;
P0=0xfd;
wela=0;
delay(1);
dula=1;
P0=table[cc];
dula=0;
P0=0xff;
wela=1;
P0=0xfb;
wela=0;
delay(1);
dula=1;
P0=table[dd];
dula=0;
P0=0xff;
wela=1;
P0=0xf7;
wela=0;
delay(1);
dula=1;
P0=table[ee];
dula=0;
P0=0xff;
wela=1;
P0=0xef;
wela=0;
delay(1);
}
void main()
{
init();
while(1)
{
if(flag==1)
{
flag=0;
ES=0;
display(a);
ES=1;
}
display(a);
}
}
void ser() interrupt 4
{
RI=0;
a=SBUF;
flag=1;
}
针对串口调试软件一次只能发八位数据,0到65536中的任意一个数要分成两个字节,第一个字节保存在某个变量当中,再接收第二个字节,第一个字节*256+第二个字节可以表示发送的数据,中断中要有个变量,这个变量来判别什么时候发的是第一个字节什么时候发的是第二个字节
我修改了一下程序:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar flag,aa,bb,cc,dd,ee,buf1,buf2,i;
uint a;
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
};
void delay(uchar z)
{
uchar x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void init()
{
TMOD=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
SM0=0;//先赋值SM0,再赋值SM1
SM1=1;
REN=1;
EA=1;
ES=1;
dula=1;
P0=table[0];
dula=0;
P0=0xff;
wela=1;
P0=0;
wela=0;
}
void display(uint kk)
{
aa=kk/10000;
bb=kk%10000/1000;
cc=kk%1000/100;
dd=kk%100/10;
ee=kk%10;
dula=1;
P0=table[aa];
dula=0;
P0=0xff;
wela=1;
P0=0xfe;
wela=0;
delay(1);
dula=1;
P0=table[bb];
dula=0;
P0=0xff;
wela=1;
P0=0xfd;
wela=0;
delay(1);
dula=1;
P0=table[cc];
dula=0;
P0=0xff;
wela=1;
P0=0xfb;
wela=0;
delay(1);
dula=1;
P0=table[dd];
dula=0;
P0=0xff;
wela=1;
P0=0xf7;
wela=0;
delay(1);
dula=1;
P0=table[ee];
dula=0;
P0=0xff;
wela=1;
P0=0xef;
wela=0;
delay(1);
}
void main()
{
init();
while(1)
{
for(i=0;i<20;i++)
{
display(a);
}
}
}
void ser() interrupt 4
{
if(RI)
{
flag++;
if(flag==1) {buf1=SBUF;}
if(flag==2)
{
buf2=SBUF;
a=(buf1*256+buf2);
flag=0;
}
RI=0;
}
}
课件的程序:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar a,i,flag=0,date;
uchar b1,b2,b3,b4,b5,b6;
uint dat=0,buf1,buf2;
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};
void delay(unsigned char i) //延时程序
{
uchar j,k;
for(j=i;j>0;j--)
for(k=125;k>0;k--);
}
void UART_init()
{
TMOD=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
PCON=0x80;
}
void display(uint date) //显示程序
{
b1=date/10000;
b2=date%10000/1000;
b3=date%1000/100;
b4=date%100/10;
b5=date%10;
P0=table[b1]; //1
dula=1;
dula=0;
P0=0xfe;
wela=1;
wela=0;
delay(5);
dula=0;
P0=table[b2]; //2
dula=1;
dula=0;
wela=0;
P0=0xfd;
wela=1;
wela=0;
delay(5);
dula=0;
P0=table[b3]; //3
dula=1;
dula=0;
wela=0;
P0=0xfb;
wela=1;
wela=0;
delay(5);
dula=0;
P0=table[b4]; //4
dula=1;
dula=0;
wela=0;
P0=0xf7;
wela=1;
wela=0;
delay(5);
dula=0;
P0=table[b5]; //5
dula=1;
dula=0;
wela=0;
P0=0xef;
wela=1;
wela=0;
delay(5);
}
void main() // 主程序
{
UART_init();
while(1)
{
for(i=0;i<20;i++)
{
display(dat);
}
}
}
void serial() interrupt 4
{
if(RI)
{
flag++;
if(flag==1) {buf1=SBUF;}
if(flag==2)
{
buf2=SBUF;
dat=(buf1*256+buf2);
flag=0;
}
RI=0;
}
}
用AD以1HZ的频率采集模拟信号,然后转换成数字量,再将其以1200bps发送到计算机,在计算机上显示:
知识点:波特率为1200bps对应的初值为E8H,1HZ就是1s采集信号一次,程序中两次延时500ms即delay(500)即可够1s采集完就发送出去
我的思路:先采集信号,将数字量存入定义的变量里,再发到计算机上即用串口调试软件显示出来
知识点:见LESSON7_串口通信的PPT的P39的表, T1 溢出率 = fosc /{12×[256 -(TH1)]},Fosc为定时器频率,[256 -(TH1)]为定时器计完一次(从TH1到256)就让串口接收或发送一个位,所以1200bps即1200位每秒,即装完初值e8H后,定时器提供波特率,每秒传送1200位
我的程序:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit adwr=P3^6;
sbit adrd=P3^7;
sbit dula=P2^6;
sbit wela=P2^6;
uchar temp;
void delay(uchar z)
{
uchar x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void init()
{
TMOD=0x20;
TH1=0xe8;
TL1=0xe8;
TR1=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
wela=1;
P0=0x7f;
wela=0;
P0=0x7f;
}
void start()
{
adwr=1;
adwr=0;
adwr=1;
}
void main()
{
init();
while(1)
{
start();
delay(100);
P1=0xff;
//P0=0;
adrd=0;
temp=P1;
delay(5);
adrd=1;
//P0=0xff;
delay(100);
// ES=0;
SBUF=temp;
while(!TI);
TI=0;
// ES=1;
}
}
课件程序:
#include<reg52.h>
#include <intrins.h>
#define uint unsigned int //宏定义
#define uchar unsigned char //宏定义
sbit dula=P2^6; //申明U1锁存器的锁存端
sbit wela=P2^7; //申明U2锁存器的锁存端
sbit adwr=P3^6; //定义AD的WR端口
sbit adrd=P3^7; //定义AD的RD端口
uchar adval;
void delay(uint z) //延时子函数
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void main()
{
wela=1;
P0=0x7f; //置CSAD为0,选通ADCS 以后不必再管ADCS
wela=0;
TMOD=0x20;//设置定时器1为工作方式2
TH1=0xe8; //波特率设置为1200
TL1=0xe8;
TR1=1;
SM0=0;
SM1=1;
REN=1;
while(1)
{
adwr=1;
_nop_();
adwr=0; //启动AD转换
_nop_();
adwr=1;
delay(500);
P1=0xff; //读取P1口之前先给其写全1
adrd=1; //选通ADCS
_nop_();
adrd=0; //AD读使能
_nop_();
adval=P1; //AD数据读取赋给P1口
delay(500);
SBUF=adval;
while(!TI);
TI=0;
}
}
按下矩阵键盘第一行时以1200bps发送,1,2,3,4,第二行时以2400bps发送5,6,7,8,第三行以4800bps发送,9,10,11,12,第四行以9600pbs 发送,13,14,15,16:
知识点:1200bps对应初值e8H,2400bps对应初值f4H,4800bps对应初值faH,9600bps对应初值fdH
我的思路:先检测键盘按下,再发送;先做检测按下键盘第一行,发送1,2,3,4(先做发送1,做好再做2,3,4),这里是按一个键发送相应的值
对于按第一行时以1200bps发送,1,2,3,4,按第二行时以2400bps发送5,6,7,8,按第三行以4800bps发送,9,10,11,12,按第四行以9600pbs 发送,13,14,15,16,不是按一个键就发送相应的键,我的程序:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar temp,aa;
void delay(uchar z)
{
uchar x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void init()
{
TMOD=0x20;//这里先不设置TH1和TL1,在程序中设置,并根据要求进行改变TH1和TL1的值
TR1=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
}
void translate1(uchar aa)//设置不同的TH1和TL1值,送给计算机
{
TH1=0xe8;
TL1=0xe8;
SBUF=aa;
while(!TI);
TI=0;
}
void translate2(uchar aa)
{
TH1=0xf4;
TL1=0xf4;
SBUF=aa;
while(!TI);
TI=0;
}
void translate3(uchar aa)
{
TH1=0xfa;
TL1=0xfa;
SBUF=aa;
while(!TI);
TI=0;
}
void translate4(uchar aa)
{
TH1=0xfd;
TL1=0xfd;
SBUF=aa;
while(!TI);
TI=0;
}
void keyscan()//键盘检测
{
P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
aa=1;
translate1(aa);
delay(100);
aa=2;
translate1(aa);
delay(100);
aa=3;
translate1(aa);
delay(100);
aa=4;
translate1(aa);
delay(100);
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
P3=0xfd;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
aa=5;
translate2(aa);
delay(100);
aa=6;
translate2(aa);
delay(100);
aa=7;
translate2(aa);
delay(100);
aa=8;
translate2(aa);
delay(100);
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
P3=0xfb;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
aa=9;
translate3(aa);
delay(100);
aa=10;
translate3(aa);
delay(100);
aa=11;
translate3(aa);
delay(100);
aa=12;
translate3(aa);
delay(100);
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
P3=0xf7;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
aa=13;
translate4(aa);
delay(100);
aa=14;
translate4(aa);
delay(100);
aa=15;
translate4(aa);
delay(100);
aa=16;
translate4(aa);
delay(100);
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
}
void main()
{
init();
while(1)
{
keyscan();
}
}
因为数据接收和发送用的是单片机的P3.0口和P3.1口,而程序中键盘检测不得不用P3口,就会出现冲突
按相应的键发送计算机相应的值,我的程序:
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
uchar temp,key,flag;
void delay(uchar z)
{
uchar x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void init()
{
TMOD=0x20;//这里先不设置TH1和TL1,在程序中设置,并根据要求进行改变TH1和TL1的值
//TR1=1;
SM0=0;
SM1=1;
REN=1;
EA=1;
ES=1;
}
void keyscan()//键盘检测,确定按下的键值key
{
TR1=0;//关闭定时器1
P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xee: key=1;
break;
case 0xde: key=2;
break;
case 0xbe: key=3;
break;
case 0x7e: key=4;
break;
}
TH1=0xe8;
TL1=0xe8;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
flag=1;
}
P3=0xfd;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xed: key=5;
break;
case 0xdd: key=6;
break;
case 0xbd: key=7;
break;
case 0x7d: key=8;
break;
}
TH1=0xf4;
TL1=0xf4;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
flag=1;
}
P3=0xfb;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xeb: key=9;
break;
case 0xdb: key=10;
break;
case 0xbb: key=11;
break;
case 0x7b: key=12;
break;
}
TH1=0xfa;
TL1=0xfa;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
flag=1;
}
P3=0xf7;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(5);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xe7: key=13;
break;
case 0xd7: key=14;
break;
case 0xb7: key=15;
break;
case 0x77: key=16;
break;
}
TH1=0xfd;
TL1=0xfd;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
flag=1;
}
TR1=1; //打开定时器1
}
void main()
{
init();
while(1)
{
keyscan();
P3=0xff;
if(flag==1)
{
flag=0;
SBUF=key;
while(!TI);
TI=0;
}
}
}
课件程序:
//由于矩阵键盘与串口引脚冲突,因此本题仅供学习参考。
#include <reg52.h> //52系列单片机头文件
#define uchar unsigned char
#define uint unsigned int
uchar flag,key;
void delayms(uint xms)
{
uint i,j;
for(i=xms;i>0;i--) //i=xms即延时约xms毫秒
for(j=110;j>0;j--);
}
void matrixkeyscan()
{
uchar temp;
TR1=0;
P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
TH1=0xe8; //波特率设置为1200
TL1=0xe8;
switch(temp)
{
case 0xee:
key=1;
break;
case 0xde:
key=2;
break;
case 0xbe:
key=3;
break;
case 0x7e:
key=4;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
flag=1;
}
}
P3=0xfd;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
TH1=0xf4; //波特率设置为2400
TL1=0xf4;
switch(temp)
{
case 0xed:
key=5;
break;
case 0xdd:
key=6;
break;
case 0xbd:
key=7;
break;
case 0x7d:
key=8;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
flag=1;
}
}
P3=0xfb;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
TH1=0xfa; //波特率设置为4800
TL1=0xfa;
switch(temp)
{
case 0xeb:
key=9;
break;
case 0xdb:
key=10;
break;
case 0xbb:
key=11;
break;
case 0x7b:
key=12;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
flag=1;
}
}
P3=0xf7;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
TH1=0xfd; //波特率设置为9600
TL1=0xfd;
switch(temp)
{
case 0xe7:
key=13;
break;
case 0xd7:
key=14;
break;
case 0xb7:
key=15;
break;
case 0x77:
key=16;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
flag=1;
}
}
TR1=1;
}
void main()
{
TMOD=0x20;//设置定时器1为工作方式2
TH1=0xe8; //波特率设置为1200
TL1=0xe8;
SM0=0;
SM1=1;
while(1)
{
matrixkeyscan();
P3=0xff;
if(flag==1)
{
flag=0;
SBUF=key;
while(!TI);
TI=0;
}
}
}
郭天祥的10天学会51单片机_第七节相关推荐
- 郭天祥的10天学会51单片机_第十三节
AD软件: AD软件可以编译C语言和VHDL语言等:AD软件的工具栏上面可以输入网络地址进行浏览网页:AD软件可以布差分信号,即可以两条线可以一起布 按住Shift键和左键可以向左移动,按住Shift ...
- 郭天祥的10天学会51单片机_第三节
单片机管脚,P3口有两个功能,上电默认情况下就使用P3口的普通IO口,当对单片机内部的某些寄存器设置时就启用P3口的第二个功能 P3^4和 P3^5可以作为计时器或定时器使用,给这两个口输入方波,进行 ...
- 10.4.4 51单片机控制系统8个LED“跑马灯”实验
10.4.4 51单片机控制系统8个LED"跑马灯"实验 仿真+代码 方法一 利用数组 #include"reg52.h"#define u8 unsigned ...
- 51单片机学习笔记(郭天祥版)(1)——单片机基础和点亮LED灯
关于单片机型号的介绍: STC89C52RC40C-PDIP 0721CV4336..... STC:STC公司 89:89系列 C:COMS 52(还有51,54,55,58,516,):2表示存储 ...
- 红外测距模块 51单片机_[51单片机] HC-SR04超声波测距仪
1.HC-SR04超声波模块工作原理 (1)采用IO口触发测距,给至少10us高电平脉冲: (2)模块自动发送八个40khz方波,并自主检测是否有电波返回: (3)当有信号返回时,通过IO口输出一个高 ...
- 2440 8字数码管 显示0到10 c语言,51单片机对8位数码管依次显示0-7的设计
共阳极数组0~9:display[]={0xC0,0xF9,0xA4,0xB0,0x91,0xA2,0x82,0xF8,0x80,0x90} 共阳极数组A~F:display[]={0x88,0x83 ...
- 单片机c语言二进制转10进制,51单片机用C语言怎么样把八位二进制转换成十进制...
满意答案 sunzhaoming 2013.07.06 采纳率:41% 等级:12 已帮助:8866人 //******************************************* ...
- 基于proteus的51单片机仿真实例七十六、8-3编码芯片74HC148应用实例
1.本例使用8-3编码芯片74HC148实现外部中断扩展,可以实现对8路外部中断信号按优先级进行处理 2.74HC148是带优先级的编码芯片,对于外部的8根数据输入线,只要有一根或者几根被置0,编码芯 ...
- 51单片机_动态数码管显示
动态数码管显示(延时函数) 编程原理 利用延迟函数,每秒钟数码管加1显示,一直加到250.延时函数精度不高,最好使用定时器中断. 数码管动态显示原理 动态数码管显示有段选和位选.段选是单个数码管显示的 ...
最新文章
- ul li前面的点怎么变大_亚马逊产品被投诉需要UL认证,该如何办理?
- 年前整理的Css规范
- 深度学习这么调参训练_聊一聊深度学习中的调参技巧?
- 你真的会用Jupyter吗?这里有7个进阶功能助你效率翻倍
- L1-047 装睡 (10 分)—团体程序设计天梯赛
- C语言实现—学生成绩管理系统(Linux下运行)
- ArcGIS教程:MapGIS转换shp攻略
- 【小样本基础】「MAML」 VS 「Model-Pre-training」MAML与预训练的区别
- 微信发送文件卡死或黑屏
- JAVA实现K-means聚类
- MacPro终端出现bash: touch: command not found
- 大数据时代物联网技术发展前景与应用分析
- CRM客户管理系统怎样对客户价值进行评估
- 高级程序员之抽象能力模型
- CIFS协议入门指南:快速部署文件共享服务器
- devops涵盖的三个阶段_3个领域推动DevOps变革
- 彩色日志,教你配置颜色分明的日志输出。
- HTML5期末大作业:商城页面——仿优分购电商静态网页(7个页面) HTML+CSS+JavaScript 电商购物网页HTML代码 学生网页课程设计期末作业下载 大学生网页设计制作成...
- php 监听用户退出,php 利用 Inotify监视程序 用于重启服务器进程
- 神秘的Flash Translation Layer (FTL)
热门文章
- Java后台微信点餐小程序开发最新版笔记,Springboot+Mysql+Freemarker+Bootstrap+微信小程序实现扫码点餐小程序,包含语音提示,微信消息推送,网页管理后台
- 伯禹-公益AI学习打卡 Task02
- qt creator插入代码块快速注释snippets代码片段的功能
- 计算机信息管理存在的问题与对策,全面信息化管理系统问题及对策(word版)...
- EFR32上实现精确的软件延时
- CTF 2020 第二届 网鼎杯 第一道 Misc 签到
- html银白色,纯CSS打造银色MacBookAir(二)_html/css_WEB-ITnose
- 逻辑强化(09)加强支持 知识练习
- 论文详读:Beyond Brightening Low-light Images (Kind++)
- Spring中的applicationContext.xml与SpringMVC的xxx-servl