弘毅 发表于 2011-11-17 22:28:58

arduino学习笔记27 - DS1307 RTC时钟芯片与DS18B20数字温度传感器实验

本次实验我使用的是购买的一个DS1307 RTC模块,上面集成了一个DS18B20温度传感器,还集成了另外一个存储芯片~~
先上图





再看下硬件连接图,DS1307是I2C接口SCL接模拟5号口,SDA接模拟4号口。DS18B20是单总线模式,他的DS接口接数字2号口。





DS18B20:



DS18x20系列数字温度传感器主要有DS18S20和DS18B20(DS18S20只有9位一种工作模式,分辨率只到0.5摄氏度,DS18B20有9、10、11、12位四种工作可编程控制的模式,分辨率最高为0.0625摄氏度。),都是由美国Dallas半导体公司(现在改名叫Maxim)生产的。这个系列最大的特点就是采用了Maxim的专利技术1-Wire。顾名思义,1-Wire就是采用单一信号线,但可像I2C,SPI一样,同时传输时钟(clock)又传输数据(data),而且数据传输是双向的。1-Wire 使用较低的数据传输速率,通常是用来沟通小型device,如数位温度计。通过1-Wire技术可以在单一信号线的基础上构成传感器网络,Maxim起名”MicroLan”。

DS18x20的供电主要有两种模式:

Parasite power mode/寄生供电



所谓的寄生供电是指DS18x20只需要两根接线,一根数据线,一根接地线,数据线上还要接一个4.7k上拉电阻连电源,数据线同时也提供了电能。DS18x20内置了电容,高电平期时把电能储存在内部电容里,低电平期内消耗内部电容里的能量工作,直到下次高电平期内再次电容充电。虽然这样的模式简化了线路同时也带来了一些缺陷:

1. 电路的电流一般很小,只有当DS18x20进行温度转化或者写EEPROM时会高达1.5mA,当DS18x20进行上述操作时,数据线必须保持电平拉高状态直到操作结束,期间master端的Arduino不能做任何操作,DS18x20温度转化时这个时间间隔大概是750ms。

2.如果要求DS18x20有精确的转化,数据线在温度转化期间必须保证足够的能量,但当你使用多个DS18x20构成MicroLan进行多点测温时,单靠4.7k的上拉电阻无法提供足够的能量,会导致较大的测温误差。

Normal (external supply) mode/标准(外部供电)



标准外部供电模式,相比寄生供电模式,每个DS18x20需要多一条独立的电源线接独立电源。虽然多用些线,但由于外部供电,保证了每个设备的进精确度和稳定性。而且没有了上述温度转换期间Arduino不能做任何事的问题。

DS18B20的详细介绍就不多讲了,具体可以查看论坛的另一篇帖子http://www.geek-workshop.com/for ... =198&extra=page%3D1

直接进入实战,调用DS18B20,需要使用OneWire库。

把下面代码下载进入arduino控制板。#include <OneWire.h>

// DS18S20 Temperature chip i/o
OneWire ds(2);// on pin 2

void setup(void) {
// initialize inputs/outputs
// start serial port
Serial.begin(9600);
}

void loop(void) {
byte i;
byte present = 0;
byte data;
byte addr;

if ( !ds.search(addr)) {
    Serial.print("No more addresses.\n");
    ds.reset_search();
    return;
}

Serial.print("R=");
for( i = 0; i < 8; i++) {
    Serial.print(addr, HEX);
    Serial.print(" ");
}

if ( OneWire::crc8( addr, 7) != addr) {
    Serial.print("CRC is not valid!\n");
    return;
}

if ( addr == 0x10) {
    Serial.print("Device is a DS18S20 family device.\n");
}
else if ( addr == 0x28) {
    Serial.print("Device is a DS18B20 family device.\n");
}
else {
    Serial.print("Device family is not recognized: 0x");
    Serial.println(addr,HEX);
    return;
}

ds.reset();
ds.select(addr);
ds.write(0x44,1);         // start conversion, with parasite power on at the end

delay(1000);   // maybe 750ms is enough, maybe not
// we might do a ds.depower() here, but the reset will take care of it.

present = ds.reset();
ds.select(addr);
ds.write(0xBE);         // Read Scratchpad

Serial.print("P=");
Serial.print(present,HEX);
Serial.print(" ");
for ( i = 0; i < 9; i++) {         // we need 9 bytes
    data = ds.read();
    Serial.print(data, HEX);
    Serial.print(" ");
}
Serial.print(" CRC=");
Serial.print( OneWire::crc8( data, 8), HEX);
Serial.println();
}代码下载好以后打开串口编辑器,然后就会出现下面这样子的画面。



