蓝桥杯之单片机设计与开发(22)——常用程序封装

版权声明:让结局不留遗憾,让过程更加完美。 https://blog.csdn.net/Xiaomo_haa/article/details/88084454

目录

0、初始化外设

1、数码管

2、独立按键

3、矩阵键盘

4、定时器初始化

5、iic和E2PROM

6、AD采集电压

7、DS18B20

8、DS1302

9、超声波


2019年3月5日更新

  1. 更改了独立按键的驱动程序
  2. 更改了DS18B20的初始化程序可以设置DS18B20精度
  3. 更改了DS1302的初始化程序,每次上电都重新设置时间
  4. 更改DS18B20的驱动程序,在时序图要求很严的地方关总中断,在时序要求不严的地方打开总中断,这样可以避免显示数码管闪烁

历时一个多月,终于把蓝桥杯的模块整完了,今天开始写比赛题,现在把常用的程序封装一下。

0、初始化外设

/*******************************************************************************
* 函数名	:All_Init
* 输入值	:无
* 返回值	:无
* 作者		:小默haa
* 时间		:2019年1月15日
* 功能描述:外设初始化
* 备注		:关闭所有外设
*******************************************************************************/
void All_Init(void)
{
	P2 = (P2 & 0x1f) | 0x80;	//打开Y4C(LED)
	P0 = 0xff;			//关闭LED
	P2 = (P2 & 0x1f) | 0xe0;	//打开Y7C(数码管)
	P0 = 0xff;			//关闭数码管
	P2 = (P2 & 0x1f) | 0xa0;	//打开Y5C
	P0 = 0x00;			//关闭蜂鸣器、继电器
	P2 = P2 & 0x1f;			//关闭所有使能
}

1、数码管

unsigned char code Nixie[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};	//共阳数码管码字
unsigned char NixieBuff[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};	//数码管显示缓冲区,初值0xff确保启动时都不亮
unsigned char smg1,smg2,smg3,smg4,smg5,smg6,smg7,smg8;

//数码管显示
void Nixie_Scan(void)
{
	static unsigned char index = 0;
	
	P2 = (P2 & 0x1f) | 0xc0;	//数码管片选
	P0 = 0x01 << index;
	
	P2 = (P2 & 0x1f) | 0xe0;	//数码管消隐
	P0 =0xff;
	
	P2 = (P2 & 0x1f) | 0xe0;	//数码管段选
	P0 = NixieBuff[index];
	P2 &= 0x1f;
	index ++;
	index &= 0x07;
}

2、独立按键

sbit s4 = P3^3;
sbit s5 = P3^2;
sbit s6 = P3^1;
sbit s7 = P3^0;

//定时器0初始化
void Time0_Init(void)
{
	AUXR |= 0x80;	//定时器时钟1T模式
	TMOD &= 0xF0;	//设置定时器模式
	TL0 = 0x9a;		//设置定时初值2ms
	TH0 = 0xa9;		//设置定时初值2ms
	TF0 = 0;		//清除TF0标志
	ET0 = 1;		//允许定时器0中断
	TR0 = 1;		//定时器0开始计时
	EA = 1;			//开总中断
}

//定时器0程序
void Time0(void) interrupt 1
{
	static u16 t0 = 0,m = 0,n = 0;

	t0 ++;
	Key_Scan();
	if(t0 == 500)
	{
		if(Led_flag == 0)
			Led_dat = _cror_(Led_dat,1);
		else if(Led_flag == 1)
			Led_dat = _crol_(Led_dat,1);
		else if(Led_flag == 2)
		{
			switch(m)
			{
				case 0:Led_dat = 0x7e;break;
				case 1:Led_dat = 0xbd;break;
				case 2:Led_dat = 0xdb;break;
				case 3:Led_dat = 0xe7;break;
			}
			m ++;
			m = m & 3;		//m到4清零			
		}
		else if(Led_flag == 3)
		{
			switch(n)
			{
				case 0:Led_dat = 0xe7;break;
				case 1:Led_dat = 0xdb;break;
				case 2:Led_dat = 0xbd;break;
				case 3:Led_dat = 0x7e;break;
			}
			n ++;
			n = n & 3;		//n到4清零
		}
		Led_illume(Led_dat);
		t0 = 0;
	}
}

