声明:代码确实写的不好,用的都是最笨的方法实现功能,不喜勿喷
第九届省赛题相对于其他几届难度还是有所增加的,第九届的题用到了AD模块、EEPROM模块、LED流水灯、独立按键以及数码管。题目中要求流水灯以四种方式来实现。
模式一:L1,L2,L3,............L8依次点亮
模式二:L8,L7,L6,............L1依次点亮
模式三:L1L8,L2L7,L3L6,L4L5依次点亮
模式四:L4L5,L3L6,L2L7,L1L8依次点亮
要求流水灯的时间间隔可调,时间范围为400-1200ms。
时间间隔的调控还是比较好解决的,直接用定时器控制流水灯是否开启的标志位就可以了。看一下具体代码:
if(Mode1_flag == 1)
{
P2=0X80;
P0 = ~(1<<i); //从左到右流水
i +=1;
if(i == 8)
i = 0;
Mode1_flag = 0;
}
void Timer0Init(void) //100微秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xAE; //设置定时初值
TH0 = 0xFB; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1;
}
void Timer0(void) interrupt 1
{
static uchar num=0;
uint Mode1_num;
uint Shuma_Timer;
TL0 = 0xAE; //设置定时初值
TH0 = 0xFB; //设置定时初值
num++;
if(num == 100)
num = 0;
if(num > PWM)
{
P2 = 0X80;P0 = 0XFF;
P2 = 0X00;
}
if(KeyS7_Open == 1) //在S7按键按下的情况下
{
Mode1_num++;
if(Mode1_num == Timer*10)//实现按照设定的时间间隔流水
{
Mode1_num = 0;
Mode1_flag = 1;
}
}
但是针对流水灯的四种模式的后两种我还是想来很久的,主要是陷入了移位的思维模式中没有跳出来,总是想用移位来实现,最后还是退而求其次用来数组的方法实现的。下面看一下模式3和模式4 的代码:
uchar tab1[4] = {0X7E,0XBD,0XDB,0XE7}; //LED从两端向中间流水
uchar tab2[4] = {0XE7,0XDB,0XBD,0X7E}; //LED从中间向两端流水
if(Mode1_flag == 1)
{
P2=0X80;
P0 = tab1[i]; //两端向中间
i +=1;
if(i == 4)
i = 0;
Mode1_flag = 0;
}
if(Mode1_flag == 1)
{
P2 = 0X80;
P0 = tab2[i]; //中间向两端
i +=1;
if(i == 4)
i = 0;
Mode1_flag = 0;
}
上面我说了了这么多还没有把题目粘出来让大家看一下,下面看一下题目的要求:
按键和数码管难度一直是一个样子,我们直接看这两个方面的代码:
#include "shuma.h"
u8 shu_tab[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0XBF,0XC6,0XFF};//0XC6是英文C
uchar yi,er,san,si,wu,liu,qi,ba;
//第一和第二个数码管
void Display1_2(uchar yi,uchar er)
{
P2=0XC0;//打开位选573 U8
P0=0X01;//选择第一个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[yi];
Delay1ms();Delay1ms();
P2=0XC0;//打开位选573 U8
P0=0X02;//选择第二个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[er];
Delay1ms();Delay1ms();
}
//第四和第五个数码管
void Display4_5(uchar si,uchar wu)
{
P2=0XC0;//打开位选573 U8
P0=0X08;//选择第一个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[si];
Delay1ms();Delay1ms();
P2=0XC0;//打开位选573 U8
P0=0X10;//选择第一个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[wu];
Delay1ms();Delay1ms();
}
//第三和第六个数码管
void Display3_6(uchar san,uchar liu)
{
P2=0XC0;//打开位选573 U8
P0=0X04;//选择第三个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[san];
Delay1ms();Delay1ms();
P2=0XC0;//打开位选573 U8
P0=0X20;//选择第四个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[liu];
Delay1ms();Delay1ms();
}
//第七和第八个数码管
void Display7_8(uchar qi,uchar ba)
{
P2=0XC0;//打开位选573 U8
P0=0X40;//选择第一个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[qi];
Delay1ms();Delay1ms();
P2=0XC0;//打开位选573 U8
P0=0X80;//选择第一个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[ba];
Delay1ms();Delay1ms();
P2=0XFF;//打开段选573 U7
P0=0XFF;//关闭所有数码管段选
}
void Delay1ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
//第三和第四个数码管
void Display3_4(uchar san,uchar si)
{
P2=0XC0;//打开位选573 U8
P0=0X04;//选择第三个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[san];
Delay1ms();Delay1ms();
P2=0XC0;//打开位选573 U8
P0=0X08;//选择第四个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[si];
Delay1ms();Delay1ms();
}
//第五和第六个数码管
void Display5_6(uchar wu,uchar liu)
{
P2=0XC0;//打开位选573 U8
P0=0X10;//选择第一个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[wu];
Delay1ms();Delay1ms();
P2=0XC0;//打开位选573 U8
P0=0X20;//选择第一个数码管
P2=0XFF;//打开段选573 U7
P0=shu_tab[liu];
Delay1ms();Delay1ms();
}
#include "key.h"
#include "shuma.h"
#include "eeprom.h"
uchar KeyS7_Open; //实现按键S7的打开流水灯的功能
uchar KeyS6_Set; //按下S6进入设置界面
uchar KeyS5_Add; //在设置情况下,实现数值加一
uchar KeyS4_Dec; //在设置情况下,实现数值减一
uchar LED_grade_flag; //LED亮度等级显示的标志位
extern uchar LED_grade;
extern uchar Mode;
extern uint Timer;
extern uchar yi,er,san,si,wu,liu,qi,ba;
//独立按键扫描函数
void D_keyscan()
{
if(P30==0)
{
Delay5ms();
if(P30==0) //按键S7
{
KeyS7_Open += 1;
if(KeyS7_Open == 2)
KeyS7_Open = 0;
}
while(!P30);
}
else if(P31==0) //按键S6
{
Delay5ms();
if(P31==0)
{
KeyS6_Set += 1;
if(KeyS6_Set == 3)
KeyS6_Set = 0;
IICEE_write(0x00,Mode); //将要锁存的数据写入eeprom
Delay5ms();
IICEE_write(0x01,Timer/100); //将设置的时间间隔写入EEPORM
Delay5ms();
}
while(!P31);
}
else if(P32==0) //按键S5
{
Delay5ms();
if(P32==0)
{
KeyS5_Add = 1;
}
while(!P32);
}
else if(P33==0) //按键S4
{
Delay5ms();
if(P33==0)
{
KeyS4_Dec = 1;
if(KeyS7_Open == 1)
{
LED_grade_flag = 1;
}
}
while(!P33)
{
if(LED_grade_flag == 1) //实现按下S4显示亮度等级的标志位,松开不显示
{
yi = 12;er = 12;san = 12;si = 12;
wu = 12; liu = 12;qi = 10;ba = LED_grade;
Display1_2(yi,er);
Display3_4(san,si);
Display5_6(wu,liu);
Display7_8(qi,ba);
}
}
LED_grade_flag = 0;
}
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
AD这个模块的使用还是很简单的,题目要求把流水灯的亮度分为四个等级,我们用AD读取滑动变阻器的值,然后区间分割就可以了。
下面看一下代码:
#include "ad.h"
#include "eeprom.h"
//AD开始函数
void IICAD_statrt(void)
{
SDA = 1;
_nop_();
SCL = 1;
_nop_();
SDA = 0;
_nop_();
SCL = 0;
}
//AD停止函数
void IICAD_stop(void)
{
SDA = 0;
_nop_();
SCL = 1;
_nop_();
SDA = 1;
}
//向AD写一个字节
void AD_writebyte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
SCL = 0;
_nop_();
SDA = dat&0x80;
_nop_();
SCL = 1;
dat<<=1;
}
SCL = 0;
_nop_();
}
//应答函数
uchar ACK_AD(void)
{
SCL = 1;
IICAD_delay(5);
if(SDA == 1)
{
SCL = 0;
Delay100us();
IICAD_stop();
return 0;
}
else
{
SCL = 0;
Delay100us();
return 1;
}
}
//从AD中读取数据
uchar IICAD_read(uchar add)
{
uchar temp;
IICAD_statrt();
AD_writebyte(0x90);
ACK_AD();
AD_writebyte(add);
ACK_AD();
IICAD_statrt();
AD_writebyte(0x91);
ACK_AD();
temp = IICAD_readbyte();
IICAD_stop();
//现在显示的是0-255,如果需要显示0-100可以加代码(temp = 0.39*temp;)
return temp;
}
//从AD中读取一个字节
uchar IICAD_readbyte()
{
uchar dat;
uchar i;
SCL = 0;
_nop_();
SCL = 1;
_nop_();
for(i=0;i<8;i++)
{
SCL = 1;
dat<<=1;
if(SDA)
{
dat |= 0x01;
}
SCL = 0;
}
return dat;
}
void IICAD_delay(uchar a)
{
do
{
_nop_();
}
while(a--);
}
void Delay100us() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
i = 2;
j = 15;
do
{
while (--j);
} while (--i);
}
题目要求我们在重新上电之后,导入上次的模式和定时时间间隔,EEPROM就是实现的这个功能(建议大家在写入EEPROM之后,读取出来用数码管显示一下结果是否正确,然后再对读取出的数据进行处理)。下面看一下程序:
#include "eeprom.h"
//IIC开始函数
void IICEE_statrt(void)
{
SDA = 1;
_nop_();
SCL = 1;
_nop_();
SDA = 0;
_nop_();
SCL = 0;
}
//IIC停止函数
void IICEE_stop(void)
{
SDA = 0;
_nop_();
SCL = 1;
_nop_();
SDA = 1;
}
//将地址和数据写进IIC
void IICEE_write(uchar add ,uchar dat)
{
IICEE_statrt();
EE_writebyte(0xa0);
ACK_EE();
EE_writebyte(add);
ACK_EE();
EE_writebyte(dat);
ACK_EE();
IICEE_stop();
Delay10ms();Delay10ms();//一定要加
}
//向IIC写一个字节
void EE_writebyte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
SCL = 0;
_nop_();
SDA = dat&0x80;
_nop_();
SCL = 1;
dat<<=1;
}
SCL = 0;
_nop_();
}
void IICEE_delay(uchar a)
{
do
{
_nop_();
}
while(a--);
}
//应答函数
uchar ACK_EE(void)
{
SCL = 1;
IICEE_delay(5);
if(SDA == 1)
{
SCL = 0;
IICEE_stop();
return 0;
}
else
{
SCL = 0;
return 1;
}
}
//从IIC中读取数据
uchar IICEE_read(uchar add)
{
uchar temp;
IICEE_statrt();
EE_writebyte(0xa0);
ACK_EE();
EE_writebyte(add);
ACK_EE();
IICEE_statrt();
EE_writebyte(0xa1);
ACK_EE();
temp = IICEE_readbyte();
IICEE_stop();
Delay10ms();
return temp;
}
//从IIC中读取一个字节
uchar IICEE_readbyte()
{
uchar dat;
uchar i;
SCL = 0;
_nop_();
SCL = 1;
_nop_();
for(i=0;i<8;i++)
{
SCL = 1;
dat<<=1;
if(SDA)
{
dat |= 0x01;
}
SCL = 0;
}
return dat;
}
void Delay10ms() //@11.0592MHz
{
unsigned char i, j;
i = 108;
j = 145;
do
{
while (--j);
} while (--i);
}
最主要的功能实现函数,我都写在主函数中,下面看一下主函数代码:
#include "system.h"
#include "shuma.h"
#include "key.h"
#include "ad.h"
#include "eeprom.h"
void Timer0Init(void);
void Delay200ms();
extern uchar yi,er,san,si,wu,liu,qi,ba;
extern uchar KeyS7_Open;
extern uchar KeyS6_Set;
extern uchar KeyS5_Add;
extern uchar KeyS4_Dec;
extern uchar LED_grade_flag;
uchar tab1[4] = {0X7E,0XBD,0XDB,0XE7}; //LED从两端向中间流水
uchar tab2[4] = {0XE7,0XDB,0XBD,0X7E}; //LED从中间向两端流水
uchar AD_num; //存储AD的值
uchar PWM; //LED亮度等级控制位
uchar LED_grade; //存储LED亮度等级的变量
uchar Mode=1; //存储LED流水模式
uchar Timer_L=4; //存储LED流水时间的低位间隔
uchar Timer_H=12; //存储LED流水时间的高位间隔
uint Timer=400; //存储LED流水总时间间隔
uchar Shuma_flag; //数码管以0.8秒显示的标志位
uchar Mode1_flag; //LED灯流水方式的标志位
uchar Init1_flag; //防止计数出乱的的清零标志位
uchar Init2_flag; //防止计数出乱的的清零标志位
uchar Init3_flag; //防止计数出乱的的清零标志位
uchar i; //实现流水灯
void main(void)
{
Init();
Timer0Init();
Mode = IICEE_read(0x00); //读取EEPROM中的模式值
Delay5ms();
Timer = IICEE_read(0x01)*100; //读取EEPROM中的时间值
Delay5ms();
while(1)
{
D_keyscan(); //调用按键扫描函数
AD_num = IICAD_read(0x03); //调用AD函数
/***************划分亮度等级*****************/
if(AD_num < 80)
{
PWM = 50;
LED_grade = 1;
}
else if((AD_num >= 80) && (AD_num < 150))
{
PWM = 80;
LED_grade = 2;
}
else if((AD_num >= 150) && (AD_num < 210))
{
PWM = 95;
LED_grade = 3;
}
else if(AD_num >= 210)
{
PWM = 100;
LED_grade = 4;
}
/****************按下按键S7实现流水灯的显示************************/
if(KeyS7_Open == 1)//防止计数出乱的的清零标志位
{
if(Init3_flag == 0)
{
i = 0;
Mode = Mode + 1;
KeyS4_Dec = 0;
KeyS5_Add = 0;
Init3_flag = 1;
}
if(Mode == 1) //模式一
{
if(Mode1_flag == 1)
{
P2=0X80;
P0 = ~(1<<i); //从左到右流水
i +=1;
if(i == 8)
i = 0;
Mode1_flag = 0;
}
}
else if(Mode == 2) //模式二
{
if(Mode1_flag == 1)
{
P2=0X80;
P0 = ~(0x80>>i); //从右到左流水
i +=1;
if(i == 8)
i = 0;
Mode1_flag = 0;
}
}
else if(Mode == 3) //模式三
{
if(Mode1_flag == 1)
{
P2=0X80;
P0 = tab1[i]; //两端向中间
i +=1;
if(i == 4)
i = 0;
Mode1_flag = 0;
}
}
else if(Mode == 4) //模式四
{
if(Mode1_flag == 1)
{
P2 = 0X80;
P0 = tab2[i]; //中间向两端
i +=1;
if(i == 4)
i = 0;
Mode1_flag = 0;
}
}
}
else if(KeyS6_Set == 1)/*****************第一次按下S6按键实现模式的改变*********************/
{
if(Init1_flag == 0)
{
KeyS4_Dec = 0;
KeyS5_Add = 0;
Init1_flag = 1;
}
yi = 10;er = Mode;san = 10;si = 12;
wu = Timer_H; liu = Timer_L;qi = 0;ba = 0;
if(Shuma_flag == 0) //实现被选中的数码管以0.8秒的间隔闪烁
{
P2=0XC0;P0=0X07;
P2=0XFF;P0=0XFF;
}
else
{
Display1_2(yi,er);
Display3_4(san,si);
}
if(KeyS5_Add == 1) //按下S5按键实现数值的加一
{
KeyS4_Dec = 0;
Mode = Mode + 1;
KeyS5_Add = 0;
if(Mode == 5) //实现四种模式的循环
Mode = 1;
}
else if(KeyS4_Dec == 1) //按下S4按键实现数值的减一
{
if(Mode >= 2) //实现四种模式的循环
{
Mode = Mode - 1;
KeyS4_Dec = 0;
}
else
{
Mode = 1;
}
}
Display5_6(wu,liu);
Display7_8(qi,ba);
}
else if(KeyS6_Set == 2)/*****************第二次按下S6按键流水时间间隔数值的改变*********************/
{
if(Init2_flag == 0)
{
KeyS4_Dec = 0;
KeyS5_Add = 0;
Init2_flag = 1;
}
yi = 10;er = Mode;san = 10;si = 12;
wu = Timer_H; liu = Timer_L;qi = 0;ba = 0;
if(Shuma_flag == 0) //实现被选中的数码管以0.8秒的间隔闪烁
{
P2=0XC0;P0=0XE0;
P2=0XFF;P0=0XFF;
}
else
{
Display5_6(wu,liu);
Display7_8(qi,ba);
}
if(KeyS5_Add == 1) //按下S5按键实现数值的加一
{
KeyS5_Add = 0;
if((Timer_L == 2) && (Timer_H == 1))//控制时间间隔的数值在要求的范围内
{
Timer_L = 2;
Timer_H = 1;
KeyS5_Add = 0;
}
else
{
Timer_L = Timer_L + 1;
if(Timer_L == 10)
{
Timer_L = 0;
Timer_H = 1;
}
}
}
else if(KeyS4_Dec == 1) //按下S4按键实现数值的减一
{
KeyS4_Dec = 0;
if(Timer_H == 1) //控制时间间隔的数值在要求的范围内
{
if(Timer_L == 0)
{
Timer_L = Timer_L - 1;
Timer_H = 12;
Timer_L = 9;
}
else
{
Timer_L = Timer_L - 1;
}
}
else if(Timer_L >= 5)
{
Timer_L = Timer_L - 1;
}
}
if(Timer_H != 12)
Timer = Timer_H*1000 + Timer_L*100; //将设定的时间间隔保存
else
Timer = Timer_L*100;
Display1_2(yi,er);
Display3_4(san,si);
}
}
}
void Timer0Init(void) //100微秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xAE; //设置定时初值
TH0 = 0xFB; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1;
}
void Timer0(void) interrupt 1
{
static uchar num=0;
uint Mode1_num;
uint Shuma_Timer;
TL0 = 0xAE; //设置定时初值
TH0 = 0xFB; //设置定时初值
num++;
if(num == 100)
num = 0;
if(num > PWM)
{
P2 = 0X80;P0 = 0XFF;
P2 = 0X00;
}
if(KeyS7_Open == 1) //在S7按键按下的情况下
{
Mode1_num++;
if(Mode1_num == Timer*10)//实现按照设定的时间间隔流水
{
Mode1_num = 0;
Mode1_flag = 1;
}
}
if(KeyS6_Set != 0) //如果S6按键按下,开始计时实现被选中的数码管以0.8秒的时间间隔闪烁
{
Shuma_Timer++;
if(Shuma_Timer == 8000)
{
Shuma_flag = 1;
}
else if(Shuma_Timer == 16000)
{
Shuma_Timer = 0;
Shuma_flag = 0;
}
}
}
最后还是把我写的完整的工程上传一下,以供大家参考https://download.csdn.net/my