蓝桥杯单片机第四届初赛程序设计——“模拟智能灌溉系统”设计任务书

综述

第四届初赛赛题除了基础的独立按键、数码管显示、继电器和蜂鸣器的控制、LED灯的控制以外,难度增加的部分体现在AD和E2PROM。需要涉及到I2C总线驱动程序的调用。

本解析不代表标准答案或官方答案,仅做分享。若有不足或是更好的写法,望在评论区进行指正!

模板搭建

基础的功能:
1、独立按键
2、数码管显示

可以独立于赛题,提前写好,适用于各类赛题,仅做修改即可。

模板部分代码与解释在第三届赛题分享中 模板代码

根据此题要求对模板做出的改变

1、我们发现该题需要调用I2C通信。而在I2C通信期间,会调用P2端口。也就是说,当我们使能一些芯片的时候,需要改变P2.5-P2.7的值,如果此时直接采用

P2 = 0xXX;

这样的赋值语句,会对I2C产生干扰,也就是说我们只能改变P2端口后三位,而不能影响前面的数值。所以我们需要对模板进行修改。

void openP2(uchar num){
	P2 &= 0x1f;
	P2 |= (num << 5);
}

编辑这样一个函数

该函数不同于普通的P2赋值语句的区别在于,它不会影响前P2前五位的值。

比如我们要控制LED灯的时候,参照CT107D单片机原理图可以发现——
CT107DLED灯控制板块
LE口对应的是Y4C,也就是要控制LED灯时,openP2函数的输入应为4,即openP2(4);
需要将showNum函数进行修改:

void showNum(){
	uchar i;
	for(i = 0; i < 8; i ++){
		if(numGrp[i] != 11){
			openP2(6);P0 = (0x01 << i);
			openP2(7);P0 = shapeOfNum[numGrp[i]];
			Delay1ms();
		}
	}
	openP2(6);P0 = 0x00;
}

程序设计

IIC通信部分(E2PROM和AD)

官方为我们提供了iic的一个c文件一个h头文件。需要在项目中导入iic.c,同时声明iic.h。
同时要注意,由于官方提供的参考驱动代码是基于52单片机的,我们需要在.c文件中将所有的延时函数扩展为原来的12倍,才能成功调用驱动。

在这里插入图片描述

IIC读取
官方提供的AT24C02芯片说明书中描述了如何用IIC读取和写入。

uchar ADRead(uchar add){
	uchar dat;
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(add);
	IIC_WaitAck();
	IIC_Stop();
	
	IIC_Start();
	IIC_SendByte(0x91);
	IIC_WaitAck();
	dat = IIC_RecByte();
	IIC_WaitAck();
	IIC_Stop();
	
	return dat;
}

这里A/D转换的 写、读硬件地址为0x90/0x91
而word address可以由CT107D单片机原理图上找到,AIN3对应的是Rb2,所以此处函数输入应为0x03。即add=0x03;
A/D

uchar EEPROMRead(uchar add){
	uchar dat;
	
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(add);
	IIC_WaitAck();
	IIC_Stop();
	
	IIC_Start();
	IIC_SendByte(0xa1);
	IIC_WaitAck();
	dat = IIC_RecByte();
	IIC_Stop();
	
	return dat;
}

void EEPROMWrite(uchar add, uchar dat){
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(add);
	IIC_WaitAck();
	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();
}

E2PROM的与A/D一样,都是通过I2C通信进行的数据转换。
唯一的区别在于,写/读地址为 0xa0/0xa1

DS1302

DS1302的原理部分不再做赘述,主要参考的表格为官方芯片资料DS1302中的下面这个表。
在这里插入图片描述
通过循环向0x80 -> 0x8c写初始数据
从0x81 ->0x8d读时间数据来实现调用。
函数实现如下,其中oriTime和nowTime都为长度为8的uchar数组

void writeTime(){
	uchar i, add, dat;
	for(i = 0; i < 7; i ++){
		add = 0x80 + i * 2;
		dat = ((oriTime[i] / 10) << 4) + (oriTime[i] % 10);
		Write_Ds1302(add,dat);    
	}
}

