蓝桥杯历年真题训练
51单片机系统浓缩图
1. HC138译码器
· 用3个输入引脚,实现8个输出引脚,而且这个八个输出引脚中只要一个低电平,所以我们只需要记住真值表就行
#include "reg52.h"
sbit HC138_A = P2^5;
sbit HC138_B = P2^6;
sbit HC138_C = P2^7;
void Init74HC138(unsigned char n)
{
switch(n)
{
case 4: //LED
HC138_A = 0;
HC138_B = 0;
HC138_C = 1;
break;
case 5: //蜂鸣器与译码器
HC138_A = 1;
HC138_B = 0;
HC138_C = 1;
break;
case 6: //数码管位置
HC138_A = 0;
HC138_B = 1;
HC138_C = 1;
break;
case 7: //数码管内容
HC138_A = 1;
HC138_B = 1;
HC138_C = 1;
break;
case 8: //关闭所有设备
HC138_A = 0;
HC138_B = 0;
HC138_C = 0;
break;
}
}
2. HC573
573锁存器有20个引脚,D1~D8是数据输入端,Q1~Q8是数据输出端,LE为锁存控制端。当锁存使能端LE为高时,573的锁存对于数据是透明的(也就是说输出同步)。当锁存使能变低时,符合建立时间和保持时间的数据会被锁存。使用其可以替换HC138,两个功能相同
#include "reg52.h"
void SelectHC573(unsigned channel)
{
switch(channel)
{
case 4:
P2 = (P2 & 0x1f) | 0x80;
break;
case 5:
P2 = (P2 & 0x1f) | 0xa0;
break;
case 6:
P2 = (P2 & 0x1f) | 0xc0;
break;
case 7:
P2 = (P2 & 0x1f) | 0xe0;
break;
case 0:
P2 = (P2 & 0x1f) | 0x00;
break;
}
P2 = (P2 & 0x1f) | 0x00;
}
两者的功能相同,因此我们可以简便的来替代一下
void InitHC138 (unsigned char n)
{
switch(n)
{
case 4:
P2=(P2&0x1f)|0x80;
break;
case 5:
P2=(P2&0x1f)|0xa0;
break;
case 6:
P2=(P2&0x1f)|0xc0;
break;
case 7:
P2=(P2&0x1f)|0xe0;
break;
}
}
3. 利用其来控制LED
// 任务:
/* 先让奇数的灯闪,再让偶数的灯闪,然后所有的灯闪3下,最后依次点亮所有的灯,然后再依次熄灭,然后循环 */
void LEDRunning()
{
char i = 0;
P0 = 0xaa;
Delay(60000);
P0 = 0x55;
Delay(60000);
for(i = 0; i < 3; i++)
{
P0 = 0x00; //全灭
Delay(60000);
P0 = 0xff; //全亮
Delay(60000);
}
for(i = 0; i < 8; i++)
{
P0 <<= 1; //按顺序依次闪过
Delay(60000);
}
for(i = 0; i < 8; i++)
{
P0 <<= 1;
P0 |= 1; //熄灭
Delay(60000);
}
}
main()
{
Init74HC138(4); //打开LED的138
while(1)
{
LEDRunning();
}
}
4. 数码管
其中a、b、c、d、e、f、g、dp引脚分别对应8个段码,该8个引脚通过74HC573锁存器与单片机的P0端口相连。另外有com1~com4四个公共控制脚,该应用为高电平则使能对应位的数码管。两个F3461BH一共有8个com控制引脚,也是通过74HC573锁存器与单片机的P0端口相连的。因此,在操控数码管显示的过程中也离不开74HC138译码器和74HC573锁存器。
// 段码
unsigned char code SMG_Duanma[18] =
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,
0xbf,0x7f};
// 数码管延时函数
void Delay2(unsigned int t)
{
while(t--)
{
Display_SMG();
}
}
//数码管显示函数
void DisplaySMG_bit(unsigned char value,unsigned char pos) //value为内容,pos为所填位置、
{
Init74HC138(6);
P0 = (0x01 << pos);
Init74HC138(7);
P0 = value;
}
任务:
在 8 位数码管中,左边 4 位数码管显示 年份“2018 ”,接着 2 位是分隔符“--”,靠右的2 位数码管显示月份。从 1 月份开始,每隔一段时间加 1 个月,到 12 月之后又从 1 月开始递增, 如此循环往复。
#include "reg52.h"
unsigned char code SMG_duanma[18]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};
unsigned char yue=1;
void Delay(unsigned int t)
{
while(t--);
}
void InitHC138 (unsigned char n) //ͨµÀÑ¡Ôñ
{
switch(n)
{
case 4:
P2=(P2&0x1f)|0x80;
break;
case 5:
P2=(P2&0x1f)|0xa0;
break;
case 6:
P2=(P2&0x1f)|0xc0;
break;
case 7:
P2=(P2&0x1f)|0xe0;
break;
}
}
void ShowSMG_Bit(unsigned char dat,unsigned int pos)
{
InitHC138(6); //ÊýÂë¹ÜµÄλÖÃ
P0=0X01<<pos;
InitHC138(7); //ÊýÂë¹ÜµÄÄÚÈÝ
P0=dat;
}
void Display_SMG()
{
ShowSMG_Bit(SMG_duanma[2],0);
Delay(500);
ShowSMG_Bit(SMG_duanma[0],1);
Delay(500);
ShowSMG_Bit(SMG_duanma[1],2);
Delay(500);
ShowSMG_Bit(SMG_duanma[8],3);
Delay(500);
ShowSMG_Bit(SMG_duanma[16],4);
Delay(500);
ShowSMG_Bit(SMG_duanma[16],5);
Delay(500);
ShowSMG_Bit(SMG_duanma[yue/10],6);
Delay(500);
ShowSMG_Bit(SMG_duanma[yue%10],7);
Delay(500);
}
void Delay2(unsigned int t)
{
while(t--)
{
Display_SMG();
}
}
void InitSystem()
{
InitHC138(5); //¹Ø±Õ¼ÌµçÆ÷
P0=0x00;
InitHC138(4); //´ò¿ªµÆ
P0=0xff;
P2=0x00; //¹Ø±ÕHC138
}
void main()
{
InitSystem();
while(1)
{
Display_SMG();
yue++;
if(yue>12)
yue=1;
Delay2(100);
}
}
6. 独立按键
一般情况下,独立按键有两个引脚,其中一个通过上拉电阻接到单片机的I/O端口,另外一端接地。也就是说,平时按键没有动作的时候,输出的是高电平,如果有按下动作发生,则输出的是低电平。那么,我们在程序设计的时候,只要扫描跟按键引脚相连的I/O端口,如果发现有低电平产生,则判定该按键处于按下状态。有些时候,电路或者外围有电磁干扰,也会使单片机的I/O端口产生低电平,这种干扰信号会让单片机误认为是按键动作。所以,在扫描按键的时候应该做去抖动处理,把干扰信号过滤掉,从而获得准确的按键状态信号。
// 按键扫描
void ShowKeyNum(unsigned char value)
{
Init74HC138(6); //数码管位置
P0 = 0x01;
Init74HC138(7); //数码管内容
P0 = value;
}
void ScanKeys()
{
if(S7 == 0)
{
Delay(200);
if(S7 == 0)
{
while(S7 == 0);
ShowKeyNum(SMG_NoDot[1]);
}
}
if(S6 == 0)
{
Delay(200);
if(S6 == 0)
{
while(S6 == 0);
ShowKeyNum(SMG_NoDot[2]);
}
}
if(S5 == 0)
{
Delay(200);
if(S5 == 0)
{
while(S5 == 0);
ShowKeyNum(SMG_NoDot[3]);
}
}
if(S4 == 0)
{
Delay(200);
if(S4 == 0)
{
while(S4 == 0);
ShowKeyNum(SMG_NoDot[4]);
}
}
}
7 .矩阵键盘的使用
与独立按键不同的是,按键的两个引脚都分别连接的单片机的I/O端口,一个作为行信号,另外一个作为列信号。
对与矩阵键盘,我们只能逐行扫描,然后读取列的状态信号。如果R3行输出低电平,那么黄色按键如果有按下动作的话,那读取C2列信号也应该为低电平,而该行上其他没有按下动作的按键的列信号则为高电平。因此,我们可以得到矩阵键盘的基本扫描步骤:
<1> R1输出低电平,R2、R3、R4输出高电平,逐个读取判断列信号,如果都为高电平则R1行上没有按键按下。
<2> R2输出低电平,R1、R3、R4输出高电平,逐个读取判断列信号。
<3> R3输出低电平,R1、R2、R4输出高电平,发现C2列信号为低电平,那么可以判断得R3行的C2列的按键有按下动作。
<4> R4输出低电平,R1、R3、R4输出高电平,逐个读取判断列信号。
任务:1、将 CT107D 上 J5 处跳帽接到 1~2 引脚,使 S4 到 S19 成为 4X4 的矩阵键盘。2、系统上电后,关闭蜂鸣器,关闭继电器,关闭 8 个 LED 灯。3、循环扫描矩阵键盘状态,发现有按键按下,等待其松开后,在数码管的最左边 1 位显示相应的数字。从左至右,从上到下,依次显示“ 0 ”到“ F ”。即按下 S7 ,显示“ 0 ”,按下 S11 显示“ 1 ”,按下 S15 显示“ 2 ”,按下 S6 显示“ 4 ”...依次类推。
#include "reg52.h"
unsigned char code SMG_duanma[18]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};
sfr P4=0xc0;
sbit R1=P3^0;
sbit R2=P3^1;
sbit R3=P3^2;
sbit R4=P3^3;
sbit C4=P3^4;
sbit C3=P3^5;
sbit C2=P4^2;
sbit C1=P4^4;
void InitHC138 (unsigned char n) //ͨµÀÑ¡Ôñ
{
switch(n)
{
case 4:
P2=(P2&0x1f)|0x80;
break;
case 5:
P2=(P2&0x1f)|0xa0;
break;
case 6:
P2=(P2&0x1f)|0xc0;
break;
case 7:
P2=(P2&0x1f)|0xe0;
break;
}
}
void DisplayKeyNum(unsigned char value)
{
InitHC138(6);
P0=0x01;
InitHC138(7);
P0=value;
}
unsigned char keynum;
void ScanKey()
{
//µÚÒ»ÐÐ
R1=0;
R2=R3=R4=1;
C1=C2=C3=C4=1;
if(C1==0)
{
while(C1==0);
keynum=0;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C2==0)
{
while(C2==0);
keynum=1;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C3==0)
{
while(C3==0);
keynum=2;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C4==0)
{
while(C4==0);
keynum=3;
DisplayKeyNum(SMG_duanma[keynum]);
}
//µÚ¶þÐÐ
R2=0;
R1=R3=R4=1;
C1=C2=C3=C4=1;
if(C1==0)
{
while(C1==0);
keynum=4;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C2==0)
{
while(C2==0);
keynum=5;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C3==0)
{
while(C3==0);
keynum=6;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C4==0)
{
while(C4==0);
keynum=7;
DisplayKeyNum(SMG_duanma[keynum]);
}
//µÚÈýÐÐ
R3=0;
R2=R1=R4=1;
C1=C2=C3=C4=1;
if(C1==0)
{
while(C1==0);
keynum=8;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C2==0)
{
while(C2==0);
keynum=9;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C3==0)
{
while(C3==0);
keynum=10;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C4==0)
{
while(C4==0);
keynum=11;
DisplayKeyNum(SMG_duanma[keynum]);
}
//µÚËÄÐÐ
R4=0;
R1=R2=R3=1;
C1=C2=C3=C4=1;
if(C1==0)
{
while(C1==0);
keynum=12;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C2==0)
{
while(C2==0);
keynum=13;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C3==0)
{
while(C3==0);
keynum=14;
DisplayKeyNum(SMG_duanma[keynum]);
}
else if(C4==0)
{
while(C4==0);
keynum=15;
DisplayKeyNum(SMG_duanma[keynum]);
}
}
void InitSystem()
{
InitHC138(5);
P0=0x00;
InitHC138(4);
P0=0xff;
P2=0x00;
}
void main()
{
InitSystem();
while(1)
{
ScanKey();
}
}
8. 中断相关寄存器
一般来说,51单片机有5个中断源(忽略定时/计数器2),分2个优先级,这个5个中断源按照自然优先级从高到低依次为:
外部中断0:INT0
定时/计数器0:TF0
外部中断1:INT1
定时/计数器1:TF1
串口中断:RI/TI
中断相关的寄存器有4个,每个寄存器都是可以位寻址的,这该编程带来了方便。 其中2个为控制寄存器:IE寄存器与IP寄存器:
另外2个为中断请求标志:TCON寄存器与SCON寄存器:
一般情况下,中断的处理函数有两个,其一为中断初始化函数,其二为中断服务函数。初始化函数就是一个普通的函数,而中断服务函数却有特殊的格式要求:
<1> 中断函数没有返回值,也不能带参数。
<2> 函数名后面要跟一个关键字interrupt,说明这是一个中断服务函数。
<3> 在关键字interrupt后面要跟上中断号,说明这个中断服务函数是为那个中断服务的。
9.定时器
51单片机有两个定时/计数器T0和T1,为16位加法计数器,由低8位TLx和高8位THx两个寄存器组成,最大计数值为65535个计数脉冲。
该加1计数器的计数脉冲来源有2个:
<1> 系统时钟振荡器输出的12分频。
<2> T0或T1引脚输入的外部脉冲信号。
每接收到一个计数脉冲,计数器就会加1,当计数值累计至全为1时(8位255,13位8191,16位65535),再输入一个计数脉冲,计数器便会溢出回零,并且计数器的溢出是TCON寄存器的TF0或TF1位置1,同时向内核提出中断请求。如果定时/计数器工作于定时模式,则表示间隔定时时间到,如果工作与计数模式,则表示计数值已满。
假设单片机的外部晶振为12MHz,那么,经过12分频后输入计数器的计数脉冲为1MHz,即每个脉冲的周期为1us。因此定时器T0的16位工作模式最大的定时时间为65535us,65.5ms。如果要定时10ms的话,计数器就不能够从0开始计数了,必须给它一个计数初值。怎么计算这个初值呢?
要定时10ms,则相当于计数10000个脉冲后计数器的值就到达65535了,那么开始计数的这个地方就是计数初值。
65535 - 10000 = 55535 = 0xd8ef
把这个计算得到的初值写入TH0和TL0寄存器即可:
TH0 = 0xd8;或者 TH0 = (65535 - 10000) / 256;
TL0 = 0xef; 或者 TL0 = (65535 - 10000) % 256;
定时/计数器相关的寄存器除了计数初值寄存器THx和TLx之外,就是TMOD寄存器和TCON寄存器,务必掌握。
<1> TMOD模式控制寄存器,不能进行位寻址,只能字节操作。
<2> TCON中断标志寄存器
定时/计数器的程序设计中,通常有两个函数:初始化函数和中断服务函数。
在初始化函数中,一般需要进行以下几个配置:
<1> 配置工作模式,即对TMOD寄存器编程。
<2> 计算技术初值,即对THx和TLx寄存器进行赋值。
<3> 使能定时/计数器中断,即ET0或ET1置1。
<4> 打开总中断,即EA =1。
<5> 启动定时器,即TR0或TR1置1。
在中断服务函数中,一般需要进行以下的编程:
<1> 如果不是自动重装模式,需要对THx和TLx重新赋值。
<2> 进行间隔定时到达的逻辑处理(越少越好)。
1、系统上电后,关闭蜂鸣器,关闭继电器,关闭 8 个 LED 灯。2、利用定时/计数器 T0 的模式 1 实现 50ms 的间隔定时。3、在 50ms 间隔定时的基础上,每隔 1 秒 L1 指示灯闪烁一次,即 L1 指示灯循环点亮0.5 秒,熄灭 0.5 秒。4、每隔 10 秒 L8 指示灯闪烁 1 次,即 L1 指示灯循环点亮 5 秒,熄灭 5 秒
#include "reg52.h"
sbit HC138_A = P2^5;
sbit HC138_B = P2^6;
sbit HC138_C = P2^7;
sbit LED1 = P0^0;
sbit LED2 = P0^1;
void Init74HC138(unsigned char n)
{
switch(n)
{
case 4:
HC138_A = 0;
HC138_B = 0;
HC138_C = 1;
break;
case 5:
HC138_A = 1;
HC138_B = 0;
HC138_C = 1;
break;
case 6:
HC138_A = 0;
HC138_B = 1;
HC138_C = 1;
break;
case 7:
HC138_A = 1;
HC138_B = 1;
HC138_C = 1;
break;
case 8:
HC138_A = 0;
HC138_B = 0;
HC138_C = 0;
break;
}
}
/*===============初始化定时器0==================*/
void Init_Timer0()
{
TMOD = 0x01; //16位定时模式
TH0 = (65536 - 50000) / 256; //定时50ms
TL0 = (65536 - 50000) % 256;
ET0 = 1; //使能定时器T0中断
EA = 1; //使能总中断
TR0 = 1; //启动定时器T0
}
/*============定时器0中断服务函数===============*/
unsigned char count = 0;
void SeviceTimer0() interrupt 1
{
TH0 = (65536 - 50000) / 256;
TL0 = (65536 - 50000) % 256;
count++;
if(count == 10) //0.5秒定时到
{
LED1 = ~LED1;
}
if(count == 20) //1秒定时到
{
LED2 = ~LED2;
count = 0;
}
}
/*==================主函数======================*/
main()
{
Init74HC138(4);
Init_Timer0();
while(1);
}
11. PWM控制
1、系统上电后,关闭蜂鸣器,关闭继电器,关闭 8 个 LED 灯。2、PWM 脉宽信号的频率为 100Hz。3、L1 指示灯有 4 种亮度,分别是:完全熄灭、10%的亮度、50%的亮度和 90%的亮度。4、按下 S7 按键,循环切换 L1 指示灯的四种亮度模式
#include "reg52.h"
#include "HC573.h"
#includd "SMG.h"
sbit L1 = P0^0;
sbit S7 = P3^0;
/*¶¨Ê±Æ÷Ïà¹Ø*/
unsigned char count = 0;
void InitTimer0()
{
TMOD = 0x01; //ʹÓö¨Ê±Æ÷1µÄ16λģʽ
TH0 = (65535 - 100)/256 // ¼ÆÊý³õÖµ
TL0 = (65535 - 100)%256
ET0 = 1;
EA = 1;
TR0 = 1;
}
void ServiceTimer0() interrupt 1
{
TH0 = (65535 - 100)/256 // ¼ÆÊý³õÖµ
TL0 = (65535 - 100)%256
count++;
if(count == pwm_duty)
{
L1 = 1;
}
if(count == 100)
{
L1 = 0;
count = 0;
}
}
// °´¼ü
unsigned char stat = 0;
void Scankeys()
{
if(S7 == 0)
{
Delay(200)
if(S7 == 0)
{
switch(stat)
{
case 0:
L1 = 0; //¿ªµÆ
pwm_duty = 10;
stat =1;
break;
case 1:
pwm_duty = 50;
stat =2;
break;
case 1:
pwm_duty = 90;
stat =3;
break;
case 3:
L1 = 0;
stat = 0;
break;
}
}
}
}
12. 串行接口
在串口通信的程序设计中,主要有串口初始化和数据收发两个部分。
在初始化函数中,基本步骤如下:
<1> 设置定时器1的工作模式,也就是对TMOD寄存器赋值。
<2> 计算波特率参数,并赋值给TH1和TL1寄存器。
<3> 打开定时器1。
如果使用的是STC 12系统单片机,则要设置AUXR寄存器。
<4> 设置SCON寄存器。
<5> 使能串口中断ES。
<6> 使能总中断EA。
1、初始化串口为模式 1,即 8 位 UART 模式,波特率 9600,允许接收。2、数据发送采用查询方式,数据接收采用中断方式。3、系统上电初始化之后,单片机向上位机发送两个字节:0x5a 和 0xa5(串口助手以十六进制 HEX 发送和显示)。4、串口每成功接收到一个字节后,在该字节基础上加 1,然后通过串口发送回上位机。5、注意 89C52 单片机和 IAP15F2K61S2 单片机串口应用的差别,使用 9600 波特率时,晶振时钟选择 11.0592MHz。
#include "reg52.h"
sfr AUXR=0x8e;
unsigned char tmpRecv;
void Init_Uart()
{
TMOD=0x20;
TH1=0xfd;
TL1=0xfd;
AUXR=0x00;
TR1=1;
SCON = 0x50;
ES=1;
EA=1;
}
void SendByte(unsigned char dat)
{
SBUF = dat;
while(TI == 0);
TI = 0;
}
main()
{
Init_Uart();
SendByte(0x5a);
SendByte(0xa5);
while(1);
}
一般情况下,上位机的命令可能不是一个字节,而是多个字节组成的命令帧,有的长度固定,有的长度变化;而且要求返回的数据可能也不是一个字节,可能是一个数组,也有可能是一个字符串等。在蓝桥杯的比赛中,也不可能让你只是收发一个字节而已,因此,在串口这一个单元中,必须多加一个强化环境,掌握多字节的数据帧收发应用。
1、初始化串口为模式 1,即 8 位 UART 模式,波特率 9600,允许接收。
2、数据发送采用查询方式,数据接收采用中断方式。
3、系统上电后,关闭蜂鸣器,关闭继电器,关闭 8 个 LED 灯,通过串口向上位机发送
字符串:“Welcome to XMF system!”,回车换行。
4、上位机通过串口发送单字节命令,控制单片机的 8 个 LED 灯开关,单片机响应正确
的控制命令后,完成相应的灯光操作。
5、上位机通过串口发送单字节命令,读取单片机运行信息,单片机响应正确的读取命
令后,向上位机返回指定的信息。
串口初始化函数Init_Uart()和单字节发送函数SendByte()就不需要修改,拷过来就能用
<1> 字符发送
<2>字符接收
<3>命令解析与执行
14.555定时器
· 在NE555内部。有3个5k电阻分压
· 基本原理:
低电平触发端TRIG和高电平THR:
两者电压均小于各自的参考电压,U0 = 1,放电管截至
两者电压均大于于各自的参考电压,U0 = 0,放电管导通
` NE555是一个纯硬件设计。其功能已经确定了,在这个板子上,555相当于一个信号发生电路。通过Rb3可改变输出信号频率
#include "reg52.h"
#include "absscc.h"
unsigned int count_f = 0;
unsigned int dat_f = 0;
unsigned char count_t = 0;
void Init_Timer()
{
//¶¨Ê±Æ÷0ÓÃ×÷¼ÆÊý£¬8λÖØ×°·½Ê½
TH0 = 0xff;
TL0 = 0xff;
//¶¨Ê±Æ÷1ÓÃ×÷¶¨Ê±£¬¶¨Ê±Îª0.05s
TH1 = (65535 - 50000) / 256;
TL1 = (65535 - 50000) % 256;
TMOD = 0x16;
ET0 = 1;
ET1 = 1;
EA = 1;
TR0 = 1;
TR1 = 1;
}
void Service_T0() interrupt 1
{
count_f++;
}
void Service_T1() interrupt 3
{
TH1 = (65535 - 50000) / 256;
TL1 = (65535 - 50000) % 256;
count_t++;
if(count_t == 20)
{
dat_f = count_f;
count_f = 0;
count_t = 0;
}
}
15.DS1802温度传感器
在蓝桥杯“单片机设计与开发”赛项中,会提供一个关于DS18B20的库文件,里面有传感器复位、写字节和读字节三个函数。所以,你不一定要把单总线的时序搞清楚,但你一定要把DS18B20的基本操作流程弄明白。
通过单线总线端口访问DS18B20的协议如下:
步骤1: 复位初始化
步骤2: ROM操作指令
步骤3: DS18B20功能指令
三个重要的指令:
<1> CCH:跳过ROM指令,忽略64位ROM地址,直接向DS18B20发起各种温度转换指令。
<2> 44H:温度转换指令,启动DS18B20进行温度转换,转换时间最长为500ms(典型值为200ms),结果保存在高速RAM中。
<3> BEH:读暂存器指令,读取高速暂存存储器9个字节的内容。
读取一次温度传感器数值的操作:
<1> 主机对DS18B20进行复位初始化。
<2> 主机向DS18B20写0xCC命令,跳过ROM。
<3> 主机向DS18B20写0x44命令,开始进行温度转换。
<4> 等待温度转换完成。
<5> 主机对DS18B20进行复位初始化。
<6> 主机向DS18B20写0xCC命令,跳过ROM。
<7> 主机向DS18B20写0xBE命令,依次读取DS18B20发出的从第0一第8,共九个字节的数据。如果只想读取温度数据,那在读完第0和第1个数据后就不再理会后面DS18B20发出的数据即可,或者通过DS18B20复位,停止数据的输出。
(如果你利用大赛提供的DS18B20的库文件,也就是onewire.c和onewire.h,进行程序设计的时候,没能正确的读出温度传感器的数值,对库文件中代码的时序进行适当的调整即可。)、
onewire.h
#include "reg52.h"
sbit DQ = P1^4;
void Delay_OneWire(unsigned int t)
{
while(t--);
}
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(50);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(50);
}
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(50);
}
return dat;
}
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(120);
DQ = 0;
Delay_OneWire(800);
DQ = 1;
Delay_OneWire(100);
initflag = DQ;
Delay_OneWire(50);
return initflag;
}
DS1308
#include "reg52.h"
#include "onewire.h"
#include "SMG.h"
unsigned char SMGNodot[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80
,0x90};
unsigned char SMGdot[10]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00
,0x10};
unsigned int temp=0;
void SelectHC138(unsigned char n)
{
switch(n)
{
case 4:
P2=(P2&0x1f)|0x80;
break;
case 5:
P2=(P2&0x1f)|0xa0;
break;
case 6:
P2=(P2&0x1f)|0xc0;
break;
case 7:
P2=(P2&0x1f)|0xe0;
break;
}
}
void SMGBit(unsigned char dat,unsigned char pos)
{
SelectHC138(6);
P0=0x01<<pos;
SelectHC138(7);
P0=dat;
}
void Display_All(unsigned char dat)
{
SelectHC138(6);
P0=0xff;
SelectHC138(7);
P0=dat;
}
void Display_SMG()
{
SMGBit(SMGNodot[temp%10],7);
DelaySMG(100);
SMGBit(SMGNodot[(temp/10)],6);
DelaySMG(100);
SMGBit(0xff,5);
DelaySMG(100);
SMGBit(0xff,4);
DelaySMG(100);
SMGBit(0xff,3);
DelaySMG(100);
SMGBit(0xff,2);
DelaySMG(100);
SMGBit(0xff,1);
DelaySMG(100);
SMGBit(0xff,0);
DelaySMG(100);
//Display_All(0xff);
}
void Delay(unsigned int t)
{
while(t--)
{
Display_SMG();
}
}
void Read_temp()
{
unsigned char LSB,MSB;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
Delay(1000);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
LSB=Read_DS18B20();
MSB=Read_DS18B20();
temp=MSB;
temp=temp<<8;
temp=temp|LSB;
temp=temp>>4;
/*
if((temp&0xf800)==0x0000)
{
temp=temp>>4;
temp=temp*10;
temp=temp+(LSB&0x0f)*0.625;
}
*/
}
void InitSystem()
{
SelectHC138(5);
P0=0x00;
SelectHC138(4);
P0=0xff;
}
void main()
{
InitSystem();
while(1)
{
Display_SMG();
Read_temp();
}
}
16.DS1302时钟系统
ds1302
/*
³ÌÐò˵Ã÷: DS1302Çý¶¯³ÌÐò
Èí¼þ»·¾³: Keil uVision 4.10
Ó²¼þ»·¾³: CT107µ¥Æ¬»ú×ÛºÏʵѵƽ̨ 8051£¬12MHz
ÈÕ ÆÚ: 2011-8-9
*/
#include <reg52.h>
#include <intrins.h>
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST = P1^3; // DS1302¸´Î»
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK=0;
SDA=temp&0x01;
temp>>=1;
SCK=1;
}
}
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
DS1302时钟模块
#include "reg52.h"
#include "ds1302.h"
unsigned char Write_DS1302[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
unsigned char Read_DS1302[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
unsigned char Timer[7]={0x50,0x59,0x12,0x18,0x04,0x06,0x22};
unsigned char code SMG_DM[18]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,
0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};
void SelectHC138(unsigned char n)
{
switch(n)
{
case 4:
P2=(P2&0x1f)|0x80;
break;
case 5:
P2=(P2&0x1f)|0xa0;
break;
case 6:
P2=(P2&0x1f)|0xc0;
break;
case 7:
P2=(P2&0x1f)|0xe0;
break;
}
}
void DS1302_Config()
{
unsigned char i;
Write_Ds1302_Byte(0x8e,0x00);
for(i=0;i<7;i++)
{
Write_Ds1302_Byte(Write_DS1302[i], Timer[i]);
}
Write_Ds1302_Byte(0x8e,0x80);
}
void Read_DS1302_Timer()
{
unsigned char i;
for(i=0;i<7;i++)
{
Timer[i]=Read_Ds1302_Byte(Read_DS1302[i]);
}
if(Timer[2]>0x12)
{
Timer[2]-=0x12;
}
}
void Delay_SMG(unsigned int t)
{
while(t--);
}
void SMG_Bit(unsigned char dat,unsigned char pos)
{
SelectHC138(6);
P0=0x01<<pos;
SelectHC138(7);
P0=SMG_DM[dat];
}
void Display_SMG()
{
SMG_Bit(Timer[2]/16,0);
Delay_SMG(100);
SMG_Bit(Timer[2]%16,1);
Delay_SMG(100);
SMG_Bit(16,2);
Delay_SMG(100);
SMG_Bit(Timer[1]/16,3);
Delay_SMG(100);
SMG_Bit(Timer[1]%16,4);
Delay_SMG(100);
SMG_Bit(16,5);
Delay_SMG(100);
SMG_Bit(Timer[0]/16,6);
Delay_SMG(100);
SMG_Bit(Timer[0]%16,7);
Delay_SMG(100);
}
void InitSystem()
{
SelectHC138(5);
P0=0x00;
SelectHC138(4);
P0=0xff;
}
void main()
{
InitSystem();
DS1302_Config();
while(1)
{
Read_DS1302_Timer();
Display_SMG();
}
}