下面记录一下自己这届省赛比赛时的思路,不太会写作文,比较口语化。而且一些看法仅仅是我个人观点,赛后我还没有看过任何讲解或例程,可能会有很多理解不对的地方希望大家能够指出一起交流
一:硬件框图
往届省赛基本上都是考两个外设,这次一看硬件框图就知道难度提高了。先由硬件框图分析:
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届国赛写的,感觉比这次省赛写的好....)后悔考前没回顾一下自己练习的题了hhh
5. 采集触发后切换到温湿度显示,3s后自动切换回原来的状态:
在切换到温度显示之前需要对数码管显示模式进行记录,然后计时到3s后再切换回来
6. 还有一个问题,采集湿度,即测量频率需要1s时间,所以可能采集触发开始时测量时间从0开始,那么触发发生1s后数据才得到更新,所以我对采集湿度和温度分别设了一个标志位,就不会在只有一个采集完时就判断为采集完成了。(而我这份比赛时写的代码中有个缺点就是为了方便,标志位设的太多了,自己看着都有点晕)。
7. LED功能每个灯不要互相影响,对P0口赋值时注意一点即可。
三:对自己这次比赛的小总结:
1. 客观题没预料到考这么难,没复习过模电数电,基本上都是靠蒙,错了很多个。
2. 个人感觉程序设计题难度其实是不如12届国赛的,我另一篇博客中也记录了一下12国赛我用三个半小时做完,自己感觉是无bug的。但是这个省赛五个小时有四个半小时在写程序,我还是觉得写的代码逻辑比较乱,而且自己暂时发现有两个bug。
趁刚考完还没忘记题目,简单写下博客记录一下。
四. 我的代码 (bug没有修改,考完也不想看了,仅作记录), 下面只给出.c文件,我懒,没有做模块化:
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;
}
}
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);//打开写保护
}