www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik DS1820 an ATtiny25 in C, Massive Timingprobleme


Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich versuche nun schon mehrere Wochen einen DS1820 per 1-wire an einen 
TINY25 zu koppeln, schaffe es jedoch nie eine Temperatur auszulesen.
Das ganze soll ein kleines Thermometer werden. Ich habe schon viel 
Erfahrung in der µC-Programmierung und einiges versucht. Das Problem 
sind wohl immer die Timings (wie eine Oszi-Messung bestätigt).

Habe schon folgendes gemacht (CLK: 8Mhz, interner Quarz):
- Timer verwendet (Vorteiler 16, Wartezeit/2, da nur 8-Bit-Timer)
- Timer verwendet (Vorteiler 8, wenn Wartezeit <255 sonst: Vorteiler 16, 
Wartezeit/2)
Beides mit und ohne Interrupt. Alles scheinbar zu ungenau.

- _delay_us
- _delay_loop
- _delay_loop_2 (lt. Doku braucht sie 4 Takte, also Wartezeit*2 + 
while(us--))
- per _asm_ volatile ("nop"); mit verschiedenen nop-Anzahlen und 
while(us--) nop();

Hat alles bisher nicht geklappt! Hat evtl. jemand eine Idee oder sogar 
Code (wenn möglich in C), ohne dass ich noch weitere Bauteile 
"anschweißen" muss.

Vielen lieben Dank!
Gruß
Erik

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn das Timing nicht stimmt, prüfe erstmal, ob es wirklich 8MHz sind.

In der Codesammlung ist ein altes Beispiel, wo ich mir das Delay noch 
selber gebastelt hab.
Ich würde aber dazu raten, die jetzigen Delay-Macros des AVR-GCC zu 
nehmen, die sind ausreichend genau.
Natürlich muß man -Os benutzen.

Was Du nicht benutzen darfst, sind long long Variablen (64 Bit), da 
verpulvert der AVR-GCC Unmengen an CPU-Zeit und Flash, sogar float ist 
schneller.


Peter

Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hatte bei einem Projekt ähnliche Probleme. Da lag es an den 
aktivierten Interrupts, die das Timing störten. Also vor jedem Zugriff 
auf die Sensoren erst die Interrupts abschalten.

Ansonsten habe ich das einfach mit delays "sequentiell" gelöst und mit 
einem Logic Analyzer kontrolliert. Durch den niedrigen Takt bei meinem 
Projekt beeinflussten schon die Funktionsaurufe zum Schreiben der 
einzelnen Bytes das Timing, d.h. ich musste die delays kürzer machen.

Ansonsten solltest du noch ein paar Infos geben.
Nutzt du 3 Pins des Sensors (VCC, Data, GND) oder nur 2? Wenn du 2 Pins 
benutzt, musst du die Pins VCC und GND verbinden und auf GND legen. 
Deine Software muss entsprechend angepasst sein so dass sie den Sensor 
während der Messung mit Strom versorgt (für ich glaube 750ms). In dieser 
Zeit darf die Leitung nicht Low werden, sonst ließt du nur 85 Grad.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann es sein, daß Delays in diesem Fall ziemlicher Murks sind?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Kann es sein, daß Delays in diesem Fall ziemlicher Murks sind?

Nein.

Peter

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Kann es sein, daß Delays in diesem Fall ziemlicher Murks sind?

Eigentlich nicht.
Peters Code hat bei mir auf Anhieb funktioniert.

Allerdings hab ich auch Probleme, wenn Interrupts erlaubt sind -> cli / 
sei verwenden.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, hat hoffentlich niemand persönlich genommen.

Aber es ist doch so, daß man ein genaues Timing mit _delay...
nicht hinbekommt und gleichzeitig noch etwas anders machen kann
als die Temperatur zu lesen; also kann man ausschließlich
sequentiell arbeiten.
Wenn das reicht, kann man es natürlich so machen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:

> Aber es ist doch so, daß man ein genaues Timing mit _delay...
> nicht hinbekommt und gleichzeitig noch etwas anders machen kann
> als die Temperatur zu lesen; also kann man ausschließlich
> sequentiell arbeiten.

Grundsätzlich richtig.

Ich bin da pragmatisch vorgegangen:

Den DS1820 auslesen sind nur wenige Bytes hin und her
Pro Byte sind Interrupts gesperrt.
Das Auslesen passiert alle heiligen Zeiten mal (alle 8 Sekunden)
Bei mir ist das schnell genug, dass ich einen anderen wichtigen
Interrupt nicht versäume (INT0, 50Hz generiert aus 230V, für die Uhr)
Der Rest ist nicht zeitkritisch.
Und am wichtigsten: Der Code war schon da :-)

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Aber es ist doch so, daß man ein genaues Timing mit _delay...
> nicht hinbekommt und gleichzeitig noch etwas anders machen kann
> als die Temperatur zu lesen; also kann man ausschließlich
> sequentiell arbeiten.

So genau muß das ja auch nicht sein.

Der Resetpuls kann 480 ... 960µs lang sein.
Wenn also alle Interrupts zusammen <480µs dauern, braucht man dabei die 
Interrupts nicht zu sperren.

Die Daten werden dann bitweise übertragen, da empfiehlt es sich die 
Interrupts für ein Bit (60µs) zu sperren.
60µs Interruptlatenz sollten in der Regel zu verkraften sein.


Peter

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, Ende der Diskussion, ich nehme alles zurück.
Ich hatte in meiner geistigen Umnachtung nicht den DS1820 vor
Augen, sondern den TSic306. Da geht es wesentlich genauer zu.
Hier müsste es auch entspannter klappen.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Um auch etwas positives beizutragen:
Hier die relevanten Abschnitte aus einem erfolgreichen Versuch mit einem 
DS18S20:

#include <avr/io.h>
#include <inttypes.h>
#include <util/delay.h>

typedef union
{
  uint8_t     byte;
  struct
  {
    unsigned int bit0:1;
    unsigned int bit1:1;
    unsigned int bit2:1;
    unsigned int bit3:1;
    unsigned int bit4:1;
    unsigned int bit5:1;
    unsigned int bit6:1;
    unsigned int bit7:1;
  } s;
} byte8bits_t;


#ifdef DDRA
#define ddra  (*(volatile byte8bits_t*)(&DDRA))
#define porta (*(volatile byte8bits_t*)(&PORTA))
#define pina  (*(volatile byte8bits_t*)(&PINA))
#endif /* ifdef DDRA */

#ifdef DDRB
#define ddrb  (*(volatile byte8bits_t*)(&DDRB))
#define portb (*(volatile byte8bits_t*)(&PORTB))
#define pinb  (*(volatile byte8bits_t*)(&PINB))
#endif /* ifdef DDRB */

#ifdef DDRC
#define ddrc  (*(volatile byte8bits_t*)(&DDRC))
#define portc (*(volatile byte8bits_t*)(&PORTC))
#define pinc  (*(volatile byte8bits_t*)(&PINC))
#endif /* ifdef DDRC */

#ifdef DDRD
#define ddrd  (*(volatile byte8bits_t*)(&DDRD))
#define portd (*(volatile byte8bits_t*)(&PORTD))
#define pind  (*(volatile byte8bits_t*)(&PIND))
#endif /* ifdef DDRD */



#define DDR_DS18S20_VCC     ddrd.s.bit1
#define PORT_DS18S20_VCC    portd.s.bit1
#define PIN_DS18S20_VCC     pind.s.bit1

#define DDR_DS18S20_DQ      ddrd.s.bit2
#define PORT_DS18S20_DQ     portd.s.bit2
#define PIN_DS18S20_DQ      pind.s.bit2




uint8_t reset1wire()
{
  int cnt_wait = 0;
  PORT_DS18S20_DQ = 0;
  _delay_us( 480 );
  PORT_DS18S20_DQ = 1;
  // jetzt wartet der slave 15...60 usec und zieht dann die Leitung
  // für 60...240 sec auf low (presence pulse).
  // Also Pin als Eingang setzen, 60 usec warten und dann warten,
  // bis die Leitung wieder hoch geht:
  DDR_DS18S20_DQ  = 0;
  _delay_us( 60 );
  while( 1 )
  {
    if( PIN_DS18S20_DQ )
    {
      break;
    }
    cnt_wait++;
    _delay_us( 1 );
  }
  DDR_DS18S20_DQ  = 1; // auf Ausgang setzen
  PORT_DS18S20_DQ = 1; // high
  return cnt_wait;
}


void putc1wire( uint8_t c )
{
  for( uint8_t iBit=0; iBit<8; ++iBit )
  {
    if( c&1 )
    {
      // 1 schreiben
      // Leitung auf low, gleich wieder (maximal in 15 usec, aber
      // warum nicht gleich?) wieder auf high und 60 usec warten:
      PORT_DS18S20_DQ = 0;
      _delay_us( 1 );
      PORT_DS18S20_DQ = 1;
      _delay_us( 60 );
    }
    else
    {
      // 0 schreiben
      // Leitung auf low, 60 usec warten, dann wieder auf high:
      PORT_DS18S20_DQ = 0;
      _delay_us( 60 );
      PORT_DS18S20_DQ = 1; // high
    }
    _delay_us( 1 ); // Sicherheitsabstand zwischen zwei Bits
    c >>= 1;
  }
}