void Key_Scan(void)
{
	u16 i;
	static u8 keybuff[] = {0xff,0xff,0xff,0xff};
	keybuff[0] = (keybuff[0] << 1) | s4;
	keybuff[1] = (keybuff[1] << 1) | s5;
	keybuff[2] = (keybuff[2] << 1) | s6;
	keybuff[3] = (keybuff[3] << 1) | s7;
	for(i = 0;i < 4;i ++)
	{
		if(keybuff[i] == 0xff)		//连续扫描8次都是1,16ms内都是弹起状态,按键已松开
		{
			KeySta[i] = 1;
		}
		else if(keybuff[i] == 0x00)	//连续扫描8次都是0,16ms内都是按下状态,按键已按下
		{
			KeySta[i] = 0;
		}
		else	//其他状态键值不稳定,不作处理
		{}
	}
}

//按键处理
void Key_Deal(void)
{
	u8 i;
	for(i = 0;i < 4;i ++)
	{
		if(KeySta[i] == 0)
		{
			switch(i)
			{
				case 0:Led_dat = 0xfe;Led_flag = 0;break;
				case 1:Led_dat = 0x7f;Led_flag = 1;break;
				case 2:Led_flag = 2;break;
				case 3:Led_flag = 3;break;
			}
		}
	}
}

//检测按键是否按下,在main函数调用
void Key_press(void)
{
	u8 i;
	
	for(i = 0; i < 4; i ++)
	{
		if(KeySta[i] != Keybackup[i])
		{
			if(Keybackup[i] != 0)		//按键松开时操作
				Key_Deal(i);
			Keybackup[i] = KeySta[i];
		}
	}
}

void main(void)
{
	unsigned char i;
	Time0_Init();
	while(1)
	{
        Key_press();
	}
}

3、矩阵键盘

sbit KEY_IN_1 = P4^4;
sbit KEY_IN_2 = P4^2;
sbit KEY_IN_3 = P3^5;
sbit KEY_IN_4 = P3^4;
sbit KEY_OUT_1 = P3^0;
sbit KEY_OUT_2 = P3^1;
sbit KEY_OUT_3 = P3^2;
sbit KEY_OUT_4 = P3^3;

//定时器0初始化
void Time0_Init(void)
{
	AUXR |= 0x80;	//定时器时钟1T模式
	TMOD &= 0xF0;	//设置定时器模式
	TL0 = 0xcd;		//设置定时初值1ms
	TH0 = 0xd4;		//设置定时初值1ms
	TF0 = 0;		//清除TF0标志
	ET0 = 1;		//允许定时器0中断
	TR0 = 1;		//定时器0开始计时
}

/*******************************************************************************
* 函数名	:Time0
* 输入值	:无
* 返回值	:无
* 作者		:小默haa
* 时间		:2019年1月29日
* 功能描述:定时器0中断函数
* 备注		:定时器0中断函数,定时器0定时1ms,
				执行Key_Scan()函数。      
*******************************************************************************/
void Time0(void) interrupt 1
{	
	Key_Scan();
}

/*******************************************************************************
* 文件名:key.c
* 描  述:
* 作  者:小默haa
* 版本号:v1.0
* 日  期: 2019年1月28日
* 备  注:矩阵键盘核心程序
*         
*******************************************************************************/
#include "sys.h"

u8 KeySta[4][4] = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}};			//当前按键状态
u8 Key_backup[4][4] = {{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}};	//按键状态备份,保存前一次的按键值
u8 code KeyCodeMap[4][4] = {{0x31, 0x32, 0x33, 0x26},			//1,2,3,↑
	            	{0x34, 0x35, 0x36, 0x25},			//4,5,6,←
	            	{0x37, 0x38, 0x39, 0x28},			//7,8,9,↓
	            	{0x30, 0x1b, 0x0d, 0x27}};		//0,ESC,ENTER,→

