1、题目
一、基本要求
使用大赛组委会提供的四梯/国信长天单片机竞赛实训平台,完成本试题的程序设计与调试。程序编写、调试完成后,选手需通过考试系统提交以准考证号命名的hex文件。不符合以上文件提交要求的作品将被评为零分或者被酌情扣分。
硬件设置:
将IAP15F2K61S2单片机内部振荡器频率设定为12MHz。
键盘工作模式跳线J5配置为KBD矩阵按键模式。
扩展方式跳线J13配置为IO模式。
请注意:选手需严格按照以上要求配置竞赛板,编写和调试程序,不符合以上配置要求的作品将被评为零分或者被酌情扣分。
二、硬件框图
图1 系统硬件框图
三、功能描述
3.1 基本功能描述
1)通过获取DS18B20温度传感器的温度数据,完成温度测量功能;
2)通过PCF8591 AD/DA芯片完成环境光测量和DAC输出功能;
3)通过LED指示灯完成试题要求的状态指示功能;
4)通过数码管、按键完成试题要求的数据显示和界面切换功能。
3.2 显示功能
1、模式界面
模式界面如图2所示,显示内容包括模式编号,测量的参数数据。
模式1为温度控制模式,显示内容包括编号‘1’和测量的温度参数。
温度参数单位为℃,保留小数点后1位,固定使用3位数码管显示。
图2.1 温度控制模式
模式2为光照度控制模式,显示内容包括编号‘2’和测量的光照度参数。
光照度参数单位为Lux,保留整数,固定使用3位数码管显示,不足3位时,高位(左侧)数码管熄灭。
图2.3 光照度控制模式
2、输出界面
输出界面如图3所示,由标识符(U)和输出的DAC数据组成。
输出的DAC数据单位为V,保留小数点后1位,固定使用2位数码管显示。
图3 输出界面
3、显示要求
按照题目要求的界面格式和切换方式进行设计。
数码管显示无重影、闪烁、过暗、亮度不均匀等严重影响显示效果的缺陷。
3.3 按键功能
1、功能说明
1)S4:定义为“模式切换”,在模式界面下,按下S4按键,切换2种控制模式,切换顺序如下图所示。
图4 模式切换顺序
2)S5:定义为“界面切换”,按下S5按键,切换模式界面和输出界面,切换顺序如图所示。
图5 模式切换顺序
2、按键要求
按键应做好消抖处理,避免出现一次按键动作导致功能多次触发。
按键动作不影响数码管显示等其他功能。
按键S4仅在“模式界面”有效。
3.4 输出功能
模式1,DAC输出与测量的温度参数有关,温度参数与DAC输出关系如图6.1所示。
图6.1 温度参数与DAC输出关系
模式2,DAC输出与测量的光照度参数有关,光照度参数与DAC输出关系如图6.2所示。
图6.2 光照度参数与DAC输出关系
3.5 LED指示灯功能
1)温度控制模式下,指示灯L1点亮,否则指示灯L1熄灭。
2)光照度控制模式下,指示灯L2点亮,否则指示灯L2熄灭。
其余指示灯均处于熄灭状态。
3.6 初始状态
请严格按照以下要求设计作品的上电初始状态。
处于模式界面,温度控制模式。
2、代码
2.1 main.c
main.c
#include <STC15F2K60S2.H>
#include <onewire.h>
#include <intrins.h>
#include <iic.h>
#include <Init.h>
#include <Key.h>
#include <Seg.h>
#include <Led.h>
/*****************变量定义区********************/
/*按键专用*/
unsigned char Key_Slow_Down;//10ms
unsigned char Key_Down,Key_Up,Key_Val,Key_Old;
/*数码管专用*/
unsigned int Seg_Slow_Down;//500ms
unsigned char Seg_Pos;
unsigned char Seg_Buf[8]={10,10,10,10,10,10,10,10};
unsigned char Seg_Point[8]={0,0,0,0,0,0,0,0};
/*Led专用*/
unsigned char ucLed[8]={0,0,0,0,0,0,0,0};
unsigned char Seg_Disp_Mode;//界面切换 0-模式界面;1-输出界面
/*模式界面*/
unsigned char Seg_Mode;//模式切换 0-模式1;1-模式2
unsigned int temperature_10x;
unsigned char Value;//实时获取光照系数(0-255)
/*输出界面*/
unsigned char Voltage_10x;
/*****************函数处理区********************/
//按键处理函数
void Key_Proc()
{
if(Key_Slow_Down) return;
Key_Slow_Down=1;
Key_Val=Key_Read();
Key_Down=Key_Val&(Key_Old^Key_Val);
Key_Up=~Key_Val&(Key_Old^Key_Val);
Key_Old=Key_Val;
switch(Key_Down)
{
case 4:
if(Seg_Disp_Mode==0)
Seg_Mode=!Seg_Mode;
break;
case 5:
Seg_Disp_Mode=!Seg_Disp_Mode;
break;
}
}
//清除数码管数据
void Clear_Seg(void)
{
unsigned char i;
for(i=0;i<8;i++)
{
Seg_Buf[i]=10;//熄灭
Seg_Point[i]=0;//熄灭
}
}
void Read_Voltage()
{
if(Seg_Mode==0)
{
if(temperature_10x/10>=0&&temperature_10x/10<10)
Voltage_10x=10;
else if(temperature_10x/10>40)
Voltage_10x=50;
else if(temperature_10x/10>=10&&temperature_10x/10<=40)
Voltage_10x=(2*temperature_10x/10 - 5)*10/15;
}
else
{
if(Value>=0&&Value<10)
Voltage_10x=10;
else if(Value>240)
Voltage_10x=50;
else
Voltage_10x=(2*Value+95)*10/115;
}
}
//数码管处理函数
void Seg_Proc()
{
float temperature;//实时获取温度
if(Seg_Slow_Down) return;
Seg_Slow_Down=1;
/*********实时读取数据*********/
temperature=Read_Temperature();
temperature_10x=temperature*10;
//如果是电压:Value/51.0;
Value=AD_Read(0x41);//光敏电阻
//读取电压值
Read_Voltage();
switch(Seg_Disp_Mode)
{
//模式界面
case 0:
switch(Seg_Mode)
{
//模式1:温度控制界面
case 0:
Clear_Seg();
Seg_Buf[0]=Seg_Mode+1;
Seg_Buf[5]=temperature_10x/100%10;
Seg_Buf[6]=temperature_10x/10%10;
Seg_Buf[7]=temperature_10x%10;
Seg_Point[6]=1;
break;
//模式2:光照控制界面
case 1:
Clear_Seg();
Seg_Buf[0]=Seg_Mode+1;
Seg_Buf[5]=Value/100%10>0?Value/100%10:10;
Seg_Buf[6]=((Value/10%10==0)&(Value/100%10==0))?10:Value/10%10;
Seg_Buf[7]=Value%10;
break;
}
break;
//输出界面
case 1:
Clear_Seg();
DA_Write(Voltage_10x*5.1);
Seg_Buf[0]=11;//U
Seg_Buf[6]=Voltage_10x/10;
Seg_Buf[7]=Voltage_10x%10;
Seg_Point[6]=1;
break;
}
}
//Led处理函数
void Led_Proc()
{
ucLed[0]=!Seg_Mode;
ucLed[1]=Seg_Mode;
}
//初始化定时器0
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
}
void Timer0Sever()interrupt 1
{
if(++Key_Slow_Down==10) Key_Slow_Down=0;
if(++Seg_Slow_Down==500) Seg_Slow_Down=0;
if(++Seg_Pos==8) Seg_Pos=0;
Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
}
void Delay750ms() //@12.000MHz
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 35;
j = 51;
k = 182;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//main()函数
void main()
{
Read_Temperature();
Delay750ms();
Timer0Init();
System_Init();
while(1)
{
Key_Proc();
Seg_Proc();
Led_Proc();
}
}
2.2 模块代码
iic.c
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <iic.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 AD_Read(unsigned char addr)
{
unsigned char temp;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
void DA_Write(unsigned char dat)
{
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x43);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
onewire.c
/* # 单总线代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <onewire.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_Temperature()
{
unsigned char high,low;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
low=Read_DS18B20();
high=Read_DS18B20();
return ((high<<8)|low)/16.0;
}
3、后话
代码可能有BUG,欢迎大家一起交流