Pages: 1 [2] 3 4 5   Go Down
Author Topic: [SOLVED] 18B20 timing problem  (Read 6656 times)
0 Members and 1 Guest are viewing this topic.
0
Online Online
Faraday Member
**
Karma: 47
Posts: 5903
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

now i'm very close to the solution,
 mix the two tip by john and markt i'm now have only little bit of display off problem,

befoure i had 750ms of black, now i think only 20ms.

what do you think, where are it from ? conversion time ?

this is actual loop
Code:
void loop(void)
{

 // blink without delay technique
  unsigned long currentMillis = millis();
 
 if(currentMillis - previousMillis > 750)
 {
  previousMillis = currentMillis;
    
    temp=sensors.getTempCByIndex(0); // mette la temperatura nella variabile temp
    sensors.setWaitForConversion(false);  // makes it async
    sensors.requestTemperatures(); // Send the command to get temperatures
    
 }

now i try the robtillart code
« Last Edit: August 04, 2011, 03:37:19 pm by Testato » Logged

- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Code:
unsigned long previousMillis = 0;  // must be outside loop
float temp = 0.0;

//  UPDATED
int timeout = 94 << (resolution - 9);  // calc the timeout for every resolution between 9..12 automatically

void loop(void)
{
  if (millis() - previousMillis > timeout)  
  {
    temp = sensors.getTempCByIndex(0);  
    sensors.setWaitForConversion(false);   <<<<<<<<  strictly not needed every time
    sensors.requestTemperatures();  
    previousMillis = millis();  <<<<<<<<<<<<<<<  moved
  }
  Serial.println(temp, 2);
}
« Last Edit: August 06, 2011, 05:59:02 am by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

0
Online Online
Faraday Member
**
Karma: 47
Posts: 5903
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

rob your code do not work, because your formula for timeout calculation give 376 for 12bit, but the 18B20 sensor need 700-750ms.

BUT this is not a problem, because the display off problem is not related to timeout, if i put 2000ms timeout i receive

............|...........|............|
ehere "I" is the problem, in electronic we call it a Spike

if a change timeout, example to 1000ms:
......|......|.....|.....|......|......|

I think it is related to command "temp=sensors.getTempCByIndex(0);" maybe this command need 20ms and during this 20ms block the uprocessor.

if we use 18B20 normally, we have 750ms uprocessor blocked, if we use it in async we have only 20ms blocked.
maybe it is a OneWire bus limitation ?
Logged

- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

0
Online Online
Faraday Member
**
Karma: 47
Posts: 5903
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I tried normal power supply mode but the problem remain the same  smiley-sad

this is the actual code

Code:
#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 16 (A2) on the Arduino
#define ONE_WIRE_BUS 16


// 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);
 
 // variabili temp
  float temp;
  long previousMillis = 0;


void setup()
{
      
  sensors.begin();
  sensors.setResolution(12);
  sensors.setWaitForConversion(false);  // makes it async asincrono=non blocca il programma durante la lettura
  sensors.requestTemperatures(); // Send the command to get temperatures
 
}



void loop(void)
{

 // blink without delay technique. Il delay normale blocca il processore, questo no

 if (millis() - previousMillis > 750)   // mette la temperatura nella variabile temp dopo 750ms
 {
    temp=sensors.getTempCByIndex(0);
    sensors.requestTemperatures(); // riparte una nuova richiesta, ma solo dopo, e non prima come facevo all'inizio
    previousMillis = millis(); // riparte il temporizzatore
 }
  
« Last Edit: August 06, 2011, 04:12:27 am by Testato » Logged

- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3701
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Can you grab & print millis before and after this statement:
Code:
temp=sensors.getTempCByIndex(0);

and see how long it takes. If your mysterious display device really needs continuous refresh, it may be the culprit in causing the black spot.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
rob your code do not work, because your formula for timeout calculation give 376 for 12bit, but the 18B20 sensor need 700-750ms.

Oops, posted a draft version  smiley-red smiley-red smiley-red 


the correct formula for timeout is   (allready updated the code above)

int timeout = 94 << (resolution - 9);

9 -> 94
10 -> 188  shift 1 == *2
11 -> 376  shift 2 == *4
12 -> 752  shift 3 == *8

while writing this, I think an unsigned int is the most appropiate datatype for timeout
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

0
Online Online
Faraday Member
**
Karma: 47
Posts: 5903
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

i recreate the problem with only arduino uno and 18b20, printing the result on arduino serial monitor.

the result is that during the transmission also the TXled of arduino blink with Spike problem.

if you have a 18b20 it is simple try,

RESULT:
during sensors.getTempCByIndex(0), the Serial line of arduino is blocked

if the serial is blocket there is no way to resolve the problem by sw

this is the code


Code:
#include <OneWire.h>
#include <DallasTemperature.h>

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

// 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);


// variabili temp
  float temp;
  long previousMillis = 0;


void setup(void)
{
   sensors.begin();
  sensors.setResolution(12);
  sensors.setWaitForConversion(false);  // makes it async asincrono=non blocca il programma durante la lettur
  Serial.begin(9600);
}

void loop(void)
{
  

 if (millis() - previousMillis > 750)   // mette la temperatura nella variabile temp dopo 750ms
 {
    temp=sensors.getTempCByIndex(0);
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println("DONE");
    previousMillis = millis(); // riparte il temporizzatore
}
  
Serial.println(temp);
}

p.s. to wildbill my mysterius display are nixie smiley
p.s. to rob, thanks a lot for the formula, it is good for future project, but now here the problem is not the timeout
« Last Edit: August 06, 2011, 06:51:55 am by Testato » Logged

- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

from #17 above
Quote
if we use 18B20 normally, we have 750ms uprocessor blocked, if we use it in async we have only 20ms blocked.
maybe it is a OneWire bus limitation ?

If I look at the code there are several steps to be made to implement the getTempC(), so your assumption may be valid. If time permits I'll dive into it further.
Rob
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3701
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Running your test code and capturing millis gives me a time of ~28ms to run getTempCByIndex, so there indeed is your delay. Sounds like you need some external latching in hardware to hold values for the nixie, although at this point, I can't envisage how it's set up to require such constant updates.
Logged

0
Online Online
Faraday Member
**
Karma: 47
Posts: 5903
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

yes i know that with hardware latching i resolve, there is no way for create a software latch ?

noob question (i came from electronic world, do not have sw base): when a variable await from data (like my "temp") it is not accessible from micro ? why the arduino serial do not send a 0 Zero, during this 28ms ?
Logged

- [Guida] IDE - http://goo.gl/ln6glr
- [Lib] ST7032i LCD I2C - http://goo.gl/GNojT6
- [Lib] PCF8574+HD44780 LCD I2C - http://goo.gl/r7CstH

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3701
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Not sure I really understand the question, but the arduino serial print commands are not asynchronous i.e. when you call them, they block until all data you gave them has been sent to  the serial port. So when the getTempCByIndex routine is running, there will be no outbound traffic on the serial port.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Looking at the OneWire code, the following functions take at least (due to delayMicros()):

- OneWire::reset() - 1000 - 1250 micros  = ~1.5 millis()
- OneWire::write() - ~70 micros / bit  == 0.6 millis per byte
- OneWire::readBit() ~65 micros / bit ==  0.6 millis per byte

with this knowledge we can calc the minimum time of getTempC(), it calls isConnected, which calls readScratchPad, which is the communication workhorse..

Code:
float DallasTemperature::getTempC(uint8_t* deviceAddress)
{
  ScratchPad scratchPad;
  if (isConnected(deviceAddress, scratchPad)) return calculateTemperature(deviceAddress, scratchPad);
  return DEVICE_DISCONNECTED;
}

bool DallasTemperature::isConnected(uint8_t* deviceAddress, uint8_t* scratchPad)
{
  readScratchPad(deviceAddress, scratchPad);
  return (_wire->crc8(scratchPad, 8) == scratchPad[SCRATCHPAD_CRC]);
}

readScratchPad()
{
  // send the command
  _wire->reset();                                 // 1.5 millis
  _wire->select(deviceAddress);           // command + 8 bytes == 9 * 0.6 = 5.4 millis.
  _wire->write(READSCRATCH);            // command == 0.6 millis
  for(int i=0; i<9; i++)                           // 9 bytes *0.6 = 5.4 millis
  {
      scratchPad[i] = _wire->read();
  }
  _wire->reset();                                 // 1.5 millis
}

So only the delayMIcroseconds in the handshake needed for getTempC take at least 14.4 ms. That is allready 70% of the 20 ms you wait. So only 30% of the time it is executing code. During all this IO it continuously clears and sets the interrupt bit, so it won't miss a bit.

Some possible tweaks to increase speed (all disclaimers apply I have not tested this!)

1) remove the last  _wire->reset();  from readScratchpad(),  ==>  win 1.5 millis  
    think this is quite save as all functions reset the wire at their start

2) replace   return (_wire->crc8(scratchPad, smiley-cool == scratchPad[SCRATCHPAD_CRC]);  with    return true;
    just don't check if data is valid ==> gain ?? 1 millis?

3) fetch only the first two bytes when reading scratchpad ==>  screws the protocol  - dangerous!!
    gain 7 *0.6 = 4.2 millis