/*******************************************************************************
* 函数名	:Key_Scan
* 输入值	:无
* 返回值	:无
* 作者		:小默haa
* 时间		:2019年1月29日
* 功能描述:矩阵按键扫描
* 备注		:按键扫描函数,在定时器中断程序里调用,定时器间隔1ms        
*******************************************************************************/
void Key_Scan(void)
{
	u8 i;
	static u8 keyout = 0;		//矩阵按键扫描输出索引
	static u8 keybuff[4][4] = {{0xff, 0xff, 0xff, 0xff},
				{0xff, 0xff, 0xff, 0xff},
				{0xff, 0xff, 0xff, 0xff},
				{0xff, 0xff, 0xff, 0xff}};	//矩阵按键扫描缓存区
	
	keybuff[keyout][0] = (keybuff[keyout][0] << 1) | KEY_IN_1;		//将每一行的4个按键值移入缓存区
	keybuff[keyout][1] = (keybuff[keyout][1] << 1) | KEY_IN_2;
	keybuff[keyout][2] = (keybuff[keyout][2] << 1) | KEY_IN_3;
	keybuff[keyout][3] = (keybuff[keyout][3] << 1) | KEY_IN_4;
		
	//消抖后更新按键状态
	for (i = 0;i < 4;i ++)
	{
		if ((keybuff[keyout][i] & 0x0f) == 0x00)
			KeySta[keyout][i] = 0;		//连续4次扫描值都是0,即4×4ms内都是按下状态,认为按键已平稳按下
		else if ((keybuff[keyout][i] & 0x0f) == 0x0f)
			KeySta[keyout][i] = 1;		//连续4次扫描值都是1,即4×4ms内都是松开状态,认为按键已稳定弹起
	}
	
	//执行下一次的扫描输出
	keyout ++;
	keyout = keyout & 0x03;		//索引加到4就归零
	switch (keyout)						//根据索引,释放当前输出引脚,拉低下次的输出引脚
	{
		case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
		case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
		case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
		case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
		default:break;
	}
}

/*******************************************************************************
* 函数名	:Key_Drive
* 输入值	:无
* 返回值	:无
* 作者		:小默haa
* 时间		:2019年1月29日
* 功能描述:矩阵按键驱动程序
* 备注		:检测矩阵键盘的动作,调用相应的函数,需要在主循环里调用        
*******************************************************************************/
void Key_Drive(void)
{
	u8 i, j;
	for (i = 0; i < 4; i ++)										//循环检测4×4的矩阵按键
	{	
		for (j = 0; j < 4; j ++)
		{
			if (Key_backup[i][j] != KeySta[i][j])		//检测按键当前动作
			{
				if (Key_backup[i][j] != 0)						//按键按下时执行动作
				{
					Key_Action(KeyCodeMap[i][j]);				//调用按键活动函数
				}
				Key_backup[i][j] = KeySta[i][j];			//更新前一次的备份值
			}
		}
	}
}

/*******************************************************************************
* 函数名	:Key_Action
* 输入值	:键值 keycode
* 返回值	:无
* 作者		:小默haa
* 时间		:2019年1月29日
* 功能描述:矩阵按键动作函数
* 备注		:矩阵键盘动作函数,根据键值执行相应程序        
*******************************************************************************/
void Key_Action(u8 keycode)
{
	static u32 addend = 0;
	static u8 num_bit = 0;		//记录输入位数

	
	if ((keycode >= 0x30) && (keycode <= 0x39) && num_bit < 8)		//输入0~9数字
	{
		addend = (addend * 10) + (keycode - 0x30);	//整体十进制左移,新数字进入个位
		num_bit ++;													//每输入一个数字,位数加1
		Show_Num(addend);										//数码管显示
	}
}

