由于疫情的原因比赛被推迟,本人在家比较懒。。所以定期写一次总结以防学习的知识忘记,废话不多说,第一次总结开始!!
DS18B20有三个管脚,DQ为数字信号输入\输出端;GND为电源地;VDD为外供电电源输入端。
DS18B20内部结构主要由四部分组成:64位光刻ROM、温度传感器、温度报警触发器TH和TL、配置寄存器。光刻ROM作用其实就是使每一个DS18B20都各不一样,以便实现一根总线上挂接多个的目的。
DS18B20的核心就是它的直接读数字的温度传感器。上电默认精度为12位(0.0625),总线控制器发出[44h]命令后,温度数据以两个字节的形式被存储到温度寄存器中,DS18B20继续保持等待状态。
[cch]:忽略ROM指令,在单点总线情况下使用该命令,器件无需发回64位ROM编码,从而节省了时间。
[44h]:这条命令用以启动一次温度转换。
[BEH]:这条命令读取暂存器的内容,读取将从字节0开始。
DS18B20的驱动代码比赛中会给出,所以自己只需要写一个读温度函数就好。
unsigned char TemperGet()
{
unsigned char low,high;
unsigned char Temp;
Init_DS18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
Delay500us();
Init_DS18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
low=Read_DS18B20();
high=Read_DS18B20();
//读出温度不需要小数点
temp=high<<4;
temp=(low>>4)|temp;
//需要小数点保留4位时
//temp=(high&0x0f);
//temp<<=8;
//temp|=low;
//temp=temp*625;
return temp;
}
驱动代码里的init_ds18b20()函数可以改为(针对比赛)
void Init_DS18B20()
{
DQ=0;
Delay500us();
DQ=1;
Delay500us();
}
若温度读出结果为0,可将驱动代码中读写函数里的的Delay_OneWire()函数改用为Delay80us();
若读出温度带小数点的话,函数返回类型改为long型且主函数里分位显示要注意。for example 21.1625℃函数会返回211625。
X1 X2为晶振管脚,CE复位管脚(读写期间必须为高),SCLK串行时钟,I/O(三线接口时的双向数据线)
对DS1302的操作就是对其内部寄存器的操作,DS1302内部共有12个寄存器,7个寄存器与日历、时钟相关,存放的数据为压缩BCD码。
写保护寄存器的BIT7:WP是写保护位,工作时出WP外的其他位都置为0,对时钟/日历寄存器或RAM进行写操作之前WP必须为0,当WP为高电平时不能对时钟/日历寄存器或RAM进行操作。
DS1302驱动代码比赛中会给出,自己写的函数如下,
void DS1302_Init()
{
unsigned char i,add;
add=0x80;
Write_DS1302(0X8E,0X00);
for(i=0;i<7;i++)
{
Write_DS1302(add,shijian[i]);
add+=2;
}
Write_DS1302(0X8E,0X80);
}
void DS1302_Get()
{
unsigned char i,add;
add=0x81;
Write_DS1302(0X8E,0X00);
for(i=0;i<7;i++)
{
shijian[i]=Read_DS1302(add);
add+=2;
}
Write_DS1302(0X8E,0X80);
}
注意:定义一个数组shijian[]={50,59,23,0,0,0,0},用来写入初始时间和存储读出时间数据。
Write_Ds1302()函数中注意写入数据应该变成((dat/10<<4)|(dat%10))。
Read_DS1302函数应注意返回的temp值应该分位处理,dat1=temp/16;dat2=temp%16;temp=dat1*10+dat2;
PCF8591具有4个模拟输入(四路采集)、1个模拟输出(数模转换)和一个串行I2C总线接口。采用I2C总线串行输入/输出,采样速率取决于I2C总线速度。
AIN0、AIN1、AIN2、AIN3为模拟输入(analog inputs)引脚,A0、A1、A2用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件。器件的地址、控制和数据通过两线双向I2C总线传输。
地址:I2C总线系统中的每一片PCF8591通过发送有效地址到该器件来激活,该地址包括固定部分和可编程部分。可编程部分必须根据地址引脚 A0、A1 和A2 来设置。在 I2C 总线协议中地址必须是起始条件后作为第一个字节发送。地址字节的最后一位是用于设置以后数据传输方向的读/写位,1读0写。
可以看到PCF8591芯片A0-A2全部接地,所以写操作时地址为0x90,读操作时地址为0x91.
控制字: 发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。如果是DA转换则第二位置1,AD转换第二位置0,三四位控制差分模式,此板子为00,第六位控制自动增量模式,一般不用置0,最后两位控制AD采集通道。
IIC驱动代码比赛会给出,自己写的函数如下:
unsigned char AD_Read(unsigned char add)
{
unsigned char temp;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WateAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
temp=IIC_RecByte();
IIC_Stop();
return temp;
}
void AD_Write(unsigned char add,unsigned char dat)
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WateAck();
IIC_SendByte(dat);
IIC_WateAck();
IIC_Stop();
}
有时候AD输出不准确时可以把驱动代码里的延时函数换成Delay6us().
AT24C02是低工作电压的2K位串行电可擦除只读存储器,内部组织为256个字节每个字节8位。
板子上如图:
板子上只搭载了一个AT24C02,其外设地址为0XA0。A0-A2引脚作用是器件地址输入,SDA为串行数据输入输出,SCL为串行时钟输入,WP为写保护,VCC电源,GND接地。
IIC驱动代码比赛给出,下面是需要写的函数,基本与AD转换函数一致:
void EEPROM_Write(unsigned char add,unsigned char dat)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
unsigned char EEPROM_Read(unsigned char add)
{
unsigned char temp;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
temp=IIC_RecByte();
IIC_Stop();
return temp;
}
采用HC-SR04超声波测距模块。
VCC供5V电源,GND为地线,TRIG触发控制信号输入,ECHO回响信号输出。
板子上用跳线帽将13,24连起来了,所以P10口对应着TRIG,P11口对应着ECHO。超声波时序图如下:
以上时序图表明你只需要提供一个10us以上的脉冲触发信号,该模块内部将发出8个40khz周期的电平并检测回波,一旦有回波信号则输出回响信号,可以通过发射信号到收到回响信号的时间间隔可计算得到距离。可以通过定时器来完成时间采集。
废话不多说上代码:
void Timer0Init()//初始化定时器,测距前先调用。
{
TMOD|=0X00;
TH1=0;
TL1=0;
}
void ShengBo_Distance()//需要定义全局变量Distance
{
uchar i,time;
for(i=0;i<8;i++)
{
Trig=1;
Delay12us();
Trig=0;
Delay12us();
}
TR1=1;//定时器启动
while((Echo==1)&&(TF1==0));//等待回响信号(Echo检测到下降沿)或定时器溢出
TR1=0;//关闭定时器
if(TF1==0)
{
time=TH1;
time=(time<<8)|TL1;
Distance=time*17/1000;
}
else
{
TF1=0;
Distance=99;
}
TH1=0;TL1=0;
}
NE555N只留下了NET_SIG,用跳线帽将P34(定时器0引脚)和其连接。信号频率大小可以通过Rb3可调电位器改变。
需要用到两个定时器,定时器0计数定时器1定时。
void Timer0Init()
{
AUXR|=0X80;
TMOD|=0X05;
TL0=0X00;
TH0=0X00;
TRO=0;
TF0=0;
ET0=0;
}
void Timer1Init(void) //2毫秒@11.0592MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x9A; //设置定时初值
TH1 = 0xA9; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1;
}
void timer1() interrupt 3
{
if(Time==0)//变量Time主函数中定义
{
TR0=1;
Time++;
}
else if(ALLTime<1000)
{
Time++;
}
else//2s到了
{
Time=0;
TR0=0;
FreqCnt=((uint)TH0<<8)|((uint)TL0)/2;
TH0=0;
TL0=0;
}
}