void readTime(){
	uchar i, dat, add;
	for(i = 0; i < 7; i ++){
		add = 0x81 + i * 2;
		dat = Read_Ds1302 (add);
		nowTime[i] = (dat >> 4) * 10 + (dat & 0x0f);
	}
}

系统设计

在系统设计的时候,尽量避免对P2的重复操作第三届系统设计示例

同时程序需要写入两次,因为题目要求E2PROM写入存储,因此在第一次运行程序的时候,要求保留这一段语句:

EEPROMWrite(0x01, 50);//初始化语句,第一次运行后即可注释掉

而写入单片机后,在将这一句话注释掉,再次写入单片机。
这一段操作与E2PROM的原理有关,不明白的朋友可以去了解一下E2PROM。

主函数代码:

#include <stc15f2k60s2.h>
#include <intrins.h>
#include <iic.h>
#include <ds1302.h>

#define uchar unsigned char

uchar keyPress[4] = {0, 0, 0, 0};
uchar keyPressFlag[4] = {0, 0, 0, 0};

uchar code shapeOfNum[11] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0xbf};
uchar numGrp[8] = {11, 0, 5, 0, 0, 0, 0, 0};

uchar oriTime[7] = {0, 30, 8, 3, 2, 7, 19};
uchar nowTime[7] = {0, 0, 0, 0, 0, 0, 0};


void Delay1ms(){
	unsigned char i, j;

	_nop_();
	_nop_();
	_nop_();
	i = 11;
	j = 190;
	do
	{
		while (--j);
	} while (--i);
}