虽然我们读到了Scratchpad的数据,但是显示的是HEX16进制代码,我们还需要转化成我们能读的温度格式。这里推荐一个叫Dallas Temperature Control的Library,大大简化了这个过程。官方地址:http://www.milesburton.com/?titl ... ure_Control_Library#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");

// Start up the library
sensors.begin();
}

void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");

Serial.print("Temperature for the device 1 (index 0) is: ");
Serial.println(sensors.getTempCByIndex(0));
}代码下载好以后,打开串口监视器,就可以看到当前室温了。



下面我们试用一下DS1307时钟芯片功能。
先把下面库自带测试代码下载进入arduino控制板#include <WProgram.h>
#include <Wire.h>
#include <DS1307.h>

int rtc;
int ledPin =13;
void setup()
{
DDRC|=_BV(2) |_BV(3);// POWER:Vcc Gnd
PORTC |=_BV(3);// VCC PINC3
pinMode(ledPin, OUTPUT);
Serial.begin(9600);

RTC.stop();
RTC.set(DS1307_SEC,1);
RTC.set(DS1307_MIN,57);
RTC.set(DS1307_HR,17);
RTC.set(DS1307_DOW,2);
RTC.set(DS1307_DATE,18);
RTC.set(DS1307_MTH,1);
RTC.set(DS1307_YR,10);
RTC.start();
}

void loop()
{
RTC.get(rtc,true);

for(int i=0; i<7; i++)
{
    Serial.print(rtc);
    Serial.print(" ");
}
Serial.println();
      digitalWrite(ledPin, HIGH);
      delay(500);
      digitalWrite(ledPin, LOW);
      delay(500);
}然后打开串口监视器,就能看到类似下图的样子。


这个模块上还有一个T24C32A EEPROM存储器。。。下面上一个全面一点的代码,对各个期间进行测试。其中刚开始会对I2C器件进行扫描。。。代码不错,大家可以参考下。/**
* I2CScanner.pde -- I2C bus scanner for Arduino
*
* 2009, Tod E. Kurt, http://todbot.com/blog/
*
*/
#include <OneWire.h>
#include "Wire.h"
#include <WProgram.h>
#include <DS1307.h>
#include <avr/io.h>
extern "C" {
#include "utility/twi.h"// from Wire library, so we can do bus scanning
}


byte start_address = 1;
byte end_address = 127;
OneWireds(2);// on pin 2
byte Tdata;
int sensorValue = 0;      // value read from the pot
int rtc;
float TT=0.0;

// Scan the I2C bus between addresses from_addr and to_addr.
// On each address, call the callback function with the address and result.
// If result==0, address was found, otherwise, address wasn't found
// (can use result to potentially get other status on the I2C bus, see twi.c)
// Assumes Wire.begin() has already been called
void scanI2CBus(byte from_addr, byte to_addr,
                void(*callback)(byte address, byte result) )
{
byte rc;
byte data = 0; // not used, just an address to feed to twi_writeTo()
for( byte addr = from_addr; addr <= to_addr; addr++ ) {
    rc = twi_writeTo(addr, &data, 0, 1);
    if(rc==0) callback( addr, rc );
}
}

// Called when address is found in scanI2CBus()
// Feel free to change this as needed
// (like adding I2C comm code to figure out what kind of I2C device is there)
void scanFunc( byte addr, byte result ) {
Serial.print("addr: ");
Serial.print(addr,DEC);
addr = addr<<1;
Serial.print("\t HEX: 0x");
Serial.print(addr,HEX);
Serial.println( (result==0) ? "\t found!":"   ");
//Serial.print( (addr%4) ? "\t":"\n");
}


void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
    int rdata = data;
    Wire.beginTransmission(deviceaddress);
    Wire.send((int)(eeaddress >> 8)); // MSB
    Wire.send((int)(eeaddress & 0xFF)); // LSB
    Wire.send(rdata);
    Wire.endTransmission();
}