// liest ein Byte
uint8_t getc1wire()
{
  uint8_t   ret = 0;
  for( uint8_t iBit=0; iBit<8; ++iBit )
  {
    ret >>= 1;

    // Leitung auf low, gleich wieder (nach min. 1 usec) wieder auf
    // high und innerhalb der nächsten 15 usec lesen:
    PORT_DS18S20_DQ = 0;
    _delay_us( 1 );
    PORT_DS18S20_DQ = 1;
    DDR_DS18S20_DQ  = 0;
    _delay_us( 10 );
    if( PIN_DS18S20_DQ )
    {
      // 1 gelesen
      ret |= 0b10000000;
    }
    // den Rest auf 60 usec warten:
    _delay_us( 60 );
    DDR_DS18S20_DQ  = 1; // Ausgang

    _delay_us( 1 ); // Sicherheitsabstand zwischen zwei Bits
  }
  return ret;
}


// liest ein Bit
uint8_t getb1wire()
{
  uint8_t   ret = 0;
  // Leitung auf low, gleich wieder (nach min. 1 usec) wieder auf
  // high und innerhalb der nächsten 15 usec lesen:
  PORT_DS18S20_DQ = 0;
  _delay_us( 1 );
  PORT_DS18S20_DQ = 1;
  DDR_DS18S20_DQ = 0; // Eingang
  _delay_us( 10 );
  if( PIN_DS18S20_DQ )
  {
    // 1 gelesen
    ret |= 0b1;
  }
  // den Rest auf 60 usec warten:
  _delay_us( 60 );
  DDR_DS18S20_DQ = 1; // Ausgang

  _delay_us( 1 ); // Sicherheitsabstand zwischen zwei Bits
  return ret;
}



int main( int nargs, char **args )
{

  // Pin DQ erstmal als Ausgang und high setzen:
  DDR_DS18S20_DQ  = 1;
  PORT_DS18S20_DQ = 1;

  DDR_DS18S20_VCC  = 1;  // VCC für DS18S20 ausschalten
  PORT_DS18S20_VCC = 0;

  reset1wire();


  while( 1 )
  {
    uint8_t   t_ist; // IST-Temperatur [grad C]

    reset1wire();
    // Initialization:
    PORT_DS18S20_VCC  = 1;   // VCC einschalten
    // 1. reset pulse (mind. 480 usec low)
    // presence pulse ist rum

    // ROM Command
    // READ ROM (0x33) kann nur verwendet werden, wenn es nur einen
    // slave gibt; danach könnte man das ROM lesen.
    // SKIP ROM (0xCC) kann nur verwendet werden, wenn es nur einen
    // slave gibt.
    //putc1wire( 0x33 );
    putc1wire( 0xCC );

    // Function Command
    // convert command
    putc1wire( 0x44 );

    // warten, bis ein 1-Bit gelesen wird:
    while( !getb1wire() )
    {
      _delay_us( 1 );
      // TODO: notfalls abbrechen!
    }

    // jetzt wieder Reset und Daten lesen:
    reset1wire();
    putc1wire( 0xCC ); // SKIP ROM
    putc1wire( 0xBE ); // READ SCRATCHPAD

    uint8_t    data1wire[9];
    for( int iData=0; iData<9; ++iData )
    {
      data1wire[iData] = getc1wire();
    }
    PORT_DS18S20_VCC  = 0;   // VCC ausschalten

    // Temperatur in Grad C
    t_ist = ( ( (int)data1wire[1] << 8 ) | data1wire[0] )/2;
    _delay_ms( 1000 );
  }

  return 0;
}

Das hat als C++ vor etwa einem halben Jahr auf einem Mega8 funktioniert
und ist hier auf das DS18S20-Relevante eingedampft.
Es ist so als C kompilierbar, getestet habe ich es aber nicht
mehr weiter.

Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, erstmal vielen Dank soweit!
Werde mal schauen, ob der Code läuft.
Der Witz ist, ich habe keine Interrupts mehr an und der DS1820 ist mit 
VCC und GND verbunden (also kein Parasiten-Power :-) )
Habe es auch schon mit "_delay_us( 480 );"... versucht, hat aber 
irgendwie nicht geklappt.
Evtl. steckt der Teufel ja irgendwo im Detail.
Werde es testen und ggf. nochmal SOS rufen!

