蓝桥杯之单片机设计与开发(34)——第十届省赛前最后总结与程序封装

目录

上电全部初始化

LED

数码管显示

蜂鸣器buzzer

继电器relay

独立按键BTN

矩阵键盘KBD

E2PROM和AD

DS18B20

DS1302

超声波


明天就是省赛了,今天最后一天。

赛前预测:今年很有可能考DS18B20

关于常用程序封装,一下程序是自己在写这么多的省赛题后总结的经验,仅供参考。


自己关于比赛写程序的一些建议,不喜勿喷:

1、所有题目从前往后依次写,有些小bug没事,功能要稳定就行。

2、程序中建议不要使用delay,比如按键消抖,一旦使用delay很有可能影响其他部件的工作状态。

3、多设置标志位,在while(1)和定时器中断中判断标志位,再操作。

4、除了定时器中断和while(1),其他地方不建议放一些需要时间很多的操作。比如不建议在按键处理函数中调用读写温度等等。

5、关于一些操作的时间。独立按键2ms扫描一次;矩阵按键1ms扫描一次;LED、数码管、蜂鸣器、继电器放在定时器中断里2ms执行一次;DS18B20在没有说明采集时间间隔情况下,在man函数的while(1)里1s采集一次数据;AD100ms采集一次。DS1302在中断中执行。


上电全部初始化

void AllInit(void)
{
	P2 = (P2 & 0x1f) | 0x80;
	P0 = 0xff;
	P2 = (P2 & 0x1f) | 0xc0;
	P0 = 0x00;
	P2 = (P2 & 0x1f) | 0xa0;
	P0 = 0x00;
	P2 = P2 & 0x1f;
}

LED

void LED_work(u8 dat)
{
	P0 = 0xff;
	P2 = (P2 & 0x1f) | 0x80;
	P0 = dat;
	P2 = P2 & 0x1f;
}

数码管显示

//以下数组定义为全局变量
//存放共阳数码管的码字,比赛会提供
u8 code Nixie[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8,
										0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e,
										0xff, 0xbf};		//16 17
//存放数码管显示缓存
u8 Nixiebuff[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
//分别设置每个数码管显示什么
u8 smg1, smg2, smg3, smg4, smg5, smg6, smg7, smg8;

//数码管扫描,定时器中断调用
void Smg_Scan(void)
{
	static u8 index = 0;
	
	P2 = (P2 & 0x1f) | 0xc0;	//打开Y6C
	P0 = 0x01 << index;
	
	P2 = (P2 & 0x1f) | 0xe0;	//打开Y7C
	P0 = 0xff;
	P0 = Nixiebuff[index];
	
	index ++;
	index &= 0x07;
}

//更新数码管值,在while(1)或者定时器中断中调用都可以
void Smg_show()
{
//在这里设置每个数码管需要显示显示的数值,根据实际情况设置即可,数字对应上述数组Nixie中的元素
	smg1 = 16;
	smg2 = 16;
	smg3 = 16;
	smg4 = 16;
	smg5 = 16;
	smg6 = 16;
	smg7 = 0;
    smg8 = 0;

	Nixiebuff[3] = Nixie[smg1];
	Nixiebuff[2] = Nixie[smg2];
	Nixiebuff[1] = Nixie[smg3];
	Nixiebuff[0] = Nixie[smg4];
	Nixiebuff[7] = Nixie[smg5];
	Nixiebuff[6] = Nixie[smg6];
	Nixiebuff[5] = Nixie[smg7];
	Nixiebuff[4] = Nixie[smg8];
}

蜂鸣器buzzer

蜂鸣器只需操作P06这一个IO口就可以了。

注意,因为所有数据都是P0口发送,为了使其不受干扰,在准备使能控制蜂鸣器这个573前,先把P0全部置零,关闭

//打开蜂鸣器
		P0 = 0x00;
		P2 = (P2 & 0x1f) | 0xa0;
		P06 = 1;     //打开蜂鸣器
		P2 = P2 & 0x1f;

//*****************************************

//关闭蜂鸣器
		P0 = 0x00;
		P2 = (P2 & 0x1f) | 0xa0;
		P06 = 0;     //关闭蜂鸣器
		P2 = P2 & 0x1f;

继电器relay

和上述继电器一模一样,控制P04这个IO

//打开继电器
		P0 = 0x00;
		P2 = (P2 & 0x1f) | 0xa0;
		P04 = 1;     //打开继电器
		P2 = P2 & 0x1f;

//*****************************************

//关闭继电器
		P0 = 0x00;
		P2 = (P2 & 0x1f) | 0xa0;
		P04 = 0;     //关闭继电器
		P2 = P2 & 0x1f;

独立按键BTN

注意,在使用独立按键时需要将开发板上J5跳线帽接在BTN那一端

u8 KeySta[] = {1, 1, 1, 1};			//键值存储区
u8 Keybackup[] = {1, 1, 1, 1};	//键值备份区

sbit S4 = P3^3;
sbit S5 = P3^2;
sbit S6 = P3^1;
sbit S7 = P3^0;

//按键扫描函数,在定时器中断里调用
void Key_Scan(void)
{
	static u8 Keybuff[] = {0xff, 0xff, 0xff, 0xff};	//按键缓冲区
	u8 i = 0;
	
	Keybuff[0] = (Keybuff[0] << 1) | S7;
	Keybuff[1] = (Keybuff[1] << 1) | S6;
	Keybuff[2] = (Keybuff[2] << 1) | S5;
	Keybuff[3] = (Keybuff[3] << 1) | S4;
	
	for(i = 0; i < 4; i++)
	{
		if(Keybuff[i] == 0xff)				//按键松开
			KeySta[i] = 1;
		else if(Keybuff[i] == 0x00)		//按键按下
			KeySta[i] = 0;
		else				//键值不稳定
		{}
	}
}

//根据检测到的哪一个按键按下,执行相应的操作
void Key_drive(u8 key)
{	
	if(key == 0)
	{
	}
	
	else if(key == 1)
	{
	}
	
	else if(key == 2)
	{
	}
	
	else if(key == 3)
	{
	}
}

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

矩阵键盘KBD

矩阵键盘和独立按键类似,J5接KBD端。

注意:开发板上的P36和P37对应实际管脚是P42和P44。

//管脚定义,一定要注意前两个
sbit KeyOut1 = P4^4;
sbit KeyOut2 = P4^2;
sbit KeyOut3 = P3^5;
sbit KeyOut4 = P3^4;
sbit KeyIn1 = P3^0;
sbit KeyIn2 = P3^1;
sbit KeyIn3 = P3^2;
sbit KeyIn4 = P3^3;

//存放键码,KeyAction函数根据传递过去的键码对应哪一个按键按下,执行相应的操作
unsigned char code KeyMap[4][4] = {{0x01, 0x02, 0x00, 0x00}, 
			{0x03, 0x04, 0x00, 0x00}, 
		    {0x05, 0x06, 0x00, 0x00}, 
			{0x07, 0x08, 0x00, 0x00}};
//存放键值,用于消抖
unsigned char KeyBuff[4][4] = {{0xff, 0xff, 0xff, 0xff},
			{0xff, 0xff, 0xff, 0xff}, 
			{0xff, 0xff, 0xff, 0xff}, 
			{0xff, 0xff, 0xff, 0xff}};
//存放每个按键是按下还是松开状态
unsigned char KeySta[4][4] = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}};
//备份按键状态,用于判断按键状态是否发生改变
unsigned char KeyBackup[4][4] = {{1,1,1,1}, {1,1,1,1}, {1,1,1,1}, {1,1,1,1}};
	