// WARNING: address is a page address, 6-bit end will wrap around
// also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.send((int)(eeaddresspage >> 8)); // MSB
    Wire.send((int)(eeaddresspage & 0xFF)); // LSB
    byte c;
    for ( c = 0; c < length; c++)
      Wire.send(data);
    Wire.endTransmission();
}

byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
    byte rdata = 0xFF;
    Wire.beginTransmission(deviceaddress);
    Wire.send((int)(eeaddress >> 8)); // MSB
    Wire.send((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,1);
    if (Wire.available()) rdata = Wire.receive();
    return rdata;
}

// maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.send((int)(eeaddress >> 8)); // MSB
    Wire.send((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,length);
    int c = 0;
    for ( c = 0; c < length; c++ )
      if (Wire.available()) buffer = Wire.receive();
}
void DS1302_SetOut(byte data ) {
    Wire.beginTransmission(B1101000);
    Wire.send(7); // LSB
    Wire.send(data);
    Wire.endTransmission();
}
byte DS1302_GetOut(void) {
    byte rdata = 0xFF;
    Wire.beginTransmission(B1101000);
    Wire.send(7); // LSB
    Wire.endTransmission();
    Wire.requestFrom(B1101000,1);
    if (Wire.available()) {
      rdata = Wire.receive();
      Serial.println(rdata,HEX);
    }
    return rdata;
}
void showtime(void){
byte i;
Serial.print("Time=");
DS1302_SetOut(0x00);
RTC.get(rtc,true);
for(int i=0; i<7; i++)      {
Serial.print(rtc);
Serial.print(" ");
}
}

void readBatVcc(void){
    sensorValue = analogRead(A1);
    TT = sensorValue*0.0047;
    Serial.print("Battery: ");
    Serial.print(TT);
    Serial.print("V");
}
   
// standard Arduino setup()
void setup()
{
    DDRC|=_BV(2) |_BV(3);
    PORTC |=_BV(3);
    Wire.begin();

    Serial.begin(19200);
    Serial.println("--- I2C Bus Scanner Test---");
    Serial.print("starting scanning of I2C bus from ");
    Serial.print(start_address,DEC);
    Serial.print(" to ");
    Serial.print(end_address,DEC);
    Serial.println("...");

    // start the scan, will call "scanFunc()" on result from each address
    scanI2CBus( start_address, end_address, scanFunc );

    Serial.println("\n");
    Serial.println("--- EEPROM Test---");
    char somedata[] = "this is data from the eeprom"; // data to write
    i2c_eeprom_write_page(0x50, 0, (byte *)somedata, sizeof(somedata)); // write to EEPROM
    delay(100); //add a small delay
    Serial.println("Written Done");   
    delay(10);
    Serial.print("Read EERPOM:");
    byte b = i2c_eeprom_read_byte(0x50, 0); // access the first address from the memory
    int addr=0; //first address
    while (b!=0)
    {
      Serial.print((char)b); //print content to serial port
      addr++; //increase address
      b = i2c_eeprom_read_byte(0x50, addr); //access an address from the memory
    }
   Serial.println("\n");
Serial.println("");
Serial.println("--- DS11307 RTC Test---");
showtime();
if(rtc<2011){
    RTC.stop();
    RTC.set(DS1307_SEC,1);
    RTC.set(DS1307_MIN,52);
    RTC.set(DS1307_HR,16);
    RTC.set(DS1307_DOW,2);
    RTC.set(DS1307_DATE,25);
    RTC.set(DS1307_MTH,1);
    RTC.set(DS1307_YR,11);
    RTC.start();
    Serial.println("SetTime:");
    showtime();   
}

Serial.println("\n\n");
Serial.println("--- Reserve Power Test---");
Serial.println("Close POWER!:");
   PORTC &=~_BV(3);
   byte time;
   for(time=0;time<5;time++){
   digitalWrite(13,HIGH);
   delay(500);
   digitalWrite(13,LOW);
   delay(500);   
   readBatVcc();
   Serial.println("");
   }
    PORTC |=_BV(3);
   Serial.println("\nPOWER On!");
      delay(500);
      showtime();
      
    Serial.println("\n");
    Serial.println("===Done   ===");
    Serial.println("\n");
}

