第16届蓝桥杯单片机4T模拟赛三
本次模拟赛涉及的模块:基础三件套(Led&Relay,按键、数码管)+ 进阶单件套(pcf8591的AD模块)
附件:
各模块底层代码在文章的结尾
一、数码管部分
1.页面1
页面1要显示的格式是:
最左边的数码管显示字母C
,最后两位显示湿度的十位和个位(不足两位十位熄灭)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
C | 3 | 0 |
湿度是由pcf8591的AD模块读取通道3的电压值转换来的,他们的关系是(从图转换成函数)
h
u
m
i
d
i
t
y
=
{
10
,
U
≤
1
0.266
∗
U
−
16.666
,
1
≤
U
≤
4
90
,
U
≥
4
humidity = \begin{cases} 10,U\le1\\ 0.266*U-16.666,\,\,1\le U \le 4\\ 90,U\ge 4\\ \end{cases}
humidity=⎩
⎨
⎧10,U≤10.266∗U−16.666,1≤U≤490,U≥4
所以在AD的处理函数就可以先将通道3的电压读出来后直接进行湿度转换。下面给出的是用float型变量接收未放大的电压值。
pcf8591.c
底层代码
#include <STC15F2K60S2.H>
#include <intrins.h>
#define DELAY_TIME 5
sbit scl = P2^0;
sbit sda = P2^1;
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_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);
}
unsigned char ADRead()
{
unsigned char temp = 0;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x03);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
main.c
中调用
/*湿度*/
idata u8 humidity;//电压转换湿度
void ADProc()
{
float RB2 = ADRead() / 51.0;//读取RB2的电压
//湿度转换
if(RB2 <= 1.0)
humidity = 10;
else if(RB2 >= 4.0)
humidity = 90;
else
humidity = 80/3.0*RB2+10-80/3.0;
}
void SegProc()
由于数码管缓存区初始化时设置为8个数码管均熄灭,所以只需对索引为0,6,7的数码管赋值即可。
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};
void SegProc()
{
switch(SegMode)
{
case 0:
SegBuf[0] = 11;//C
SegBuf[6] = humidity / 10 ? humidity / 10 : 10;
SegBuf[7] = humidity % 10;
break;
}
}
void Timer0_Isr(void) interrupt 1
{
if(++SegPos == 8)
SegPos = 0;
SegDisp(SegPos, SegBuf[SegPos]);
}
2.页面2
页面2要显示的格式是:
最左边的数码管显示字母E
,最后两位显示湿度参数的十位和个位(不足两位十位熄灭)
湿度参数默认值为50
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
E | 5 | 0 |
这个就特别简单了,页面1的代码直接复制下来就可以了,唯一要改的地方就是页面2的显示要对湿度参数的十位进行判断,为0时熄灭。
idata u8 humidity_set = 50;
void SegProc()
{
switch(SegMode)
{
case 1:
SegBuf[0] = 12;//E
SegBuf[6] = humidity_set / 10 ? humidity_set / 10 : 10;
SegBuf[7] = humidity_set % 10;
break;
}
}
3. 页面3
页面3要显示的格式是:
最左边的数码管显示字母H
,最后两位显示时间间隔的十位和个位(不足两位十位熄灭)
时间间隔默认值为3。
时间间隔是继电器点亮的间隔,跟数码管没关系,直接显示即可。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
E | 3 |
idata u8 time = 3;//时间间隔
void SegProc()
{
switch(SegMode)
{
case 2:
SegBuf[0] = 13;//H
SegBuf[6] = time / 10 ? time / 10 : 10;
SegBuf[7] = time % 10;
break;
}
}
二、按键部分
按键只用到了按键S4、S5、S8、S9。
按键S4是切换页面,实现也很简单。
按键5是继电器启动标志位,在任何界面按一次标志位取反。
idata bit relay_work; //继电器工作 0-停止 1-开始
void KeyProc()
{
KeyVal = KeyDisp();
KeyDown = KeyVal & ~KeyOld;
KeyUp = ~KeyVal & KeyOld;
KeyOld = KeyVal;
switch(KeyDown)
{
case 4://页面流转
if(++SegMode == 3)
SegMode = 0;
break;
case 5://切换继电器工作模式
relay_work = !relay_work;
break;
case 8://参数-
if(SegMode == 1)
{
humidity_set -= 5;
if(humidity_set == 25)
humidity_set = 90;
}
else if(SegMode == 2)
{
if(--time == 0)
time = 10;
}
break;
case 9://参数+
if(SegMode == 1)
{
humidity_set += 5;
if(humidity_set == 95)
humidity_set = 30;
}
else if(SegMode == 2)
{
if(++time == 11)
time = 1;
}
break;
}
}
三、继电器部分
第一次写继电器的时候理解错了,题目也没有明确提到,根据第一次提交的测评分析这道题目的意思是继电器工作模式下触发吸合条件后继电器吸合,经过一定时间间隔后断开,持续处于该状态不重复吸合,直到经过一次湿度>湿度参数后再次触发条件(湿度<湿度参数),继电器才吸合。
idata bit relay_work; //继电器工作 0-停止 1-开始
idata bit relay_flag; //继电器使能标志位
idata u16 TimeCount; //定时器计时变量
idata bit humidity_flag; //0-湿度值不小于参数
idata bit humidity_has_flag;//0-继电器未重复吸合 1-继电器已重复吸合过
void LedProc()
{
/*Relay*/
if(!relay_work)//继电器不工作
{
relay_flag = 0;//关闭继电器
humidity_has_flag = 0;//继电器重复吸合标志位清零
}
else//继电器工作
{
humidity_flag = (humidity < humidity_set);//如果湿度小于湿度参数,humidity_flag为真
if(!humidity_flag)//如果湿度大于湿度参数
humidity_has_flag = 0;//重复吸合标志位清零
if(humidity_flag && !humidity_has_flag)//如果湿度小于湿度参数并且还未触发继电器吸合
{
humidity_has_flag = 1;//继电器已经吸合过了
relay_flag = 1;//继电器开始工作
}
}
Relay(relay_flag);
}
void Timer0_Isr(void) interrupt 1
{
//如果处于继电器工作模式下并且继电器开始工作时计时
if(relay_work && relay_flag)
{
if(++TimeCount == time*1000)//吸合后达到一定时间间隔断开继电器
{
TimeCount = 0;//计时变量清零
relay_flag = 0;//继电器不工作
}
}
}
四、代码整合(改一下main函数可以运行)
Init.c
#include <STC15F2K60S2.H>
void SystemInit()
{
P0 = 0xff;
P2 = P2 & 0x1f | 0x80;
P2 &= 0x1f;
P0 = 0x00;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
}
Led.h
#include <STC15F2K60S2.H>
void LedDisp(unsigned char *ucLed)
{
unsigned char i, temp = 0x00;
static unsigned char temp_old = 0xff;
for(i = 0; i < 8; i++)
temp |= (ucLed[i] << i);
if(temp != temp_old)
{
P0 = ~temp;
P2 = P2 & 0x1f | 0x80;
P2 &= 0x1f;
temp_old = temp;
}
}
void Relay(bit flag)
{
unsigned char temp = 0x00;
static unsigned char temp_old = 0xff;
if(flag)
temp |= 0x10;
else
temp &= 0xef;
if(temp != temp_old)
{
P0 = temp;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old = temp;
}
}
Key.h
#include <STC15F2K60S2.H>
unsigned char KeyDisp()
{
unsigned char temp = 0x00;
P44 = 0;
P42 = 1;
P35 = 1;
P34 = 1;
if(P32 == 0) temp = 5;
if(P33 == 0) temp = 4;
P44 = 1;
P42 = 0;
P35 = 1;
P34 = 1;
if(P32 == 0) temp = 9;
if(P33 == 0) temp = 8;
return temp;
}
Seg.c
#include <STC15F2K60S2.H>
code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0xff, //空
0xc6, //C
0x86, //E
0x89 //H
};
void SegDisp(unsigned char wela, unsigned char dula)
{
P0 = 0xff;
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
P0 = (0x01 << wela);
P2 = P2 & 0x1f | 0xc0;
P2 &= 0x1f;
P0 = Seg_Table[dula];
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
}
pcf8591.c
#include <STC15F2K60S2.H>
#include <intrins.h>
#define DELAY_TIME 5
sbit scl = P2^0;
sbit sda = P2^1;
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_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);
}
unsigned char ADRead()
{
unsigned char temp = 0;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x03);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
main.c
#include <STC15F2K60S2.H>
#include "Init.h"
#include "Led.h"
#include "Key.h"
#include "Seg.h"
#include "pcf8591.h"
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long int u32;
/*按键*/
idata u8 KeyVal, KeyDown, KeyUp, KeyOld;
/*数码管*/
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};
idata u8 SegMode;
/*指示灯*/
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};
idata bit relay_work; //继电器工作 0-停止 1-开始
idata bit relay_flag; //继电器使能标志位
idata u16 TimeCount; //定时器计时变量
/*湿度*/
idata u8 humidity;
idata u8 humidity_set = 50;
idata u8 time = 3;
idata bit humidity_flag; //0-湿度值不小于参数
idata bit humidity_has_flag;
void KeyProc()
{
KeyVal = KeyDisp();
KeyDown = KeyVal & ~KeyOld;
KeyUp = ~KeyVal & KeyOld;
KeyOld = KeyVal;
switch(KeyDown)
{
case 4:
if(++SegMode == 3)
SegMode = 0;
break;
case 5:
relay_work = !relay_work;
break;
case 8:
if(SegMode == 1)
{
humidity_set -= 5;
if(humidity_set == 25)
humidity_set = 90;
}
else if(SegMode == 2)
{
if(--time == 0)
time = 10;
}
break;
case 9:
if(SegMode == 1)
{
humidity_set += 5;
if(humidity_set == 95)
humidity_set = 30;
}
else if(SegMode == 2)
{
if(++time == 11)
time = 1;
}
break;
}
}
void SegProc()
{
switch(SegMode)
{
case 0:
SegBuf[0] = 11;//C
SegBuf[6] = humidity / 10 ? humidity / 10 : 10;
SegBuf[7] = humidity % 10;
break;
case 1:
SegBuf[0] = 12;//E
SegBuf[6] = humidity_set / 10 ? humidity_set / 10 : 10;
SegBuf[7] = humidity_set % 10;
break;
case 2:
SegBuf[0] = 13;//H
SegBuf[6] = time / 10 ? time / 10 : 10;
SegBuf[7] = time % 10;
break;
}
}
void LedProc()
{
u8 i;
/*Relay*/
if(!relay_work)
{
relay_flag = 0;
humidity_has_flag = 0;
}
else
{
humidity_flag = (humidity < humidity_set);
if(!humidity_flag)
humidity_has_flag = 0;
if(humidity_flag && !humidity_has_flag)
{
humidity_has_flag = 1;
relay_flag = 1;
}
}
Relay(relay_flag);
/*Led*/
for(i = 0; i < 3; i++)
ucLed[i] = (i == SegMode);
ucLed[3] = relay_work;
LedDisp(ucLed);
}
void ADProc()
{
float RB2 = ADRead() / 51.0;
if(RB2 <= 1.0)
humidity = 10;
else if(RB2 >= 4.0)
humidity = 90;
else
humidity = 80/3.0*RB2+10-80/3.0;
}
void Timer0_Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
EA = 1;
}
void Timer0_Isr(void) interrupt 1
{
if(++SegPos == 8) SegPos = 0;
SegDisp(SegPos, SegBuf[SegPos]);
if(relay_work && relay_flag)
{
if(++TimeCount == time*1000)
{
TimeCount = 0;
relay_flag = 0;
}
}
}
void main()
{
SystemInit();
Timer0_Init();
while(1)
{
//...
}
}