4、定时器初始化

//定时器定时指定us
void Timer0Init(u16 us)		//@11.0592MHz
{
	float tmp = 0;
	u16 temp = 0;
	
	tmp = (12 * 1000000) / FOSC * 1.0;
	temp = (u16)(us / tmp);
	
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	
	a = (65535 - temp) / 256;
	b = (65535 - temp) % 256;
	TH0 = a;
	TL0 = b;
	
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;
	EA = 1;
}

5、iic和E2PROM

#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}    

#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1

//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */

//总线启动条件
void IIC_Start(void)
{
	SDA = 1;
	SCL = 1;
	somenop;
	SDA = 0;
	somenop;
	SCL = 0;	
}

//总线停止条件
void IIC_Stop(void)
{
	SDA = 0;
	SCL = 1;
	somenop;
	SDA = 1;
}

//应答位控制
void IIC_Ack(bit ackbit)
{
	if(ackbit) 
	{	
		SDA = 0;
	}
	else 
	{
		SDA = 1;
	}
	somenop;
	SCL = 1;
	somenop;
	SCL = 0;
	SDA = 1; 
	somenop;
}

//等待应答
bit IIC_WaitAck(void)
{
	SDA = 1;
	somenop;
	SCL = 1;
	somenop;
	if(SDA)    
	{   
		SCL = 0;
		IIC_Stop();
		return 0;
	}
	else  
	{ 
		SCL = 0;
		return 1;
	}
}

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{   
		if(byt&0x80) 
		{	
			SDA = 1;
		}
		else 
		{
			SDA = 0;
		}
		somenop;
		SCL = 1;
		byt <<= 1;
		somenop;
		SCL = 0;
	}
}

//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
	unsigned char da;
	unsigned char i;
	
	for(i=0;i<8;i++)
	{   
		SCL = 1;
		somenop;
		da <<= 1;
		if(SDA) 
		da |= 0x01;
		SCL = 0;
		somenop;
	}
	return da;
}

/*******************************************************************************
* 函数名	:E2Read
* 输入值	:unsigned char *buf, unsigned char addr, unsigned char led
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月25日
* 功能描述:连续读取多个数据
* 备注		:buf为数据接收指针,addr为E2PROM中要读取的数据起始地址,len为读取长度
*******************************************************************************/
void E2Read(unsigned char *buf, unsigned char addr, unsigned char len)
{
	do{			            //用寻址操作查询当前是否可进行读写操作
		IIC_Start();
		IIC_SendByte(0xa0);         //发送器件地址
		if(IIC_WaitAck() == 1)	    //应答则跳出循环,非应答则进行下一次查询
			break;
		IIC_Stop();
	}while(1);
	
	IIC_SendByte(addr);                 //写入起始地址
	IIC_WaitAck();
	IIC_Stop();
	
	
	IIC_Start();                        //发送重复启动信号
	IIC_SendByte(0xa1);	
	IIC_WaitAck();
	
	while(len > 1)		        	//连续读取len-1个字节
	{
		*buf++ = IIC_RecByte();	     //最后字节之前为读取操作并且应答
		IIC_Ack(1); 					
		len--;
	}
	*buf = IIC_RecByte();	            //最后一个字节为读取操作并且非应答
	IIC_Ack(0);							
	IIC_Stop();
}

/*******************************************************************************
* 函数名	:E2Write_page
* 输入值	:unsigned char *buf, unsigned char addr, unsigned char led
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月25日
* 功能描述:连续写入一页数据
* 备注		:buf为源数据指针,addr为E2PROM中要写入的数据起始地址,len为写入长度
*******************************************************************************/
void E2Write_page(unsigned char *buf, unsigned char addr, unsigned char len)
{
	while(len)
	{
		do{					//用寻址操作查询当前是否可进行读写操作
			IIC_Start();
			IIC_SendByte(0xa0);
			if(IIC_WaitAck() == 1)		//应答则跳出循环,非应答则进行下一次查询
				break;
			IIC_Stop();
		}while(1);
		
		IIC_SendByte(addr);		        //写入起始地址
		IIC_WaitAck();
		
		while(len > 0)
		{
			IIC_SendByte(*buf++);		//写入一个字节数据
			IIC_WaitAck();
			len--;				//待写入长度计数递减
			addr++;				//E2PROM地址递增
			if((addr & 0x07) == 0)		//检查地址是否到达页边界,24C02每页8字节
				break;			//到达页边界时,跳出循环,结束本次写操作
		}
		IIC_Stop();
	}
}

