蓝桥杯——根据手册写底层

一、 DS18B20温度传感器


1.官方所给源码

/*	# 	单总线代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/

//
void Delay_OneWire(unsigned int t)  
{
	unsigned char i;
	while(t--){
		for(i=0;i<12;i++);
	}
}

//
void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = dat&0x01;
		Delay_OneWire(5);
		DQ = 1;
		dat >>= 1;
	}
	Delay_OneWire(5);
}

//
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(5);
	}
	return dat;
}

//
bit init_ds18b20(void)
{
  	bit initflag = 0;
  	
  	DQ = 1;
  	Delay_OneWire(12);
  	DQ = 0;
  	Delay_OneWire(80);
  	DQ = 1;
  	Delay_OneWire(10); 
    initflag = DQ;     
  	Delay_OneWire(5);
  
  	return initflag;
}

过程:

                             1. 初始化 ——> 2.ROM跳过指令 ——> 3.功能指令 


功能指令:

温度转化参数

 

读取寄存器 


由上面总结的流程:

1. 初始化

2. ROM(0xcc命令)

3. 读取温度转化函数(0x44)

4. 初始化

5. ROM

6. 读取寄存器

7. 先读低八位

8. 再读高八位

10. 精度处理


原理引脚图:

根据引脚图的到我们所需要设置的引脚和头文件

#include <reg52.h>
#include <intrins.h>
#include "ds1302.h"

sbit SCK=P1^7;		
sbit SDA=P2^3;		
sbit RST = P1^3;

同时,得到代码:

float rd_temperature(void) //浮点数
{
  unsigned float temp;
  unsigned char low,high; //高八位与低八位

  init_ds18b20() //第一部初始化  1.Initialization
  Write_DS18B20(0xcc); //2.ROM 跳过指令 
  Write_DS18B20(0x44); //3.功能指令
  /* 题目说每次上电都需要初始化一次 */
  init_ds18b20() //第一部初始化  1.Initialization
  Write_DS18B20(0xcc); //2.ROM 跳过指令 
  Write_DS18B20(0xbe); //3.功能指令
 /* 接下来就可以读 */
  low = Read_DS18B20();
  high = Read_DS19B20();
  /* 返回一个值 */
  temp = ((high << 8)|low)/16.0; // 可以选择 *0.0625
  return temp;

}

我们可以用char类型来接收这个函数,也可以直接用浮点数,都是一样的


 二、AT24C02_EEPROM储存器

官方所给源码(IIC)

/*	#   I2C代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/
/* 根据引脚*/
#include <STC15F2K602K.H>
#include "intrins.h"

sbit sda = P21;
sbit scl = P20;

#define DELAY_TIME	5

//
static void I2C_Delay(unsigned char n)
{
    do
    {
        _nop_();	
    }
    while(n--);      	
}

//
void I2CStart(void)
{
    sda = 1;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 0;
	I2C_Delay(DELAY_TIME);
    scl = 0;    
}

//
void I2CStop(void)
{
    sda = 0;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 1;
	I2C_Delay(DELAY_TIME);
}

//
void I2CSendByte(unsigned char byt)
{
    unsigned char i;
	
    for(i=0; i<8; i++){
        scl = 0;
		I2C_Delay(DELAY_TIME);
        if(byt & 0x80){
            sda = 1;
        }
        else{
            sda = 0;
        }
		I2C_Delay(DELAY_TIME);
        scl = 1;
        byt <<= 1;
		I2C_Delay(DELAY_TIME);
    }
	
    scl = 0;  
}

//
unsigned char I2CReceiveByte(void)
{
	unsigned char da;
	unsigned char i;
	for(i=0;i<8;i++){   
		scl = 1;
		I2C_Delay(DELAY_TIME);
		da <<= 1;
		if(sda) 
			da |= 0x01;
		scl = 0;
		I2C_Delay(DELAY_TIME);
	}
	return da;    
}