//按键扫描函数,在定时器中断中调用,1ms调用一次
void KeyScan(void)
{
	static u8 index = 0;
	u8 i;
	
	KeyBuff[0][index] = (KeyBuff[0][index] << 1) | KeyIn1;
	KeyBuff[1][index] = (KeyBuff[1][index] << 1) | KeyIn2;
	KeyBuff[2][index] = (KeyBuff[2][index] << 1) | KeyIn3;
	KeyBuff[3][index] = (KeyBuff[3][index] << 1) | KeyIn4;
	
	for(i = 0; i < 4; i ++)
	{
		if((KeyBuff[i][index] & 0x0f) == 0x00)
			KeySta[i][index] = 0;
		else if((KeyBuff[i][index] & 0x0f) == 0x0f)
			KeySta[i][index] = 1;	
	}
	
	index ++;
	index &= 0x03;
	switch(index)
	{
		case 0: KeyOut4 = 1; KeyOut1 = 0; break;
		case 1: KeyOut1 = 1; KeyOut2 = 0; break;
		case 2: KeyOut2 = 1; KeyOut3 = 0; break;
		case 3: KeyOut3 = 1; KeyOut4 = 0; break;
	}
}

//根据键码keycode判断哪一个按键按下,进行相应的操作。
void KeyAction(unsigned char keycode)
{
	if(keycode == 0x01)	
	{
	}
	else if(keycode == 0x03)
	{
	}
	else if(keycode == 0x02)
	{
	}
	else if(keycode == 0x04)		
	{
	}
	else if(keycode == 0x05)	
	{
	}
	else if(keycode == 0x06)	
	{
	}
	else if(keycode == 0x07)	
	{
	}
}

//检测按键是否按下吗,在main函数while(1)中调用
void KeyPress(void)
{
	u8 i, j;
	
	for(i = 0; i < 4; i ++)
	{
		for(j = 0; j < 4; j ++)
		{
			if(KeyBackup[i][j] != KeySta[i][j])
			{
				if(KeySta[i][j] == 0)        //按键按下时操作
					KeyAction(KeyMap[i][j]);
				KeyBackup[i][j] = KeySta[i][j];
			}
		}
	}
}

