实例32 、实例33和实例34 课件(6份打包) 《单片机基础与应用》(C语言版) 同步教学

资源下载
  1. 二一教育资源

实例32 、实例33和实例34 课件(6份打包) 《单片机基础与应用》(C语言版) 同步教学

资源简介

(共17张PPT)
实例32 串口控制16个LED流水灯
《单片机基础与应用(C语言版)》
高等教育出版社
实例要求
实例分析
采用串入并出移位寄存器74LSl64实现单片机串行口的I/O扩展,一片74LS164可以扩展8位并行输出口,系统需要2片74LS164级联实现16个LED的控制。
利用单片机串行口扩展并行I/O口电路,驱动16个LED,使16个LED逐一点亮,实现16个LED流水灯效果。
硬件电路
控制程序设计
//程序:ex35.c
//功能:串口控制16个LED流水灯程序
#include //包含头文件REGX51.H,定义了51单片机的所有SFR
//函数名:sendbyte
//函数功能:向串口发送一个字符,采用查询方式实现
//形式参数:无符号整型变量i,定义发送的字符
//返回值:无
void sendbyte(unsigned char i)
{
SBUF=i; //发送字符写入SBUF
while(!TI); //查询TI是否由0变1
TI=0; //软件给TI清0
}
//定义流水灯显示数据
unsigned char dat[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
void main()
{
unsigned char i;
unsigned int t;
SCON=0x00; //设置串行口工作方式为方式0
while(1){
for(i=0;i<8;i++){//第2片74LS164连接的8个灯实现流水
sendbyte(dat[i]); //发送第2片74LS164连接8个灯显示数据
sendbyte(0xff); //第1片74LS164连接8个灯熄灭
for(t=0;t<20000;t++); //延时
}
for(i=0;i<8;i++){//第1片74LS164连接的8个灯实现流水
sendbyte(0xff); //第2片74LS164连接8个灯熄灭
sendbyte(dat[i]);//第1片74LS164连接8个灯显示数据
for(t=0;t<20000;t++); //延时
}
}
}
《单片机基础与应用(C语言版)》
高等教育出版社
实例33 单片机双机通信
实例要求
实例分析
如果2个单片机共在一个电路板上或同处于一个机箱内,这时只要将2个单片机的TXD和RXD引出线交叉相连,并共地即可;如果2个单片机不在一个机箱内,距离较远,这时要采用 RS-232C接口进行连接。本系统假定两机处于一个机箱内。
实现一个单片机串行口双机通信测试系统。系统中,发送与接收各用一套51单片机电路,称为甲机和乙机。甲机作为发送端,连接一个按键;乙机作为接收端,连接一个LED。将甲机按键次数(0~9,超过10次后又回到0)发送给乙机,并在乙机的LED上显示出来。
硬件电路
控制程序设计
//程序:ex36.c
//功能:甲机串行发送程序
#include //包含头文件REGX51.H,定义了51单片机的所有SFR
sbit S=P0^0; //定义P0.0引脚位名称为S
unsigned char count; //全局变量,用于存放按键次数
//函数名:key
//函数功能:检测按键S是否按下,如果按下count加1计数
//形式参数:无
//返回值:无
void key()
{ unsigned int k;
if(S==0) //第1次判断S是否按下
{
for(k=0;k<1200;k++); //延时去抖
if(S==0) { //再次判断S是否按下
if(++count==10)count=0; //按键次数处理
while(!S); //等待S释放
} } }
甲机发送通信程序如下。
void main()
{
TMOD=0x20; //设置定时器T1为方式2
TL1=0xfd; //波特率为9600Bd
TH1=0xfd;
TR1=1;
SCON=0x40; //定义串行口工作于方式1,不接收
PCON=0x00; //SMOD=0
count=0; //次数清0
while(1)
{
key(); //调用按键处理函数
SBUF=count; //发送次数
while(!TI); //查询TI是否由0变1
TI=0; //软件给TI清0
}
}
控制程序设计
乙机发送通信程序如下。
//程序:ex7-2.c
//功能:乙机串行接收程序,采用查询方式实现
#include //包含头文件REGX51.H,定义了51单片机的所有SFR
//定义0~9共阳极显示字型码
code unsigned char tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void main() //主函数
{
unsigned char i;
TMOD=0x20; //设置定时器T1为方式2
TL1=0xfd; //波特率为9600Bd
TH1=0xfd;
TR1=1;
SCON=0x40; //定义串行口工作于方式1,不接收
PCON=0x00; //SMOD=0
while(1)
{
REN=1; //接收允许
while(RI==0); //查询等待接收标志为1,表示接收到数据
i=SBUF; //接收数据
P1=tab[i]; //显示接收数据
RI=0; //RI由软件清0
}
}
思考题:接收程序修改为中断方式,怎么修改程序。
控制程序设计
//程序:ex37.c
//功能:乙机接收程序,中断方式实现
#include //包含头文件REGX51.H,定义了51单片机的所有SFR
code unsigned char tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//定义0~9共阳极显示字型码
void main() //主函数
{
TMOD=0x20; //设置定时器T1为方式2
TL1=0xfd; //波特率为9600Bd
TH1=0xfd;
TR1=1;
SCON=0x40; //定义串行口工作于方式1,不接收
PCON=0x00; //SMOD=0
ES=1; //开串行口中断
EA=1; //开总中断允许位
REN=1; //接收允许
P1=tab[0]; //LED显示0
while(1); //等待接收中断
}
//函数功能:串行口中断接收函数
void serial()interrupt 4 //串口中断类型号为4
{
EA=0; //关中断
RI=0; //软件清除中断标志位
P1=tab[SBUF]; //显示接收数据
EA=1;
}
《单片机基础与应用(C语言版)》
高等教育出版社
实例34 单片机与PC通信
实例要求
实例分析
单片机通过一个电平转换电路MAX232芯片,与RS232-C插座连接,可以用一条串口线与PC的RS232_C口通信。单片机收到PC发送的字符后,回送给PC,同时用识别字符’A’和’B’来控制8个LED的显示,从而可以直接看到单片机与PC之间成功通信。
PC上可以用“串口调试助手”软件来发送字符、显示接收到的单片机回送字符。
通信协议如下:波特率9600Bd,1位起始位,8位数据位,无奇偶校验,1位停止位。
实现一个单片机与PC之间的串行口通信测试系统。系统中,单片机通过串口与PC通信,同时连接8个LED。8个LED闪烁过程中,可以接收PC发送的字符,并回送给PC。如果接收的字符为’A’,则LED左移流水灯显示一遍,继续闪烁;如果接收的字符为’B’,则LED右移流水灯显示一遍,再返回闪烁状态。
硬件电路
控制程序设计
//程序:ex38.c
//功能:单片机通信程序,接收采用中断方式实现
#include //包含头文件REGX51.H,定义了51单片机的所有SFR
#include //包含头文件INTRINS.h,定义了移位函数
unsigned char Recbyte; //全局变量,存放接收的字符
//函数名:delay
//函数功能:实现软件延时
//形式参数:无符号整型变量i,控制空循环的循环次数
//返回值:无
void delay(unsigned int i)
{ while(i--); }
//函数名:flash
//函数功能:8个LED闪烁一次
//形式参数:无
//返回值:无
void flash()
{
P1=0x00; //点亮8个LED
delay(10000);
P1=0xff; //熄灭8个LED
delay(10000);
}
//函数名:lsd1
//函数功能:8个LED逐一点亮一次,左移方向
void lsd1()
{
unsigned char i,w;
w=0xfe; //流水灯初值
for(i=0;i<8;i++)
{
P1=w;
delay(10000);
w=_crol_(w,1); //循环左移1位
}
}
//函数名:lsd2
//函数功能:8个LED逐一点亮一次,右移方向
void lsd2()
{
unsigned char i,w;
w=0x7f; //流水灯初值
for(i=0;i<8;i++)
{
P1=w;
delay(10000);
w=_cror_(w,1); //循环右移1位
}
}
控制程序设计
//函数名:sendbyte
//函数功能:向串口发送一个字符,采用查询方式实现
//形式参数:无符号整型变量i,定义发送的字符
//返回值:无
void sendbyte(unsigned char i)
{
SBUF=i; //发送字符写入SBUF
while(!TI); //查询TI是否由0变1
TI=0; //软件给TI清0
}
void main() //主函数
{
TMOD=0x20; //设置定时器T1为方式2
TL1=0xfd; //波特率为9600Bd
TH1=0xfd;
TR1=1;
SCON=0x40; //定义串行口工作于方式1,不接收
PCON=0x00; //SMOD=0
ES=1; //开串行口中断
EA=1; //开总中断允许位
REN=1; //接收允许
Recbyte=0; //接收字符初值0
while(1)
{
switch(Recbyte)
{
case 'A':lsd1(); Recbyte=0;break; //左移流水灯
case 'B':lsd2(); Recbyte=0;break; //右移流水灯
default: flash(); //闪烁
}
}
}
//函数名:serial
//函数功能:串行口中断接收函数
//形式参数:无
//返回值:无
void serial()interrupt 4 //串口中断类型号为4
{
EA=0; //关中断
RI=0; //软件清除中断标志位
Recbyte=SBUF ; //接收数据
sendbyte(Recbyte); //再送回PC
EA=1; //开中断
}
Thanks for your attention.
感谢您的观看(共14张PPT)
《单片机基础与应用(C语言版)》
高等教育出版社
实例35 简易数字电压表
实例要求
实例分析
系统需要将模拟信号转变为数字信号,再将数字信号转化为输入模拟信号的电压值,并进行显示,所以需要2次转换和一次显示操作。
采用PCF8591实现A/D转换,采集0~5 V连续可变的模拟电压信号,用电位器调节来实现这个模拟信号。转变为8位二进制数字信号(0x00~0xFF)后,送单片机处理,并在4位LED上显示出0.000~5.000 V(小数点不用显示)。
硬件电路
主函数流程
控制程序设计
//程序:ex39.c
//功能:0~5 V连续可变的模拟电压信号测量,并在4位LED上显示出0000~5000
#include //包含头文件reg51.h,定义了51单片机的专用寄存器
#include
sbit SDA=P3^6; //定义P3.6引脚位名称为SDA
sbit SCL=P3^7; //定义P3.7引脚位名称为SCL
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
//无符号字符型变量
unsigned char code SEGTAB[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x83,0xF8,0x80,0x98};
//定义共阳极7段LED显示字型码
#define SEGDATA P2 //定义LED段选信号数据接口
#define SEGSELT P0 //定义LED位选信号数据接口
#define PCF8591_WRITE 0x90 //PCF8591器件写地址
#define PCF8591_READ 0x91 //PCF8591器件读地址
unsigned char disp[4]={0,0,0,0}; //定义全局变量disp[],存储4个显示LED对应的显示值
bit SystemError;
//函数名:delay_ms
//函数功能:实现单位时间的延时,这个函数采用软件延时,
//有一定的误差,在精度要求不高的情况下,人们习惯这种用法
//形式参数:延时毫秒数
//返回值:无
void delay_ms(uint ms)
{
unsigned int i,j;
for(;ms>0; ms--)
{
for(i=0;i<7;i++)
for(j=0;j<210;j++);
}
}
控制程序设计
//函数名: data_process
//函数功能:把ADC转换的8位数据转换为实际的电压值
//形式参数:输入数据
//返回值:无,实际电压值分离后存放在全局数组disp[]中
void data_process(unsigned char value)
{
unsigned int temp;
temp = value *196; //0~255转换为0~50000
disp[3]=temp/10000; //得到万位
disp[2]=(temp/1000)%10; //得到千位
disp[1]=(temp/100)%10; //得到百位
disp[0]=(temp/10)%10; //得到十位,个位不需要,只显示高4位
}
//函数名: seg_display
//函数功能:将全局数组变量的值动态显示在4个LED上
//形式参数:引用全局数组变量disp
//返回值:无
void seg_display(void)
{
unsigned char i,scan;
scan=1;
for(i=0;i<4;i++) //控制4位LED显示
{
SEGDATA=0xFF;
SEGSELT=~scan; //送位选码
SEGDATA= SEGTAB[disp[i]]; //送段选码
delay_ms(5);
scan<<=1; //位选码左移1位
}
}
void main() //主函数
{
unsigned char voltage;
iic_init(); //IIC初始化,参见8.3.2节
while(1)
{
voltage = ADC_PCF8591(0); //测0通道电压,参见8.3.3节
data_process(voltage); //数据处理
seg_display (); //数据显示
delay_ms(10);
}
}
要将一个数据显示在LED上,需要把这个数据的每位数值单独分离出来,因为只是显示高4位,程序中做了以下处理。
disp[3]=temp/10000; //得到万位
disp[2]=(temp/1000)%10; //得到千位
disp[1]=(temp/100)%10; //得到百位
disp[0]=(temp/10)%10; //得到十位
《单片机基础与应用(C语言版)》
高等教育出版社
实例36 信号发生器
实例要求
实例分析
锯齿波是一种常见的信号波形,先呈直线上升,随后陡落,再上升,再陡落,如此反复,实际上是一个线性波形。
采用PCF8591实现简易波形发生器,产生锯齿波信号。通过软件调整波形设定参数,可用示波器观察输出波形的幅值、周期及频率的变化。
硬件电路
初始化
开始
输出数字量进行D/A转换
输出电压值加1
确定输出数字量初值0
延时
输出值=255?
Y
N
控制程序设计
//程序:ex40.c
//功能:锯齿波产生控制程序
#include //包含头文件reg51.h,定义了51单片机的专用寄存器
#include //包含头文件intrins.h
sbit SDA=P2^7; // P2.7定义为IIC数据
sbit SCL=P2^6; // P2.6定义为IIC时钟
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
bit bdata SystemError; // 从机错误标志位
//------------PCF8591专用变量定义-----------------------------------
#define PCF8591_WRITE 0x90 //器件写地址
#define PCF8591_READ 0x91 //器件读地址
//函数名:delay_ms
//函数功能: 采用定时器T1延时t毫秒,工作方式1,定时器初值64536
//形式参数:延时毫秒数
//返回值:无
void delay_ms(unsigned char t)
{
unsigned char i;
TMOD=0x10; //设置T1为工作方式1
for(i=0;i{
TH1=0xFC; //置定时器初值0xFC18=64536
TL1=0x18;
TR1=1; //启动定时器1
while(!TF1); //查询计数是否溢出,即1ms定时时间到,TF1=1
TF1=0; //1ms定时时间到,将定时器溢出标志位TF1清0
}
}
控制程序设计
void main() //主函数
{
unsigned char i;
iic_init(); //参见8.3.2节
while(1)
{
for(i=0;i<=255;i++) //形成锯齿波输出值,最大255
{
DAC_PCF8591(0x40, i); //控制字为0100 0000,允许模拟量输出,参见8.3.4
delay_ms(1);
}
}
}
Thanks for your attention.
感谢您的观看(共13张PPT)
实例37 数字钟
《单片机基础与应用(C语言版)》
高等教育出版社
实例要求
数字钟设计要求如下。
(1)显示当前的时间。
(2)手动修改时间信息。
(3)手动开启/关闭闹钟功能,且有闹钟状态指示灯。
(4)手动设置闹钟,并且当达到设置好的时间点时报警,报警时间定时5s。
实例分析
51单片机核心模块包括51单片机最小系统模块,是设计应用系统的控制核心;显示模块用于显示时间和闹钟设置时间等时间信息;闹钟指示灯指示闹钟当前状态:开启或关闭;闹钟采用声音报警;键盘输入模块完成时间设置、闹钟开启或关闭、闹钟时间设置等。
硬件电路
(1)显示模块
采用8个共阴极LED显示时间和闹钟时间,显示格式:hh-mm-ss(时-分-秒),单片机的P1口连接段选码,用8同相三态缓冲器/线驱动器74LS245驱动,P2口连接位选码。
(2)闹钟指示灯
一个LED作为闹钟指示灯,LED亮,表示闹钟功能开启;LED灭,表示闹钟功能关闭,由单片机的P3.1引脚控制。
(3)闹钟报警
闹钟报警采用蜂鸣器发声装置,由单片机的P3.0引脚控制。
(4)键盘输入
一般来说,当系统需要键盘数量超过8个时,采用矩阵键盘实现;否则,采用独立式按键实现。按照设计要求,数字钟需要5个按键,所以采用独立式按键连接方法,分别由单片机的P3.2~P3.6控制。按键功能定义见表9-1。
序号 连接引脚 定义名称 功能
1 P3.2 SWITCH 外部中断0,闹钟开关
2 P3.3 SET 外部中断1,进入设置闹钟状态,进行闹铃设置,QUIT键按下则退出
3 P3.4 INC_h 调节时钟和闹钟时,按一下小时加1,加到23后回到0
4 P3.5 INC_f 调节时钟和闹钟时,按一下分钟加1,加到59后回到0
5 P3.6 QUIT 退出设置闹钟
表9-1 数字钟按键功能定义
控制程序设计
控制程序设计
//程序名:ex41.c
//功能:数字钟程序
#include
#define uint unsigned int
#define uchar unsigned char
//函数声明
void tiaojie(); //调节时时间函数声明
void naozhong(); //闹钟设置函数声明
void alarm(); //闹铃函数声明
//5个按键定义
sbit SWITCH=P3^2; //外部中断0,闹钟功能切换键
sbit SET=P3^3; //外部中断1,进入设置闹钟状态,进行闹铃设置,QUIT键按下则退出
sbit INC_h=P3^4; //调节时钟和闹钟时,小时加1按键,加到59后回到0
sbit INC_f=P3^5; //调节时钟和闹钟时,分钟加1按键,加到59后回到0
sbit QUIT=P3^6; //退出设置闹钟
//蜂鸣器和闹钟开关指示灯定义
sbit BEEP=P3^0; //蜂鸣器端口
sbit LED=P3^1; //闹钟开关指示灯,灯灭--闹钟关闭,灯亮--闹钟开启
//定义全局变量
uchar m,f,s,w; //w为累计50ms的次数,m为秒计数,f为分计数,h为时计数
uchar f_nao,s_nao; //f_nao为闹钟分计数,s_nao为闹钟时计数
bit flag_nao; //flag_nao=0,off,1--on
控制程序设计
//函数名:ledscan
//函数功能:实现8位LED动态显示时-分-秒
//形式参数:unsigned char h,unsigned char m,unsigned char s
// 扫描的是h,m,s。分别对应时,分,秒。
//返回值:无
void ledscan(unsigned char h,unsigned char m,unsigned char s) reentrant
{
uchar led[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//0~9共阴极显示码表
uint j;
P1=led[s%10]; //第1位显示,显示秒个位并送入P1口
P2=0x7f; //位选码01111111送入P2口
for(j=0;j<100;j++); //小延时。以下同理
P2=0xff; //关闭全部使能端,消影。以下同理
P1=led[s/10]; //第2位显示,显示秒十位并送入P1口
P2=0xbf; //位选码10111111送入P2口
for(j=0;j<100;j++);
P2=0xff;
P1=0x40; //第3位显示,显示字符“-”并送入P1口
P2=0xdf; //位选码11011111送入P2口
for(j=0;j<100;j++);
P2=0xff;
P1=led[m%10]; //第4位显示,显示分个位并送入P1口
P2=0xef; //位选码11101111送入P2口
for(j=0;j<100;j++);
P2=0xff;
P1=led[m/10]; //第5位显示,显示分十位并送入P1口
P2=0xf7; //位选码11110111送入P2口
for(j=0;j<100;j++);
P2=0xff;
P1=0x40; //第6位显示,显示字符“-”并送入P1口
P2=0xfb; //位选码11111011送入P2口
for(j=0;j<100;j++);
P2=0xff;
P1=led[h%10]; //第7位显示,显示时个位并送入P1口
P2=0xfd; //位选码11111101送入P2口
for(j=0;j<100;j++);
P2=0xff;
P1=led[h/10]; //第8位显示,显示时十位并送入P1口
P2=0xfe; //位选码11111110送入P2口
for(j=0;j<100;j++);
P2=0xff;
}
控制程序设计
//函数名: tiaojie
//函数功能:每次按INC_h实时时钟的小时就加1,每次按INC_f实时时钟的分钟就加1。
// 用来调节实时时间
//形式参数:无
//返回值:无
void tiaojie()
{
if(INC_h==0) //判断是否有键按下,按键延时去抖处理,以下同理
{
ledscan(s,f,m);
if(INC_h==0) //调节小时,当按下INC_h:如果小时等于23,
{ //则小时为0;否则小时加1
if(s==23) s=0; else s++;
while(!INC_h)ledscan(s,f,m);//判断键是否释放
}
}
else if(INC_f==0) //调节分钟,当按下INC_f:如果分钟等于59,
{ //则分钟为0
ledscan(s,f,m);
if(INC_f==0)
{ if(f==59)f=0;
else f++;
while(!INC_f)ledscan(s,f,m);
}
}
}
//函数名: alarm
//函数功能:当时间和设置闹铃时间相等时,闹铃响5s。
//形式参数:无
//返回值:无
void alarm()
{
if(flag_nao) //如果flag_nao为真,闹钟开启
{
if(f==f_nao&&s==s_nao) //判断实时时间和闹钟时间的分,时是否同时相等
{
while(m<=5&&flag_nao) //如果m<=5和flag_nao都为真,闹铃响5s
{
BEEP=!BEEP;
ledscan(s,f,m);
}
}
}
}
控制程序设计
//函数名:t0
//函数功能:T0中断服务函数,在T0的工作方式1,采用内部时钟定时实现实时时钟
//形式参数:无
//返回值:无
void t0() interrupt 1 //内部定时T0中断服务函数
{
TH0=(65536-50000)/256;//重新设置T0计数初值高8位,定时时间50ms
TL0=(65536-50000)%256;//重新设置T0计数初值低8位,定时时间50ms
w++;
if(w==20) //当w==20时,w为0,秒计数加一
{
w=0;m++;
if(m==60) //当秒计数为60时,秒计数为0,分计数加一
{f++;m=0;
if(f==60) {s++;f=0;if(s==24)s=0;} //当分计数为60时,分计数为0,时计数加一
} //当时计数为24时,时计数为0
}
}
//函数名:guan_naozhong
//函数功能:外部中断0中断服务函数,SWITCH按键控制flag_nao。当闹钟响时,按下此键立即关掉闹铃,同时关掉闹钟功能
//形式参数:无
//返回值:无
void guan_naozhong() interrupt 0
{
flag_nao=~flag_nao; //flag_nao取反
LED=~LED;
while(!SWITCH)ledscan(s,f,m); //等待按键释放
}
控制程序设计
//函数名:naozhong
//函数功能:外部中断1中断服务函数,用来调节闹钟时间,实现每次按键INC_h时钟就加1,每次按键INC_f分钟就加1。
void naozhong() interrupt 2
{
f_nao=f; //当前时间作为闹钟初始值
s_nao=s;
while(QUIT){ //等待按下QUIT退出,否则循环执行闹钟时间设置
ledscan(s_nao,f_nao,0); //显示闹钟设置
if(INC_h==0){
ledscan(s_nao,f_nao,0);
if(INC_h==0){
if(s_nao==24) s_nao=0; //调节闹钟时间,当按下INC_h:如果闹钟小时等于23,
else s_nao++; //则闹钟小时为0;否则闹钟小时加1
}
while(!INC_h)ledscan(s_nao,f_nao,0); //等待按键释放
}
else if(INC_f==0){
ledscan(s_nao,f_nao,0);
if(INC_f==0) //调节闹钟分钟,当按下INC_f:如果闹钟分钟等于59,
{ //则闹钟分钟为0;否则闹钟时钟加1
if(f_nao==59)f_nao=0;
else f_nao++;
}
while(!INC_f)
ledscan(s_nao,f_nao,0);
}
}
}
控制程序设计
void main() //主函数
{
TMOD=0x01; //设置T0工作方式1
TH0=(65536-50000)/256; //设置T0计数初值高8位,12MHz下定时时间50ms
TL0=(65536-50000)%256; //设置T0计数初值低8位,12MHz下定时时间50ms
TR0=1; //开启T0
ET0=1; //打开外部中断1允许位
IT1=1; //设置外部中断2为下降沿触发方式
IT0=0; //设置外部中断0为低电平触发方式
EX1=1; //打开外部中断2允许位
EX0=1; //打开外部中断0允许位
PT0=1; //设置内部定时中断优先级大于外部中断优先级
EA=1; //打开中断总允许位
f=0; //赋初值
m=0;
BEEP=0;
flag_nao=0;
while(1)
{
ledscan(s,f,m); //不断扫描显示实时时钟(时-分-秒)
tiaojie(); //调节实时时钟(调节时,分)
alarm(); //判断闹钟
}
}
感谢您的观看
Thanks for your attention. (共18张PPT)
《单片机基础与应用(C语言版)》
高等教育出版社
实例38 倒计时交通灯控制
实例要求
(1)要求南北方向车道和东西方向车道2条交叉道路上的车辆自动交替运行,东西和南北方向各有一组红、黄、绿灯用于指挥交通。
(2)红灯的持续时间为55s,绿灯持续时间为50s,闪烁时间3s。
(3)黄灯在绿灯转为红灯之前亮2s
(4)东西方向、南北方向车道除了有红、黄、绿指示灯外,每一种灯亮的时间都用LED进行倒计时显示。
(5)有紧急情况车辆要求通过时,系统要能禁止普通车辆通行,南北、东西方向都是红灯,时间持续20s,当紧急情况结束后,继续正常工作。
(6)有特殊情况车辆要求通过时,系统要求南北方向通行10s,当特殊情况结束后,继续正常工作。
实例分析
硬件电路
(1)交通灯显示电路
采用6个LED模拟2个方向的红黄绿交通灯,采用单片机的P0口控制,采用灌电流控制方式,低电平点亮。
(2)倒计时显示电路
4个动态连接LED分别模拟2个方向的倒计时显示器,每个方向2位,P2.0~P2.3控制LED的段选,P1口连接LED的位选端,采用同相三态数据缓冲器74LS245驱动。
(3)紧急和特殊情况按键输入
按键S1、S2分别模拟紧急情况和特殊情况的发生,当S1、S2没有按下时,表示正常情况。当S1按下时,表示紧急情况,将S1信号接至 脚(P3.2)即可实现外部中断0中断申请。当S2按下时,表示特殊情况,将S2信号接至 脚(P3.3)即可实现外部中断1中断申请。
控制程序设计
南北方向 东西方向 P0口数据 交通灯状态
红P0.5 黄P0.4 绿P0.3 倒计时 红P0.2 黄P0.1 绿P0.0 倒计时 1 1 0 50 0 1 1 55 F3H 状态1:南北绿灯,东西红灯
1 1 0、1 3 0 1 1 F3H FBH 状态2:南北绿灯闪,东西红灯
1 0 1 2 0 1 1 EBH 状态3:南北黄灯,东西红灯
0 1 1 55 1 1 0 50 DEH 状态4:南北红灯,东西绿灯
0 1 1 1 1 0、1 3 DEH DFH 状态5:南北红灯,东西绿灯闪
0 1 1 1 0 1 2 DDH 状态6:南北红灯,东西黄灯
控制程序设计
控制程序设计
//功能:倒计时交通灯控制程序
#include
#define uint unsigned int
#define uchar unsigned char
//全局变量定义
uchar e_w,s_n; //e_w为东西方向倒计时,s_n为南北方向倒计时
//函数名:ledscans
//函数功能:实现4位LED动态显示扫描函数,分别显示东西和南北倒计时秒数
void ledscans() reentrant
{
uchar led[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
uchar wei[]={0xfe,0xfd,0xfb,0xf7}; //位选码
uchar b;
P2=wei[0]; //第1位显示
P1=led[e_w/10]; //显示东西倒计时高位
for(b=0;b<100;b++); //延时
P1=0x00; //关显示
P2=wei[1]; //第2位显示
P1=led[e_w%10]; //显示东西倒计时低位
for(b=0;b<100;b++); //延时
P1=0x00; //关显示
P2=wei[2]; //第3位显示
P1=led[s_n/10]; //显示南北倒计时高位
for(b=0;b<100;b++); //延时
P1=0x00; //关显示
P2=wei[3]; //第4位显示
P1=led[s_n%10]; //显示南北倒计时低位
for(b=0;b<100;b++); //延时
P1=0x00; //关显示
}
控制程序设计
//函数名:time50ms
//函数功能:在T1工作方式1下定时50ms,采用查询方式实现
//形式参数:无符号整型变量i,控制定时时间,i*50ms
//返回值:无
void time50ms(uchar i) reentrant
{
uchar k;
for(k=0;kTH1=(65536-50000)/256; //重新设置T1计数初值高8位,定时时间50ms
TL1=(65536-50000)%256; //重新设置T1计数初值低8位
TR1=1; //启动T1
while(!TF1)ledscans();//查询计数是否溢出,即定时50ms时间到,TF1=1
TF1=0; //50ms定时时间到,将T1溢出标志位TF1清0
}
}
//函数名:int_0
//函数功能:外部中断0服务函数,紧急情况下2个方向红灯亮20s,高优先级
//形式参数:无
//返回值:无
void int_0() interrupt 0 //外部中断0
{
uchar i,l,m,n,p;
i=P0; //保护现场,暂存P1口、t0、t1、
l=TH1;
m=TL1;
n=s_n;
p=e_w;
e_w=20;
for(s_n=20;s_n>0;s_n--) //2个方向都倒计时计数20s
{
P0=0xdb; //2个方向红灯20s
time50ms(20);
e_w--;
}
P0=i; //恢复现场
TH1=l;
TL1=m;
s_n=n;
e_w=p;
}
控制程序设计
//函数名:int_1
//函数功能:外部中断1服务函数,特殊情况下南北方向通行10s,低优先级
//形式参数:无
//返回值:无
void int_1() interrupt 2 //外部中断1
{
uchar i,l,m,n,p;
EA=0; //关闭总中断
i=P0; //保护现场
l=TH1;
m=TL1;
n=s_n;
p=e_w;
EA=1; //开中断
e_w=10; //2个方向倒计时计数都是10秒
for(s_n=10;s_n>0;s_n--){
P0=0xf3; //南北方向绿灯,东西方向红灯
time50ms(20);
e_w--;
}
EA=0; //关中断
P0=i; //恢复现场
TH1=l;
TL1=m;
s_n=n;
e_w=p;
EA=1;
}
void main() //主函数
{
TMOD=0x10; //设置T1工作于方式1
IT0=1; //设置外部中断0为下降沿触发中断
EX0=1; //允许外部中断0
IT1=1; //设置外部中断1为下降沿触发中断
EX1=1; //允许外部中断1
PX0=1; //设置外部中断0(紧急情况)为高优先级)
EA=1; //允许总中断
while(1)
{ //南北方向绿灯50s,绿灯闪3s,黄灯2s;东西方向红灯,倒计时为55s,状态1-状态3
e_w=55; //东西方向倒计时初值
for(s_n=50;s_n>0;s_n--) //南北方向50s倒计时
{
P0=0xf3; //南北方向绿灯,东西方向红灯
time50ms(20);
e_w--; //东西方向同步倒计时
}
for(s_n=3;s_n>0;s_n--) //南北方向3s倒计时
{
P0=0xf3; //南北方向绿灯闪烁,东西方向红灯
time50ms(10);
P0=0xfb;
time50ms(10);
e_w--; //东西方向同步倒计时
}
控制程序设计
for(s_n=2;s_n>0;s_n--) //南北方向2s倒计时
{
P0=0xeb; //南北方向黄灯,东西方向红灯
time50ms(20);
e_w--; //东西方向同步倒计时
}//东西方向绿灯50s,绿灯闪3s,黄灯2s;南北方向红灯,倒计时为55s,状态4-状态6
s_n=55;
for(e_w=50;e_w>0;e_w--) //东西方向50s倒计时计数
{
P0=0xde; //东西方向绿灯,南北方向红灯
time50ms(20);
s_n--; //南北方向同步倒计时
}
for(e_w=3;e_w>0;e_w--) //东西方向3s倒计时
{
P0=0xde; //东西方向绿灯闪烁,南北方向红灯
time50ms(10);
P0=0xdf;
time50ms(10);
s_n--; //南北方向同步倒计时
}
for(e_w=2;e_w>0;e_w--) //东西方向2s倒计时
{
P0=0xdd; //东西方向黄灯,南北方向红灯
time50ms(20);
s_n--; //南北方向同步倒计时
}
}
}
《单片机基础与应用(C语言版)》
高等教育出版社
实例39 16×16点阵显示屏移动广告牌
实例要求
(1)将4个8×8点阵模块拼接成1个16×16点阵屏。
(2)在16×16点阵屏上移动显示图形、汉字等广告内容。
实例分析
因为LED点阵大屏幕的尺寸增加,行数和列数也随之增加,而单片机往往不能提供足够的电流和足够多的I/O端口来控制每行或每列LED的扫描。所以,需要为大尺寸的LED点阵显示屏设计行驱动电路和列驱动电路
硬件电路
(1)16×16点阵显示屏
由4片8×8 LED点阵组成的16×16 LED点阵显示屏如图9-13所示。将上面2片8×8 LED点阵模块的行并联在一起组成ROW0~ROW7,下面2片点阵模块的行并联在一起组成ROW8~ROW15,由此组成16根行扫描线;将左边上、下2片点阵模块的列并联在一起组成COL0~COL7,右边上、下2片点阵模块的列并联在一起组成COL8~COL15,由此组成16根列选线。
(2)行驱动电路
采用单片机的P2口和P3口共计16条I/O口线控制16条行线ROW0~ROW16,为了增强I/O口线的驱动能力,使用2片8位三态缓冲器74LS244驱动。
(3)列驱动电路
在列驱动电路设计中考虑采用译码器芯片来减少I/O端口的使用。选用2片3/8线译码器74LS138,生成16条列选通信号线,也可以采用1片4/16线译码器74LS154来实现16条列选通控制信号。
控制程序设计
//程序:ex43.c
//功能:16×16点阵移动广告牌,移动显示“★课程设计★”,采用逐列扫描法
#include
#define uchar unsigned char
#define uint unsigned int
sbit U1=P1^3; //138控制端,高电平138工作
sbit U2=P1^4; //138控制端,高电平138工作
//P2口连接的8行显示数据
uchar code tab1[]=
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 0x40,0x40,0xc0,0xc0,0xc0,0xe0,0xfc,0xff,0xfc,0xe0,0xc0,0xc0,0xc0,0x40,0x40,0x00,//★
0x40,0x42,0xCC,0x00,0x00,0x00,0xFE,0x92,0x92,0xFE,0x92,0x92,0xFE,0x00,0x00,0x00,//课
0x24,0x24,0xA4,0xFE,0x23,0x22,0x00,0x3E,0x22,0x22,0x22,0x22,0x22,0x3E,0x00,0x00,//程
0x40,0x40,0x42,0xCC,0x00,0x40,0xA0,0x9E,0x82,0x82,0x82,0x9E,0xA0,0x20,0x20,0x00,//设
0x40,0x40,0x42,0xCC,0x00,0x40,0x40,0x40,0x40,0xFF,0x40,0x40,0x40,0x40,0x40,0x00,//计
0x40,0x40,0xc0,0xc0,0xc0,0xe0,0xfc,0xff,0xfc,0xe0,0xc0,0xc0,0xc0,0x40,0x40,0x00,//★
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
//P3口连接的8行显示数据
uchar code tab2[]=
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 0x00,0x00,0x40,0x39,0x3f,0x1f,0x1f,0x0f,0x1f,0x1f,0x3f,0x39,0x40,0x00,0x00,0x00,//★
0x00,0x00,0x3F,0x10,0x08,0x22,0x12,0x0A,0x06,0xFF,0x06,0x0A,0x12,0x22,0x20,0x00,//课
0x08,0x06,0x01,0xFF,0x01,0x06,0x40,0x49,0x49,0x49,0x7F,0x49,0x49,0x49,0x41,0x00,//程
0x00,0x00,0x00,0x3F,0x90,0x88,0x40,0x43,0x2C,0x10,0x28,0x46,0x41,0x80,0x80,0x00,//设
0x00,0x00,0x00,0x7F,0x20,0x10,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,//计
0x00,0x00,0x40,0x39,0x3f,0x1f,0x1f,0x0f,0x1f,0x1f,0x3f,0x39,0x40,0x00,0x00,0x00,//★
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
控制程序设计
void main() //主函数
{
uchar n,col;//n用于延时,col控制每屏16列扫描0-15
uchar w,v; //w用于控制每屏扫描次数,可以控制移动,v控制移动,每次加1,控制逐列移动
U1=0; //74LS138控制端清0,74LS138不工作
U2=0; //74LS138控制端清0,74LS138不工作
col=0; //col控制列
v=0;
while(1)
{
for(v=0;v<=112;v++) //移动显示控制,16*7=112
{
for(w=0;w<=320;w++) //扫描列次计数器,一屏共扫描320列次,扫描一屏20次
{
P1=0; //P1控制列线,采用2片74LS138控制,预先清0
P2=tab1[v+col]; //送P2口连接的8位行数据(低8位)v控制取数位置,即移动显示控制;col控制列
P3=tab2[v+col]; //送P3口连接的8位行数据(高8位)
P1=col&0x07; //把P1口的低3位取出来,选中列
if(col<8)
{U1=1;U2=0;} //打开低7列的74LS138的控制口
else
{U2=1;U1=0;} //打开高7列的74LS138的控制口
col++;
if(col>15)col=0; //到15列,列清0
for(n=0;n<80;n++); //延时
}
}
}
}
Thanks for your attention.
感谢您的观看(共17张PPT)
实例40 简易打字游戏机
《单片机基础与应用(C语言版)》
高等教育出版社
实例要求
1)设计并显示一个简单的游戏logo。
(2)具有90s倒计时功能。
(3)具有12个按键,包括10个(0~9)打字按键,1个开始键,1个退出键。
(4)具有随机数(0~9)生成功能。
(5)在90s内输入屏幕上显示的随机数,如果正确,得1分,时间到或按下退出键,游戏结束,显示分数2s,回到游戏开始画面;按下开始键,重新开始游戏。
实例分析
显示模块用于显示游戏logo和游戏各种画面等内容,是人机输出通道;键盘输入模块完成游戏开始、退出、打字等功能,是人机输入通道。
硬件电路
控制程序设计
1)游戏logo设计与显示
字符型LCD一般内置了160个5×7点阵字符图形,可以显示英文、日文及数字。除此之外,还可以通过自定义字符,在片内自定义8个字符,显示特殊字符甚至笔画比较少的汉字,一般LCD模块所提供的CGRAM能够自编8个5×7字符。设计一个游戏logo,并把它用6个自编字符显示。
对LCD1602模块设置CGRAM地址以显示自编字符的命令字格式如下。
A7 A6 A5 A4 A3 A2 A1 A0
0 1 AC5 AC4 AC3 AC2 AC1 AC0
控制程序设计
字模及CGRAM地址、CGRAM字模和DDRAM字符代码的关系
DDRAM字符代码 A7A6A5 A4 A3 A2 A1 A0 CGRAM地址 P7P6 P5P4P3 P2 P1 P0 CGRAM字模
0x00 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 1 1 0 0 1 0 0 0 1 1 1 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 ×××□ □ □ □ □ ×××□ □ □ □ □ ×××□ □ □ □■ ×××□ □ □ ■■ ×××□ □ □ ■■ ×××□ □ ■ ■■ ×××□ □ ■ ■■ ×××□ □ □ □ □ 0x00
0x00
0x01
0x03
0x03
0x07
0x07
0x00
0x01 0 1 0 0 10 0 0 0 1 0 0 10 0 1 0 1 0 0 10 1 0 0 1 0 0 10 1 1 0 1 0 0 11 0 0 0 1 0 0 11 0 1 0 1 0 0 11 1 0 0 1 0 0 11 1 1 0x48 0x49 0x4A 0x4B 0x4C 0x4D 0x4E 0x4F ×××□ □ □ □ □ ×××■■ ■■ □ ×××■■ ■■ ■ ×××■■ ■■ ■ ×××■■ ■■ ■ ×××■■ □□□ ×××■□ □□□ ×××□ □ □ □ □ 0x00
0x1e
0x1f
0x1f
0x1f
0x18
0x10
0x00
0x02 0 1 0 100 0 0 0 1 0 100 0 1 0 1 0 100 1 0 0 1 0 100 1 1 0 1 0 101 0 0 0 1 0 101 0 1 0 1 0 101 1 0 0 1 0 101 1 1 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 ×××□ □ □ □ □ ×××□ □ □ □ □ ×××□ □ □ □ □ ×××■□ □ □ □ ×××■■□ □ □ ×××■■□ □ □ ×××□ ■□ □ □ ×××□ □ □ □ □ 0x00
0x00
0x00
0x10
0x18
0x18
0x08
0x00
控制程序设计
(2)随机数产生
随机数产生一般有2种方法,一是采用C51函数库中的rand()和srand()函数;二是直接采用定时器/计数器的中断计数产生。本项目采用第2种方法,同时还利用按键等待时间的不确定性,在等待按键过程中进行计数,按下键后计数停止,从而产生1个随机数种子。
(3)程序流程图
控制程序设计
lcd.h头文件内容如图
1602LCD程序如下
//程序:lcd.c
//功能:提供1602LCD各种显示函数
#include
#include
//定义控制引脚
sbit RS=P3^0;
sbit RW=P3^1;
sbit E=P3^2;
//函数名:delay
//函数功能:实现软件延时
//形式参数:无符号整型变量i,控制空循环的循环次数
//返回值:无
void delay(unsigned int i)
{
while(i--); //i次空操作
}
控制程序设计
//函数名:delay1
//函数功能:采用软件实现延时,大约几个机器周期
void delay1()
{
_nop_();
_nop_();
_nop_();
}
//函数名:lcd_r_start
//函数功能:读状态字
//形式参数:无
//返回值:返回状态字,最高位D7=0,LCD控制器空闲;D7=1,LCD控制器忙
unsigned char lcd_r_start()
{
unsigned char s;
RW=1;
delay1();
RS=0;
delay1();
E=1;
delay1();
s=P1;
delay1();
E=0;
delay1();
RW=0;
delay1();
return(s);
}
//函数名:lcd_w_cmd
//函数功能:写命令字
//形式参数:命令字已存入com单元中
//返回值:无
void lcd_w_cmd(unsigned char com)
{
unsigned char i;
do{
i=lcd_r_start();
i=i&0x80;
delay(100);
}while(i!=0);
RW=0;
delay1();
RS=0;
delay1();
E=1;
delay1();
P1=com;
delay1();
E=0;
delay1();
RW=1;
delay(255);
}
控制程序设计
//函数名:lcd_w_dat
//函数功能:写数据
//形式参数:数据已存入dat单元中
//返回值:无
void lcd_w_dat(unsigned char dat)
{
unsigned char i;
do{
i=lcd_r_start();
i=i&0x80;
delay(100);
}while(i!=0);
RW=0;
delay1();
RS=1;
delay1();
E=1;
delay1();
P1=dat;
delay1();
E=0;
delay1();
RW=1;
delay(255);
}
//函数名:lcd_init
//函数功能:lcd初始化
//形式参数:无
//返回值:无
void lcd_init()
{
lcd_w_cmd(0x3c);
lcd_w_cmd(0x0e);
lcd_w_cmd(0x01);
lcd_w_cmd(0x06);
lcd_w_cmd(0x80);
}
//函数名:puts
//函数功能:在lcd上显示字符串函数
//形式参数:待显示字符串的首地址
//返回值:无
void puts(unsigned char ch[])
{
unsigned char i;
i=0;
while(ch[i]!='\0') //字符串结束符 ‘/0’
{
lcd_w_dat(ch[i]);
i++;
}
}
控制程序设计
//函数名:putdata
//函数功能:显示数据函数(0-255)
//形式参数:无符号字符型,待显示数据
//返回值:无
void putdata(unsigned char dbyte) reentrant
{
unsigned char b,s,g;
b=dbyte/100; //分离百位
s=(dbyte%100)/10; //分离十位
g=dbyte%10; //分离个位
if(b!=0) //百位不为0,显示3位
{
lcd_w_dat(b+0x30);
lcd_w_dat(s+0x30);
lcd_w_dat(g+0x30);
}
else if(s!=0) //百位为0,十位不为0,显示2位
{
lcd_w_dat(0x20);
lcd_w_dat(s+0x30);
lcd_w_dat(g+0x30);
}
else //百位为0,十位也为0,显示1位
lcd_w_dat(0x20);
lcd_w_dat(0x20);
lcd_w_dat(g+0x30);
}
控制程序设计
简易打字机游戏程序如下
//程序:ex44.c
//功能:简易打字游戏机程序,倒计时90秒,按照屏幕出现的随机数打字,(0~9),10号键是开始键,11号键为退出键
#include
#include //包含lcd.h头文件
unsigned char code key_code[]={0xee,0xde,0xbe,0x7e,0xed,0xdd,0xbd,0x7d,0xeb,0xdb,0xbb,0x7b};
// 3*4矩阵按键对应的扫描码表3*4矩阵接键
unsigned int count250us; //250μs计数,用于定时和随机数产生
unsigned char sec=90,score=0; //秒计数sec,分数score
bit flag=0; //时间到标志
//游戏logo的字模生成数组,共6个5×7字符
unsigned char code logo[6][8]={{0x00,0x00,0x01,0x03,0x03,0x07,0x07,0x00}, //DDRAM:0x00
{0x00,0x1e,0x1f,0x1f,0x1f,0x18,0x10,0x00}, //DDRAM:0x01
{0x00,0x00,0x00,0x10,0x18,0x18,0x08,0x00}, //DDRAM:0x02
{0x07,0x07,0x03,0x03,0x01,0x00,0x00,0x00}, //DDRAM:0x03
{0x17,0x17,0x11,0x1f,0x1f,0x1f,0x0e,0x00}, //DDRAM:0x04
{0x18,0x18,0x18,0x10,0x10,0x00,0x00,0x00}}; //DDRAM:0x05
//函数名: delayms
//函数功能:延时处理程序,单位毫秒
//形式参数:无符号整型数,延时的毫秒数
//返回值:无
void delayms (unsigned int ms)
{
unsigned int i;
while(ms--){
//for(i=0;i<112;i++) //11.0592M晶体
for(i=0;i<120;i++); //12M晶体
}
}
控制程序设计
//函数名: CGRAM
//函数功能:游戏logo字符生成程序,生成6个字符,拼成一个logo
//形式参数:无
//返回值:无
void CGRAM()
{ unsigned char i,w,c;
w=0x40;
for(i=0;i<6;i++){ //6个字符
for(c=0;c<8;c++) { //每个字符8行
lcd_w_cmd(w); //写入CGRAM地址
lcd_w_dat(logo[i][c]); //写入CGRAM数据
w++;
}
}
}
//函数名:display_logo
//函数功能:在LCD中央显示游戏logo
//形式参数:无
//返回值:无
void display_logo()
{
lcd_w_cmd(0x86); //第1行定位
lcd_w_dat(0x00);
lcd_w_dat(0x01);
lcd_w_dat(0x02);
lcd_w_cmd(0xc6); //第2行定位
lcd_w_dat(0x03);
lcd_w_dat(0x04);
lcd_w_dat(0x05);
}
//函数名: keyscan
//函数功能:判断是否有键按下,如果有键按下,行列反转法得到键值
//形式参数:无
//返回值:键值0~11,-1表示无键按下
char keyscan()
{
char scan1,scan2,keycode,j,key;
key=-1; //键值初值为-1,如果没有扫描到按键,函数返回-1
P2=0xf0; //写:行为全1,列为全0
scan1=P2; //读:行列值
if(scan1!=0xf0) //如果读入的值不为0xf0,则表示有键按下
{
delayms(10); //延时去抖
scan1=P2; //再次读入行列值
if(scan1!=0xf0) //再次判断是否有键按下
{
P2=0x0f; //行列反转,写:行为全0,列为全1
scan2=P2; //读入行列值
keycode=scan1|scan2; //2次读入值按位或操作,合并在一起,得到扫描码
for(j=0;j<12;j++) { //由扫描码表得到键值
if(keycode==key_code[j])
{
key=j;break;
}
}
}
}
return(key); //返回键值
}
控制程序设计
//函数名:t0
//函数功能:T0中断服务函数,倒计时90秒
//形式参数:无
//返回值:无
void t0() interrupt 1
{
count250us++; //250μs计数器
if(count250us==4000) //1s计时到
{ count250us=0;
sec--; //秒倒计时
if(sec==0){flag=1;}//置时间到标志
lcd_w_cmd(0x8c);
putdata(sec); //显示秒
}
}
//函数名:sys_init
//函数功能:系统初始化,包括LCD、T0和中断等初始化
//形式参数:无
//返回值:无
void sys_init()
{
lcd_init(); //LCD初始化
TMOD=0x02; //定时器T0工作于方式2
ET0=1; //允许T0中断
TH0=0x06; // 250μs初值
TL0=0x06;
EA=1; //允许总中断
sec=90; //90s倒计时初值
score=0; //分数初值
}
//函数名:game_start
//函数功能:游戏开始画面显示,包括倒计时时间和分数显示
//形式参数:无
//返回值:无
void game_start()
{
lcd_w_cmd(0x01); //清屏
lcd_w_cmd(0x8c);
putdata(sec); //显示倒计时时间初值
lcd_w_cmd(0xcc);
putdata(score); //显示分数初值
}
//函数名:game_stop
//函数功能:游戏停止画面显示,包括提示语和分数显示
//形式参数:无
//返回值:无
void game_stop()
{
lcd_w_cmd(0x01); //清屏
lcd_w_cmd(0x83);
puts("Game over!");
lcd_w_cmd(0xc5);
puts("score="); //显示得分
putdata(score);
}
控制程序设计
void main(){ //主函数
char key;
unsigned char rdate; //存放随机数
sys_init(); //系统初始化
CGRAM(); //logo字模生成
display_logo(); //显示logo
delayms(3000); //持续3s
while(1)
{
lcd_w_cmd(0x01); //清屏
lcd_w_cmd(0x85);
puts("START"); //显示START,然后等待按键
while(1) //等待"10号"==>开始键
{
count250us++; //等待期间count250μs计数,因为按键时间的不确定性,可以得到随机数,作为250μs计数初值
key=keyscan();
if(key==10)break;
}
count250us%=10; //1个0-9的随机数作为250μs计数的初值,保证第1个随机数
game_start(); //游戏开始画面显示
TR0=1; //启动定时器
EA=1;
//游戏内容
while(1)
{
lcd_w_cmd(0xc0);
rdate=count250us%10; //得到随机数0-9
lcd_w_dat(rdate+0x30); //显示随机数
控制程序设计
while(1) //等待按键
{
key=keyscan();
if(flag==1)break; //游戏时间到,退出按键
if(key!=-1){while(keyscan()!=-1);break;} }
//输入有效数字,等待按键释放并退出
//按键处理
if(key==rdate) //输入正确
{
score++; //分数增1
lcd_w_cmd(0xcc);
putdata(score); //显示分数
}
if(flag==1||key==11) //游戏时间结束或“11号键”==》退出键
{
flag=0;
TR0=0; //T0停止计数
EA=0;
sec=90;
game_stop(); //显示游戏结束画面
score=0;
delayms(2000); //持续2s
break; //回到等待游戏开始画面
}
}
}
}
Thanks for your attention.
感谢您的观看(共14张PPT)
《单片机基础与应用(C语言版)》
高等教育出版社
实例41 智能温度测量
实例要求
(1)能够按照规定时间间隔(1s)自动检测环境温度。
(2)能够在字符型LCD上显示温度值。
(3)能够通过串口与PC通信,当收到PC命令时,上传温度值给PC。
实例分析
显示模块用于显示温度值;温度检测模块采用智能温度传感器DS18B20实现;电平转换模块实现单片机与RS-232C电平的转换。
硬件电路
温度检测电路采用Dallas公司生产的1-Wire接口数字温度传感器DSl8B20,如图所示,它采用3引脚TO-92封装,各引脚功能说明如下。
GND(1):地信号。
DQ(2):数据输入/输出引脚,开漏单总线接口引脚,需上拉电阻。
VDD(3):电源引脚。
DS18B20时序控制
(1)初始化复位时序
(2)DS18B20写时序
(3)DS18B20读时序
DS18B20的写入指令
指 令 指令代码 操 作 说 明
温度转换命令 44H 启动DS18B20进行温度转换
读温度值命令 BEH 读暂存器中的温度值
写暂存器命令 4EH 将数据写入暂存器的高8位TH和低8位TL中
复制暂存器命令 48H 把暂存器TH、TL中的内容复制到E2RAM中
重新调E2RAM命令 B8H 把E2RAM中的内容重新写回到暂存器TH、TL字节中
读电源供电方式命令 B4H 启动DS18B20发送电源供电方式信号给单片机
SKIP ROM操作命令 CCH 跳过ROM匹配,跳过读序列号的操作,可节省操作时间
DS18B20的温度值转换
控制程序设计
DS18B20程序如下
//程序:DS18b20.c
//功能:提供DS18B20的初始化、读时序、写时序等函数
#include "reg51.h"
#include "intrins.h" //包含内部函数头文件intrins.h
#define uchar unsigned char
#define uint unsigned int
sbit DQ=P3^7; //温度采集
//函数名:delay5us
//函数功能:精确延时5μs
//形式参数:延时时间参数i,无符号字符型
//返回值:无
void delay5us(uchar i)
{
while(i--) {
_nop_();
_nop_();
_nop_();
}
}
//函数名:init_ds18b20
//函数功能:总线初始化复位
//形式参数:无
//返回值:复位状态
uchar init_ds18b20(void)
{
uchar x=0;
DQ=1;
delay5us(10);
DQ=0;
delay5us(120); //低电平480-960s
DQ=1;
delay5us(15); //等待50-100s
x=DQ;
delay5us(80);
DQ=1;
return(x);
}
//函数名:readbyte
//函数功能: 读取一个字节
//形式参数:无
//返回值:读取字节数据date, unsigned char 类型
uchar readbyte(void){
uchar i=0;
uchar date=0;
for(i=8;i>0;i--){
DQ=0;
delay5us(2);
DQ=1; //15μs内释放总线
date>>=1;
if(DQ)date|=0x80;
delay5us(9); }//读完需要45μs的等待
return(date);
}
控制程序设计
//函数名:writebyte
//函数功能: 写一个字节
//形式参数:写字节数据dat, unsigned char 类型
//返回值:无
void writebyte(uchar dat)
{
uchar i=0;
for(i=8;i>0;i--)
{
DQ=0;
DQ=dat&0x01; //写"1" 在15微秒内拉低
delay5us(12); //写"0" 拉低60微秒等待写完
DQ=1; //恢复高电平,至少保持1μs
dat>>=1; //下次写作准备,移位数据
delay5us(5); //延时25μs
}
}
//函数名: readtemp
//函数功能:读取温度
//形式参数:无
//返回值:单字节的温度值tt, unsigned char 类型
uchar readtemp(void)
{
uchar templ,temph,temp;
uint temp_int;
init_ds18b20();
writebyte(0xCC);
writebyte(0x44); //跳过ROM匹配,跳过读序列号的操作,可节省操作时间
init_ds18b20(); //开始操作前需要复位
writebyte(0xCC);
writebyte(0xBE); //读暂存器中的温度值
templ=readbyte(); //分别读取温度的低、高字节
temph=readbyte();
temp_int=temph;
temp_int<<=8;
temp_int|=templ;
temp=temp_int*0.0625; //温度转换
return(temp);
}
控制程序设计
//程序:ds18b20.h
//功能:ds18b20头文件,声明外部函数
extern void delay5us(uchar n); //精确延时n*5μs子程序
extern void init_ds18b20(void); //总线初始化复位
extern uchar readbyte(void); //读取一个字节
extern void writebyte(uchar); //写一个字节
extern uchar readtemp(void); //读取温度
DS18B20头文件如下。
智能温度测量系统程序
控制程序设计
控制程序设计
//功能:智能温度测量系统
#include "reg51.h"
#include "lcd.h"
#include "ds18b20.h"
//函数名:UartInit
//函数功能:串口和定时器/计数器初始化设置
//形式参数:无
void UartInit(void)
{
TMOD=0x21; //设置定时器T1为方式2,T0为方式1
TL1=0xfd; //波特率为9600Bd
TH1=0xfd;
TR1=1;
SCON=0x40; //定义串行口工作于方式1,不接收
PCON=0x00; //SMOD=0
ES=1; //开串行口中断
EA=1; //开总中断允许位
REN=1; //接收允许
}
//函数名:UartSend
//函数功能:串口发送数据
//形式参数:待发送数据SendData,unsigned char数组类型
//返回值: 无
void UartSend(uchar SendData)
{
SBUF=SendData; //发送第i个数据
while(TI==0); //等待发送是否完成
TI=0; //TI清0
}
//函数名:serial
//函数功能:串行口中断接收函数
void serial()interrupt 4 //串口中断类型号为4
{
uchar temp;
EA=0; //关中断
RI=0; //软件清除中断标志位
temp=SBUF ; //接收数据
UartSend(temp); //再送回PC
if(temp=='A'){
temp=readtemp(); //读取转换的温度
UartSend(temp/10+0x30);//把温度值发送给PC
UartSend(temp%10+0x30);}
EA=1; //开中断
}
//函数名:time50ms
//函数功能:在T0工作方式1下定时50ms,采用查询方式实现
//形式参数:无符号整型变量i,控制定时时间,i*50ms
//返回值:无
void time50ms(uchar i) reentrant
{
uchar k;
for(k=0;kTH0=(65536-50000)/256;//重新设置T0计数初值高8位,定时时间50ms
TL0=(65536-50000)%256; //重新设置T0计数初值低8位
TR0=1; //启动T0
while(!TF0);//查询计数是否溢出,即定时50ms时间到,TF0=1
TF0=0; //50ms定时时间到,将T0溢出标志位TF0清0
}
}
控制程序设计
//函数名:lcd_wd
//函数功能:显示温度值
//形式参数:无符号字符型数据,温度值
//返回值:无
void lcd_wd(uchar temp)
{
lcd_w_cmd(0x80);
puts("Temprature is:");
lcd_w_cmd(0xc0);
putdata(temp);
lcd_w_dat(0xdf);
lcd_w_dat('C');
}
void main(void) //主函数
{
uchar temp;
lcd_init(); //LCD初始化
UartInit(); //串口和定时器/计数器初始化设置
while(1)
{
temp=readtemp(); //读取转换的温度
lcd_wd(temp); //显示温度
time50ms(20); //定时1s
}
}
Thanks for your attention.
感谢您的观看

展开更多......

收起↑

资源列表