6、AD采集电压

/*******************************************************************************
* 函数名	:Read_AIN
* 输入值	:unsigned char chn
* 返回值	:unsigend char dat
* 作者		:小默haa
* 时间		:2019年2月25日
* 功能描述:读取PCF8591AIN采集数据
* 备注		:chn为PCF8591的通道
*******************************************************************************/
unsigned char Read_AIN(unsigned char chn)
{
	unsigned char dat;
	EA = 0;
	IIC_Start();				//IIC总线起始信号							
	IIC_SendByte(0x90); 	        	//PCF8591的写设备地址		
	IIC_WaitAck();  		    	//等待从机应答		
	IIC_SendByte(chn); 			//写入PCF8591的控制字节		
	IIC_WaitAck();  			//等待从机应答						
	IIC_Stop(); 				//IIC总线停止信号					
	
	IIC_Start();				//IIC总线起始信号									
	IIC_SendByte(0x91); 	        	//PCF8591的读设备地址		
	IIC_WaitAck(); 			    	//等待从机应答		
	dat = IIC_RecByte();	        	//读取PCF8591通道3的数据 			
	IIC_Ack(0); 				//产生非应答信号				
	IIC_Stop(); 				//IIC总线停止信号		
	EA = 1;
	return dat;	
}

/*******************************************************************************
* 函数名	:ValueToString
* 输入值	:unsigned char *str, unsigned char val
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月25日
* 功能描述:将PCF8591AIN采集的数据转换为字符型
* 备注		:注意这里把电压扩大了10倍
*******************************************************************************/
void ValueToString(unsigned char *str, unsigned char val)
{
    val = (val * 50) / 255;                  //电压5V,256个刻度分成255份!
    str[0] = val / 10;
    str[2] = val % 10;
}

7、DS18B20

sbit DS18B20_IO = P1^4;

/*******************************************************************************
* 函数名	:Delayus
* 输入值	:unsigned int us
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月17日
* 功能描述:1T单片机延时指定us
* 备注		:最大形参65535,即最大延时65ms
*******************************************************************************/
void Delayus(unsigned int us)
{
	do{
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
	}while(--us);
}

/*******************************************************************************
* 函数名	:Get18B20Ack
* 输入值	:none
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:复位总线,获取18B20存在脉冲,以启动一次读写操作
* 备注		:
*******************************************************************************/
bit Get18B20Ack(void)
{
	bit ack;
	
	EA = 0;                //禁止总中断
	DS18B20_IO = 0;			//产生500us的复位脉冲
	Delayus(500);
	DS18B20_IO = 1;			//延时60us
	Delayus(60);
	ack = DS18B20_IO;		//读取存在脉冲
	while(!DS18B20_IO);	        //等待存在脉冲结束
	
	EA = 1;			        //重新使能总中断
	
	return ack; 	
}

/*******************************************************************************
* 函数名	:DS18B20Write
* 输入值	:unsigned char dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:向18B20写入一个字节
* 备注		:dat为待写入字节
*******************************************************************************/
void DS18B20Write(unsigned char dat)
{
 	unsigned char mask;
						
	for(mask = 0x01; mask != 0; mask <<= 1)     //低位在先,依次移出8个bit
	{
        EA = 0;				    	    //禁止总中断
		DS18B20_IO = 0;                     //产生2us低电平脉冲
		Delayus(2);
	 	if(dat & mask)                      //输出该bit值
		 	DS18B20_IO = 1;
		else
		 	DS18B20_IO = 0;
        EA = 1;					    	//重新使能总中断
		Delayus(60);		        	//延时60us
		DS18B20_IO = 1;		        	//拉高通信引脚
	}
}