Mixing these latter 2 could make create something like:
Code:
void readScratchPadFast()
{
  // send the command                      
  _wire->reset();                                 // 1.5 millis
  _wire->select(deviceAddress);           // command + 8 bytes == 9 * 0.6 = 5.4 millis. **
  _wire->write(READSCRATCH);            // command == 0.6 millis
  scratchPad[0] = _wire->read();          // read only the temperature bytes
  scratchPad[1] = _wire->read();          // 2 * 0.6 = 1.2 millis
}

If all these tweaks work and don't harm the protocoll too much you can win approx 6 ms;  
This could bring the time for getTempC from 20 ms down to ~14 ms. ??  (still 8 millis active waiting ..)


AFAIK sending the   _wire->select(deviceAddress);   is needed even with one sensor, otherwise another 5.4 millis might be won. (** in code above)

As said before not tested, no guarantees.  


Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
during sensors.getTempCByIndex(0), the Serial line of arduino is blocked

The onewire lib uses sei() and cli() around every bit transported so it definitely influeces the Serial irq's
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
AFAIK sending the   _wire->select(deviceAddress);   is needed even with one sensor, otherwise another 5.4 millis might be won. (** in code above)

From the DS18B20 datasheet, page 11

SKIP ROM [CCh]
The master can use this command to address all devices on the bus simultaneously without sending out
any ROM code information. For example, the master can make all DS18B20s on the bus perform
simultaneous temperature conversions by issuing a Skip ROM command followed by a Convert T [44h]
command. 
Note that the Read Scratchpad [BEh] command can  follow the Skip ROM command only if there is a
single slave device on the bus. In this case, time is saved by allowing the master  to read from the slave
without sending the device’s 64-bit ROM code.
A Skip ROM command followed by a Read Scratchpad
command will cause a data collision on the bus if there is more than one slave since multiple devices will
attempt to transmit data simultaneously. 


