Forum: Compiler & IDEs SPI-Temperaturinterface und Timer Interrupt


von Patrick .. (Firma: ka) (patrick99)


Lesenswert?

Hallo,

ich stehe vor einem Problem mit einem Temperatursensor MAX31855 und 
einem Sekunden-Timer.

Mein Code funktioniert ohne Timer problemlos nutze ich aber den Timer1 
oder andere Timer die einen Interrupt auslösen, kommt entweder keine 
Temperaturausgabe oder aber Chaos.
Habe es auch schon mit SREG probiert, da bekam ich zumindest Daten 
angezeigt die aber unrealistisch erscheinen(bspw. 16xxx).

Zur ersten "nutzlosen" while-Abfrage, ohne diese Abfrage wäre das erste 
Byte mit dem Wert 255 "gefüllt".

Seltsamerweise funktioniert die gleiche Funktion für den Internen 
Temperatursensor problemlos.
1
volatile int zeit=0;
2
3
ISR(TIMER3_COMPA_vect)
4
{
5
    zeit++;
6
  
7
   i=1;
8
}
9
10
11
int main(void)
12
{
13
        float Temperatur=0;
14
15
        TCCR3B |= (1 << WGM12);                //CTC Modus
16
  TCCR3B |= (1 << CS12);   //Prescaler 256
17
  TCNT3=0;
18
  OCR3A = 28800;
19
  TCCR3A=0;
20
  TCNT3 = 0;
21
  sei();
22
  ETIMSK |= (1<<OCIE3A)|(1<<OCIE3A);
23
24
25
for (;;)
26
    {
27
    
28
      
29
    cli();
30
      if (i==1)
31
      {
32
        
33
      Temperatur=Temperatur(Temperatur);
34
            dtostrf(Temperatur,4,2,string3);
35
            UART0_String_SEND(string3);    
36
      sei();  
37
        
38
        i=0;
39
      }
40
               
41
}
42
}
43
44
_________________________________
45
46
void SPI_Master_Init(void)
47
{
48
  DDRB &= ~(1<<PB3);
49
  PORTB |= (1<<PB3);
50
  SPCR |= (1<<SPIE)|(1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0)|(1<<CPHA);
51
  SPSR |= (1<<SPI2X);
52
  DDRB |= (1<<PB3);
53
}
54
55
float Temperatur(float Temp )
56
{
57
  volatile uint8_t Daten0;
58
  volatile uint8_t Daten1;
59
  
60
  PORTB &=~(1<<PB0);
61
  SPDR=0x00;
62
while(!(SPSR & (1<<SPIF)))
63
  ;
64
  SPDR;
65
  SPSR |=(0<<SPIF);
66
  _delay_us(2);
67
  SPDR=0x00;
68
while (!(SPSR & (1<<SPIF)))
69
  ;
70
  Daten0 = SPDR;
71
  SPSR |=(0<<SPIF);
72
  _delay_us(2);
73
  SPDR=0x00;
74
while (!(SPSR & (1<<SPIF)))
75
  ;
76
  Daten1 = SPDR;
77
  SPSR |=(0<<SPIF);
78
 PORTB |=(1<<PB0);
79
80
        if (Daten0 & 0x10)
81
  {
82
    Temp=Temp+256;
83
    }
84
    if (Daten0 & 0x08)
85
    {
86
      Temp=Temp+128;
87
    }
88
    if (Daten0 & 0x04)
89
    {
90
      Temp=Temp+64;
91
    }
92
    if (Daten0 & 0x02)
93
    {
94
      Temp=Temp+32;
95
    }
96
....
97
return Temp;
98
}

von (prx) A. K. (prx)


Lesenswert?

Wie du siehst sind TABs zur Einrückung ungünstig im Forum.

Da fehlt mal wieder die wichtigste Hälfte, wie string3 und i.

Was ist denn das für eine Stilblüte:
   float Temperatur=0;
   Temperatur=Temperatur(Temperatur);
Das soll der Compiler kommentarlos gefressen haben?

von Patrick .. (Firma: ka) (patrick99)


Lesenswert?

Hab das ganze nur gekürzt, da ich auch noch mehr auslese auser den 
SPI-Bus und Die UART-Ausgabe.
Die anderen Programmteile werden aber für den Test nicht 
genutzt(auskommentiert).

Ja, die Namen der Funktion und der Varable sind eigentlich länger. Der 
Compiler macht da keine Probleme. Hier noch der Rest vom Code.

