蓝桥杯之单片机设计与开发(23)——2018_第九届_蓝桥杯_省赛——“彩灯控制器”

2019-04-15 17:41发布

2019年3月12日更新 在连续多次单字节读取E22PROM时,不需要延时。
搞完所有模块,开始写真题。

1、题目解读

  看到这个题,大致要知道用到了哪些模块。 LED、数码管、AD、E2PROM、独立按键。  

2、代码

在这里我直接把所有代码给贴上来吧,程序可能写的很麻烦,大家参考一下就行了 也可以在这下载https://download.csdn.net/download/xiaomo_haa/10991680 main.c #include #include "sys.h" u8 time_led[] = {0x11, 0x04, 0x04, 0x04, 0x04}; //LED流转间隔 *100 u8 Mode_led = 1; u8 pwm = 4; u8 Mode_Option = 0; //设置模式位 0无/1流转方式/2流转间隔 u8 led_lighting = 0; //LED亮度 bit flag_led = 0; //LED工作标志 bit flag_800ms = 1; bit display_pwm = 0; void main(void) { u8 flag_dat = 0; All_init(); Timer0Init(); Timer1Init(); flag_dat = Read_E2PROM(0x00); //读取E2PROM写入标志位 if(flag_dat == 0x11) { time_led[0] = flag_dat; time_led[1] = Read_E2PROM(0x01); time_led[2] = Read_E2PROM(0x02); time_led[3] = Read_E2PROM(0x03); time_led[4] = Read_E2PROM(0x04); } LED_work(0xff); EA = 1; while(1) { Key_press(); } } sys.c #include "sys.h" void All_init(void) { P2 = (P2 & 0x1f) | 0x80; //打开Y4C P0 = 0xff; //关闭LED P2 = (P2 & 0x1f) | 0xc0; //打开Y6C P0 = 0x00; //关闭所有数码管 P2 = (P2 & 0x1f) | 0xa0; //打开Y5C P0 = 0x00; //关闭蜂鸣器和继电器 P2 = P2 & 0x1f; //关闭所有573 } void Timer0Init(void) //2毫秒@11.0592MHz { AUXR |= 0x80; //定时器时钟1T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0x9a; //设置定时初值 TH0 = 0xa9; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; } void Timer0(void) interrupt 1 { static u16 T0count1 = 0, T0count2 = 0; static u8 index = 0, e2_addr = 0x00; T0count2 ++; if(Mode_Option > 0) //800ms数码管闪烁 { T0count1 ++; if(T0count1 >= 400) { T0count1 = 0; flag_800ms = ~flag_800ms; } } if((T0count2 % 5 == 0) && (T0count2 < 30)) //每10ms时写一次E2PROM { switch(index) { case 0: Write_E2PROM(0x00, time_led[e2_addr]); break; case 1: Write_E2PROM(0x01, time_led[e2_addr]); break; case 2: Write_E2PROM(0x02, time_led[e2_addr]); break; case 3: Write_E2PROM(0x03, time_led[e2_addr]); break; case 4: Write_E2PROM(0x04, time_led[e2_addr]); break; default : break; } index ++; e2_addr ++; if(index >= 5) { index = 0; e2_addr = 0; } } else if(T0count2 >= 30) //60ms时读取一次AD值 { T0count2 = 0; pwm = Read_AIN(0x03); } Key_Scan(); Smg_show(); Smg_Scan(); } void Timer1Init(void) //100us@11.0592MHz { AUXR |= 0x40; //定时器时钟1T模式 TMOD &= 0x0F; //设置定时器模式 TL1 = 0xAE; //设置定时初值 TH1 = 0xFB; //设置定时初值 TF1 = 0; //清除TF1标志 TR1 = 0; //定时器1暂停计时 ET1 = 1; } void Timer1(void) interrupt 3 { static u8 dat; static u16 T1count1 = 0; static u8 mode_backup = 0; static u8 index = 0, T1count2 = 0; static u8 mode_index = 1; u16 temp = 0; u8 hightime = 0; T1count1 ++; T1count2 ++; T1count2 &= 0x0f; //最大计数到15 temp = 1000 * time_led[mode_index]; //流转时间间隔 hightime = pwm * pwm; //高电平时间 if(T1count1 >= temp) { T1count1 = 0; if(mode_index == 1) { if(mode_backup != mode_index) { dat = 0x7f; mode_backup = mode_index; } dat = _crol_(dat, 1); //模式1 if(dat == 0x7f) mode_index ++; } else if(mode_index == 2) { if(mode_backup != mode_index) { dat = 0xfe; mode_backup = mode_index; } dat = _cror_(dat, 1); //模式2 if(dat == 0xfe) mode_index ++; } else if(mode_index == 3) { if(mode_backup != mode_index) { index = 0; mode_backup = mode_index; } switch(index) { case 0: dat = 0x7e; break; //0111 1110 case 1: dat = 0xbd; break; //1011 1101 case 2: dat = 0xdb; break; case 3: dat = 0xe7; break; default : break; } index ++; index &= 0x03; if(dat == 0xe7) mode_index ++; } else if(mode_index == 4) { if(mode_backup != mode_index) { index = 0; mode_backup = mode_index; } switch(index) { case 0: dat = 0xe7; break; //1110 0111 case 1: dat = 0xdb; break; //1011 1101 case 2: dat = 0xbd; break; case 3: dat = 0x7e; break; default : break; } index ++; index &= 0x03; if(dat == 0x7e) mode_index = 1; } } if(T1count2 < hightime) //PWM LED_work(dat); else LED_work(0xff); } sys.h #ifndef _SYS_H_ #define _SYS_H_ //头文件包含 #include #include #include "iic.h" typedef unsigned char u8; typedef unsigned int u16; typedef unsigned long u32; //外部变量 extern u8 time_led[]; //LED流转间隔 *100 extern u8 Mode_led; extern u8 pwm; extern u8 Mode_Option; //设置模式位 0无/1流转方式/2流转间隔 extern u8 led_lighting; //LED亮度 extern bit flag_led; //LED工作标志 extern bit flag_800ms; extern bit display_pwm; extern u8 KeySta[]; //键值存储区 extern u8 Keybackup[]; //管脚定义 //函数声明 void All_init(void); void Timer0Init(void); void Timer1Init(void); void Smg_Scan(void); void Smg_show(); void Smg_show(); void LED_work(u8 dat); void Key_Scan(void); void Key_drive(u8 key); void Key_press(void); #endif key.c #include "sys.h" u8 KeySta[] = {1, 1, 1, 1}; //键值存储区 u8 Keybackup[] = {1, 1, 1, 1}; //键值备份区 sbit S4 = P3^3; sbit S5 = P3^2; sbit S6 = P3^1; sbit S7 = P3^0; //按键扫描函数,在定时器中断里调用 void Key_Scan(void) { static u8 Keybuff[] = {0xff, 0xff, 0xff, 0xff}; //按键缓冲区 u8 i = 0; Keybuff[0] = (Keybuff[0] << 1) | S7; Keybuff[1] = (Keybuff[1] << 1) | S6; Keybuff[2] = (Keybuff[2] << 1) | S5; Keybuff[3] = (Keybuff[3] << 1) | S4; for(i = 0; i < 4; i++) { if(Keybuff[i] == 0xff) //按键松开 KeySta[i] = 1; else if(Keybuff[i] == 0x00) //按键按下 KeySta[i] = 0; else //键值不稳定 {} } } void Key_drive(u8 key) { if(key == 0) { if(TR1 == 0) TR1 = 1; else TR1 = 0; } else if(key == 1) { Mode_Option ++; if(Mode_Option >= 3) Mode_Option = 0; } else if(key == 2) { if(Mode_Option == 1) { Mode_led ++; if(Mode_led >= 4) //模式最大4 Mode_led = 4; } else if(Mode_Option == 2) { time_led[Mode_led] ++; if(time_led[Mode_led] >= 12) //最大1200ms time_led[Mode_led] = 12; } } else if(key == 3) { if(Mode_Option == 1) { Mode_led --; if(Mode_led <= 1) //模式最小1 Mode_led = 1; } else if(Mode_Option == 2) { time_led[Mode_led] --; if(time_led[Mode_led] <= 4) //最小400ms time_led[Mode_led] = 4; } } } //检测按键是否按下,在main函数调用 void Key_press(void) { u8 i; for(i = 0; i < 4; i ++) { if(KeySta[i] != Keybackup[i]) { if(Keybackup[i] != 0) //按键松开时操作 Key_drive(i); Keybackup[i] = KeySta[i]; } } if(Mode_Option == 0) { if(KeySta[3] == 0) display_pwm = 1; else display_pwm = 0; } } display.c #include "sys.h" u8 code Nixie[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e, 0xff, 0xbf}; //16 17 u8 Nixiebuff[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; u8 smg1, smg2, smg3, smg4, smg5, smg6, smg7, smg8; //数码管扫描 void Smg_Scan(void) { static u8 index = 0; P2 = (P2 & 0x1f) | 0xc0; //打开Y6C P0 = 0x01 << index; P2 = (P2 & 0x1f) | 0xe0; //打开Y7C P0 = 0xff; P0 = Nixiebuff[index]; index ++; index &= 0x07; } //更新数码管值 void Smg_show() { if(Mode_Option == 0) //不是设置模式不显示 { if(display_pwm == 0) smg1 = smg2 = smg3 = smg4 = smg5 = smg6 = smg7 = smg8 = 16; else { smg1 = smg2 = smg3 = smg4 = smg5 = smg6 = 16; smg7 = 17; smg8 = pwm % 10; } } else if(Mode_Option == 1) { if(flag_800ms == 1) //显示 { smg1 = 17; smg2 = Mode_led % 10; smg3 = 17; smg4 = 16; smg5 = time_led[Mode_led] / 10; smg6 = time_led[Mode_led] % 10; smg7 = 0; smg8 = 0; } else //不显示,实现闪烁 { smg1 = 16; smg2 = 16; smg3 = 16; smg4 = 16; smg5 = time_led[Mode_led] / 10; smg6 = time_led[Mode_led] % 10; smg7 = 0; smg8 = 0; } } else if(Mode_Option == 2) { if(flag_800ms == 1) //显示 { smg1 = 17; smg2 = Mode_led % 10; smg3 = 17; smg4 = 16; smg5 = time_led[Mode_led] / 10; smg6 = time_led[Mode_led] % 10; smg7 = 0; smg8 = 0; } else //不显示,实现闪烁 { smg1 = 17; smg2 = Mode_led % 10; smg3 = 17; smg4 = 16; smg5 = 16; smg6 = 16; smg7 = 16; smg8 = 16; } } Nixiebuff[0] = Nixie[smg1]; Nixiebuff[1] = Nixie[smg2]; Nixiebuff[2] = Nixie[smg3]; Nixiebuff[3] = Nixie[smg4]; Nixiebuff[4] = Nixie[smg5]; Nixiebuff[5] = Nixie[smg6]; Nixiebuff[6] = Nixie[smg7]; Nixiebuff[7] = Nixie[smg8]; } //LED显示 void LED_work(u8 dat) { P2 = (P2 & 0x1f) | 0x80; P0 = dat; P2 = P2 & 0x1f; } iic.c #include "sys.h" #define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();} #define SlaveAddrW 0xA0 #define SlaveAddrR 0xA1 //总线引脚定义 sbit SDA = P2^1; /* 数据线 */ sbit SCL = P2^0; /* 时钟线 */ //总线启动条件 void IIC_Start(void) { SDA = 1; SCL = 1; somenop; SDA = 0; somenop; SCL = 0; } //总线停止条件 void IIC_Stop(void) { SDA = 0; SCL = 1; somenop; SDA = 1; } //应答位控制 void IIC_Ack(bit ackbit) { if(ackbit) { SDA = 0; } else { SDA = 1; } somenop; SCL = 1; somenop; SCL = 0; SDA = 1; somenop; } //等待应答 bit IIC_WaitAck(void) { SDA = 1; somenop; SCL = 1; somenop; if(SDA) { SCL = 0; IIC_Stop(); return 0; } else { SCL = 0; return 1; } } //通过I2C总线发送数据 void IIC_SendByte(unsigned char byt) { unsigned char i; for(i=0;i<8;i++) { if(byt&0x80) { SDA = 1; } else { SDA = 0; } somenop; SCL = 1; byt <<= 1; somenop; SCL = 0; } } //从I2C总线上接收数据 unsigned char IIC_RecByte(void) { unsigned char da; unsigned char i; for(i=0;i<8;i++) { SCL = 1; somenop; da <<= 1; if(SDA) da |= 0x01; SCL = 0; somenop; } return da; } /******************************************************************************* * 函数名 :Read_AIN * 输入值 :unsigned char chn * 返回值 :unsigend char dat * 作者 :小默haa * 时间 :2019年2月25日 * 功能描述:读取PCF8591AIN采集数据 * 备注 :chn为PCF8591的通道 *******************************************************************************/ unsigned char Read_AIN(unsigned char chn) { unsigned char dat, val, ad_pwm; EA = 0; IIC_Start(); //IIC总线起始信号 IIC_SendByte(0x90); //PCF8591的写设备地址 IIC_WaitAck(); //等待从机应答 IIC_SendByte(chn); //写入PCF8591的控制字节 IIC_WaitAck(); //等待从机应答 IIC_Stop(); //IIC总线停止信号 IIC_Start(); //IIC总线起始信号 IIC_SendByte(0x91); //PCF8591的读设备地址 IIC_WaitAck(); //等待从机应答 dat = IIC_RecByte(); //读取PCF8591通道3的数据 IIC_Ack(0); //产生非应答信号 IIC_Stop(); //IIC总线停止信号 val = (dat * 50) / 255; if((val >= 38) && (val <= 50)) ad_pwm = 4; else if((val >= 26) && (val < 38)) ad_pwm = 3; else if((val >= 14) && (val < 26)) ad_pwm = 2; else if((val >= 0) && (val < 14)) ad_pwm = 1; EA = 1; return ad_pwm; } void Write_E2PROM(unsigned char add, unsigned char dat) { EA = 0; IIC_Start(); IIC_SendByte(0xa0); //发送器件地址 IIC_WaitAck(); IIC_SendByte(add); //发送操作地址 IIC_WaitAck(); IIC_SendByte(dat); //写一字节 IIC_WaitAck(); IIC_Stop(); somenop; EA = 1; } unsigned char Read_E2PROM(unsigned char add) { unsigned char d; IIC_Start(); IIC_SendByte(0xa0); //发送器件地址 IIC_WaitAck(); IIC_SendByte(add); //发送要操作的地址 IIC_WaitAck(); IIC_Stop(); IIC_Start(); IIC_SendByte(0xa1); //发送读操作 IIC_WaitAck(); d = IIC_RecByte(); //读一字节 IIC_Ack(0); IIC_Stop(); return d; } iic.h #ifndef _IIC_H #define _IIC_H //函数声明 void IIC_Start(void); void IIC_Stop(void); void IIC_Ack(bit ackbit); void IIC_SendByte(unsigned char byt); bit IIC_WaitAck(void); unsigned char IIC_RecByte(void); unsigned char Read_AIN(unsigned char chn); void Write_E2PROM(unsigned char add, unsigned char dat); unsigned char Read_E2PROM(unsigned char add); #endif