// standard Arduino loop()
void loop()
{
    byte i;
    byte present = 0;
    unsigned int Temper=0;

   
readBatVcc();
   
    ds.reset();
    ds.write(0xCC,1);
    ds.write(0x44,1);         // start conversion, with parasite power on at the end
    digitalWrite(13,HIGH);
    delay(450);
    digitalWrite(13,LOW);
    delay(450);
    present = ds.reset();
    ds.write(0xCC,1);   
    ds.write(0xBE);         // Read Scratchpad
    for ( i = 0; i < 9; i++) {         // we need 9 bytes
      Tdata = ds.read();
    }   
    Temper = (Tdata<<8 | Tdata);
    TT =Temper*0.0625;
    if(TT>200){
   Serial.println("\t DS18B20 Not installed!");
    }else{
      Serial.print("\t Temperature=");
      Serial.println(TT);
    }
    Serial.println("");
}然后打开串口监视器,波特率要调节为19200.


附件是这次需要用到的库(适用于0022与0023 IDE):





补充1.0.1下可用的DS180B20库

hzpyj 发表于 2012-8-24 08:43:05

本例OneWire的库编译中有点问题,无法通过,需更新为V2.1版本

NaceMichael 发表于 2011-11-26 22:49:45

谢谢!标记学习!

プ汏坏疍 发表于 2011-11-30 14:06:47

呵呵,对这比较感兴趣,打算买个回来玩。

プ汏坏疍 发表于 2011-12-10 22:48:47

激动,今天收到了从淘宝买来的时钟模块了。打算做一个这个出来。嘎嘎。

milkgirl 发表于 2012-1-14 17:51:14

请教一下,I2C接口SCL接模拟5号口,SDA接模拟4号口。I2C接口是不是指4、5 pin ?求教

弘毅 发表于 2012-1-14 18:22:20

milkgirl 发表于 2012-1-14 17:51 static/image/common/back.gif
请教一下,I2C接口SCL接模拟5号口,SDA接模拟4号口。I2C接口是不是指4、5 pin ?求教

I2C口就是A4和A5

milkgirl 发表于 2012-1-14 19:10:14

:loveliness: 谢谢解答~谢谢

河山 发表于 2012-4-16 15:28:31

请教怎么设定N个温度到一定数值就输出N个信号呢?

MicroCao 发表于 2012-5-2 14:50:09

在淘宝上看到这个模块了,淘宝上的商品介绍很简单。
1、不知道为什么要在时间模块里加测温模块?
2、所说里面还加了24C56存储模块,这又是为了什么?
总感觉把时间、温度、存储这三种做在同一个模块里看着别扭。

弘毅 发表于 2012-5-2 14:52:58

MicroCao 发表于 2012-5-2 14:50 static/image/common/back.gif
在淘宝上看到这个模块了,淘宝上的商品介绍很简单。
1、不知道为什么要在时间模块里加测温模块?
2、所说 ...

额。。。。设计者设计为3合1,其实可以分开的。。。

MicroCao 发表于 2012-5-2 15:01:42

据卖家的商品介绍,此模块“解决DS1307带备用电池不能读写的问题”,是不是就是模块上加了锂电后,ARDUINO只需要通过A4、A5两条I2C线(不接VCC、GND)就可以读写模块的时间了?
真若这样倒是简化了模块的连接线。

夏异 发表于 2012-7-13 20:45:19

学习一下了

夏异 发表于 2012-7-13 23:22:44

本帖最后由 夏异 于 2012-7-13 23:34 编辑

我怎么已编译就这么多错误,好像这个库件有问题

弘毅 发表于 2012-7-13 23:29:48

夏异 发表于 2012-7-13 23:22 static/image/common/back.gif
我怎么已编译就这么多错误,好像这个库件有问题

这个需要把附件的库全部放进去。当时环境是0022 IDE,1.0下估计会有报错问题。

夏异 发表于 2012-7-13 23:32:49

弘毅 发表于 2012-7-13 23:29 static/image/common/back.gif
这个需要把附件的库全部放进去。当时环境是0022 IDE,1.0下估计会有报错问题。

库我已经加进去了,可能是编译环境的缘故
页: [1] 2 3 4 5 6
查看完整版本: arduino学习笔记27 - DS1307 RTC时钟芯片与DS18B20数字温度传感器实验