Gruß
Erik

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik schrieb:
> Habe es auch schon mit "_delay_us( 480 );"... versucht, hat aber
> irgendwie nicht geklappt.

Dann stimmt Deine F_CPU Definition nicht mit der tatsächlichen 
AVR-Frequenz überein.

Der AVR-GCC kann ja nicht riechen, welche Fuses und Prescaler Du gesetzt 
hast und welcher Quarz angeschlossen ist. Du mußt es ihm schon sagen.

Du solltest auch die Compiler-Warnungen lesen.


Peter

Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter Dannegger
Hey, hey, selbstverständlich stimmt das ;-)
F_CPU=8000000, CLK: 8Mhz, intern, CKDIV ist nicht gesetzt (also keine 
Teilung) => alles richtig => keine Warnung

Hatte ich nur vergessen zu schreiben, sorry.

Gruß
Erik

Autor: Jojo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich hab auch mal den DS1820 benutzt und der ist eigentlich wirklich 
recht tolerant bei den Zeiten. Bei mir lag es damals an was ganz 
anderem:
ich hatte auch nur einen Sensor dran und dachte darum, ich müsse mich 
beim Auslesen auch um keine IDs kümmern, und so. Aber: dann muß man 
das dem Sensor auch sagen! Ich hab den Befehl nicht mehr im Kopf, aber 
man kann was senden, was dann den Abgleich mit der Sensor-ID 
überspringt. Als das drin war hat es super geklappt, lag also an meinem 
Protokoll...
Sicher, daß das bei dir richtig ist?

Gruß

Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo nochmals...
Danke für die vielen Rückmeldungen!
> Bei mir lag es damals an was ganz anderem
Unglaublich aber wahr: Alle drei (!) Sensoren, die ich hatte, waren 
defekt. Sie haben zwar das Kommando zur Temp.-Messung angenommen, dann 
aber immer den Default von 85°C zurück gemeldet.
Fazit: Neuer Sensor angeschlossen und alles rennt!
Der Lerneffekt: Ich habe jetzt ca. 8 Arten griffbereit um Timings zu 
erzeugen. ( Und es geht mit allen :-) )

...Murphy lässt grüßen...

Danke an alle & Gruß
Erik

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik schrieb:
> Unglaublich aber wahr: Alle drei (!) Sensoren, die ich hatte, waren
> defekt. Sie haben zwar das Kommando zur Temp.-Messung angenommen, dann
> aber immer den Default von 85°C zurück gemeldet.

Die müssen aber nicht kaputt sein.
Warscheinlich sinds nur DS1820-PAR.

Da ist VCC nicht nach außen geführt, sondern intern mit GND verbunden.
D.h. ohne parasite Power liefern die immer 85°C

Parasite Power geht aber recht einfach:
Direkt nach dem Start Conversion Kommando den Pin auf high Output setzen 
für ~1s.

Guck mal, ob hinter der 20 nicht noch ein P steht.


Peter

Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter
> ...sondern intern mit GND verbunden.
Da ich aber VCC mit 5V versorgt habe, hätte es doch schön rauchen 
müssen. Der Stromfluss war aber nicht hoch.
> Guck mal, ob hinter der 20 nicht noch ein P steht.
Nein, kein "P". Also wohl doch kaputt.
Denke jetzt aber darüber nach die neuen DS1820 mit parasite Power zu 
betreiben. So kann ich eine weitere Leitung einsparen.

Danke & Gruß
Erik

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was Delays angeht: Bei mir läuft Peters Code u.A. leicht variiert unter 
einem RTOS, bei dem nur die kurzen Delays bis zum unteren 2-stelligen 
µs-Bereich per Warteschleife mit abgeschalteten Interrupts abgesichert 
sind und der Rest über das RTOS-Scheduling mit dessen 10KHz Timer-Tick 
abgewickelt wird.

Das einzige Timing-Problem ergab sich durch einen kleinen Fehler in der 
Originalversion, der bei höherer Taktfrequenz zu einer zu kurzen 
Erholzeit zwischen den Bits führte. Die nicht ausreichte, um eine lange 
Leitung passiv wieder sauber auf 1 zu ziehen (im zuständigen Thread 
dokumentiert). An dieser Stelle fehlte ein Delay von ein paar µs.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Erik schrieb:
> Da ich aber VCC mit 5V versorgt habe, hätte es doch schön rauchen
> müssen.

Nö, beim -PAR ist der Pin ja unbeschaltet.
Du kannst also nichtmal die Substratdiode messen.

Peter

Autor: Erik (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter
Jo, hatte das "intern" überlesen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.