void Delay5ms(){
	unsigned char i, j;

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

void openP2(uchar num){
	P2 &= 0x1f;
	P2 |= (num << 5);
}


void showNum(){
	uchar i;
	for(i = 0; i < 8; i ++){
		if(numGrp[i] != 11){
			openP2(6);P0 = (0x01 << i);
			openP2(7);P0 = shapeOfNum[numGrp[i]];
			Delay1ms();
		}
	}
	openP2(6);P0 = 0x00;
}

void keyScan(){
	uchar i;
	for(i = 0; i < 4; i ++) keyPress[i] = 0;
	if((P3 & 0x0f) != 0x0f){
		Delay5ms();
		if((P3 & 0x0f) != 0x0f){
			switch(P3 & 0x0f){
				case 0x0e:keyPressFlag[0] = 1;break;
				case 0x0d:keyPressFlag[1] = 1;break;
				case 0x0b:keyPressFlag[2] = 1;break;
				case 0x07:keyPressFlag[3] = 1;break;
			}
		}
	}
	for(i = 0; i < 4; i ++){
		if(keyPressFlag[i] != 0){
			if((P3 & 0x0f) == 0x0f){
				keyPress[i] = 1;
				keyPressFlag[i] = 0;
			}
		}
	}
}

uchar ADRead(uchar add){
	uchar dat;
	
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(add);
	IIC_WaitAck();
	IIC_Stop();
	
	IIC_Start();
	IIC_SendByte(0x91);
	IIC_WaitAck();
	dat = IIC_RecByte();
	IIC_Stop();
	
	return dat;
}

uchar EEPROMRead(uchar add){
	uchar dat;
	
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(add);
	IIC_WaitAck();
	IIC_Stop();
	
	IIC_Start();
	IIC_SendByte(0xa1);
	IIC_WaitAck();
	dat = IIC_RecByte();
	IIC_Stop();
	
	return dat;
}

void EEPROMWrite(uchar add, uchar dat){
	IIC_Start();
	IIC_SendByte(0xa0);
	IIC_WaitAck();
	IIC_SendByte(add);
	IIC_WaitAck();
	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();
}

void writeTime(){
	uchar i, add, dat;
	for(i = 0; i < 7; i ++){
		add = 0x80 + i * 2;
		dat = ((oriTime[i] / 10) << 4) + (oriTime[i] % 10);
		Write_Ds1302(add,dat);    
	}
}

void readTime(){
	uchar i, dat, add;
	for(i = 0; i < 7; i ++){
		add = 0x81 + i * 2;
		dat = Read_Ds1302 (add);
		nowTime[i] = (dat >> 4) * 10 + (dat & 0x0f);
	}
}

void main(){
	uchar mod = 0, preMod = 1, wetMod = 0;
	uchar wetLine = 50, wet = 0, wetCh = 0;
	uchar L10 = 0, buzz = 0, buzzOn = 1;
	P2 = 0xa0;P0 = 0x00;
	P2 = 0x80;P0 = 0xff;
	writeTime();
	//EEPROMWrite(0x01, 50);//初始化语句,第一次运行后即可注释掉
	Delay5ms();Delay5ms();Delay5ms();//初始化的时候给一小段延时,否则立即读出的时候会出现数据错误
	wetLine = EEPROMRead(0x01);
	Delay5ms();Delay5ms();Delay5ms();
	while(1){
		keyScan();
		if(mod == 0){  //这里表示进入第一种模式,即自动模式
			if(preMod == 1){  //如果该次循环发生在,由手动模式跳转到自动模式,则需要进行LED灯的切换,下面同理
				openP2(4);P0 = ~(0x01);openP2(0);
				preMod = 0;
			}
			readTime();
			wet = (ADRead(0x03) * 99) / 255;
			if(wet >= wetLine){
				if(L10 == 1){
					openP2(5);P0 = 0x00;openP2(0);
					L10 = 0;
				}
			}
			else{
				if(L10 == 0){
					openP2(5);P0 = 0x10;openP2(0);
					L10 = 1;
				}
			}
			if(wetMod == 0){
				//设置数码管
				numGrp[0] = (nowTime[2] / 10) % 10;
				numGrp[1] = nowTime[2] % 10;
				numGrp[2] = 10;
				numGrp[3] = (nowTime[1] / 10) % 10;
				numGrp[4] = nowTime[1] % 10;
				numGrp[5] = 11;
				numGrp[6] = (wet / 10) % 10;
				numGrp[7] = wet % 10;
				if(keyPress[1] == 1){
					wetMod = 1;
					wetCh = wetLine;
				}
			}
			else{
				numGrp[0] = 10;
				numGrp[1] = 10;
				numGrp[2] = 11;
				numGrp[3] = 11;
				numGrp[4] = 11;
				numGrp[5] = 11;
				numGrp[6] = (wetCh / 10) % 10;
				numGrp[7] = wetCh % 10;
				if((keyPress[2] == 1) && (wetCh < 99)){
					wetCh ++;
				}
				else if((keyPress[3] == 1) && (wetCh > 0)){
					wetCh --;
				}
				if(keyPress[1] == 1){
					wetLine = wetCh;
					EEPROMWrite(0x01, wetLine);
					wetMod = 0;
				}
			}			
			if(keyPress[0] == 1){
				mod = 1;
			}
		}
		else if(mod == 1){
			if(preMod == 0){
				openP2(4);P0 = ~(0x02);openP2(0);
				preMod = 1;
			}
			readTime();
			wet = (ADRead(0x03) * 99) / 255;
	    //数码管显示
			numGrp[0] = (nowTime[2] / 10) % 10;
			numGrp[1] = nowTime[2] % 10;
			numGrp[2] = 10;
			numGrp[3] = (nowTime[1] / 10) % 10;
			numGrp[4] = nowTime[1] % 10;
			numGrp[5] = 11;
			numGrp[6] = (wet / 10) % 10;
			numGrp[7] = wet % 10;
			if(buzzOn == 1){
				if(wet >= wetLine){
					if(buzz == 1){
						openP2(5);P0 = 0x00;openP2(0);
						buzz = 0;
					}
				}
				else{
					if(buzz == 0){
						openP2(5);P0 = 0x40;openP2(0);
						buzz = 1;
					}
				}
			}
			else{
				if(buzz == 1){
					openP2(5);P0 = 0x00;openP2(0);
					buzz = 0;
				}
			}
			if(keyPress[1] == 1){
				buzzOn = !buzzOn;
			}
			if(keyPress[2] == 1){
				openP2(5);P0 = 0x10;openP2(0);
				L10 = 1;
			}
			if(keyPress[3] == 1){
				openP2(5);P0 = 0x00;openP2(0);
				L10 = 0;
			}
			if(keyPress[0] == 1){
				mod = 0;
			}
		}
		
		showNum();
	}
}

猜你喜欢

转载自blog.csdn.net/qq_39589692/article/details/87932600