蓝桥杯单片机组24号就要考试了,是时候把考试用到的每个模块做一个总结了。其实对于省赛来说用到的模块总共就那么几个:
1、数码管
2、按键(独立按键和矩阵按键)
3、DS18B20(温度传感器)
4、DS1302(时钟)
5、AD
6、EEPROM
7、蜂鸣器和继电器
8、LED灯
一共八个模块,在这八个模块里面每年都考的是:按键、数码管、LED灯。
下面就来一个一个模块的说一下(纯属个人总结,仅供参考):
1、数码管:
从往届的题目要求我们可以看出来,数码管要求显示的是:数字1~~9,、字母C(大写)、—(中斜杠),还有一个比较冷门的就是显示数字的时候第八段数码管也要求点亮(就是右下角那一点)。从这里我们可以看出来真正有困难的不在数码管的显示,而是数码管和按键结合的显示。当然了现在我们写的函数肯定都是封装好的了,具体如何封装就看你自己个个人习惯了。
下面给大家看一下我写的函数代码:
#include "shuma.h"
u8 shu_tab[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0XBF,0XFF};//0XC6是英文C
uchar yi,er,san,si,wu,liu,qi,ba;
void Display1_2(uchar yi,uchar er)
{
P2=0XC0;
P0=0X01;
P2=0XFF;
P0=shu_tab[yi];
Delay1ms();Delay1ms();
P2=0XC0;
P0=0X02;
P2=0XFF;
P0=shu_tab[er];
Delay1ms();Delay1ms();
}
void Display4_5(uchar si,uchar wu)
{
P2=0XC0;
P0=0X08;
P2=0XFF;
P0=shu_tab[si];
Delay1ms();Delay1ms();
P2=0XC0;
P0=0X10;
P2=0XFF;
P0=shu_tab[wu];
Delay1ms();Delay1ms();
}
void Display3_6(uchar san,uchar liu)
{
P2=0XC0;
P0=0X04;
P2=0XFF;
P0=shu_tab[san];
Delay1ms();Delay1ms();
P2=0XC0;
P0=0X20;
P2=0XFF;
P0=shu_tab[liu];
Delay1ms();Delay1ms();
}
void Display7_8(uchar qi,uchar ba)
{
P2=0XC0;
P0=0X40;
P2=0XFF;
P0=shu_tab[qi];
Delay1ms();Delay1ms();
P2=0XC0;
P0=0X80;
P2=0XFF;
P0=shu_tab[ba];
Delay1ms();Delay1ms();
P2=0XFF;
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;
P0=0X04;
P2=0XFF;
P0=shu_tab[san];
Delay1ms();Delay1ms();
P2=0XC0;
P0=0X08;
P2=0XFF;
P0=shu_tab[si];
Delay1ms();Delay1ms();
}
void Display5_6(uchar wu,uchar liu)
{
P2=0XC0;
P0=0X10;
P2=0XFF;
P0=shu_tab[wu];
Delay1ms();Delay1ms();
P2=0XC0;
P0=0X20;
P2=0XFF;
P0=shu_tab[liu];
Delay1ms();Delay1ms();
}
上面的代码比较简单我就简单的说一下,数码管的显示控制就要就是P2口和P0口,我们先打开位选端,选中需要显示的数码管,然后打开段选端,选中需要显示的段就可以了。
2、按键
我们可以看看所有的省赛题,每一届的题基本上难点都在于数码管和按键的配合,但是如果把这两者进行比较难度的话,我个人认为如果你把按键搞定了,那么数码管的显示基本上不会有什么问题,主要就在于按键的花样你能不能搞得定。独立按键和矩阵按键难度相差不大,也就是几行代码的差距,因为往届的题中只有一届的题用到了矩阵按键而且还很简单,看来出题还是比较人性化的,如果出矩阵按键应该都不会太难,但是独立按键就比较有花样了,通常都是一个按键实现很多功能。
下面看一下我写的按键的检测函数:
#include "key.h"
void D_keyscan()
{
if(P30==0)
{
Delay5ms();
if(P30==0) //按键S7
{
}
while(!P30);
}
else if(P31==0) //按键S6
{
Delay5ms();
if(P31==0)
{
}
while(!P31);
}
else if(P32==0) //按键S5
{
Delay5ms();
if(P32==0)
{
}
while(!P32);
}
else if(P33==0) //按键S4
{
Delay5ms();
if(P33==0)
{
}
while(!P33);
}
}
void Delay5ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void keysca16()
{
uchar temp;
//第一列按键
P3 = 0X7F;P42 = 1;P44=0;
temp = P3;
temp = temp&0X0F;
if(temp!=0X0F)
{
Delay5ms();
if(temp!=0X0F)
{
temp = P3;
switch(temp)
{
case 0X7E: break;//按键S7按下
case 0X7D: break;//按键S6按下
case 0X7B: break;//按键S5按下
case 0X77: break;//按键S4按下
}
while(temp!=0X0F)
{
temp = P3;
temp = temp&0X0F;
}
}
}
//第二列按键
P3 = 0XBF;P42 = 0;P44 = 1;
temp = P3;
temp = temp&0X0F;
if(temp!=0X0F)
{
Delay5ms();
if(temp!=0X0F)
{
temp = P3;
switch(temp)
{
case 0XBE: break;//按键S11按下
case 0XBD: break;//按键S10按下
case 0XBB: break;//按键S9按下
case 0XB7: break;//按键S8按下
}
while(temp!=0X0F)
{
temp = P3;
temp = temp&0X0F;
}
}
}
//第三列按键
P3 = 0XDF;P42 = 1;
temp = P3;
temp = temp&0X0F;
if(temp!=0X0F)
{
Delay5ms();
if(temp!=0X0F)
{
temp = P3;
switch(temp)
{
case 0XDE: break;//按键S15按下
case 0XDD: break;//按键S14按下
case 0XDB: break;//按键S13按下
case 0XD7: break;//按键S12按下
}
while(temp!=0X0F)
{
temp = P3;
temp = temp&0X0F;
}
}
}
//第四列按键
P3 = 0XEF;
temp = P3;
temp = temp&0X0F;
if(temp!=0X0F)
{
Delay5ms();
if(temp!=0X0F)
{
temp = P3;
switch(temp)
{
case 0XEE: break;//按键S19按下
case 0XED: break;//按键S18按下
case 0XEB: break;//按键S17按下
case 0XE7: break;//按键S16按下
}
while(temp!=0X0F)
{
temp = P3;
temp = temp&0X0F;
}
}
}
}
3、DS18B20(温度传感器)
温度数据的读取相对来说比较简单,唯一的难点在于我们要会改官方给的驱动代码,只要数据读取的格式正确那一般数据的后期显示都不是问题。怎样改官方驱动我已经在另一篇博客了写过了DS18B20官方驱动修改
下面看一下我写的函数:
#include "ds18b20.h"
void DS18Init()
{
DQ=0;
Delay_OneWire(40);
DQ=1;
Delay_OneWire(40);
}
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0; i<8; i++);
}
}
//温度处理函数
uchar Date_Handing()
{
uchar high,low;
uchar temp;
DS18Init();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
Delay_OneWire(40);
DS18Init();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
low=Read_DS18B20();
high=Read_DS18B20();
temp=high<<4;
temp|=(low>>4);
return temp;
}
//向DS写控制字
//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//读取温度存储器的数据
//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
4、DS1302
时钟数据的读取相对来说也比较简单,唯一的难点是我们也要会改官方给的驱动代码,只要数据读取的格式正确那一般数据的后期显示都不是问题。怎样改官方驱动我已经在另一篇博客了写过了DS1302官方驱动修改
下面看一下我写的函数:
#include "ds1302.h"
uchar shijian[7];
uchar code ds[]={50,59,23,10,10,1,16};
void DS13Init()
{
uchar i;
uchar add=0x80;
write_DS(0x8e,0x00);
for(i=0;i<7;i++)
{
write_DS(add,ds[i]);
add=add+2;
}
write_DS(0x8e,0x80);
}
void write_DS(uchar add,uchar dat)
{
uchar num;
CE=0;
SCLK=0;
CE=1;
writebyte(add);
num=(dat/10<<4)|(dat%10);
writebyte(num);
CE=0;
}
//将数据存储到指定数组中
void Read_Time()
{
uchar i;
uchar add=0x81;
write_DS(0x8e,0x00);
for(i=0;i<7;i++)
{
shijian[i]=read(add);
add=add+2;
}
write_DS(0x8e,0x80);
}
uchar read(uchar add)
{
uchar i,temp;
uchar dat_low,dat_high;
CE=0;
SCLK=0;
CE=1;
writebyte(add);
for(i=0;i<8;i++)
{
SCLK=0;
temp>>=1;
if(IO)
{
temp|=0x80;
}
SCLK=1;
}
IO=0;
dat_high=temp/16;
dat_low=temp%16;
temp=dat_high*10+dat_low;
return temp;
}
void writebyte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
SCLK=0;
IO=dat&0x01;
SCLK=1;
dat>>=1;
}
}
DS1302这里有一个地方要注意一下,就是题目中会要求我们赋初值,不要忘记就好。
5、AD
AD采集模块基本上跟上面两个模块的难度相当主要就是官方驱动的修改,一般来说AD采集之后要么是比较,然后让LED灯、继电器、蜂鸣器有某些操作,要么就是在数码管上显示出当前的值,,还有一个值得注意的地方就是AD读取的时间间隔,也就是多长时间读取一次,一般我们是100毫秒读取一次(定时器可以实现)。数码管显示的时候要注意的地方是显示的亮度要均衡不要一个很亮一个很暗,还有一个就是不要显示的乱跳。
下面看一下我写的函数:
#include "ad.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);
}
6、EEPROM
EEPROM和AD几乎一模一样,唯一不一样的地方就是EEPROM有些操作的延时和AD的不同。废话不多说直接看代码:
#include "eeprom.h"
void IICEE_statrt(void)
{
SDA = 1;
_nop_();
SCL = 1;
_nop_();
SDA = 0;
_nop_();
SCL = 0;
}
void IICEE_stop(void)
{
SDA = 0;
_nop_();
SCL = 1;
_nop_();
SDA = 1;
}
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();//一定要加
}
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;
}
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);
}
蜂鸣器、继电器和LED灯我也没有封装函数,因为这些模块的操作一般都比较简单几行代码就可以实现的,到这里为止省赛考试所需要用到的所有代码基本上我都已经写完了,这也意味着最后一波准备已经结束了,接下来就是坐等考试了。