//
unsigned char I2CWaitAck(void)
{
	unsigned char ackbit;
	
    scl = 1;
	I2C_Delay(DELAY_TIME);
    ackbit = sda; 
    scl = 0;
	I2C_Delay(DELAY_TIME);
	
	return ackbit;
}

//
void I2CSendAck(unsigned char ackbit)
{
    scl = 0;
    sda = ackbit; 
	I2C_Delay(DELAY_TIME);
    scl = 1;
	I2C_Delay(DELAY_TIME);
    scl = 0; 
	sda = 1;
	I2C_Delay(DELAY_TIME);
}


2. 器件地址


3. 字节写入流程

我们根据图八就可以明白整个流程

开始  ->  发送(写)器件地址  ->  等待  ->  发送字节地址  ->  等待  ->  写数据  -> 等待  ->  停止

其中 写器件地址为(0xa0),读器件地址为(0xa1)


4. EEPROM代码


 
void EEPROM_Write(unsigned char* EEPROM_String, unsigned char addr, unsigned char num)
{
    I2CStart(); //1.开始
    I2CSendByte(0xa0); //2.写入器件地址
    I2CWaitAck(); //3.等待
 
    I2CSendByte(addr); //3.写入字节地址 ,这个地方使用的时候一般为0
    I2CWaitAck();//4 等待
 
  /*  I2CSendByte(dat); // 5.数据发送
    I2CWaitAck(); */

    // 上面是发送单个,我们可以改为发送整个数组长度,用num计数,一个个发
    while(num--)
    {
         I2CSendByte(EEPROM_String);
         I2CWaitAck();
         I2C_Delay(200); //补充数据
    }

    I2CStop();
}

写入函数,在这里其实和发送函数是一样的


5. 随机读流程

 我们就可以得到整个流程

开始  ->  发送(写)器件地址  ->  等待  ->  发送字节地址  ->  等待  ->  开始  ->  发送(读)器件地址  ->  等待  ->   读数据  ->  停止


6. 随机读Read代码


void EEPROM_Read(unsigned char* EEPROM_String, unsigned char addr, unsigned char num)
{
    I2CStart(); //1.开始
    I2CSendByte(0xa0); //2.写入器件地址
    I2CWaitAck(); //3.等待
 
    I2CSendByte(addr); //3.写入字节地址 
    I2CWaitAck();//4 等待
 
    I2CStart();
    I2CSendByte(0xa1); //修改这个地址
    I2CWaitAck();
 
  /*  I2CSendByte(dat); // 5.数据发送
    I2CWaitAck(); */
    // 上面是发送单个,我们可以改为发送整个数组长度,用num计数,一个个发
 
/* 下面这个读数据可以实现整个字符全部读取,是需要记住的 */
    while(num--)
    {
        *EEPROM_String++=I2CReceiveByte();
        if(num) I2CSendAck(0); //是1,说明有消息,发送应答
        else I2CSendAck(1); /是0.说明没消息,不应答
    }
    I2CStop();
}

三、PCF8591模块

1. 原理图及引脚

两个模块都是IIC模块里的,所以我们的引脚都是一样的

代码如下:

#include "reg52.h"
#include "intrins.h"
 
sbit sda = P2^1;
sbit scl = P2^0;


上面说过两者都是在IIC里面的,所以通信协议是相同的,我们就可以使用同样的流程来进行读写操作,同时,我们不需要对写内容进行修改(同时读取后,我们使用比例对参数进行修改,让其符合我们的要求)

代码如下:

Ad_Read 读取函数

unsigned char Ad_Read(unsigned char addr)
{
	unsigned char temp;
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();	

	temp = I2CReceiveByte(); //接收消息
	I2CSendAck(1); //答应
	I2CStop(); //停止
	return temp; //返回AD值
}

读取的函数我们需要/51.0 ,转化为0-5V,这样化为一个浮点数,我们也用一个浮点数接收,注意我们不能用浮点数进行比较,但是我们注意显示就好


Da_Write 写入函数

void Da_Write(unsigned char dat)
{
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
//word
	I2CSendByte(0x43); //选择需要写入器件的地址 0x41与0x43,写入的时候为
	I2CWaitAck();


//dat
	I2CSendByte(dat); //发送数据
	I2CWaitAck();	
    I2CStop();
}
 

