超声波
超声波测距离的原理非常简单,单片机的一个引脚连到发射模块 发出一定频率的信号,此时打开定时器开始计时,如果发射模块发出的声波被物体反射回来,就会在接收端连出的一极产生下降沿,此时读取定时器时间 知道了来回时间和声速,就可以算出距离并显示出来。
这听起来非常简单明了,所以我在弄这个模块的时候也有点掉以轻心,直接一整份打完,下载调试,结果调试了好长时间。。。
在这里吸取一点教训,对于没用过的,不熟悉的东西,一定要打一点,测试一下,否则调试的时候,错误的可能性太多,不好找
我出问题的地方是在发射的频率上,官方给的超声波资料基本没谈怎么用,所以我上网搜了一下,一份资料里说发射频率一般是40khz,我就照办了
然而我后面仔细看过之后,发现官方板子超声波模块选用的电阻电容和那份资料并不太一样,因此使发射频率上升到了50khz,所以我自然就一直调试不正常。。。
我是用下降沿触发外部中断,在外部中断里读取时间的,所以连线时要把P3^2和接收端连载一起,接收端在动态数码管上面,具体哪个看原理图。。。
DS18B20
控制ds18b20的协议如下:
①:初始化
②:rom操作命令
③:存储器操作命令
④:执行
注:每次使ds18b20执行一次存储器操作命令都得按上面流程来一次,不能连着写两个存储器操作命令
各部分命令看后面参考资料P9.
具体实现:
init_ds18b20();//初始化
Write_DS18B20(0xcc);//0xcc命令,跳过配对rom(因为只有一个)
Write_DS18B20(0x44);//转换温度命令,最长500ms,如果是不断循环检测就不用delay等待
init_ds18b20();//同上
Write_DS18B20(0xcc);//同上
Write_DS18B20(0xbe);//读取存储器命令,从0字节到8字节,0,1是温度值,故下面读两字节就初始化,后面的不需要了
templ=Read_DS18B20();// 先读到低字节
temph=Read_DS18B20();
init_ds18b20();
temp=temph;
temp<<=8;
temp|=templ;//以上操作把两个字节整合成一个int型变量temp(keil貌似int16位)
return temp;
得到了temp之后,只要稍加处理就会变成所需温度的值了
若temp为负数,考虑到temp是以补码形式存储,(补码:负数的补码为其正数对应的原码取反加1,例:- 5的8位补码为+5对应原码00000101取反11111010加1=11111011. 正数补码为其本身)补码转原码:易知补码首位为1即为负数,为0则正数,如补码11111001,先减1再取反得00000111,考虑到为负数,将首位变为1即得原码(原码首位0,1分别代表正负)
若为正数,直接处理,因为默认为12位精度,16位中有8位代表整数部分,四位为小数部分,还有四位没用,即temp*0.0625(即1/16)即得温度值
参考资料:点击打开链接
代码:点击打开链接
AT24C02
驱动程序照着时序图写或上网找,这里不说了
24c02是按照i2c通信的,
在此谈一下鄙人关于i2c的认识:
首先需一个起始信号表示操作开始:先将sda和scl均置高(释放总线)再将sda置0,即在scl为高时sda低跳变,就是开始信号 顺便把scl拉低。
开始写一字节:在scl为低时把sda赋好数据,scl上升沿后写过去,再将scl置低,sda装载数据,如此重复,写完8位后,释放总线,等待应答 ,最后把scl置0
等待ack:等待从件把sda拉低 表示接受成功
读一个字节:先将sda置高(个人理解:若读字节时是下降沿后读到信号,该信号靠从件发出,若主机sda拉低,从件是无力拉高的,因此主机先将sda拉高)此后与写类似,只不过有可能下降沿后读,结束后把scl置0
读后也需要释放总线
结束信号:先将scl,sda均置低(释放总线,若上述均保证结束时scl为0,则不需置scl)再将scl置高,此时sda上升跳变,就是结束信号
具体实现:
向某个地址写一个数据:
void At24c02Write(uchar addr,uchar dat)
{
I2C_Start();//起始信号 ·
I2C_SendByte(0xa0,1);//器件寻址,1表等待响应,0则略过,另外:器件地址高四位固定为1010,低四位中最低位为写入与读取的选择,
//其余三位为器件选择,这里全部接地所以为0
I2C_SendByte(addr,1);//写入数据的目标地址
I2C_SendByte(dat,1);//要写的数据
I2C_Stop(); //停止信号 ,此后器件进入擦写工作,期间不接受主机的信号
}
向某个地址读一个数据:
<pre name="code" class="cpp">uchar At24c02Read(uchar addr)
{
uchar dat=0;
I2C_Start();//起始信号
I2C_SendByte(0xa0,1);
I2C_SendByte(addr,1);//假写入 ,地址即为要读的地址,具体看所含芯片文档
I2C_Start();//起始信号
I2C_SendByte(0xa1,1); //最低位变1,设为读取模式
dat=I2C_ReadByte(); //接受数据
I2C_Stop();//停止信号
return dat;
}
接着上次读或写的地址读数据:
uchar At24c02Read_next()
{
uchar dat=0;
I2C_Start();
I2C_SendByte(0xa1,1);//直接读命令,接着上一次读写的地址读数据
dat=I2C_ReadByte();
I2C_Stop();
return dat;
}
注意:写入操作后一段时间不能对24c02进行任何操作(正在擦写) 需延时10ms左右
代码:点击打开链接
DS1302
驱动程序照着时序图写或上网找(下面也有的下),这里不说了
需对DS1302进行的操作
①关保护:通过8eH将写保护去掉,这样我们才能将日期,时间的初值写时各个寄存器。
②赋初值:对80H、82H、84H、86H、88H、8AH、8CH进行初值的写入。同时也通过秒寄存器将位7的CH值改成0,这样DS1302就开始走时运行了。
③开保护:将写保护寄存器再写为80H,防止误改写寄存器的值
④读数值:不断读取80H-8CH的值,将它们格式化后显示出来。
<pre name="code" class="html"><pre name="code" class="cpp"><pre name="code" class="objc">uchar write_addr[]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
uchar write_data[]={0x00,0x00,0x20,0x16,0x06,0x04,0x16} ;
uchar read_addr[]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d} ;
uchar read_data[7]={0,0,0,0,0,0,0};
void set_time()
{
uchar write_addr[]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
uchar write_data[]={0x00,0x00,0x20,0x16,0x06,0x04,0x16} ;
uchar read_addr[]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d} ;
uchar read_data[7]={0,0,0,0,0,0,0};
void set_time()
{
uchar i;
Ds1302_Single_Byte_Write(0x8e,0x00);//关保护
for(i=0;i<7;i++)
{
Ds1302_Single_Byte_Write(write_addr[i],write_data[i]);//写初值
}
Ds1302_Single_Byte_Write(0x8e,0x80);//开保护}
void read_time()
{
uchar i;
for(i=0;i<7;i++)
{
read_data[i]=Ds1302_Single_Byte_Read(read_addr[i]);//读取
}
} <span style="font-family: 宋体, Arial;"> </span><span style="font-family: 宋体, Arial;"> </span><span style="font-family: 宋体, Arial;"> </span>
关于驱动的一点问题
/单字节读出一字节数据*/
unsigned char Read_Ds1302_Byte(void)
{
unsigned char i, dat=0;
for (i=0;i<8;i++)
{
dat = dat >> 1;
if (SDA_R) //if(SDA_R==1) #define SDA_R SDA /*电平读取*/
{
dat |= 0x80;
}
else
{
dat &= 0x7F;
}
SCK_SET;//时钟置高
SCK_CLR;//时钟置低
} SDA_CLR;//!!!!!!!!!!!!!!!!!!!!!!!!这里有时候不打会显示问号,最好还是把这个数据线拉低
return dat;
}