E2PROM和AD

根据官方提供的驱动添加读取AD值函数,读写E2PROM函数。

切记,写E2PROM后要等待5ms再写下一个字节。

读取E2PROM时不需要等待,直接连续读取就可以。

注意:somenop 中改为33个_nop()_

/*
  程序说明: IIC总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/

#include "sys.h"

#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_();}    

//总线引脚定义
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;
}

//读取AD值
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;	
}

//写E2PROM,一次写一个字节
void Write_E2PROM(unsigned char add, unsigned char dat)
{
	EA = 0;
  IIC_Start();
  IIC_SendByte(0xa0);	//发送器件地址
  IIC_WaitAck();
  IIC_SendByte(add);	//发送操作地址
  IIC_WaitAck();
  IIC_SendByte(dat);	//写一字节
  IIC_WaitAck();
  IIC_Stop();
  somenop;
	EA = 1;
}

//读E2PROM,一次读一个字节
unsigned char Read_E2PROM(unsigned char add)
{
  unsigned char d;
	
	IIC_Start();
	IIC_SendByte(0xa0); 	//发送器件地址
	IIC_WaitAck();
	IIC_SendByte(add);		//发送要操作的地址
	IIC_WaitAck();
	IIC_Stop();
	
	IIC_Start();
	IIC_SendByte(0xa1);		//发送读操作
	IIC_WaitAck();
	d = IIC_RecByte();	//读一字节
	IIC_Ack(0);
	IIC_Stop();
	return d;
}

DS18B20

DS18B20在官方驱动的基础上,增加开始温度转换函数和读取温度函数。

注意:DS18B20先读取出来的是低字节

/*
  程序说明: 单总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051 12MHz
  日    期: 2011-8-9
*/
#include "sys.h"

sbit DQ = P1^4;  //单总线接口

//单总线延时函数
void Delay_OneWire(unsigned int t)  
{
	unsigned char i;
	while(t--){
		for(i=0; i<8; i++);
	}
}


//通过单总线向DS18B20写一个字节
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);
}

//从DS18B20读取一个字节
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;
}

//DS18B20设备初始化
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;
}

//开始温度转换
void Trans_temper()
{	
	init_ds18b20();					//初始化
	Write_DS18B20(0xcc);		//跳过ROM,因为只有一个DS18B20
	Write_DS18B20(0x44);		//启动温度转换
}

//读取温度
int Gettemper()
{
	unsigned char higt, low;	//定义高八位低八位
	unsigned int temper;			//温度值
	
	init_ds18b20();					//初始化
	Write_DS18B20(0xcc);		//跳过ROM,因为只有一个DS18B20
	Write_DS18B20(0xbe);
	
	low = Read_DS18B20();
	higt = Read_DS18B20();
	temper = ((unsigned int)higt << 8) | low;
	
	return temper;
}

DS1302

DS1302在官方驱动基础上增加初始化函数。

注意:写DS1302前一定要取消写保护

/*
  程序说明: DS1302驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/

#include "sys.h"

sbit SCK=P1^7;		
sbit SDA=P2^3;		
sbit RST = P1^3;   // DS1302复位												

void Write_Ds1302_Byte(unsigned  char temp) 
{
	unsigned char i;
	for (i=0;i<8;i++)     	
	{ 
		SCK=0;
		SDA=temp&0x01;
		temp>>=1; 
		SCK=1;
	}
}   

void Write_Ds1302( unsigned char address,unsigned char dat )     
{
 	RST=0;
	_nop_();
 	SCK=0;
	_nop_();
 	RST=1;	
   	_nop_();  
 	Write_Ds1302_Byte(address);	
 	Write_Ds1302_Byte(dat);		
 	RST=0; 
}

unsigned char Read_Ds1302 ( unsigned char address )
{
 	unsigned char i,temp=0x00;
 	RST=0;
	_nop_();
 	SCK=0;
	_nop_();
 	RST=1;
	_nop_();
 	Write_Ds1302_Byte(address);
 	for (i=0;i<8;i++) 	
 	{		
		SCK=0;
		temp>>=1;	
 		if(SDA)
 		temp|=0x80;	
 		SCK=1;
	} 
 	RST=0;
	_nop_();
 	RST=0;
	SCK=0;
	_nop_();
	SCK=1;
	_nop_();
	SDA=0;
	_nop_();
	SDA=1;
	_nop_();
	return (temp);			
}

void DS1302Init(void)
{
	unsigned char timeinit[] = {0x23, 0x59, 0x55};
	
	Write_Ds1302(0x8e, 0x00);					//取消写保护
	Write_Ds1302(0x84, timeinit[0]);	//初始化时钟
	Write_Ds1302(0x82, timeinit[1]);	//初始化分钟
	Write_Ds1302(0x80, timeinit[2]);	//初始化秒钟
}

超声波

#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;
		}
	}
}

 

 

 

 

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值