目录
前言:
由于本身之前有学过单片机的基本内容,并有笔记,所以对于基础的内容就不做赘述,本学习笔记主要是结合国信长天单片机上的各个模块内容来学习,具体是为备考而准备的,同时可以巩固之前所学习的通信方式:IIC,SPI,one-wire等
1.1DS18B20简介:
DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点
其测温范围广泛在-55°C 到 +125°C之间,通信接口:1-Wire(单总线之后会讲),其它特征:可形成总线结构(实现单总线通信)、内置温度报警功能、可寄生供电。
其具体实物图与引脚定义图如下:
实物图:
引脚定义与电路图:
1.1.2内部结构框图学习:
其内部结构框图如下:
可以看到该模块只有三根线组成其中两根线是VCC与GND用来进行供电,其中的数据线只有一根DQ该模块只通过这一根数据线来进行数据交互,其内部主要内容结构如下:
PARASITE POWER CIRCUIT(寄生电源电路):用来实现寄生供电功能,正常情况下DS18B20会用VDD通过下方的二极管来供电,可当VDD不接时,DQ数据线就会接替VDD的作用来通过上方的二极管供电,同时会给CPP电容充电,该CPP的作用是当通过DQ进来的是低电平时,低电平与GND无法对模块进行供电,那么CPP就会担负起内部供电的工作(寄生供电),,当模块实现一些比较耗电的操作时,会给DQ加上强上拉电阻,帮助实现其中的较为耗电的操作。
64-BIT ROM AND 1-wire PORT(64位只读存储器与单总线接口):作为器件地址,用于总线通信的寻址,这里64位地址不可编程皆为只读地址,DQ数据线进来就首先在单总线接口这里,这里相当于一个大门,每次进行数据交互的时候都要经过这个rom,但也可以发送指令跳过此rom,然后进行数据交互,
SCRATCHPAD(暂存器):用于总线的数据交互,当DQ经过这个64位rom后就来到暂存器这里,这个暂存器是一个ram,我们在这里编程写指令用于单片机与DS18B20的信息进行交互,这个暂存器ram与DS18B20中真正工作的部件相连,当我们发送对应的指令时,对应的设备开始工作,这些设备从上往下分别是:温度转换器,报警高触发计算器,报警低触发计算器,分辨率设置(精度设置),CRC生成器。
温度转换器:进行温度转换的设备。
报警高触发计算器,报警低触发计算器:设置温度报警,通常我们自己手写更好,这里报警阈值的设置用到的是EEPROM保存温度报警触发阈值,用EEPROM是因为它有着掉电不丢失的特性所以采用他来存储。
分辨率设置,初始化的分辨率是0.0625当我们对该设备进行修改时,可以改变分辨率最低可改为0.摄氏度,当分辨率变低时,温度转换器的速率也会加快。
CRC生成器:是一种数据校验码,通常来对数据的传输进行校验。
暂存器具体结构如下:
字节0与字节1:用来存储温度转换器转换出来的值共16位,其中高5位为符号位,当高五位全部为0时读到的数据是正的,否则读到的数据为负,中间7位为读到的整数部分,后四位为温度的小数部分。
字节2与字节3:用来存储温度报警的阈值,但是由于该寄存器是ram所以通常会把设定的值写入到EEPROM中,这样下一次上电之后会保证数据不丢失;
字节4:用来进行分辨率设置的,同样也会将其写入到EEPROM中,这样下一次上电之后,就会将EEPROM中的内容复制到暂存器中,保证内容不丢失;
字节5,6,7:保留位,基本不会用到,
字节8:校验位,来校验数据对错。
1.1.3DS18B20实现温度读取的具体过程:
由此我们看完了DS18B20全部的存储结构,DS18B20是通过单总线来进行通讯的,所以我们要实现对暂存器的操作应该学会单总线(下文会有介绍),而对于其中的具体步骤如下:
【1】DS18B20复位。
【2】写入字节0xCC,跳过ROM指令。
【3】写入字节0x44,开始温度转换。
【4】延时700~900ms。
【5】DS18B20复位。
【6】写入字节0xCC,跳过ROM指令。
【7】写入字节0xBE,读取高速暂存器。
【8】读取暂存器的第0字节,即温度数据的LSB。
【9】读取暂存器的第1字节,即温度数据的MSB。
【10】DS18B20复位。表示读取数据结束。
【11】将LSB和MSB整合成为一个16位数据。
【12】判断读取结果的符号,进行正负温度的数据处理
1.2单总线协议:
上面介绍完DS18B20我们来学习一下如何用单总线协议来进行数据的交互,其实对于只是备赛的同学来说,不需要知道单总线协议具体是怎么实现的,因为比赛时,主办方会提供相关的底层协议的驱动代码但是为了本身的学习与就业,对于单总线的学习是非常有必要的,接下来就一起来学习单总线:
在学习具体单总线之前我们来认识一下通信协议:它是制定通信的规则。通信双方按照协议规则进行数据收发。
下图展示了常见的几种通信协议:
对通信方式的具体介绍如下:
全双工:通信双方可以在同一时刻互相传输数据
半双工:通信双方可以互相传输数据,但必须分时复用一根数据线
单工:通信只能有一方发送到另一方,不能反向传输
异步:通信双方各自约定通信速率
同步:通信双方靠一根时钟线来约定通信速率
总线:连接各个设备的数据传输线路(类似于一条马路,把路边各住户连接起来,使住户可以相互交流)
点对点通信:指通信设备只有两个,两个设备之间互相传递信号。
多设备通信:一个总线上面可以挂载多个设备,可以对多个设备进行通信。
1.2.1单总线简介:
单总线是由Dallas公司开发的一种通用数据总线他只有一根通信线:DQ,同时由上图可知他是异步、半双工的通信协议,因此他就有以下的优点:单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以省去设备的VDD线路,此时,供电加通信只需要DQ和GND两根线,非常的节省资源。
具体的设备如下:
1.2.3单总线的具体时序:
初始化:
主机将总线拉低至少480us,然后释放总线,等待15~60us后,存在的从机会拉低总线60~240us以响应主机,之后从机将释放总线
发送一位数据:
主机将总线拉低60~120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60u
接收一位:
主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us
具体的单总线完整的操作时序如下:
温度变换:初始化→跳过ROM →开始温度变换:
温度读取:初始化→跳过ROM →读暂存器→连续的读操作
与DS18B20中操作相对应这里只是更细分的说明了写操作与读操作是怎么实现的。
1.3基础训练内容:
新建工程,编写代码,在CT107D单片机综合训练平台上,实现以下功能:
- 将DS18B20 的底层驱动代码文件正确移植到工程中。
- 循环采样启动DS18B20进行温度转换。
- 将DS18B20 的温度转换结果读出,进行换算,保留1位 小数,并显示在数码管靠右端。
- 设定报警系统,数码管最左边四个表示报警温度,最右边四个表示室内温度,如果室内温度超过报警温度就通过蜂鸣器和LED来报警。
- 注意,在进行DS18B20底层驱动代码文件移植时,需确认单总线的时序参数是否匹配。
具体实现实现结果如下图:
1.4代码部分:
main.c
#include "DS18B20.h"
void main(){
Timer0_Init();//定时器初始化
EXIT0_Init();//外部中断0初始化
EXIT1_Init();//外部中断1初始化
System_Init();//系统初始化
while(1){
Display_Time();//数码管显示温度
warning_for_temp();//报警函数
}
}
Init_System.c
#include "Init_System.h"
void HC138_Select(uchar choice){//IO口控制选择
switch(choice){
case 0: P2 = (P2 & 0x1f) | 0x00; break;
case 4: P2 = (P2 & 0x1f) | 0x80; break;
case 5: P2 = (P2 & 0x1f) | 0xa0; break;
case 6: P2 = (P2 & 0x1f) | 0xc0; break;
case 7: P2 = (P2 & 0x1f) | 0xe0; break;
}
}
void System_Init(){//系统初始化
HC138_Select(4);
P0 = 0xFF;
HC138_Select(5);
P0 = 0x00;
HC138_Select(0);
}
DS18B20.c
#include "DS18B20.h"
#include "SMG.h"
uint temp = 0;//获取温度
uint Alarm_temperature = 230;//设定报警温度
uint warnning_flag=0;//报警标志位
uint Last_temp;//读取温度
void Timer0_Init(){//定时器0初始化
TMOD = 0x00;
TL0 = 0x18;
TH0 = 0xFC;
TR0 = 1;
EA = 1;
ET0 = 1;
PT0 = 0;
}
uint read_temperature(void){//温度读取
uint temp_read=0,LSB=0,MSB=0;
Init_DS18B20();//DS18B20复位
Write_DS18B20(0xcc);//跳过ram指令
Write_DS18B20(0x44);//写入温度转换指令
//Delay_DS18B20(1000);//延时但是已经在定时器中读取了所以不用了
Init_DS18B20();//DS18B20复位
Write_DS18B20(0xcc);//跳过ram指令
Write_DS18B20(0xBE);//读取暂存器指令
LSB = Read_DS18B20();//读取低八位
MSB = Read_DS18B20();//读取高八位
Init_DS18B20();//复位
//对数据进行处理
temp_read = MSB;
temp_read = (temp_read << 8) | LSB;
if((temp_read & 0xF800) == 0x00){
temp_read = temp_read >> 4;
temp_read = temp_read *10;
temp_read = temp_read +(LSB & 0x0F) * 0.625;
}
return temp_read;
}
void warnning_for_LED(uint flag){//LED 报警
if(flag){//大于阈值进行报警
HC138_Select(4);
P0 = 0x00;
}
else if(flag == 0){//小于阈值不进行报警
HC138_Select(4);
P0 = 0xFF;
HC138_Select(0);
}
}
void warnning_for_Buzzer(uint flag){//蜂鸣器报警
if(flag){/大于阈值进行报警
HC138_Select(5);
P0 = 0xFF;
}
else if(flag == 0){//大于阈值不进行报警
HC138_Select(5);
P0 = 0x00;
}
}
void Delay_warning(uchar t) //Display中有延时
{
while(t--){
Display_Time();
}
}
void warning_for_temp(void){//具体报警函数
if(warnning_flag == 1){
warnning_for_Buzzer(1);
warnning_for_LED(1);
Delay_warning(25);
warnning_for_Buzzer(0);
warnning_for_LED(0);
Delay_warning(25);
}
else if(warnning_flag == 0){
warnning_for_Buzzer(0);
warnning_for_LED(0);
}
}
void Timer0_handel(void) interrupt 1{
static uint T0Count=0,i=0;
T0Count++;//计数
if(T0Count>=1000){//每隔1s读取一次温度
T0Count=0;
Last_temp = temp;
temp = read_temperature();
if(Last_temp < Alarm_temperature ){//判断是否达到报警阈值
warnning_flag=0;
i = 0;
}
else {
warnning_flag=1;
}
}
}
SMG.c
#include "SMG.h"
#include "DS18B20.h"
extern uint temp;//显示函数使用
extern uint Last_temp;
extern uint Alarm_temperature
/*0 1 2 3 4 5 6 7 8 9 */
uchar code NixieTube_nodot[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,
/* A B C D E F H L N P */
0x88,0x83,0xC6,0xA1,0x86,0x8E,0x89,0xC7,0xC8,0x8C,
/* U - ' ' */
0xC1,0xBF,0xFF};//无小数点
uchar code NixieTube_havedot[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};//有小数点
void EXIT0_Init(){//外部中断0初始化
EA = 1;
EX0 = 1;
IT0 = 1;
PX0 = 0;
}
void EXIT1_Init(){//外部中断1初始化
EA = 1;
EX1 = 1;
IT1 = 1;
PX1 = 0;
}
void DelaySMG(uchar t){//数码管延时函数
unsigned char data i, j;
while(t--){
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
void SMGTranslate_Bit(uchar pos,uchar value){//显示一位的函数
HC138_Select(7);
P0 = 0xFF;//¶ÔÊýÂë¹Ü½øÐÐÏûÒþ
HC138_Select(6);
P0 = 0x01<<pos;
HC138_Select(7);
P0 = value;
DelaySMG(2);
}
void Display_Time(){//数码管显示函数
Last_temp = temp;
SMGTranslate_Bit(0, NixieTube_nodot[Alarm_temperature/100]);
SMGTranslate_Bit(1, NixieTube_havedot[Alarm_temperature/10%10]);
SMGTranslate_Bit(2, NixieTube_nodot[Alarm_temperature%10]);
SMGTranslate_Bit(3, NixieTube_nodot[12]);
SMGTranslate_Bit(4, NixieTube_nodot[Last_temp/100]);
SMGTranslate_Bit(5, NixieTube_havedot[Last_temp/10%10]);
SMGTranslate_Bit(6, NixieTube_nodot[Last_temp%10]);
SMGTranslate_Bit(7, NixieTube_nodot[12]);
}
void EXIT0_handel(void) interrupt 0{//外部中断0函数当按键按下时报警阈值加0.1
if(Alarm_temperature<1000){
Alarm_temperature++;
}
else {
Alarm_temperature = 230;
}
}
void EXIT1_handel(void) interrupt 2{//外部中断1函数当按键按下时报警阈值加1
if(Alarm_temperature>0){
Alarm_temperature--;
}
else {
Alarm_temperature = 230;
}
}
one_wire.c
/*
程序说明: 单总线驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台
日 期: 2011-8-9
*/
#include "onewire.h"
//单总线延时函数
void Delay_OneWire(unsigned int t)
{
unsigned int i;
while(t--){
for(i = 0; i < 12 ; i++);//本来没有这一句但是由于主办方给出驱动代码为12T的晶振,
//而我们自己的板子晶振为1T所以要延时更长时间才可以让时序合理不紊乱。
}
}
//DS18B20芯片初始化
bit Init_DS18B20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
//通过单总线向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;
}