输入的值我们和上面一样,都需要注意乘以51.0,他可以是一个浮点数


四、DS1302时钟模块

他的协议是SPI协议,我们的头文件是使用的是onewrie.h,单总线

1. 官方所给底层

/*	# 	DS1302代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/								

//
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);			
}

2. 原理图及引脚

由上面的原理图,我们就可以得到其头文件与引脚

#include "ds1302.h"  									
#include <reg52.h>
#include <intrins.h>

sbit SCK = P1^7;		
sbit SDA = P2^3;		
sbit RST = P1^3; 

3. 手册重要地点:

 通过上面的,我们就知道如何写入和读取时间数据


4. 如何读写数据

我们根据表格上的 WRITE,与READ一列,其之后对应了各个时间

很多时候我们不需要到 日月周年,我们接着把需要的时间设置到一个数组里:ucRtc[]

 所以我们得到一个流程:


code unsigned char write_addr[]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
code unsigned char read_addr[]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
code unsigned char ucRtc[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

void Set_Rtc()
{
    unsigned char i;
	Write_Ds1302_Byte(0x8e,0x00);// 关闭写保护
	for(i = 0;i<7;i++)
	{
		Write_Ds1302_Byte(write_addr[i],ucRtc[i]); //每位时间地址写入需要时间
	}
	Write_Ds1302_Byte(0x8e,0x80);// 开启保护
}

void Read_Rtc()
{
	unsigned char i;
	for(i=0;i<7;i++)
		ucRtc[i] = Read_Ds1302_Byte(read_addr[i]);
}

 这是在我们设置好数组的需要填的时间参数后进行的,如果我们需要把时间放在主函数设置,我们可以把ucRtc的位置不设置,而使用指针代替

unsigned char* ucRtc

我们也可以和上面一样直接在onewire里面写入即可,我们也可以不写,只需要在定义里面添加,在主函数写即可

修改如下:

void Set_Rtc(unsigned char* ucRtc)
{
    unsigned char i;
	Write_Ds1302_Byte(0x8e,0x00);// 关闭写保护
	for(i = 0;i<7;i++)
	{
		Write_Ds1302_Byte(write_addr[i],ucRtc[i]); //每位时间地址写入需要时间
	}
	Write_Ds1302_Byte(0x8e,0x00);// 开启保护
}

void Read_Rtc(unsigned char* ucRtc)
{
	unsigned char i;
	for(i=0;i<7;i++)
		ucRtc[i] = Read_Ds1302_Byte(read_addr[i]);
}

之所以要在主函数里面写,是因为到时候我们如果需要修改时间的话,就可以直接选择数组数据去改,然后显示也方便,因为都是两位,也都是char类型


六、NE555

通过滑动变阻器来改变输出频率

我们就需要选择定时器的计数功能,但是因为定时和计数只能选一个,所以我们使用两个中断,一个计时(定时器1)一个计数(定时器0)

我们直接来看他的代码:

定时器 与下多一个 0x05,且不需要打开中断使能,计时初始值都为0

void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x05;		//设置计数模式
	TL0 = 0;		//设置定时初始值
	TH0 = 0;		//设置定时初始值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}

然后在计时下算频率

void Timer1Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0xBF;		//定时器时钟12T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0x18;		//设置定时初始值
	TH1 = 0xFC;		//设置定时初始值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	ET1 = 1;		//定时器中断1打开
	EA = 1;			//总中断打开
}



/* 定时器1中断服务函数 */
void Timer1Server() interrupt 3
{  	
	if(++Timer_1000Ms == 1000) //实时读取频率值
	{
		Timer_1000Ms = 0;
		Freq = TH0 << 8 | TL0; //和时间函数很相似
		TH0 = TL0 = 0;
	}
}

NE555最多会到5位数,所以我们需要拿5位出来显示他

猜你喜欢

转载自blog.csdn.net/ArtoriaLili/article/details/129805229