So there seems to be some optimization possible...
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 217
Posts: 13739
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
3) fetch only the first two bytes when reading scratchpad ==>  screws the protocol  - dangerous!!
    gain 7 *0.6 = 4.2 millis

From the DS18B20 datasheet, page 11

READ SCRATCHPAD [BEh]
This command allows the master to read the contents of the scratchpad. The data transfer starts with the
least significant bit of byte 0 and continues  through the scratchpad until the 9th byte (byte 8 – CRC) is
read. The master may issue a reset to terminate reading at any time if only part of the scratchpad data is
needed.


So the protocol may be screwed less than thought before if you do a reset()
And as all functions start with a onewire->reset() this seems to be solved.

Single device can be tested with with getDeviceCount() ==> optional use the device address)

Combined with my prev post void readScratchPadFast for a single device could become:


Code:
void readScratchPadFast(uint8_t* deviceAddress, uint8_t* scratchPad)
{
  // send the command                      
  _wire->reset();                                 // 1.5 millis
  if (devices > 1) _wire->select(deviceAddress);           // command + 8 bytes == 9 * 0.6 = 5.4 millis.
  _wire->write(READSCRATCH);            // command == 0.6 millis
  scratchPad[0] = _wire->read();          // read only the temperature bytes
  scratchPad[1] = _wire->read();          // 2 * 0.6 = 1.2 millis
}

In case of onde device ~3.2 millis active waiting iso 14 ;  That could bring getTempC from 20 ms down to 9 ms.

Worth testing & investigating.  

Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Pages: 1 [2] 3 4 5   Go Up
Jump to: