【个人经验分享】【八】第九届蓝桥杯单片机题目解析
>说在前面的话:纠结了很久要不要把每一届的省赛编程题目都解析一遍。。。想想还是算了,本人不善言辞...但是个人认为最有挑战性的题目是第九届的题目,它比往届的省赛题目都稍难一些,因此我决定对这个题目进行完整的分析。在本帖的最最后,我会发布调试好的代码。注:经过本人调试,完全可以运行,按键相应速度很完美,并且也不会发生按键按下影响数码管显示等一系列问题,总的来说,非常的耐操!【一】先看题目,解释一下它的“难”的原因
这个硬件框图说明了要用到的硬件资源,先罗列一下:1、PCF85912、AT24C02 3、独立按键 4、LED发光二极管 5、数码管
所用到软件方面的操作有 1、软件IIC(大赛给的有) 2、按键松手检测软件消抖等 3、至少一个定时器中断用来输出PWM波 4、其它....
而这些硬件以及软件当中,有密度相当程度的“重叠部分”;例如:1、PCF8591 AT24C02 占用一个IIC 总线 2、LED灯+数码管都需要动态显示 共用P2 P0 口 138译码器共用3、其它。。
图片/题目 来源:https://wenku.baidu.com/view/c7010e0100f69e3143323968011ca300a6c3f626.html
【二】编程思路
1、按键部分,大赛要求 一个设置键 加减 开始/停止键 四个独立按键还是比较正常的,符合往年一贯作风。
2、调光部分:只能采用PWM调光,一般情况下,为保证输出频率,需要占用一个定时器中断中断优先级需要最高。
3:数码管显示部分:首先按键扫描按键按一下 需要有相应的反应。。。。但是,按键按下的时候不能影响数码管的显示,一般情况把按键松手检测和数码管动态显示写在大循环里会发生这种情况。在本系列三中有提到过解决方案https://bbs.21ic.com/icview-2622588-1-1.html 总之也需要占用一个定时器中断。。
4、通讯部分:IIC协议,虽然EEPROM和AD 转换芯片都要用,但是,按照时间顺序一个个执行,基本没有影响,放在定时器中断也是可以的,但是为了保证LEDPWM的正常调光,还是建议写在while(1)循环里面,或者和数码管所占用的中断服务函数写到一起
5、流水灯速度调整:有一个思路,定时器中断仅仅负责显示和调光 但是P0赋予的值是一个变量,这个变量的更新代表这LED灯哪一个位是发亮的,这样,把装载数据的部分写在另一个次优先级中断里,即使被打断,也对正常数据的装载影响不大,而且又保证了LED的稳定显示。另外数码管的数据装载也可以如法**制。
6、时间要求部分:大赛要求的是,流水灯流水时间是多少多少;数码管显示的时间是多少多少,由于本人在写程序的时候没有考虑这个因素(因为懒),因此变量都是随便定义的阈值,但是并不是不能实现,所以下载完这个例程之后,可以根据需求修改相关阈值。
7、其它:流水灯的流水形状本人比较懒惰,所以直接编码成了code类型的数据,4个数组,装载到了ROM里,用的时候直接拿出来用;数码管选中闪烁部分:用的是数据控制闪烁的方法,段码0xff是熄灭 与正常显示相互切换,就完成了闪烁。
至于按键检测了,数码管动态显示了,IIC驱动程序了,这些都是一些基础的东西了,没什么好说的,如果还是不会或者是不熟练的话,赶紧练习练习!
【三】部分主要代码
感觉有点小乱(追求的速度4小时完成~)没有彻彻底底的模块化编程,但是结构还是可以看的清的:
#include "CT107D.h"
#include "PCF8591.h"
#include "AT24C02.h"
#define LED_SPEED_LOW 70
#define LED_SPEED_MED 60
#define LED_SPEED_HIG 50
#define LED_SPEED_VERY_HIG 40
#define SMG_OFF 10
sbit SET_KEY=P3^0;
sbit ADD_KEY=P3^1;
sbit DEL_KEY=P3^2;
sbit START_KEY=P3^3;
/*******************LED相关变量**********************/
unsigned char code led_dis_mod1={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
unsigned char code led_dis_mod2={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
unsigned char code led_dis_mod3={0xe7,0xdb,0xbd,0x7e,0xe7,0xdb,0xbd,0x7e};
unsigned char code led_dis_mod4={0x7e,0xbd,0xdb,0xe7,0x7e,0xbd,0xdb,0xe7};
unsigned char led_dis_buf;
unsigned char we_init_val=0x01,du_val_count=0;
/*******************数码管相关变量**********************/
unsigned char code smg_code={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf}; //正常显示模式
//10 熄灭 11 -
unsigned char display_buf={11,10,11,10,10,10,10,10};
unsigned char smg_special_buf={0xff,0xff,0xff,0xff,0xff,0xff,0xbf,0xff}; //特殊显示模式
//- 1 - 4 0 0
bit smg_display_flag=0; //0notdis 1dis
unsigned int smg_flash_counter=0; //数码管闪烁计数
bit smg_flash_flag=0; //0 不闪烁 1 闪烁
/********************定时器相关变量***********************/
unsigned char timer_counter1;
unsigned int timer_counter0;
/********************LED调光变量************************/
unsigned char led_dis_speed_buf;
unsigned char led_dis_speed=40; //40506070 。。。。120
unsigned char LED_PWM_VAL; //0~100
/********************LED模式**************************/
unsigned char led_mod_flag=0; //0 1 2 3
/********************按键相关变量************************/
bit setting_flag=0;
unsigned char setting_counter=0; //1mod 2time 0 display
bit start_stop_led_dis=0; //0 stop 1start
/**********开灯**************/
void LED_ON(){
switch_138(4);
P0=led_dis_buf;
switch_138(5);
P0=0X00;
}
/***********关灯*************/
void LED_OFF(){
switch_138(4);
P0=0xff;
switch_138(5);
P0=0X00;
}
void smg_display(){
switch_138(6); //WE
P0=we_init_val;
switch_138(7); //DAT
P0=smg_code];
switch_138(5);
P0=0x00;
du_val_count++;
if(du_val_count==8){
du_val_count=0;
}
we_init_val=_crol_(we_init_val,1);
}
void smg_special_dis(unsigned char n){
unsigned char i;
for(i=0;i<8;i++){
smg_special_buf=smg_code; //10分4 份
switch_138(6); //WE
P0=1<<i;
switch_138(7); //DAT
P0=smg_special_buf;
delay_ms(2);
}
}
// mod=10熄灭 time=
void smg_reload_dat(unsigned char mod,unsigned char time){
display_buf=mod; //流水模式
//速度
if(time>=100){
display_buf=time/100;
}else{
display_buf=SMG_OFF;
}
display_buf=time%100/10;
display_buf=time%10;
display_buf=0;
}
void smg_off(unsigned char choose){
if(choose==1){
display_buf=SMG_OFF;
}else if(choose==2){
display_buf=SMG_OFF;
display_buf=SMG_OFF;
display_buf=SMG_OFF;
display_buf=SMG_OFF;
}
}
void keysCan(){
if(SET_KEY==0){
setting_counter++;
if(setting_counter>=3)setting_counter=0;
}while(SET_KEY==0);
if(START_KEY==0){
start_stop_led_dis=~start_stop_led_dis;
}while(START_KEY==0);
}
/**********上传数据到AT24C02************************/
void upload_dat(){
switch(led_mod_flag){
case 0:AT_write_dat(0x00,led_dis_speed);break;
case 1:AT_write_dat(0x01,led_dis_speed);break;
case 2:AT_write_dat(0x02,led_dis_speed);break;
case 3:AT_write_dat(0x03,led_dis_speed);break;
default:break;
}
}
/***********下载数据到单片机*************************/
void download_dat(){
unsigned char i;
for(i=0;i<4;i++){
led_dis_speed_buf=AT_read_dat(i);
delay_ms(5);
}
}
void timer_init(){
TMOD=0x11;
TL0 = 0xA4; //设置定时初值
TH0 = 0xFF; //设置定时初值
TL1 = 0xCD; //设置定时初值
TH1 = 0xF8; //设置定时初值 2MS
TR0=1;
TR1=1;
EA=1;
ET0=1;
ET1=1;
}
void main(){
all_init();
download_dat();
switch(led_mod_flag){
case 0:led_dis_speed=led_dis_speed_buf;break;
case 1:led_dis_speed=led_dis_speed_buf;break;
case 2:led_dis_speed=led_dis_speed_buf;break;
case 3:led_dis_speed=led_dis_speed_buf;break;
default:break;
}
/*
while(1){
if(START_KEY==0){
break;
}
}
*/
while(1){
if(START_KEY==0){
delay_ms(50);
break;
}
}
timer_init();
while(1){
keysCan();
if(setting_counter>0){
setting_flag=1;
}else{
setting_flag=0;
}
if(setting_flag){
if(setting_counter==1){ //流水灯模式
if(ADD_KEY==0){
led_mod_flag++;
if(led_mod_flag>3)led_mod_flag=0;
}while(ADD_KEY==0);
if(DEL_KEY==0){
led_mod_flag--;
if(led_mod_flag==0)led_mod_flag=3;
}while(DEL_KEY==0);
}else if(setting_counter==2){ //流水灯速度
switch(led_mod_flag){
case 0:led_dis_speed=led_dis_speed_buf;break;
case 1:led_dis_speed=led_dis_speed_buf;break;
case 2:led_dis_speed=led_dis_speed_buf;break;
case 3:led_dis_speed=led_dis_speed_buf;break;
default:break;
}
if(ADD_KEY==0){
led_dis_speed+=10;
if(led_dis_speed>120)led_dis_speed=40;
}while(ADD_KEY==0);
if(DEL_KEY==0){
led_dis_speed-=10;
if(led_dis_speed<40)led_dis_speed=120;
}while(DEL_KEY==0);
switch(led_mod_flag){
case 0:led_dis_speed_buf=led_dis_speed;break;
case 1:led_dis_speed_buf=led_dis_speed;break;
case 2:led_dis_speed_buf=led_dis_speed;break;
case 3:led_dis_speed_buf=led_dis_speed;break;
default:break;
}
}
}else{
if(ADD_KEY==0){
}while(ADD_KEY==0){
smg_display_flag=1;
}
if(start_stop_led_dis){
TR0=0;
ET0=0;
LED_OFF();
}else{
TR0=1;
ET0=1;
}
}
smg_display_flag=0; //数码管开显示
}
}
/****************display*******************/
void timer0() interrupt 1{
TL0 = 0xA4; //设置定时初值
TH0 = 0xFF; //设置定时初值
timer_counter0++;
if(timer_counter0<=(LED_PWM_VAL*2)){ //PWM调光
LED_ON();
}else{
LED_OFF();
}
if(timer_counter0>=8)timer_counter0=0;
}
/****************control******************/
void timer1() interrupt 3{
unsigned char i;
TL1 = 0xCD; //设置定时初值
TH1 = 0xF8; //设置定时初值 2MS
if(start_stop_led_dis){
LED_OFF();
}
timer_counter1++;
if(!smg_display_flag){
smg_display(); //数码管显示
}else{
smg_special_dis(LED_PWM_VAL);
}
smg_flash_counter++;
if(smg_flash_counter>=100){ //200ms
smg_flash_counter=0;
smg_flash_flag=~smg_flash_flag;
}
switch(setting_counter){ //数码管闪烁控制和是否闪烁
case 0:smg_reload_dat(led_mod_flag+1,led_dis_speed);break;
case 1:{if(smg_flash_flag)smg_off(1);else smg_reload_dat(led_mod_flag+1,led_dis_speed);}break;
case 2:{if(smg_flash_flag)smg_off(2);else smg_reload_dat(led_mod_flag+1,led_dis_speed);}break;
default:break;
}
if(!start_stop_led_dis){
switch(led_mod_flag){ //装入LED数据
case 0:led_dis_buf=led_dis_mod1;break;
case 1:led_dis_buf=led_dis_mod2;break;
case 2:led_dis_buf=led_dis_mod3;break;
case 3:led_dis_buf=led_dis_mod4;break;
default:break;
}
}else{
led_dis_buf=0xff;
}
//数码管装入数据与显示不冲突
if(timer_counter1==led_dis_speed){ //更新数据速度 随便写的,可调
timer_counter1=0;
i++;
if(i==8)i=0;
}
LED_PWM_VAL=(AD_read_dat(0x03)>>6)+1; //转 10
upload_dat();
}
【四】说在最后的话,其实这个例程还是有一点点的小BUG 的,就是关闭流水灯显示的时候,流水灯还是会微微亮那么一点点....个人推断是程序里没有消隐之类的东西把(其实是懒得写了),但是总的来说是基本完全实现的(接近完美的了);当然,如果还让我再写一遍,我可能还会采取其它不同的方法,总之实现的方法有很多,只要功能实现就OK;个人水平还是有待提高了,也请各个dalao们也多多给出一些指导意见的啦。蟹蟹~ PS.下回我们来谈谈第九届国赛的题目吧~
页:
[1]