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

Posted _Li.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蓝桥杯——根据手册写底层相关的知识,希望对你有一定的参考价值。

一、 DS18B20温度传感器


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


过程:

                             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;


 二、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(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();	

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


Da_Write 写入函数

void Da_Write(unsigned char dat)

	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(0x43); //选择需要写入器件的地址 0x41与0x43,写入的时候为
	
	I2CSendByte(dat); //发送数据
	I2CWaitAck();	
    I2CStop();

 

四、DS1302时钟模块

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

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;


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]);

六、NE555

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

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

我们直接来看他的代码:

定时器 与下多一个 0x05,且不需要打开中断使能

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;
	

2016年 蓝桥杯决赛体验

本来想搜决赛题解的...

结果搜到了“如何评价16年蓝桥杯......”看到一众人说自己只会第一题....刚觉得安慰了许多...

然后就搜到了决赛成绩...优秀奖...完美打铁...

.........看到同行的一等...二等...三等...怀疑自己的水平已经.....

说多了都是泪....已经不能理解只提交了第一题我了...

比赛的时候就感觉不想写不想写...看上去都是可以暴力过30%~50%的数据...然后暴力每每写的太丑...放弃找bug...填空题又太笨没有想出来...

就这样思考完了每个题...敲敲删删...蓝桥杯就这样结束了...

第一场个人参加的大比赛...惨淡收场...虽然断断续续也算准备了许久...

悲伤的心情...

以上是关于蓝桥杯——根据手册写底层的主要内容,如果未能解决你的问题,请参考以下文章

蓝桥杯单片机芯片型号

蓝桥杯单片机自锁怎么解决

蓝桥杯单片机——第十二届蓝桥杯单片机第一场省赛

蓝桥杯嵌入式第十三届客观题解析

蓝桥杯上的一题,题目为排列数,用了暴力算法超时,请问该怎么处理,谢谢!

蓝桥杯单片机组流程是啥,​请说具体点