/*******************************************************************************
* 函数名	:DS18B20Read
* 输入值	:none
* 返回值	:unsigend char dat
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:从18B20读取一个字节
* 备注		:返回值为读取到的字节
*******************************************************************************/
unsigned char DS18B20Read(void)
{
 	unsigned char mask, dat = 0;
	
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在先,依次采集8个bit
	{
        EA = 0;					//禁止总中断
		DS18B20_IO = 0;				//产生2us低电平脉冲
		Delayus(2);
		DS18B20_IO = 1;				//结束低电平脉冲,等待18B20输出数据
		Delayus(2);					//延时2us
	 	if(DS18B20_IO)				//读取通信引脚上的值
		 	dat |= mask;
	    EA = 1;						//重新使能总中断
		Delayus(60);				//再延时60us
	}
	
	return dat;	
}

/*******************************************************************************
* 函数名	:Start18B20
* 输入值	:none
* 返回值	:bit ~ack
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:启动一次18B20温度转换
* 备注		:返回值为是否启动成功
*******************************************************************************/
bit Start18B20()
{
 	bit ack;
	static bit flag = 1;

	ack = Get18B20Ack();		//执行总线复位,并获取18B20应答
	if(ack == 0)						//如18B20正确应答,则启动一次转换
	{
		DS18B20Write(0xCC);		//跳过ROM操作
		
		if(flag)
		{
			flag = 0;
			DS18B20Write(0x4e);			//写暂存器指令4E
			DS18B20Write(0x4b);			//写高速缓存器TH高温限值75度
			DS18B20Write(0x00);			//写高速缓存器TL低温限值0度
			DS18B20Write(0x1f);			//写配置寄存器4
						//0x1f : 0.5000°C  转换时间93.75ms
						//0x3f : 0.2000°C  转换时间187.5ms
						//0x5f : 0.1250°C  转换时间375ms
						//0x7f : 0.0625°C  转换时间750ms
		}
		
		ack = Get18B20Ack();		//执行总线复位,并获取18B20应答
		if(ack == 0)						//如18B20正确应答,则启动一次转换
		{
			DS18B20Write(0xCC);		//跳过ROM操作
			DS18B20Write(0x44);		//启动一次温度转换
		}
	}

	return ~ack;						//ack == 0 表示操作成功,所以返回值对其取反
}

/*******************************************************************************
* 函数名	:Get18B20Temp
* 输入值	:int *temp
* 返回值	:bit ~ack
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:读取18B20转换的温度值
* 备注		:返回值为是否读取成功
*******************************************************************************/
bit Get18B20Temp(int *temp)
{
 	bit ack;
	unsigned char LSB, MSB;			//16bit温度值的低字节和高字节

	ack = Get18B20Ack();				//执行总线复位,并获取18B20应答
	if(ack == 0)				//如18B20正确应答,则读取温度值
	{
	 	DS18B20Write(0xCC);				//跳过ROM操作
		DS18B20Write(0xBE);				//发送读命令
		LSB = DS18B20Read();			//读温度值的低字节
		MSB = DS18B20Read();			//读温度值的高字节
		*temp = ((unsigned int) MSB << 8) + LSB;	//合成16bit的整数
	}

	return ~ack;				//ack == 0 表示操作应答,所以返回值为1其取反值
} 

8、DS1302

sbit DS1302_IO = P2^3;
sbit DS1302_CK = P1^7;
sbit DS1302_CE = P1^3;

/*******************************************************************************
* 函数名	:DS1302ByteWrite
* 输入值	:unsigned char dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:发送一个字节到DS1302通信总线上
* 备注		:
*******************************************************************************/
void DS1302ByteWrite(unsigned char dat) 
{
	unsigned char mask;
	DS1302_IO = 1;					//拉低IO总线
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在前,逐位移出
	{
		if((dat&mask) != 0)		//首先输出该位数据
			DS1302_IO = 1;
		else
			DS1302_IO = 0;
		DS1302_CK = 1;				//拉高时钟线
		DS1302_CK = 0;				//拉低时钟线,完成一个位的操作
	}
	DS1302_IO = 1;  				//写完之后确保释放IO总线
}  

/*******************************************************************************
* 函数名	:DS1302ByteRead
* 输入值	:none
* 返回值	:unsigned char dat
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:由DS1302通信总线上读取一个字节
* 备注		:返回读到的字节数据
*******************************************************************************/
unsigned char DS1302ByteRead(void)
{
	unsigned char mask, dat = 0;
	
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在前,逐位读取
	{
		if(DS1302_IO)			//首先读取此时的IO引脚,并设置dat中的对应位
			dat |= mask;

		DS1302_CK = 1;		//拉高时钟
		DS1302_CK = 0;		//再拉低时钟,完成一个位的操作
	}
	return dat;				//返回读到的字节数据
} 

/*******************************************************************************
* 函数名	:DS1302SingleWrite
* 输入值	:unsigned char reg, unsigned char dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用单次写操作向某一寄存器写入一个字节
* 备注		:reg为寄存器地址,dat为待写入字节
*******************************************************************************/
void DS1302SingleWrite(unsigned char reg, unsigned char dat)     
{
	DS1302_CE = 1;				//使能片选信号
	DS1302ByteWrite((reg << 1) | 0x80);	//发送写寄存器指令
	DS1302ByteWrite(dat);		//写入字节数据
	DS1302_CE = 0;				//除能片选信号
}

/*******************************************************************************
* 函数名	:DS1302SingleRead
* 输入值	:unsigned char reg
* 返回值	:unsigned char dat
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用单次读操作从某一寄存器读取一个字节
* 备注		:reg为寄存器地址,返回值dat为读到的字节
*******************************************************************************/
unsigned char DS1302SingleRead(unsigned char reg)
{
	unsigned char dat;
	
	DS1302_CE = 1;					//使能片选信号
	DS1302ByteWrite((reg << 1) | 0x81);	//发送读寄存器指令
	dat = DS1302ByteRead();	//读取字节数据
	DS1302_CE = 0;					//除能片选信号
	
	DS1302_IO = 0;					//单字节读必须加的!
	
	return dat;         
}

/*******************************************************************************
* 函数名	:DS1302BurstWrite
* 输入值	:unsigned char *dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用突发模式连续写入8个寄存器数据
* 备注		:reg为寄存器地址,返回值dat为读到的字节
*******************************************************************************/
void DS1302BurstWrite(unsigned char *dat)
{
	unsigned char i;
	
	DS1302_CE = 1;
	DS1302ByteWrite(0xBE);				//发送突发写寄存器指令
	for(i = 0; i < 7; i ++)				//连续写入8字节数据
		DS1302ByteWrite(*dat++);        
	DS1302_CE = 0;      
}

/*******************************************************************************
* 函数名	:DS1302BurstRead
* 输入值	:unsigned char *dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用突发模式连续读取8个寄存器的数据
* 备注		:dat为读到的字节
*******************************************************************************/
void DS1302BurstRead (unsigned char *dat)
{
	unsigned char i;
	
	DS1302_CE = 1;
	DS1302ByteWrite(0xBF);				//发送突发读寄存器指令
	for(i = 0; i < 7; i++)				//连续读取8个字节
		dat[i] = DS1302ByteRead();      
	DS1302_CE = 0;  
	
	DS1302_IO = 0;						//突发读必须加
}	

/*******************************************************************************
* 函数名	:GetRealTime
* 输入值	:struct sTime *time
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:读取DS1302时间
* 备注		:
*******************************************************************************/
void GetRealTime(struct sTime *time)
{
	unsigned char buf[8];
	
	DS1302BurstRead(buf);
	time -> year = buf[6];
	time -> mon  = buf[4];
	time -> day  = buf[3];
	time -> hour = buf[2];
	time -> min  = buf[1];
	time -> sec  = buf[0];
	time -> week = buf[5];
}

/*******************************************************************************
* 函数名	:SetRealTime
* 输入值	:struct sTime *time
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:设置DS1302时间
* 备注		:
*******************************************************************************/
void SetRealTime(struct sTime *time)
{
	unsigned char buf[8];
	
	buf[7] = 0;
	buf[6] = time -> year;
	buf[4] = time -> mon;
	buf[3] = time -> day;
	buf[2] = time -> hour;
	buf[1] = time -> min;
	buf[0] = time -> sec;
	buf[5] = time -> week;
	DS1302BurstWrite(buf);
}

/*******************************************************************************
* 函数名	:InitDS1302
* 输入值	:none
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:DS1302初始化
* 备注		:
*******************************************************************************/
void InitDS1302()
{
	struct sTime InitTime[] = {  //2019年3月2日 星期六 16:40:00
			0x19, 0x03, 0x02, 0x16, 0x40, 0x00, 0x06
	};
	unsigned char dat;
	DS1302_CE = 0;						//初始化DS1302通信引脚
	DS1302_CK = 0;
	dat = DS1302SingleRead(0);			//读取秒寄存器

	DS1302SingleWrite(7, 0x00);  	//撤销写保护以允许写入数据
	SetRealTime(&InitTime);			//设置DS1302为默认时间
}

9、超声波

#define sonic_nop {_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_();};

void SendSonic(void)
{
	unsigned char i = 8;
	
	while(i --)
	{
		TX = 1;
		sonic_nop; sonic_nop; sonic_nop; sonic_nop; sonic_nop;
		sonic_nop; sonic_nop; sonic_nop; sonic_nop; sonic_nop;
		TX = 0;
		sonic_nop; sonic_nop; sonic_nop; sonic_nop; sonic_nop;
		sonic_nop; sonic_nop; sonic_nop; sonic_nop; sonic_nop;
	}
}

//定时器0初始化
void Time0_Init(void)
{
	AUXR |= 0x80;	//定时器时钟1T模式
	TMOD &= 0xF0;	//设置定时器模式
	TL0 = 0xcd;		//设置定时初值1ms
	TH0 = 0xd4;		//设置定时初值1ms
	TF0 = 0;		//清除TF0标志
	ET0 = 1;		//允许定时器0中断
	TR0 = 1;		//定时器0开始计时
}

//定时器0中断程序
void Time0(void) interrupt 1
{
	static uint t0 = 0;
	static uchar index = 0;

	t0 ++;

	if(t0 == 125)
	{
		t0 = 0;
		flag_125ms = 1;
		Led_illume(0xfe << index);
		
		index ++;
		index &= 0x07;
	}
}

void Timer1Init(void)		//0微秒@11.0592MHz
{
	AUXR &= 0xBF;		//定时器时钟12T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0;		//设置定时初值
	TH1 = 0;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 0;
}

void main(void)
{
	uint t, distance = 0;
	
	All_Init();
	Time0_Init();
	Timer1Init();
	EA = 1;
	while(1)
	{
		if(flag_125ms)
		{
			flag_125ms = 0;
			SendSonic();			//发送50KHz的超声波	
			TR1 = 1;					//开启定时器1计时
			while((RX == 1) && (TF1 == 0));	//如果接收到回波或者定时器1溢出
			TR1 = 0;					//关闭定时器1
			if(TF1 == 1)
			{
				TF1 = 0;
				distance = 999;	//距离999
			}
			else
			{
				t = TH1;
				t <<= 8;
				t |= TL1;
				distance = (uint)(t * 0.017);
			}
			Deal_distance(distance);
			TH1 = 0;
			TL1 = 0;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Xiaomo_haa/article/details/88084454
今日推荐