第九届省赛总体难度不大,和第八届有相似的地方(比如数码管选中闪烁——其实只要使用定时器就可以解决),疑难点主要是LED灯亮的四个等级,其实也是可以通过PWM波来调制的。但我试了一下感觉效果一般,感觉是我还不太会用定时器(新手),就直接采用延时的方法进行这一步。话不多说,接下来将呈现最简洁,通俗易懂的代码。
首先看题目,这届赛题也是只使用了IIC通信这一个底层程序,其中包括Rb2电位器的AD转换和EEPROM的存储和读取。可以先添加底层程序(由官网给出)。
iic.c
#include <stc15f2k60s2.h>
#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;
}
uchar IIC_Read(uchar add)
{
uchar temp;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp=I2CReceiveByte();
I2CWaitAck();
I2CStop();
return temp;
}
void EEPROM_Write(uchar add,uchar dat)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
uchar EEPROM_Read(uchar add)
{
uchar temp;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
temp=I2CReceiveByte();
I2CWaitAck();
I2CStop();
return temp;
}
iic.h
#ifndef _IIC_H
#define _IIC_H
#define uchar unsigned char
#define uint unsigned int
static void I2C_Delay(unsigned char n);
void I2CStart(void);
void I2CStop(void);
void I2CSendByte(unsigned char byt);
unsigned char I2CReceiveByte(void);
unsigned char I2CWaitAck(void);
uchar IIC_Read(uchar add);
void EEPROM_Write(uchar add,uchar dat);
uchar EEPROM_Read(uchar add);
#endif
底层程序代码写完之后,接下来可以写基本运行函数,比如数码管显示,外设初始化,延时函数等
数码管动态显示
void display(void)//动态显示
{
uchar i;
for(i=0;i<8;i++)
{
P0=0x00;
P2=(P2&0x1f)|0xc0;
P0=dis_bit[i];
P2=0;//防止外设冲突
P0=0xff;
P2=(P2&0x1f)|0xf0;
P0=tab[dis_buf[i]];//根据dis_buf[]数组修改值,在tab[]数组中进行段选
P2=0;
Delayms(1);
P0=0xff;//消除鬼影
P2=(P2&0x1f)|0xf0;
P2=0;
}
}
外设初始化与延时函数
void Allinit(void)
{
P2=(P2&0x1f)|0xa0;
P0=0x00;
P2=0;
P2=(P2&0x1f)|0x80;
P0=0xff;
P2=0;
P2=(P2&0x1f)|0xc0;
P0=0x00;
P2=0;
P2=(P2&0x1f)|0xf0;
P0=0xff;
P2=0;
}
void Delayms(uint ms)
{
uint i,j;
for(i=ms;i>0;i--)
for(j=845;j>0;j--);
}
接下来可以按照自己编程的习惯,写独立按键(别忘记修改跳线帽)或者数码显示和Led灯,个人喜欢调用函数的方式,使代码更简洁,主函数如下(有注释讲解):
main.c
#include <stc15f2k60s2.h>
#include <iic.h>
#include <intrins.h>
uchar tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff};
uchar dis_bit[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//位选
uchar dis_buf[]={11,11,11,11,11,11,11,11};//段选
uchar mode1[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//四种工作模式
uchar mode2[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
uchar mode3[]={0x7e,0xbd,0xdb,0xe7};
uchar mode4[]={0x7e,0xbd,0xdb,0xe7};
void display(void);//显示函数
void Delayms(uint ms); //延时函数
void Allinit(void);//外设初始化函数
void keyscan(void);//矩阵键盘扫描
void shuma(void);//数码管显示
void Timer0Init(void);//定时器初始化
void Led_Level_Judge(void);//判断亮度等级
void Led_work(void);//Led工作模式
void delay(uchar t);//空延时函数
uchar AD;//Rb2的输出值
bit qidong=0;//启动1/停止0
uchar mode=0;//LED四种工作模式
uchar shezhi=0;//设置界面 0熄灭 1运行 2间隔
bit yxflag=0;//运行闪烁标志位
bit spanflag=0;//流转间隔闪烁标志位
uint i;//0.8s闪烁
uint tt;//判断是否达到流转时间
uchar index;//模式下的索引
uchar Copyspan;//由于存储只能是0-256,再建一个变量缩小十倍span
uchar finish;//设置完成
uchar t;//改变亮度变化的快慢,持续的时间
uchar pwm;//延迟的占空比
uchar noshezhi=0;//不在设置状态时按下s4
uchar level=0;//亮度等级显示
uint span=400;//流转间隔
void main(void)
{
Allinit();
Timer0Init();
span=EEPROM_Read(0x10)*10;//地址从0x00-0xff都可以进行存储,存进去是/10,读出时要*10
Delayms(10);//读和写EEPROM都应延迟5ms以上,防止太快读写错误
while(1)
{
AD=IIC_Read(0x03);//0x03为rb2电位器的地址
keyscan();
Led_Level_Judge();//亮度等级判断函数
shuma();//数码如何显示函数
display();
}
}
void Led_work(void)//led四种工作模式
{
if(mode==1)
{
P2=(P2&0x1f)|0x80;P0=mode1[index];P2=0;
}
else if(mode==2)
{
P2=(P2&0x1f)|0x80;P0=mode2[7-index];P2=0;
}
else if(mode==3)
{
P2=(P2&0x1f)|0x80;P0=mode3[index];P2=0;
}
else if(mode==4)
{
P2=(P2&0x1f)|0x80;P0=mode4[3-index];P2=0;
}
}
void keyscan(void)//独立按键程序
{
if(P30==0)//s7
{
Delayms(10);
if(P30==0)
{
if(finish==1)
{
qidong=~qidong; //取反
}
}
while(P30==0);//等待按键弹起
}
if(P31==0)//s6
{
Delayms(10);
if(P31==0)
{
shezhi++;finish=0;if(mode==0)mode=1;//防止led还未配置完就亮
if(shezhi==3){shezhi=0;finish=1;index=0;Copyspan=span/10;EEPROM_Write(0x10,Copyspan);Delayms(10);}
}
while(P31==0);
}
if(P32==0)//s5
{
Delayms(10);
if(P32==0)
{
if(shezhi==1)
{
mode++;
if(mode>4)mode=1;
}
else if(shezhi==2)
{
span+=100;
if(span>1200)span=400;
}
}
while(P32==0);
}
//以下是长按操作,有很多写法,当然通常是设置标志位,当然也可以在等待按键弹起里面写
if(P33==0&&(shezhi==1||shezhi==2))//设置模式下的s4
{
Delayms(10);
if(P33==0)
{
if(shezhi==1)
{
mode--;
if(mode<1)mode=4;
}
else if(shezhi==2)
{
span-=100;
if(span<400)span=1200;
}
}
while(P33==0);
}
if(P33==0&&(shezhi!=1||shezhi!=2))//非设置模式下的s4
{
Delayms(10);
if(P33==0)
{
noshezhi=1;
}
}
if(P33==1)
{
noshezhi=0;
}
}
void shuma(void)
{
if(shezhi==0)
{
dis_buf[0]=11;dis_buf[1]=11;dis_buf[2]=11;dis_buf[3]=11;
dis_buf[4]=11;dis_buf[5]=11;dis_buf[6]=11;dis_buf[7]=11;
}
if(shezhi==1&&yxflag==0)//0.8s为间隔闪烁
{
dis_buf[0]=10;dis_buf[1]=mode;dis_buf[2]=10;dis_buf[3]=11;
dis_buf[4]=span/1000;dis_buf[5]=span%1000/100;dis_buf[6]=span%100/10;dis_buf[7]=span%10;
}
else if(shezhi==1&&yxflag==1)
{
dis_buf[0]=11;dis_buf[1]=11;dis_buf[2]=11;dis_buf[3]=11;
dis_buf[4]=span/1000;dis_buf[5]=span%1000/100;dis_buf[6]=span%100/10;dis_buf[7]=span%10;
}
if(shezhi==2&&spanflag==0)
{
dis_buf[0]=10;dis_buf[1]=mode;dis_buf[2]=10;dis_buf[3]=11;
dis_buf[4]=span/1000;dis_buf[5]=span%1000/100;dis_buf[6]=span%100/10;dis_buf[7]=span%10;
}
else if(shezhi==2&&spanflag==1)
{
dis_buf[0]=10;dis_buf[1]=mode;dis_buf[2]=10;dis_buf[3]=11;
dis_buf[4]=11;dis_buf[5]=11;dis_buf[6]=11;dis_buf[7]=11;
}
if(noshezhi==1)//长按显示
{
dis_buf[0]=11;dis_buf[1]=11;dis_buf[2]=11;dis_buf[3]=11;
dis_buf[4]=11;dis_buf[5]=11;dis_buf[6]=10;dis_buf[7]=level;
}
else if(shezhi==0&&noshezhi==0)
{
dis_buf[0]=11;dis_buf[1]=11;dis_buf[2]=11;dis_buf[3]=11;
dis_buf[4]=11;dis_buf[5]=11;dis_buf[6]=11;dis_buf[7]=11;
}
}
void Led_Level_Judge(void)//亮度等级判断函数
{
if(AD>=0&&AD<64){level=1;pwm=25;}
else if(AD>=64&&AD<128){level=2;pwm=50;}
else if(AD>=128&&AD<196){level=3;pwm=75;}
else if(AD>=196&&AD<256){level=4;pwm=100;}
}
void Timer0Init(void) //5毫秒@11.0592MHz //可直接去stc-isp里复制
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初值
TH0 = 0x28; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA=1;
ET0=1;
}
void Tim0(void) interrupt 1
{
if(shezhi==1||shezhi==2)//在设置模式下才开始闪烁
{
i++;
if(i==160)
{
i=0;
if(shezhi==1)yxflag=~yxflag;
else if(shezhi==2)spanflag=~spanflag;
}
}
if(qidong==1&&finish==1)
{
tt++;
if(tt==span/5)
{
for(t=0;t<200;t++)
{
Led_work();
delay(pwm);//通过亮的时间从而来控制亮度
P2=(P2&0x1f)|0x80;P0=0xff;P2=0;
delay(100-pwm);
}
tt=0;index++;//时间到移位
if(mode==1||mode==2)
{
if(index==8)index=0;
}
else if(mode==3||mode==4)
{
if(index==4)index=0;
}
}
}
else if(qidong==0)
{
tt=0;
}
}
void delay(uchar t)//一定要是空循环,不然用软件生成的一闪一闪很明显
{
while(t--);
}
void Allinit(void)
{
P2=(P2&0x1f)|0xa0;
P0=0x00;
P2=0;
P2=(P2&0x1f)|0x80;
P0=0xff;
P2=0;
P2=(P2&0x1f)|0xc0;
P0=0x00;
P2=0;
P2=(P2&0x1f)|0xf0;
P0=0xff;
P2=0;
}
void display(void)
{
uchar i;
for(i=0;i<8;i++)
{
P0=0x00;
P2=(P2&0x1f)|0xc0;
P0=dis_bit[i];
P2=0;
P0=0xff;
P2=(P2&0x1f)|0xf0;
P0=tab[dis_buf[i]];
P2=0;
Delayms(1);
P0=0xff;//消除鬼影
P2=(P2&0x1f)|0xf0;
P2=0;
}
}
void Delayms(uint ms)
{
uint i,j;
for(i=ms;i>0;i--)
for(j=845;j>0;j--);
}
以上就是蓝桥杯单片机第九届省赛的内容,欢迎大佬们的指点。