文章目录
蓝桥杯第四届初赛“模拟智能灌溉系统”设计任务书
Author:Luis
Time:2022-04-10
Version:v1.0
说明
蓝桥杯系列的代码都是本人亲手写和调试,可能有时会有点错误,请见谅。此外,代码仅供大家学习,祝福各位在蓝桥杯比赛中能获得比较满意的成绩。
忙着期中考试,今天就来“水”一下今天的博客。
从第五届的题目开始,我就会开始一步步地分析题目并且写出代码。分析能力和思路更重要,代码只是实现手段,大家不能局限于代码。
功能简介
1.1 自动工作状态,根据湿度数据自动控制打开或关闭灌溉设备,以L1 点亮指示;
1.2 手动工作状态,通过按键控制打开或关闭灌溉设备,以L2 点亮指示;
1.3 系统上电后处于自动工作状态,系统初始湿度阈值为50%,此时若湿度低于
50%,灌溉设备自动打开,达到50%后,灌溉设备自动关闭;
1.4 灌溉设备打开或关闭通过继电器工作状态模拟。
具体代码
main.c
/*******************************************************************************
* 目标:第四届题目 “模拟智能灌溉系统”设计任务书
* 注意:J3跳线配置为IO方式,J5配置为BTN、J2配置为1-3和2-4
*******************************************************************************/
#include <STC15F2K60S2.h>
#include "intrins.h"
#include "ds1302.h"
#include "iic.h"
unsigned char *time_ds1302; // 时间
unsigned char ds1302_flag = 0; // 0 1
unsigned char key_value = 0;
unsigned char key_flag = 0;
unsigned char adc_flag = 0;
// 小数点是第8位为0 原本数字 - 0x80
unsigned char data_pros[] = {
0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0xff, 0xbf};
unsigned char dspbuf[8] = {10, 10, 10, 10, 10, 10, 10, 10}; //显示缓冲区
// 初始化值
void close_init()
{
P2 = 0xA0;
P0 = 0x00;
P2 = 0x80;
P0 = 0xff;
P2 = 0x00;
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j)
;
} while (--i);
}
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 22;
j = 3;
k = 227;
do
{
do
{
while (--k)
;
} while (--j);
} while (--i);
}
// 显示函数 dspbuf为数码管的缓冲区
void display()
{
static unsigned char dspnum = 0; // 数码管的位置
P2 = 0xE0;
P2 = 0xff; // 消影
P2 = 0xc0;
P0 = (1 << dspnum);
P2 = 0xe0;
P0 = data_pros[dspbuf[dspnum]];
P2 = 0x00;
if (++dspnum == 8)
dspnum = 0;
}
void Timer0Init(void) // 1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
// add
TH1 = 0;
TL1 = 0;
ET0 = 1;
EA = 1;
}
// 读取独立键值 J5为BTN模式
unsigned char read_key()
{
static unsigned char key_press = 0; // 按下次数
unsigned char key_temp;
static bit key_re = 0;
static unsigned char key_value = 0;
key_temp = (P3 & 0x0f); // 取低位值
/**************多次检测,可以可以防止抖动*********************/
if (key_temp != 0x0f)
key_press++;
else
key_press = 0;
if (key_press == 5) // 连续5次按压
{
key_press = 0;
key_re = 1; // 按下
switch (key_temp)
{
case 0x0e:
key_value = 1; // S7
break;
case 0x0d:
key_value = 2; // S6
break;
case 0x0b:
key_value = 3; // S5
break;
case 0x07:
key_value = 4; // S4
break;
}
}
//连续5次检测到按键被按下,并且该按键已经释放
if ((key_re == 1) && (key_temp == 0x0f))
{
key_re = 0;
return key_value;
}
return 0xff; //无按键按下或被按下的按键未被释放
}
void main()
{
unsigned char eeprom_value[2] = {5, 0};
unsigned char eeprom_location[2] = {0x00, 0x01};
unsigned char shidu = 0; // 湿度
unsigned char i = 0;
bit mode = 1; // 模式 0 手动 1 自动
bit mode6 = 0; // 模式 自动模式 调节页面
unsigned char adc_pcf8591_value = 0;
bit buzz_flag = 1; // 警报声
bit guangai = 0; // 是否打开灌溉系统
close_init();
Timer0Init();
SetRTC();
init_pcf8591();
Delay5ms();
// for (i = 0; i < 2; i++)
// {
// Write_EEPROM(eeprom_location[i], eeprom_value[i]);
// Delay500ms();
// }
for (i = 0; i < 2; i++)
{
eeprom_value[i] = Read_EEPROM(eeprom_location[i]);
Delay500ms();
}
shidu = 10 * eeprom_value[0] + eeprom_value[1];
dspbuf[6] = shidu / 10;
dspbuf[7] = shidu % 10;
P2 = 0x80;
P0 = 0xFE;
P2 = 0x00;
while (1)
{
if ((ds1302_flag == 1) && (mode6 == 0))
{
ds1302_flag = 0;
time_ds1302 = ReadRTC();
// 更新时间
dspbuf[0] = time_ds1302[0];
dspbuf[1] = time_ds1302[1];
dspbuf[2] = 11;
dspbuf[3] = time_ds1302[2];
dspbuf[4] = time_ds1302[3];
}
if (key_flag == 1)
{
key_value = read_key();
if (key_value != 0xff)
{
switch (key_value)
{
case 1:
{
mode = !(mode);
if (mode == 0) // 手动模式必须 mode6=0
{
mode6 = 0;
P2 = 0x80;
P0 = 0xFD;
P2 = 0x00;
}
if (mode == 1)
{
mode6 = 0;
P2 = 0x80;
P0 = 0xFE;
P2 = 0x00;
P2 = 0xA0;
P0 = 0x00;
P2 = 0x00; // 关掉蜂鸣器
}
}
break;
case 2:
{
buzz_flag = 1; // 给检测权限
mode6 = !(mode6); // mode=0 mode6=0是打开警报 mode=1 mode6=1 是开启状态
if ((mode == 1) && (mode6 == 1)) // 自动模式下调节S6
{
dspbuf[0] = 11;
dspbuf[1] = 11;
for (i = 2; i < 6; i++)
dspbuf[i] = 10;
dspbuf[6] = shidu / 10;
dspbuf[7] = shidu % 10;
}
if ((mode == 1) && (mode6 == 0))
{
Write_EEPROM(eeprom_location[0], dspbuf[6]);
Delay500ms();
Write_EEPROM(eeprom_location[1], dspbuf[7]);
}
}
break;
case 3:
{
if (mode == 0)
guangai = 1;
if (mode6 == 1)
{
shidu++;
if (shidu >= 100)
shidu = 99;
dspbuf[6] = shidu / 10;
dspbuf[7] = shidu % 10;
}
}
break;
case 4:
{
if (mode == 0)
guangai = 0;
if (mode6 == 1)
{
shidu--;
if (shidu <= 0)
shidu = 1;
dspbuf[6] = shidu / 10;
dspbuf[7] = shidu % 10;
}
}
break;
}
}
}
if ((mode == 0) && (adc_flag == 1) && (guangai == 1))
{
adc_flag = 0;
adc_pcf8591_value = adc_pcf8591(); // 255 -->100
shidu = (adc_pcf8591_value * 99 / (255.0));
dspbuf[6] = shidu / 10;
dspbuf[7] = shidu % 10;
}
if ((mode == 0) && (shidu < 50)) // 湿度检测 低于50 且 mode6模式为开响
{
if ((mode6 == 0) && (buzz_flag == 1))
{
buzz_flag = 0; // 防止鬼影
P2 = 0xA0;
P0 = 0x40;
P2 = 0x00;
}
if ((mode6 == 1))
{
P2 = 0xA0;
P0 = 0x00;
P2 = 0x00;
}
}
if ((mode == 0) && (shidu >= 50))
{
buzz_flag = 1;
P2 = 0xA0;
P0 = 0x00;
P2 = 0x00;
}
}
}
void isr_time_0() interrupt 1
{
static unsigned char ds1302_time = 0;
static unsigned char key_time = 0;
static unsigned char adc_time = 0;
display();
if (++ds1302_time == 200) // 必须设置刷新时间,不然有问题
{
ds1302_time = 0;
ds1302_flag = 1;
}
if (++key_time == 20)
{
key_time = 0;
key_flag = 1;
}
if (++adc_time == 200)
{
adc_time = 0;
adc_flag = 1;
}
}
iic.c
/*
程序说明: IIC总线驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include "reg52.h"
#include "intrins.h"
#define DELAY_TIME 5
#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1
//总线引脚定义
sbit SDA = P2 ^ 1; /* 数据线 */
sbit SCL = P2 ^ 0; /* 时钟线 */
void IIC_Delay(unsigned char i)
{
do
{
_nop_();
} while (i--);
}
//总线启动条件
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 0;
}
//总线停止条件
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//发送应答
void IIC_SendAck(bit ackbit)
{
SCL = 0;
SDA = ackbit; // 0:应答,1:非应答
IIC_Delay(DELAY_TIME);
SCL = 1;
IIC_Delay(DELAY_TIME);
SCL = 0;
SDA = 1;
IIC_Delay(DELAY_TIME);
}
//等待应答
bit IIC_WaitAck(void)
{
bit ackbit;
SCL = 1;
IIC_Delay(DELAY_TIME);
ackbit = SDA;
SCL = 0;
IIC_Delay(DELAY_TIME);
return ackbit;
}
//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
SCL = 0;
IIC_Delay(DELAY_TIME);
if (byt & 0x80)
SDA = 1;
else
SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 1;
byt <<= 1;
IIC_Delay(DELAY_TIME);
}
SCL = 0;
}
//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
unsigned char i, da;
for (i = 0; i < 8; i++)
{
SCL = 1;
IIC_Delay(DELAY_TIME);
da <<= 1;
if (SDA)
da |= 1;
SCL = 0;
IIC_Delay(DELAY_TIME);
}
return da;
}
// add
// AT24C02
// 写数据
void Write_EEPROM(unsigned char addr, unsigned char dat)
{
EA = 0;
IIC_Start();
IIC_SendByte(0xA0); // 看芯片手册Figure 7 -W为低电平有效
IIC_WaitAck();
IIC_SendByte(addr); // 0x00-0xff
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
EA = 1;
}
// 读数据
unsigned char Read_EEPROM(unsigned char addr)
{
unsigned char da;
EA = 0;
IIC_Start();
IIC_SendByte(0xA0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xA1);
IIC_WaitAck();
da = IIC_RecByte();
IIC_SendAck(1); // 非应答,停止读取
IIC_Stop();
EA = 1;
return da;
}
// addd
// PCF8591
void init_pcf8591()
{
EA = 0;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0X03); // 第一通道
IIC_WaitAck();
IIC_Stop();
EA = 1;
}
// 通过I2C总线读取ADC结果
unsigned char adc_pcf8591()
{
unsigned char da;
EA = 0;
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
da = IIC_RecByte();
IIC_SendAck(1);
IIC_Stop();
EA = 1;
return da;
}
iic.h
#ifndef __IIC_H
#define __IIC_H
void IIC_Start(void);
void IIC_Stop(void);
bit IIC_WaitAck(void);
void IIC_SendAck(bit ackbit);
void IIC_SendByte(unsigned char byt);
unsigned char IIC_RecByte(void);
void Write_EEPROM(unsigned char addr, unsigned char dat);
unsigned char Read_EEPROM(unsigned char addr);
void init_pcf8591(); // 初始化pcf8591
unsigned char adc_pcf8591(); // 通过I2C总线读取ADC结果
#endif
ds1302.c
/*
程序说明: DS1302驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台 8051,12MHz
日 期: 2011-8-9
*/
#include <reg52.h>
#include <intrins.h>
sbit SCK = P1 ^ 7;
sbit SDA = P2 ^ 3;
sbit RST = P1 ^ 3; // DS1302复位
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);
}
// 添加的
code unsigned char w_rtc_address[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c}; //秒分时日月周年
code unsigned char r_rtc_address[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
unsigned char set[6] = {0, 8, 3, 0, 0, 0}; // 时间可以自己修改
unsigned char rtc[6];
// 设置时间
void SetRTC(void)
{
Write_Ds1302_Byte(0x8E, 0X00); // WP=0,允许写操作
Write_Ds1302_Byte(w_rtc_address[0], (set[4] << 4) | (set[5])); //秒分时 10进制转16进制 组合起来
Write_Ds1302_Byte(w_rtc_address[1], (set[2] << 4) | (set[3]));
Write_Ds1302_Byte(w_rtc_address[2], (set[0] << 4) | (set[1]));
Write_Ds1302_Byte(0x8E, 0x80); // WP=1,禁止写操作
}
// 读取时间
unsigned char *ReadRTC(void)
{
unsigned char i, *p;
unsigned char tmp[3];
p = (unsigned char *)r_rtc_address; //地址传递
for (i = 0; i < 3; i++)
{
tmp[i] = Read_Ds1302_Byte(*p);
p++;
}
rtc[0] = (tmp[2] >> 4); // 16进制转10进制
rtc[1] = (tmp[2] & 0x0F);
rtc[2] = (tmp[1] >> 4);
rtc[3] = (tmp[1] & 0x0F);
rtc[4] = (tmp[0] >> 4);
rtc[5] = (tmp[0] & 0x0F);
return rtc;
}
ds1302.h
#ifndef __DS1302_H
#define __DS1302_H
// void Write_Ds1302(unsigned char temp);
// void Write_Ds1302_Byte(unsigned char address, unsigned char dat);
// unsigned char Read_Ds1302_Byte(unsigned char address);
// add
void SetRTC(void);
unsigned char *ReadRTC(void); // unsigned char *p_rtc;读取
#endif