数码管和按键肯定是必不可少的模块了。
数码管的话,主要是消隐和小数点。
#include "sys.h"
uchar code nixie[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff};
void DisplayBit(uchar pos,value,bit k)
{
P0 = 0X00;
Select_74HC138(6);
Select_74HC138(0);
if(k) //显示小数点
P0 = nixie[value] & 0x7f;
else //不显示小数点
P0 = nixie[value];
Select_74HC138(7);
Select_74HC138(0);
P0 = _crol_(0x01,pos);
Select_74HC138(6);
Select_74HC138(0);
Delay(1);
}
有时侯要显示任意多位数码管(其实这个也不经常用)
//显示任意长度数字
uchar w[8];
void DisplaySMG(unsigned long num)
{
uchar pos,len;
unsigned long temp = 1;
for(len = 0; len < 8; len++)
{
w[len] = num / temp % 10;
temp *= 10;
if(num < temp)
break;
}
len++;
for(pos = 0; pos < len; pos++)
DisplayBit(pos,w[len-1-pos],0);
}
其次就是按键了
独立按键就很好写了
sbit s4 = P3^3;
sbit s5 = P3^2;
sbit s6 = P3^1;
sbit s7 = P3^0;
uchar key_val = 20;
void KeyScans()
{
if(s4 == 0)
{
Delay(10);
key_val = 4;
while(s4 == 0)
{
}
}
if(s5 == 0)
{
Delay(10);
key_val = 5;
while(s5 == 0)
{
}
}
if(s6 == 0)
{
Delay(10);
key_val = 6;
while(s6 == 0)
{
}
}
if(s7 == 0)
{
Delay(10);
key_val = 7;
while(s7 == 0)
{
}
}
}
矩阵按键
sbit h1 = P3^2;
sbit h2 = P3^3;
sbit l1 = P4^4;
sbit l2 = P4^2;
void KeyScans()
{
h1 = 0;
l1 = l2 = h2 = 1;
if(l1 == 0)
{
Delay(10);
key_val = 5;
while(l1 == 0)
{
}
}
if(l2 == 0)
{
Delay(10);
key_val = 9;
while(l2 == 0)
{
}
}
h2 = 0;
h1 = l1 = l2 = 1;
if(l1 == 0)
{
Delay(10);
key_val = 4;
while(l1 == 0)
{
}
}
if(l2 == 0)
{
Delay(10);
key_val = 8;
while(l2 == 0)
{
}
}
}
如果矩阵按键全部要用到的话,用上面那种方法就看着有点点头晕了
sbit l1 = P4^4;
sbit l2 = P4^2;
sbit l3 = P3^5;
sbit l4 = P3^4;
void KeyScans()
{
uchar hang;
for(hang = 0; hang < 4; hang++)
{
P3 = _cror_(0xf7,hang) & 0xff;
if(l1 == 0)
{
Delay(10);
key_val = hang + 4*1;
while(l1 == 0)
{
}
}
if(l2 == 0)
{
Delay(10);
key_val = hang + 4*2;
while(l2 == 0)
{
}
}
if(l3 == 0)
{
Delay(10);
key_val = hang + 4*3;
while(l3 == 0)
{
}
}
if(l4 == 0)
{
Delay(10);
key_val = hang + 4*4;
while(l4 == 0)
{
}
}
}
}
在我看来,最头疼的还是流水灯了,因为控制不好的话它会和数码管冲突
void OperateLed(uchar who,status)
{
static uchar temp;
uchar zt;
switch(status)
{
case 0:
temp = temp | _crol_(0x01,who-1);
P0 = ~temp;
break;
case 1:
if(who == 0xff)
{
P0 = 0xff;
temp = 0;
}
else
{
zt = (~temp) | _crol_(0x01,who-1);
if(temp != (~zt))
temp = ~zt;
P0 = zt;
}
break;
case 2:
P0 = ~who;
temp = who;
break;
default: break;
}
Select_74HC138(4);
Select_74HC138(0);
}
然后就是一些外设(蜂鸣器和继电器)
下面的这种写法,给个0x10就是让继电器吸合了,但是如果想让蜂鸣器响,给个0x50,这不就把继电器给关了嘛,所以可以先定义一个变量stat = 0x00;stat = 0x10; 再stat |= 0x50,就不会了
void Operatews(uchar num,status)
{
static uchar t;
if(status)
{
t = num;
P0 = t;
}
else
{
t = t & (~num);
P0 = t;
}
Select_74HC138(5);
Select_74HC138(0);
}
然后复习的就是NE555频率测量的模块了,记得把跳线帽接在P34和SIGNAL上,不过有一次做题,忘了把跳线帽摘下来,就影响了我的矩阵按键了,因为矩阵按键第4列也接在P34上了,当时还以为是板子又坏了。。
uint count_freq;
uchar t1 = 0;
//定时器0用作做计数
void Timer0Init()
{
AUXR |= 0X80; //定时器时钟12T模式
TMOD &= 0XF0;
TMOD |= 0X04; //设置计数模式
TL0 = 0XFF;
TH0 = 0XFF;
TF0 = 0; //TF0 = 1;表示溢出
TR0 = 1;
ET0 = 1;
EA = 1;
}
//定时器1用作定时
void Timer1Init()
{
AUXR &= 0XBF;
TMOD &= 0X0F;
TL1 = 0XB0;
TH1 = 0X3C;
TF1 = 0;
TR1 = 1;
ET1 = 1;
EA = 1;
}
void ServiceTimer0() interrupt 1
{
count_freq++;
}
void ServiceTimer1() interrupt 3
{
t1++;
if(t1 == 20)
{
t1 = 0;
freq = count_freq;
count_freq = 0;
}
}
iic 应该是我最头疼的了,但是比赛要考的话,也就只有写数据,然后读数据了,应该没啥花样,除非上次看到一套真题智能门锁,我是真的看的就不想写了,等比完赛我再看看那套题。。
IIC读和写
void Write_AT24C02(uchar addr,dat)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
uchar Read_AT24C02(uchar addr)
{
uchar dat;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
dat = IIC_RecByte();
IIC_SendAck(1);
IIC_Stop();
return dat;
}
要读入几个数据的话就写完一个之后适当延时
Write_AT24C02(0x00,dat[0]);
Delay(10);
Write_AT24C02(0x01,dat[1]);
还有最近一个看了一位我们组学长的一次写几个数进去的代码,感觉......可以借鉴
void Write_N_AT24C02(uchar *buf,uchar addr,uchar len)
{
while(len--)
{
do{
IIC_Start();
IIC_SendByte(0xa0);
if(!IIC_WaitAck())
break;
IIC_Stop();
}while(1);
IIC_SendByte(addr++);
IIC_WaitAck();
IIC_SendByte(*buf++);
IIC_WaitAck();
IIC_Stop();
}
}
然后就是PCF8591了,读数据和写数据都和AT24C02差不多,改下地址就好了
我一般都不怎么喜欢写带返回值的
uchar rb2;
void ReadAIN3()
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x03);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
rb2 = IIC_RecByte();
IIC_SendAck(1);
IIC_Stop();
}
DS1302刚开始学的时候感觉它挺难的,当时一直追着老师问,但是总结了,就也不怎么难了
首先是BCD和十进制的相互转换,可能还有更简洁的方法写,但是,我已经习惯这样写了。。
uchar DAT_chg_BCD(uchar dat)
{
uchar dat1,dat2;
dat1 = dat / 10;
dat2 = dat % 10;
dat2 = dat1*16 + dat2;
return dat2;
}
uchar BCD_chg_DAT(uchar dat)
{
uchar dat1,dat2;
dat1 = dat / 16;
dat2 = dat % 16;
dat2 = dat1*10 + dat2;
return dat2;
}
一开始肯定要往里面写一个时间进去了
void Set_DS1302()
{
uchar i;
Write_Ds1302_Byte(0x8e,0x00);
for(i = 0; i < 7; i++)
Write_Ds1302_Byte(WriteReg[i],Time[i]);
Write_Ds1302_Byte(0x8e,0x80);
}
然后把时间读出来
void Get_DS1302()
{
uchar i;
for(i = 0; i < 7; i++)
Time[i] = Read_Ds1302_Byte(ReadReg[i]);
}
当然还要定义一下时间和读写寄存器了
uchar WriteReg[7] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
uchar ReadReg[7] = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};
uchar Time[7] = {55,59,23,0,0,0,0};
然后这个模块就没其他的了。。。
温度传感器DS18B20
unsigned int temp;
void Temper_Get()
{
unsigned char lsb,msb;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
lsb = Read_DS18B20();
msb = Read_DS18B20();
temp = (msb << 8) | lsb;
if((temp & 0xf8000) == 0x0000)
temp = temp >> 4;
}
一位小数
temp = (msb << 8) | lsb;
temp = temp * 0.625 + 0.5;
两位小数
temp = (msb << 8) | lsb;
temp >>= 4;
temp *= 100;
temp = temp + (lsb&0x0f) * 6.25;
if((msb >> 4) == 0x0f)
temp |= 0x8000;
嗯~再就是超声波了
说实话,一接触超声波还是挺害怕的,但是后来发现它和温度传感器也没有啥区别。如果真的不会用它的话,就当成温度传感器用就好了。
void Delay12us() //@12.000MHz
{
unsigned char i;
_nop_();
_nop_();
i = 33;
while (--i);
}
void Send_Sonic()
{
uchar i = 8;
while(i--)
{
TX = 1;
Delay12us();
TX = 0;
Delay12us();
}
}
uint distance;
void Mea_Distance()
{
uint time = 0;
Send_Sonic();
AUXR &= 0XBF;
TMOD &= 0X0F;
TL1 = 0;
TH1 = 0;
TF1 = 0;
TR1 = 1;
while((RX == 1) && (TF1 == 0));//等待超声波信号返回(RX引脚变为低电平)或等超出测量范围
TR1 = 0;
if(TF1 == 0) //正常范围内
{
time = (TH1 << 8) | TL1;
distance = time * 0.0172;
}
else
{
TF1 = 0;
distance = 999;
}
}
uchar t0 = 0;
void Timer0Init()
{
AUXR &= 0X7F;
TMOD &= 0XF0;
TL0 = 0XB0;
TH0 = 0X3C;
TF0 = 0;
TR0 = 1;
ET0 = 1;
EA = 1;
}
void ServiceTimer0() interrupt 1
{
t0++;
if(t0 == 20)
{
t0 = 0;
FLAG_WAVE = 1;
}
}
当然也可以减小测量的时间,这里是1s测量一次。。
差不多就这些吧。。