记录一下自己这届省赛比赛时的思路。
一:硬件框图
往届省赛基本上都是考两个外设,这次一看硬件框图就知道难度提高了。先由硬件框图分析:
1. NE555需要单独占用一个定时器0,作为计数器来计脉冲;还需要一个定时器来做各种定时工作,比如多久采集一次数据或者数码管消隐,由于要用于数码管消隐,所以将第二个定时器设为1ms即,采集数据如果需要100ms就定义一个变量在该定时器中断里++就可以了。
2. NE555测量脉冲数用到P34引脚,与矩阵键盘中的最右边一列有引脚冲突,所以在扫描矩阵键盘时需要注意不能影响到P34引脚(后面题目中也只用了S4,S5,S8,S9,所以只要不扫描最后一列即可)。
下面对不常见的功能部分进行分析。
二:功能分析
1. PCF8591采集光敏电阻的电压值,且能够检测“亮”和“暗”两种状态:
采集电压是常见的,就那一小段代码。而亮和暗如何界定,可以自己先把电压值显示在数码管上,遮挡住光敏电阻看显示的值是多少(我测了是三十多或四十多,属于我将电压值低于50设为暗状态),这个可以自己试一试,很简单。
2. 将NE555测得的频率转为环境湿度数据:
由图二很容易知道是线性关系,y=kx+b,代入两个频率和湿度就能算出来k和b是多少,最好在求出来后再把频率=200代入算一下湿度是否为10,因为求出来的kb并不是整数,舍弃后面一些小数后会有误差。
3. 数码管显示功能:
听说很多人没完全实现。其实看起来很复杂,实际上可以看作只是一个模式下有一个子模式而已,比如mode_1这变量表示时间界面,回显界面,参数界面,温湿度界面;而mode_2可以作为回显界面下的温度回显,湿度回显和时间回显。写在代码里就是一个嵌套if判断而已。
4. 由“亮”变“暗”触发采集功能:
这个功能好像是在12届国赛里出现过类似的,其实就是一个下降沿触发(注意是单次触发),比赛时写的逻辑不太清晰,可以按照下面这段代码实现(这是我之前练习12届国赛写的,感觉比这次省赛写的好....)
5. 采集触发后切换到温湿度显示,3s后自动切换回原来的状态:
在切换到温度显示之前需要对数码管显示模式进行记录,然后计时到3s后再切换回来
6. 还有一个问题,采集湿度,即测量频率需要1s时间,所以可能采集触发开始时测量时间从0开始,那么触发发生1s后数据才得到更新,所以我对采集湿度和温度分别设了一个标志位,就不会在只有一个采集完时就判断为采集完成了。(而我这份比赛时写的代码中有个缺点就是为了方便,标志位设的太多了,自己看着都有点晕)。
7. LED功能每个灯不要互相影响,对P0口赋值时注意一点即可。
三:对自己这次比赛的小总结:
1. 客观题没预料到考这么难,没复习过模电数电,基本上都是靠蒙,错了很多个。
2. 个人感觉程序设计题难度其实是不如12届国赛的,我另一篇博客中也记录了一下12国赛我用三个半小时做完,自己感觉是无bug的。但是这个省赛五个小时有四个半小时在写程序,我还是觉得写的代码逻辑比较乱,而且自己暂时发现有两个bug。
趁刚考完还没忘记题目,简单写下博客记录一下。
四. 我的代码 (bug没有修改), 没有模块化:
main.c
#include <STC15F2K60S2.H>
#include "iic.h"
#include "onewire.h"
#include "ds1302.h"
#include <intrins.h>
//BCD转十进制
#define BCDToInt(bcd) (bcd/16*10)+(bcd%16);
//LED
typedef struct
{
unsigned char b1:1;
unsigned char b2:1;
unsigned char b3:1;
unsigned char b4:1;
unsigned char b5:1;
unsigned char b6:1;
unsigned char b7:1;
unsigned char b8:1;
}Bit;
typedef union
{
unsigned char Hex;
Bit B;
}HexToBit;
HexToBit led_control;
bit flag_3s=0;
code unsigned char Seg_Table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88};
unsigned char smg[8];
//时间 秒 分 时
unsigned char time[]={0x05,0x03,0x13};
unsigned char sec,min,hour;
//HC573操作 io口
void Device_Process(unsigned char p2dat,unsigned char p0dat)
{
P0 = p0dat;
P2 = (P2&0x1f)|p2dat;
P2 = P2&0x1f;
}
//----------定时器-------------
//定时器2
void Timer2Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x04; //定时器时钟1T模式
T2L = 0x20; //设置定时初值
T2H = 0xD1; //设置定时初值
AUXR |= 0x10; //定时器2开始计时
EA=1;
IE2 |= 0x04;
}
//计数器0 NE555
void Timer0Init(void) //65535微秒@12.000MHz
{
TMOD |= 0x05; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x00; //设置定时初值
TR0 = 1; //定时器0开始计时
}
//---------触发(光敏电阻)-----------
unsigned char rd1;
unsigned char cnt_rd1;
bit flag_rd1=0; //亮是0 暗是1触发 下降沿
bit flag_getsd=0;
bit flag_gettp=0;
unsigned char pre_hour,pre_min; //上次触发时间
unsigned char cnt_chufa;
unsigned int cnt_3s; //三秒显示
void vRead_rd1()
{
if(cnt_rd1>=10)
{
cnt_rd1=0;
rd1=Read_rd1();
if((rd1<50)&&(flag_rd1==0))//flag置1后下次这个if一定不满足,所以单次触发
{
flag_rd1=1; //暗
flag_getsd=1;
flag_gettp=1;
cnt_chufa++;
pre_hour =hour;
pre_min = min;
flag_3s=1;
}
if(rd1>100) flag_rd1=0;
}
}
//-----------参数设置----------------
unsigned char temp_set=25;
//---------采集时间---------
//1.时间采集
unsigned char cnt_time;
void vRead_time()
{
if(cnt_time>=100)
{
cnt_time=0;
sec = BCDToInt(Read_Ds1302_Byte(0x81));
min = BCDToInt(Read_Ds1302_Byte(0x83));
hour = BCDToInt(Read_Ds1302_Byte(0x85));
}
}
//2.温度采集
unsigned char temp;
unsigned int temp_mean=0; //平均温度
unsigned char max_temp=0; //最大温度
unsigned char cnt_temp;
unsigned char last_tp;//上一次
unsigned char led_6=0;
unsigned char ca;
bit flag_tp=0; //温度是否有效
bit flash_led=0;//led闪烁
void vRead_temp()
{
if(cnt_temp>=100)
{
cnt_temp=0;
last_tp=ca;
ca=Read_tempture();
if(flag_gettp==1)
{
if(cnt_chufa>=1)
{
}
temp = ca;
if(temp>last_tp) led_6++;
if(temp>temp_set)//报警
{
flash_led=1;
}
else if(temp<temp_set)
{
led_control.B.b4=1;
flash_led=0;
}
if((temp>0)&&(temp<99)) //有效
{
flag_tp=1;
}
else flag_tp=0;
if(temp>max_temp) max_temp=temp;//最大温度记录更新
temp_mean = (temp*10+temp_mean*(cnt_chufa-1))/cnt_chufa;
flag_gettp=0;
}
}
}
//3.湿度采集 NE555频率
unsigned int freq;
unsigned int cnt_freq; //计时一秒
unsigned char ShiDu; //湿度
unsigned char max_sd=0; //最大值湿度
unsigned int mean_sd=0; //均值湿度
unsigned int ShiDu_mean; //湿度平均值
unsigned char last_sd;
bit flag_sd=1; //湿度有效标志位
bit flag_led5=0;
void vNE555_Process()
{
if(cnt_freq>=1000)
{
cnt_freq=0;
if(flag_getsd==1)
{
if(cnt_chufa>=1)
{
last_sd=freq;
}
freq = (TH0<<8)|TL0;
if(freq>last_sd) led_6++;
if((freq>200) && (freq<2000))//有效范围内
{
ShiDu = 0.044*freq+1.2;
flag_sd=1;
flag_led5=0;
if(ShiDu>max_sd) max_sd=ShiDu;
mean_sd = ((mean_sd*(cnt_chufa-1)+ShiDu*10))/cnt_chufa; //均值有问题
}
else
{
flag_led5=1;
flag_sd=0; //字符A
}
flag_getsd=0;
}
TH0=0;TL0=0;
}
}
//----------数码管-------------
//数码管操作刷新
unsigned char mode_1=0; // 时间,回显,参数,温湿度
unsigned char mode_2=0; //回显->温度 适度 时间
void SMG_Process()
{
if(mode_1==0)//时间界面
{
smg[0] = Seg_Table[hour/10];
smg[1] = Seg_Table[hour%10];
smg[2] = 0xbf;
smg[3] = Seg_Table[min/10];
smg[4] = Seg_Table[min%10];
smg[5] = 0xbf;
smg[6] = Seg_Table[sec/10];
smg[7] = Seg_Table[sec%10];
}
if(mode_1==1)//回显
{
if(mode_2==0)//温度回显
{
smg[0] = 0xc6;
smg[1] = 0xff;
if(cnt_chufa==0)
{
smg[2]=0xff;
smg[3]=0xff;
smg[4]=0xff;
smg[5]=0xff;
smg[6]=0xff;
smg[7]=0xff;
}
else
{
smg[2] = Seg_Table[max_temp/10];
smg[3] = Seg_Table[max_temp%10];
smg[4] = 0xbf;
smg[5] = Seg_Table[temp_mean/100];
smg[6] = Seg_Table[temp_mean/10%10]&0x7f;
smg[7] = Seg_Table[temp_mean%10];
}
}
else if(mode_2==1)//湿度回显
{
smg[0] = 0x89;
smg[1] = 0xff;
if(cnt_chufa==0)
{
smg[2]=0xff;
smg[3]=0xff;
smg[4]=0xff;
smg[5]=0xff;
smg[6]=0xff;
smg[7]=0xff;
}
else
{
smg[2] = Seg_Table[max_sd/10];
smg[3] = Seg_Table[max_sd%10];
smg[4] = 0xbf;
smg[5] = Seg_Table[mean_sd/100];
smg[6] = Seg_Table[mean_sd/10%10]&0x7f;
smg[7] = Seg_Table[mean_sd%10];
}
}
else if(mode_2==2)//时间回显
{
smg[0] = 0x8e;
smg[1] = Seg_Table[cnt_chufa/10];
smg[2] = Seg_Table[cnt_chufa%10];
if(cnt_chufa==0)
{
smg[3]=0xff;
smg[4]=0xff;
smg[5]=0xff;
smg[6]=0xff;
}
else
{
smg[3] = Seg_Table[pre_hour/10];
smg[4] = Seg_Table[pre_hour%10];
smg[5] = 0xbf;
smg[6] = Seg_Table[pre_min/10];
smg[7] = Seg_Table[pre_min%10];
}
}
}
if(mode_1==2)//参数界面
{
smg[0] = 0x8c;
smg[1] = 0xff;
smg[2] = 0xff;
smg[3] = 0xff;
smg[4] = 0xff;
smg[5] = 0xff;
smg[6] = Seg_Table[temp_set/10];
smg[7] = Seg_Table[temp_set%10];
}
if(mode_1==3)//温湿度显示
{
smg[0] = 0x86;
smg[1] = 0xff;
smg[2] = 0xff;
if(flag_tp==1)
{
smg[3] = Seg_Table[temp/10];
smg[4] = Seg_Table[temp%10];
}
else
{
smg[3] = 0xff;
smg[4] = 0xff;
}
smg[5] = 0xbf;
if(flag_sd==1)
{
smg[6] = Seg_Table[ShiDu/10];
smg[7] = Seg_Table[ShiDu%10];
}
else
{
smg[6]=0x88;
smg[7]=0x88;
}
// smg[0] = 0x86;测试NE555
// smg[1] = Seg_Table[freq/10000];
// smg[2] = Seg_Table[freq/1000%10];
// smg[3] = Seg_Table[freq/100%10];
// smg[4] = Seg_Table[freq/10%10];
// smg[5] = Seg_Table[freq%10];
// if(flag_sd==1)
// {
// smg[6] = Seg_Table[ShiDu/10];
// smg[7] = Seg_Table[ShiDu%10];
// }
// else
// {
// smg[6]=0x88;
// smg[7]=0x88;
// }
}
}
//数码管显示
void vSMG_Show()
{
static unsigned char i;
Device_Process(0xc0, 0x00);
Device_Process(0xe0, smg[i]);
Device_Process(0xc0, 0x01<<i);
i = (i+1)%8;
}
//-----------按键功能---------------
unsigned char vKey_Scan()
{
unsigned char key_io=0xff;
P32=0;P33=0;
P42=1;P44=1;
if(P44==0) key_io=0x70;
if(P42==0) key_io=0xb0;
P32=1;P33=1;
P42=0;P44=0;
if(P33==0) key_io=key_io|0x07;
if(P32==0) key_io=key_io|0x0b;
return key_io;
}
//3hang
unsigned char Trg,Cont;
void Three_key()
{
unsigned char kio;
unsigned char ReadData;
kio=vKey_Scan();
ReadData = kio^0xff;
Trg = ReadData&(ReadData^Cont);
Cont = ReadData;
}
//按键操作
unsigned char cnt_key;
unsigned int cnt_k2s;//长按2s
bit flag_longkey=0;
void vKey_Process()
{
if(cnt_key>=10)
{
cnt_key=0;
Three_key();
if(Trg==0x88) //S4
{
mode_1=(mode_1+1)%3;
if(mode_1==1)mode_2=0; //温度回显
}
if(Trg==0x84) //S5
{
if(mode_1==1)
{
mode_2=(mode_2+1)%3;
}
}
if(Trg==0x48) //S8
{
if(mode_1==2)
{
temp_set++;
}
}
if(Trg==0x44) //S9
{
if(mode_1==2)
{
temp_set--;
}
}
if(Cont==0x44)
{
flag_longkey=1;
}
else if((Cont==0)&&(flag_longkey==1))
{
if((mode_1==1)&&(mode_2==2))
{
if(cnt_k2s>=2000)
{
cnt_k2s=0;
flag_longkey=0;
cnt_chufa=0;
max_sd=0;
mean_sd=0;
temp_mean=0;
max_temp=0;
pre_hour=0;
pre_min=0;
}
}
}
}
}
//-----------LED控制--------------
void LED_Process()
{
if(mode_1==0)
{
led_control.B.b1=0;
led_control.B.b2=1;
led_control.B.b3=1;
}
else if(mode_1==1)
{
led_control.B.b1=1;
led_control.B.b2=0;
led_control.B.b3=1;
}
else if(mode_1==2)
{
led_control.B.b1=1;
led_control.B.b2=1;
led_control.B.b3=1;
}
else if(mode_1==3)
{
led_control.B.b1=1;
led_control.B.b2=1;
led_control.B.b3=0;
}
if(flag_led5==1)
{
led_control.B.b5=0;
}
if(led_6>0)
{
if(led_6==2)
{
led_6=0;
led_control.B.b6=0;
}
else
{
led_6=0;
led_control.B.b6=1;
}
}
else
{
led_control.B.b6=1;
}
Device_Process(0x80,led_control.Hex);
}
void Delay100ms() //@12.000MHz
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 5;
j = 144;
k = 71;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//系统初始化
void System_init()
{
led_control.Hex=0xff;
Read_tempture();
Delay100ms();
Write_time(time[0],time[1],time[2]);
Device_Process(0x80,0xff);
Device_Process(0xa0,0x00);
}
void main()
{
System_init();
Timer2Init();
Timer0Init();
while(1)
{
SMG_Process();
vRead_time();
vRead_temp();
vNE555_Process();
vRead_rd1();
vKey_Process();
}
}
//---------中断服务函数----------
unsigned char last_state1;
unsigned char cnt_flash;
void Timer2_service() interrupt 12
{
cnt_time++;
cnt_temp++;
cnt_rd1++;
cnt_freq++;
cnt_key++;
if(flag_longkey==1)cnt_k2s++;
if(flag_3s==1)
{
if(cnt_3s==0)
{
last_state1=mode_1;
mode_1=3;
}
cnt_3s++;
}
if(cnt_3s>=3000)
{
cnt_3s=0;
flag_3s=0;
mode_1=last_state1;
}
if(flash_led==1)
{
cnt_flash++;
if(cnt_flash<100)
{
led_control.B.b4=1;
}
else if(cnt_flash<200)
{
led_control.B.b4=0;
}
else
{
cnt_flash=0;
}
Device_Process(0x80,led_control.Hex);
}
vSMG_Show();
LED_Process();
}
IIC.C
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <reg52.h>
#include <intrins.h>
#define DELAY_TIME 5
sbit sda=P2^1;
sbit scl=P2^0;
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 Read_rd1()
{
unsigned char dat;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x01);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
dat = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return dat;
}
onewire.c
/* # 单总线代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
//
#include <reg52.h>
sbit DQ=P1^4;
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
//
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);
}
//
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;
}
//
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;
}
//读取温度
float Read_tempture()
{
float temp;
unsigned char LSB, MSB;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xBE);
LSB = Read_DS18B20();
MSB = Read_DS18B20();
temp = ((MSB<<8)|LSB)*0.0625;
return temp;
}
ds1302.c
/* # DS1302代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
//
#include <reg52.h>
#include <intrins.h>
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST=P1^3;
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);
}
//写入时间
void Write_time(unsigned char sec,unsigned char min, unsigned char hour)
{
Write_Ds1302_Byte(0x8e,0x00); //关闭写保护
Write_Ds1302_Byte(0x80,sec);
Write_Ds1302_Byte(0x82,min);
Write_Ds1302_Byte(0x84,hour);
Write_Ds1302_Byte(0x8e,0x80);//打开写保护
}