一、题目
二、难点剖析
1.计数值触发条件
思路分析:由图可知计数值加1的条件是:Vain3在下降沿时等于参数值Vp,所以在程序中定义了一个Vain3_old去记录上一个Vain3的值,通过判断Vain3_old>Vp 并且 Vain3 <=Vp实现计数值的加1了。代码实现如下:
//计数功能
if((vain3 <= temp_v[0]*10) && (vain3_old>temp_v[0]*10))
count++;
vain3_old = vain3 ;
2.无效按键状态的判断
通过 switch
语句对按键值 Key_down
进行判断,当按键为无效按键时,没有与之匹配的case语句,所以则用default语句去处理无效按键,值得注意的是:当吗没有按键按下时,Key_down=0,所以得将这种情况排除掉,只记录当Key_down
不为0的状态,无效按键的计数值才加1,且只要有其他按键按下,无效按键的计数值就置0。
switch(Key_down)
{
case 12:
if(++Smg_mode == 3) Smg_mode = 0;
if(Smg_mode == 2)
{
temp_v[0] = temp_v1;
EEPROM_write(temp_v,0,1);
}
no_key = 0;
break;
case 13:
if(Smg_mode == 2){
count = 0;
}
no_key = 0;
break;
case 16: //参数电压加0.5,扩大100倍,便于处理
if(Smg_mode == 1){
if(temp_v1 == 50) temp_v1 = 0;
else
temp_v1 = temp_v1 + 5;
}
no_key = 0;
break;
case 17: //参数电压减0.5
if(Smg_mode == 1){
if(temp_v1 == 0) temp_v1 = 50;
else
temp_v1 = temp_v1 - 5;
}
no_key = 0;
break;
default:if(Key_down != 0) no_key++; //当没有按键按下时,Key_down为0,所以排除0值
}
三.参考程序
1.main.c
/*头文件声明区*/
#include <STC15F2K60S2.H>
#include <key.H>
#include <seg.H>
#include <iic.H>
/*变量声明区*/
unsigned char Smg_slow_down,Key_slow_down;//数码管、扫描按键减速变量
unsigned char Key_up,Key_down,Key_old,Key_val;
unsigned char Smg_pos,Smg_mode = 0; //数码管扫描变量,数码管模式变量
unsigned char Smg_buff[8] = {10,10,10,10,10,10,10,10};
unsigned char Smg_point_buff[8] = {0,0,0,0,0,0,0,0};
unsigned char Led_buff[8] = {0,0,0,0,0,0,0,0};
unsigned int vain3 = 0; //读取滑动变阻器的值,为实际值的100倍
unsigned int vain3_old = 0;
unsigned char temp_v1 = 10;//电压参数
unsigned int temp_vdsip = 100;//数码管显示电压参数
unsigned int count = 0; //计数值变量
unsigned char temp_v[] = {10};//电压参数
unsigned int Led_5s = 0;//VAIN3<参数5s时,L1亮
bit enable_5s = 0;//使能5s计数器
unsigned char no_key = 0;//无效按键计数参数
/*按键处理函数*/
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 12:
if(++Smg_mode == 3) Smg_mode = 0;
if(Smg_mode == 2)
{
temp_v[0] = temp_v1;
EEPROM_write(temp_v,0,1);
}
no_key = 0;
break;
case 13:
if(Smg_mode == 2){
count = 0;
}
no_key = 0;
break;
case 16: //参数电压加0.5,扩大100倍,便于处理
if(Smg_mode == 1){
if(temp_v1 == 50) temp_v1 = 0;
else
temp_v1 = temp_v1 + 5;
}
no_key = 0;
break;
case 17: //参数电压减0.5
if(Smg_mode == 1){
if(temp_v1 == 0) temp_v1 = 50;
else
temp_v1 = temp_v1 - 5;
}
no_key = 0;
break;
default:if(Key_down != 0) no_key++; //当没有按键按下时,Key_down为0,所以排除0值
}
}
/*信息处理函数*/
void Smg_proc()
{
if(Smg_slow_down) return;
Smg_slow_down = 1;
//信息读取区域
vain3 = PCF8591_ADC();
/*数据显示区域*/
switch(Smg_mode){
case 0:
Smg_buff[0] = 18;
Smg_buff[5] = vain3/100;
Smg_buff[6] = (vain3/10)%10;
Smg_buff[7] = vain3%10;
Smg_point_buff[5] = 1;
break;
case 1:
Smg_buff[0] = 17;
temp_vdsip = temp_v1*10;
Smg_buff[5] = temp_vdsip/100;
Smg_buff[6] = (temp_vdsip/10)%10;
Smg_buff[7] = temp_vdsip%10;
Smg_point_buff[5] = 1;
break;
case 2:
Smg_buff[0] = 19;
Smg_buff[5] = 10;
//当计数值高位为0时,熄灭
if(count/10 == 0) Smg_buff[6] = 10;
else Smg_buff[6] = count/10;
Smg_buff[7] = count%10;
Smg_point_buff[5] =0;
break;
}
}
/*其他处理函数*/
void Led_proc()
{
// EEPROM_write(temp_v,0,1);
//计数功能
if((vain3 <= temp_v[0]*10) && (vain3_old>temp_v[0]*10))
count++;
vain3_old = vain3 ;
//L1:实时电压小于参数超过5s,L1点亮
if(vain3<temp_v[0]*10)
enable_5s = 1;
else{
enable_5s = 0;
Led_buff[0] = 0;
}
//大于的原因:在非定时器中断的程序中不一定能执行到刚好等于4999的状态,定时中断1ms执行一次
if(Led_5s > 5000-1)
Led_buff[0] = 1;
//L2:计数值为奇数时,点亮
if(count%2 == 0)
Led_buff[1] = 0;
else
Led_buff[1] = 1;
//L3:无效按键的次数大于等于3次时,L3亮
if(no_key >2 )
Led_buff[2] = 1;
else
Led_buff[2] = 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 Timer0_service() interrupt 1
{
//Smg_slow_down,Key_slow_down
if(++Smg_slow_down == 200) Smg_slow_down = 0;
if(++Key_slow_down == 10) Key_slow_down = 0;
if(++Smg_pos == 8) Smg_pos = 0;
Led_disp(Smg_pos,Led_buff[Smg_pos]);
Smg_disp(Smg_pos,Smg_buff[Smg_pos],Smg_point_buff[Smg_pos]);
if(enable_5s) Led_5s++;
else Led_5s = 0;
}
void main()
{
System_init();
Timer0Init();
EEPROM_write(temp_v,0,1);
PCF8591_ADC(); //先读一次数据丢掉,避免初始显示不正确
while(1)
{
Key_proc();
Smg_proc();
Led_proc();
}
}
2.key.c
#include <key.H>
unsigned char Key_read()
{
unsigned char temp = 0;
//独立按键
// if(P33 == 0) temp = 4;
// if(P32 == 0) temp = 5;
// if(P31 == 0) temp = 6;
// if(P30 == 0) temp = 7;
//矩阵按键
P44 = 0;P42 =1 ;P35 = 1;P34 = 1;
if(P33 == 0) temp = 4;
if(P32 == 0) temp = 5;
if(P31 == 0) temp = 6;
if(P30 == 0) temp = 7;
P44 = 1;P42 =0 ;P35 = 1;P34 = 1;
if(P33 == 0) temp = 8;
if(P32 == 0) temp = 9;
if(P31 == 0) temp = 10;
if(P30 == 0) temp = 11;
P44 = 1;P42 =1 ;P35 = 0;P34 = 1;
if(P33 == 0) temp = 12;
if(P32 == 0) temp = 13;
if(P31 == 0) temp = 14;
if(P30 == 0) temp = 15;
P44 = 1;P42 =1 ;P35 = 1;P34 = 0;
if(P33 == 0) temp = 16;
if(P32 == 0) temp = 17;
if(P31 == 0) temp = 18;
if(P30 == 0) temp = 19;
return temp;
}
3.seg.c
#include <seg.H>
code unsigned char Seg_Table[]={ //标准字库
// 0 1 2 3 4 5 6 7 8 9 全灭 B C D E F
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x7C,0x39,0x5E,0x79,0x71,
//black - P U N K L N o P U t G Q r M y
0x00,0x73,0x3E,0x37,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};
static unsigned char temp = 0x00;
static unsigned char temp_old = 0x00;
void Select_138(unsigned char channel)
{
switch(channel)
{
case 4: //led
P2 = P2 & 0X1F | 0X80;
break;
case 5: //蜂鸣器、继电器
P2 = P2 & 0X1F | 0Xa0;
break;
case 6: //位选
P2 = P2 & 0X1F | 0Xc0;
break;
case 7: //段选
P2 = P2 & 0X1F | 0Xe0;
break;
}
P2 &= 0X1F; //关闭锁存器
}
void System_init()
{
//关闭LED、蜂鸣器、继电器
P0 = 0Xff;
Select_138(4);
P0 = 0X00;
Select_138(5);
}
void Led_disp(unsigned char pos,enable)
{
static unsigned char temp_led = 0x00;
static unsigned char temp_led_old = 0x00;
if(enable)
temp_led = temp_led | (0x01<<pos);
else
temp_led = temp_led & ~(0x01<<pos);
if(temp_led != temp_led_old )
{
P0 = ~temp_led;
Select_138(4);
temp_led_old = temp_led;
}
}
void Buzz(unsigned char enable)
{
if(enable)
temp |= 0x40;
else
temp &= ~0x40;
if(temp != temp_old )
{
P0 = temp;
Select_138(5);
temp_old = temp;
}
}
void Ready(unsigned char enable)
{
if(enable)
temp |=0x10;
else
temp &= ~0x10;
if(temp != temp_old )
{
P0 = temp;
Select_138(5);
temp_old = temp;
}
}
void Smg_disp(unsigned char wela,dula,point)
{
P0 = 0XFF;//消隐
Select_138(7);
P0 = 0X01<<wela;
Select_138(6);
P0 = ~Seg_Table[dula];//消隐
Select_138(7);
if(point)
{
P0 &=0X7F;
Select_138(7);
}
}
4.iic.c
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <iic.H>
#include <intrins.H>
#define DELAY_TIME 10
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 int PCF8591_ADC()
{
unsigned char dat;
I2CStart();
I2CSendByte(0X90);
I2CWaitAck();
I2CSendByte(0X03);
I2CWaitAck();
I2CStart();
I2CSendByte(0X91);
I2CWaitAck();
dat = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return dat*(5.0/255)*100;
}
//函数名:写EEPROM函数
//入口函数:需要写的字符串,写入的地址(务必为8的整数),写入数量
//返回值:无
//函数功能:向EEPROM的某个地址写入字符串中特定数量的字符
void EEPROM_write(unsigned char *EEPROM_string,unsigned char addr,unsigned num)
{
I2CStart(); //发送开始信号
I2CSendByte(0xa0); //选择EEPROM芯片,确定写模式
I2CWaitAck(); //等待EEPROM反馈
I2CSendByte(addr); //选择EEPROM芯片,确定写模式
I2CWaitAck(); //等待EEPROM反馈
while(num--)
{
I2CSendByte(*EEPROM_string++); //写入信息
I2CWaitAck(); //等待EEPROM反馈
I2C_Delay(200);
}
I2CStop(); //停止发送
}
//函数名:读EEPROM函数
//入口函数:读取数据需要存储的字符串,读取的地址(务必为8的整数),读取数量
//返回值:无
//函数功能:读取EEPROM的某个地址的数据,并存放在字符串数组中
void EEPROM_read(unsigned char *EEPROM_string,unsigned char addr,unsigned num)
{
I2CStart();
I2CSendByte(0xa0); //选择EEPROM芯片,确定写模式
I2CWaitAck(); //等待EEPROM反馈
I2CSendByte(addr); //写入读取的数据地址
I2CWaitAck(); //等待EEPROM反馈
I2CStart(); //发送开启信号
I2CSendByte(0xa1); //选择EEPROM芯片,确定读模式
I2CWaitAck(); //等待EEPROM反馈
while(num--)
{
*EEPROM_string++ = I2CReceiveByte();
if(num) I2CSendAck(0); //发送应答
else I2CSendAck(1); //不应答
}
I2CStop(); //停止发送
}
四、现存问题
烧录程序后在板子上看到的所有功能均实现了,但是在四梯科技上测评时还有两个问题,如下:
程序部分在网站上的得分为67.2/70,这2.8分是真没发现问题在哪儿,要是比赛时能写到这个程度也不错了,不去纠结这点儿分了。