#define F_CPU 7372800UL
#include "UART.h"
#include "SPI_Thermoelement.h"
#include <stdlib.h>
#include <avr/interrupt.h>
#include <avr/io.h>

volatile int zeit=0;
volatile int i=0;

void main()
{
float Temperatur=0;
const char* string3[8];


  ...


}

von Karl H. (kbuchegg)


Lesenswert?

A.K. hat nichgt so ganz unrecht.

Eine ordentliche und vor allen Dingen konsistente Einrückung ist oft 
schon mal die halbe Miete. Auch für Dich!
Es geht nicht darum, dass nicht alles am linken Rand klebt und der Code 
ein wenig aufgelockerter erscheint, sondern es geht darum, das man 
erkennen kann, welche Codeteile von anderen abhängig sind (welche Teile 
zb im then Teil eines if abgearbeitet werden), und welche auf gleicher 
Logikstufe stehen.

Ich mach das mal für einen Teil und hinten nach dann eine Frage an dich
1
  for (;;)
2
  {
3
    cli();
4
5
    if (i == 1)
6
    {
7
      Temperatur = Temperatur( Temperatur );
8
      dtostrf( Temperatur, 4, 2, string3 );
9
      UART0_String_SEND( string3 );    
10
11
      sei();  
12
        
13
      i = 0;
14
    }
15
  }
16
}

wenn i nicht gleich 1 ist, wird in der Schleife als erstes trotzdem der 
cli gemacht. Wie genau stellst du dir dann vor, dass dann jemals ein 
Interrupt dir das i auf 1 stellen kann?





1
float Temperatur(float Temp )
2
{
3
  volatile uint8_t Daten0;
4
  volatile uint8_t Daten1;
5
  
6
  PORTB &=~(1<<PB0);
7
  SPDR=0x00;
8
while(!(SPSR & (1<<SPIF)))
9
  ;
10
  SPDR;
11
  SPSR |=(0<<SPIF);
12
  _delay_us(2);
13
  SPDR=0x00;
14
while (!(SPSR & (1<<SPIF)))
15
  ;
16
  Daten0 = SPDR;
17
  SPSR |=(0<<SPIF);
18
  _delay_us(2);
19
  SPDR=0x00;
20
while (!(SPSR & (1<<SPIF)))
21
  ;
22
  Daten1 = SPDR;
23
  SPSR |=(0<<SPIF);
24
 PORTB |=(1<<PB0);

Schon mal was von Funktionen gehört? Ich hab mir sagen lassen, dass die 
Übersicht in einen Code bringen können, indem man immer gleiche Teile in 
eine Funktion auslagert und dadurch der Code an der aufrufenden Stelle 
vereinfacht wird.


Ts ts
  SPSR |=(0<<SPIF);
so löscht man doch nicht ein Bit.
Abgesehen davon ist das ein Interrupt Bit, das wird höchst 
wahrscheinlich dadurch gelöscht (Datenblatt nachsehen), dass man eine 1 
an die entsprechende Bitposition einschreibt. So gesehen funktioniert 
deine Version - aber nur zufällig, weil du Glück hattest.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Ts ts
>   SPSR |=(0<<SPIF);
> so löscht man doch nicht ein Bit.
> Abgesehen davon ist das ein Interrupt Bit, das wird höchst
> wahrscheinlich dadurch gelöscht (Datenblatt nachsehen), dass man eine 1
> an die entsprechende Bitposition einschreibt. So gesehen funktioniert
> deine Version - aber nur zufällig, weil du Glück hattest.

Edit:
Gerade im Datenblatt nachgesehen.
Das SPIF Flag löscht sich in deinem Code ganz von alleine, einfach nur 
durch Zugriff auf SPDR. D.h. überhaupt keine Aktion notwendig.

von Patrick .. (Firma: ka) (patrick99)


Lesenswert?

Mein Programm ist nur so zerpflügt, weil ich meinen Code ein dutzend mal 
umgeschrieben habe. Naja jetzt läufts aber :) Hatte den Code nicht in 
Funktionen gepackt, weil das ganze erstmal so laufen sollte.
Nachdem ich die Empfangsfunktion in eine Funktion gesetzt hab, lief es 
einwandfrei. Jetzt muss ich nur noch Zweierkomplement einsetzten und 
schon läuft alles.

Danke für die Ratschläge, manche Fehler sind mir aus Frust schon nicht 
mehr aufgefallen...

uint8_t spi_receive() {
  SPDR = 0x00;
  while (!(SPSR & (1<<SPIF)))
  ;
  return SPDR;
}

von PittyJ (Gast)


Lesenswert?

Was mir hier mal wieder auffällt, ist das Fehlen von Kommentaren.
Spätestens in einem halben Jahr hat man vergessen, was die Funktion 
Temperatur macht. Dann muss man sein eigenes Programm entschlüsseln.

Warum nicht einfach mal hinschreiben, was das Setzen der Bytes bewirkt, 
und worauf die while-Schleifen warten.

von Karl H. (kbuchegg)


Lesenswert?

Patrick .... schrieb:

> umgeschrieben habe. Naja jetzt läufts aber :) Hatte den Code nicht in
> Funktionen gepackt, weil das ganze erstmal so laufen sollte.

Und wie man sieht funktioniert das nicht.
Die Einstellung "zuerst bring ichs zum Laufen und dann kümmere ich mich 
um die Schönheit" ist ein Trugschluss, wie die alten Hasen wissen.

Codeformatierung, Codestrukturierung SIND essentielle Werkzeuge um Code 
verständlich zu machen und um Logikfehler sehen zu können, die aus 
Strukturfehlern entstehen. Und das gilt auch, bzw insbesondere, in der 
Entwicklungszeit eines Programms. Von Anfang an!

von Patrick .. (Firma: ka) (patrick99)


Lesenswert?

Ja, das stimmt allerdings. Aber man lernt ja aus seinen Fehlern;)

von (prx) A. K. (prx)


Lesenswert?

Wieviele Compiler-Warnungen hast du eigentlich noch ignoriert? 
Definition von string3 und Verwendung in dtostrf passen nicht zusammen.

von Patrick .. (Firma: ka) (patrick99)


Lesenswert?

Wie gesagt, es war alles nur als Test gedacht. Hab meinen Code auch 
aufgeräumt und laut AVR-Studio sind es auch 0 Fehler und 0 Warnungen :)

von Patrick .. (Firma: ka) (patrick99)


Lesenswert?

Eine Frage hab ich da noch, mit itoa oder utoa kann man integer bzw. 
unsigned integer in strings konvertieren. Diese haben aber nie eine fest 
definierte Länge. Ich hab es jetzt erstmal mit dtostrf probiert. Das 
funktioniert auch, nur bekomme ich eine Warnung dass dtostrf nicht für 
Integer geeignet ist. Gibt es eine saubere Möglichkeit eine feste Länge 
einzustellen? Ich will den String später mit einer Software auslesen und 
da ist eine feste Stringlänge vorteilhafter?

von Karl H. (kbuchegg)


Lesenswert?

Es gibt da mehrere Möglichkeiten.

* itua bzw utoa benutzen und dann einfach vorher entsprechend
  viele Leerzeichen einfügen bzw. ausgeben um die gewünschte
  Länge zu erreichen

* sprintf kann man im Formatstring die Anweisung geben, eine fixe
  Feldbreite einzuhalten

* eine eigene Formatierfunktion schreiben.


Am schnellsten benutzt ist die mittlere Variante - sprintf.
Hat man den Speicher und spielt auch die Laufzeit keine Rolle, dann ist 
das die einfachste Variante.

Etwas mehr Überlegung erfordert die 3. Variante.
Die Sache ist die: wenn man mal Fehlerbehandlung beiseite lässt, dann 
ist die die technisch bei weitem einfachste und schnellste. Denn 
eigentlich muss man Aufwand treiben um führende 0-en bzw. Leerzeichen zu 
unterdrücken. Mit einer eigenenn Ausgaberoutine vermeidet man diesen 
Aufwand und hat als Nebeneffekt eine Ausgaberoutine, die man auf einem 
µC mit zb einem LCD gut gebrauchen kann.

Die 1. Variante scheint die am logischten zu sein. Sie hat nur einen 
Haken: itoa bzw. utoa treiben Aufwand um die Leerzeichen loszuwerden und 
man selbst treibt hinten nach wieder Aufwand um sie wieder 
reinzubringen.

> Ich will den String später mit einer Software auslesen
> und da ist eine feste Stringlänge vorteilhafter?

Nicht wirklich. Ein Übertragungsformat bzw. Fileformat welches bei 
Zahlen auf feste Feldbreiten setzt bringt nichts. Genausowenig wie eine 
Eingafunktion die nicht bei Zahlen mit variablen Zeichenzahlen klar 
kommt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.