Forum: Compiler & IDEs Byte Wert suchen und Folgebyte speichern


von Firebird (Gast)


Lesenswert?

Hallo zusammen

Über eine SPI Schnittstelle möchte ich 10 empfangene Bytes abspeichern. 
Sie sind paarweise angeordnet, bestehend aus einem Address Byte gefolgt 
von einem Data Byte die ich in Temp0 bis Temp9 zwischenspeichere.
1
void Receive_Bytes(void)
2
{
3
  while( ! (SPSR & (1<<SPIF)));
4
  Temp[X] = SPDR;          
5
  
6
  for(x=0;x<10;x++);
7
}

Jetzt kommt meine Frage.

Wie kann man aus einer Reihe von gespeicherten Bytes (Temp0 bis Temp9) 
einen bestimmten Wert suchen und das darauf folgende Data Byte 
speichern?

Beispiel: Temp3 beinhaltet das gesuchte Address Byte, folglich ist Temp4 
das Data Byte das zur Weiterverarbeitung gebraucht wird:
1
 DataByte = Temp4

Danke und Gruss
Firebird

von Fragender (Gast)


Lesenswert?

Ich kann aus deiner Beschreibung nicht nachvollziehen, was Du möchtest.

Estelle erst mal eine PAP und wenn dieser die Funktion erfüllt, dann in 
der Programmiersprache umsetzten.

von Markus B. (markusborti)


Lesenswert?

1
char search=123;
2
char dataByte;
3
For(char i=0;i<10;i++)
4
{
5
if(temp[i]==search)
6
{
7
dataByte=temp[i+1];
8
}

Verzeih mir die schlechte Formatierung, aber mit einem Handy ist das 
nicht so einfach.

von Firebird (Gast)


Lesenswert?

Vielen Dank für den Denkanstoss.

Um jetzt mehrere gesuchte Addressen auszuwerten könnte man doch eine 
Switch Anweisung verwenden.
1
void search(void)
2
{  
3
  switch (temp[i])
4
  {
5
      case 36:                 
6
              DataByte1 = temp[i+1];
7
              break;
8
      case 37:
9
              DataByte2 = temp[i+1];
10
              break;
11
      case 38:
12
              DataByte3 = temp[i+1];
13
              break;
14
     //usw...
15
  }
16
}

Ist das so denkbar?

von Karl H. (kbuchegg)


Lesenswert?

Firebird schrieb:

> Ist das so denkbar?

Denkbar ist vieles.
Warum probierst du nicht ein paar Alternativen aus und siehst dir an, 
welcher Code
a) funktioniert
b) dir vom grundsätzlichen Aufbau her am meisten zusagt?

Du kannst wochenlang dir Vorträge über Bildkomposition ansehen und Malen 
nach Zahlen betreiben und Kritiker fragen, wie sie Bilder malen würden.
Wenn du aber Bilder malen willst, dann kommst du nicht umhin Bilder zu 
malen. Selbst wenn sich dann rausstellt, dass deine Idee nicht so toll 
war und du was anderes probieren solltest.

Nur Mut!
Das ist wie Radfahren lernen. Es führt kein Weg daran vorbei, dass man 
auch mal auf die Schnauze fällt. Das gehört zum Lernprozess genauso 
dazu.

von DirkB (Gast)


Lesenswert?

Im Augenblick findest du auch DataBytes als Adressen, wenn du mit 
Einerschritten durch das Array gehst.
Ist da eine struct nicht besser?

Wenn du Variablen wie DataByte1, DataByte2, DataByte3 hast, die alle vom 
selben Typ sind, nimm ein Array.

von Peter D. (peda)


Lesenswert?

Da fehlt zuallererst mal eine Synchronisation.
Also woher weiß der Slave, was ist Byte 0 und was Byte 9.

von Firebird (Gast)


Lesenswert?

Vielen Dank für die Anregungen. Ich habe einiges Nachgelesen und bin auf 
folgende Lösung gekommen. Ich werde den Code ausprobieren und testen.
1
void receive_bytes(void)
2
{
3
  for( i = 0; i < 10; i++);
4
5
  while( ! (SPSR & (1<<SPIF)));
6
  temp[i] = SPDR;           
7
}

1
void search(void)
2
{  
3
  for( i = 0; i < 10; i++);
4
5
  AddrByte = temp[i]  
6
7
  switch (AddrByte)
8
  {
9
      case 36:
10
              DataByte1 = temp[i+1];
11
              break;
12
      case 37:
13
              DataByte2 = temp[i+1];
14
              break;
15
      case 38:
16
              DataByte3 = temp[i+1];
17
              break;
18
      default:
19
              break;
20
  }
21
}

Gruss
Firebird

von Karl H. (kbuchegg)


Lesenswert?

Firebird schrieb:
> Vielen Dank für die Anregungen. Ich habe einiges Nachgelesen und bin auf
> folgende Lösung gekommen. Ich werde den Code ausprobieren und testen.
>
>
1
void receive_bytes(void)
2
> {
3
>   for( i = 0; i < 10; i++);
4
> 
5
>   while( ! (SPSR & (1<<SPIF)));
6
>   temp[i] = SPDR;
7
> }

Das wird aus dem Funktionsprinzip von SPI heraus nicht funktionieren. 
Das andere Ende der SPI Kette kann nur übertragen, wenn der Sender ein 
Byte sendet. D.h. du musst selbst an SPDR etwas Zuweisen um die 
Übertragung in Gang zu setzen.
Und dann schau dir nochmal an, wie die Syntax von for-Schleifen ist und 
wo das mit dem Strichpunkt zur abhängigen Anweisung in den Schleifen 
ist.

>
1
void search(void)
2
> {
3
>   for( i = 0; i < 10; i++);
4
> 
5
>   AddrByte = temp[i]
6
> 
7
>   switch (AddrByte)
8
>   {
9
>       case 36:
10
>               DataByte1 = temp[i+1];
11
>               break;
12
>       case 37:
13
>               DataByte2 = temp[i+1];
14
>               break;
15
>       case 38:
16
>               DataByte3 = temp[i+1];
17
>               break;
18
>       default:
19
>               break;
20
>   }
21
> }

Und was, wenn in den Daten selbst ein 36, 37 oder 38 steckt?
Wenn in deinen Daten immer Abwechselnd das Steuerbyte und das Datenbyte 
steckt, dann genügt es, wenn du immer nur jedes 2.te Byte testest. Wobei 
- genügen ist zu viel gesagt. Du willst eigentlich nur jedes 2.te 
Steuerbyte testen.

von Firebird (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das wird aus dem Funktionsprinzip von SPI heraus nicht funktionieren.
> Das andere Ende der SPI Kette kann nur übertragen, wenn der Sender ein
> Byte sendet. D.h. du musst selbst an SPDR etwas Zuweisen um die
> Übertragung in Gang zu setzen.
> Und dann schau dir nochmal an, wie die Syntax von for-Schleifen ist und
> wo das mit dem Strichpunkt zur abhängigen Anweisung in den Schleifen
> ist.

Hier sind meine Anpassungen wobei ich nicht sicher bin ob "SPDR = 0" am 
richtigen Ort ist.
1
void receive_bytes(void)
2
{
3
   SPDR = 0;          
4
5
   for( i = 0; i < 10; i++)
6
   {
7
       while( ! (SPSR & (1<<SPIF)));
8
       temp[i] = SPDR;
9
   }
10
}

> Und was, wenn in den Daten selbst ein 36, 37 oder 38 steckt?
> Wenn in deinen Daten immer Abwechselnd das Steuerbyte und das Datenbyte
> steckt, dann genügt es, wenn du immer nur jedes 2.te Byte testest. Wobei
> - genügen ist zu viel gesagt. Du willst eigentlich nur jedes 2.te
> Steuerbyte testen.

Hier habe ich die for-Schleife angepasst, sodass jedes 2te Byte getestet 
wird. Es ist durchaus möglich, dass bei den Daten selbst ein 36, 37 oder 
38 steckt. Nach meinen Überlegungen müsste das keinen Einfluss haben, 
denn wenn AddrByte ein 36 ist so wäre temp[i+1] das DataByte mit 36.

Ich denke es wird sich heraustellen ob das in der Praxis funktioniert.
1
void search(void) 
2
{ 
3
   for( i = 0; i < 10; i = i + 2)
4
   {
5
       AddrByte = temp[i] 
6
 
7
       switch (AddrByte) 
8
       { 
9
           case 36: 
10
                   DataByte1 = temp[i+1]; 
11
                   break; 
12
           case 37: 
13
                   DataByte2 = temp[i+1]; 
14
                   break; 
15
           case 38: 
16
                   DataByte3 = temp[i+1]; 
17
                   break; 
18
          default: 
19
                   break;
20
       } 
21
   } 
22
}

von Peter D. (peda)


Lesenswert?

Ich bewundere ja Dein Gottvertrauen, daß alles wie von Geisterhand Bit- 
und Bytesynchron ist.

Als Master sollte man ja wenigstens mit /SS = 0 dem Slave Bescheid 
geben, wann die Daten beginnen.
Hellseher-ICs habe ich noch in keinem Datenbuch gefunden.

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:
> Ich bewundere ja Dein Gottvertrauen, daß alles wie von Geisterhand Bit-
> und Bytesynchron ist.

Nicht nur dieses.
Ich bewundere auch das Gottvertrauen, dass man ohne zu testen als 
Anfänger haufenweise Code schreiben könnte, der funktioniert.

So wie das aussieht, ist die Übertragung überhaupt noch nie getestet 
worden, und er macht sich Sorgen darüber ob er einen switch-case oder 
doch ein paar if nehmen sollte.

von Firebird (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ich bewundere ja Dein Gottvertrauen, daß alles wie von Geisterhand
> Bit-
> und Bytesynchron ist.
>
> Als Master sollte man ja wenigstens mit /SS = 0 dem Slave Bescheid
> geben, wann die Daten beginnen.
> Hellseher-ICs habe ich noch in keinem Datenbuch gefunden.

Synchronisiert wird mit einer 8ms Pause zwischen gesendeten Bytes, d.h. 
aufeinander folgende Kommandos (bestehend aus Address Byte und Data 
Byte) müssen innerhalb oder ausserhalb 8ms gesendet werden. Tritt eine 
Pause ein wird neu synchronisiert.

von Peter D. (peda)


Lesenswert?

Firebird schrieb:
> müssen innerhalb oder ausserhalb 8ms gesendet werden.

Was denn nun?

Aus Erfahrung kann ich Dir sagen, Zeit gesteuerte Protokolle sind weder 
einfach noch zuverlässig.
Im Profibereich nimmt man daher Daten gesteuerte Protokolle. Diese 
lassen sich auch leicht über andere Medien tunneln.
Zeitabhängigkeiten gehen aber über andere Medien verloren.

Bevor Du die Daten auswertest, mußt Du erstmal das Protokoll 
implementieren.
Wie willst Du sonst wissen, daß die Daten korrekt sind.
Du versuchst hier, zuerst das Dach zu bauen, aber ohne die Seitenwände 
fällt es immer wieder runter.

von Firebird (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Peter Dannegger schrieb:
>> Ich bewundere ja Dein Gottvertrauen, daß alles wie von Geisterhand Bit-
>> und Bytesynchron ist.
>
> Nicht nur dieses.
> Ich bewundere auch das Gottvertrauen, dass man ohne zu testen als
> Anfänger haufenweise Code schreiben könnte, der funktioniert.
>
> So wie das aussieht, ist die Übertragung überhaupt noch nie getestet
> worden, und er macht sich Sorgen darüber ob er einen switch-case oder
> doch ein paar if nehmen sollte.

Ja, ich bin Anfänger und es stimmt, ich habe den Code noch nicht 
getestet. Mir fehlte (oder fehlt) der Teil "Byte Wert suchen und 
Folgebyte speichern".

von Firebird (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Firebird schrieb:
>> müssen innerhalb oder ausserhalb 8ms gesendet werden.
>
> Was denn nun?
>
> Aus Erfahrung kann ich Dir sagen, Zeit gesteuerte Protokolle sind weder
> einfach noch zuverlässig.
> Im Profibereich nimmt man daher Daten gesteuerte Protokolle. Diese
> lassen sich auch leicht über andere Medien tunneln.
> Zeitabhängigkeiten gehen aber über andere Medien verloren.
>
> Bevor Du die Daten auswertest, mußt Du erstmal das Protokoll
> implementieren.
> Wie willst Du sonst wissen, daß die Daten korrekt sind.
> Du versuchst hier, zuerst das Dach zu bauen, aber ohne die Seitenwände
> fällt es immer wieder runter.

Die Daten die ich empfangen und lesen möchte kommen von der SUSI 
Schnittstelle. Die Spezifikation findet man hier:

http://www.d-i-e-t-z.de/jd/plaene/SUSI_310.zip

Hier ist der Sourcecode denn ich bis jetzt erstellt habe.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define F_CPU 8000000UL
5
#define no_sync 0
6
#define in_sync 1
7
8
volatile uint8_t state = no_sync;
9
10
volatile uint8_t AddrByte = 0;
11
volatile uint8_t DataByte1 = 0;
12
volatile uint8_t DataByte2 = 0;
13
volatile uint8_t DataByte3 = 0;
14
15
16
ISR (TIMER1_COMPA_vect)
17
{
18
  state = in_sync;
19
}
20
21
22
void init_timer1(void)
23
{  
24
  TCCR1B |= ((1<<CS11) | (1<<WGM12));    // Prescaler 8, CTC mode
25
  OCR1A = 8000;                          // Set CTC compare value to 8ms
26
  TIMSK |= (1<<OCIE1A);                  // Enable CompareA Match Interrupt
27
}
28
29
30
void init_Slave(void)
31
{
32
  DDRB |= (1<<PB2) | (1<<PB3) | (1<<PB5);      // Set SS, MOSI and SCK input
33
  SPCR |= (1<<SPE) | (1<<DORD) | (1<<CPHA);    // SPI enable, LSB transmitted first, sample at trailing edge 
34
}
35
36
37
void receive_bytes(void)
38
{
39
     SPDR = 0;          
40
41
     for( i = 0; i < 10; i++)
42
     {
43
           while( ! (SPSR & (1<<SPIF)));
44
           temp[i] = SPDR;
45
     }
46
}
47
48
49
void search(void)
50
{  
51
  for( i = 0; i < 10; i = i + 2)
52
  {
53
    AddrByte = temp[i]  
54
55
    switch (AddrByte)
56
    {
57
      case 36:
58
        DataByte1 = temp[i+1];
59
        break;
60
      case 37:
61
        DataByte2 = temp[i+1];
62
        break;
63
      case 38:
64
        DataByte3 = temp[i+1];
65
        break;
66
      case 0:
67
        break;
68
      default:
69
        state = no_sync;
70
        break;
71
    }
72
  }
73
}
74
75
76
int main(void)
77
{
78
  init_timer1();
79
  init_Slave();
80
      
81
  DDRD |= 0xFF;          // Set DDRD as output
82
  
83
  sei();                 // Enable Global Interrupt
84
  
85
  while (1)
86
  {
87
    if (state == in_sync)
88
    {
89
      receive_bytes();
90
      search();
91
    }
92
    else
93
    {
94
      TIFR |= (1 << OCF1A);    // Clear the CTC flag, reset timer, initiate ISR
95
                               // (writing a logic one to the set flag clears it)
96
    }
97
  }
98
}

von Karl H. (kbuchegg)


Lesenswert?

Firebird schrieb:

> Ja, ich bin Anfänger und es stimmt, ich habe den Code noch nicht
> getestet. Mir fehlte (oder fehlt) der Teil "Byte Wert suchen und
> Folgebyte speichern".

Dir fehlt zuallerst mal der Teil:
kriege ich überhaupt die Daten rüber!

Zuallererst musst du die Daten mal da haben, ehe du sie auswerten 
kannst.
Und genau diesen Teil kann man völlig unabhängig von irgendwelchen 
Auswertungen schon mal programmieren und testen.

Denn wenn deine 10 Bytes schon nicht korrekt sind, dann kannst du 
auswerten und suchen bis du (bzw. der Prozessor) schwarz wird. Aus 
falschen Daten kann auch die beste Suche nichts gescheites mehr machen. 
Daher muss dieser Teil, die Übertragung von 10 Bytes, 100% zuverlässig 
funktionieren. Und zwar noch bevor du dir um irgendwelche Suchstrategien 
auch nur irgendwelche Gedanken machst.

> Mir fehlte (oder fehlt) der Teil "Byte Wert suchen und
> Folgebyte speichern".
Den Teil brauchst du noch nicht, wenn deine erste Teilaufgabe lautet: 10 
Bytes korrekt zu empfangen.

von amateur (Gast)


Lesenswert?

Spar' Dir Platz und Zeit:

>    AddrByte = temp[i]
>    switch (AddrByte)

... ist das Selbe wie:

    switch (temp[i])

von Firebird (Gast)


Lesenswert?

Wie überprüfe ich ob die Übertragung von 10 Bytes korrekt und 100% 
zuverlässig funktioniert?

Ich sehe die Möglichkeit nach dem Empfang von 10 Bytes eine LED toggeln 
zu lassen und vielleicht zusätzlich eine LED toggeln zu lassen wenn der 
ISR ausgelöst wird. Wie macht man das Toggeln der LEDS sichtbar bei so 
hoher Datenübertragungsrate?

von Peter D. (peda)


Lesenswert?

Ich würde erstmal die Synchronisation implementieren und dann z.B. 100 
Byte in ne FIFO einlesen und über die UART an den PC ausgeben.

Wie kann man sich nur so ein bescheuertes Protokoll ausdenken.

von Firebird (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Ich würde erstmal die Synchronisation implementieren und dann z.B.
> 100
> Byte in ne FIFO einlesen und über die UART an den PC ausgeben.
>

Verstehe ich das richtig? Es braucht ein Code um z.B. 100 Bytes zu 
senden, mit ensprechender Hardware? Und der Synchronisationscode so 
erweitern, dass die empfangenen Bytes über UART an den PC gesendet 
werden?

von Peter D. (peda)


Lesenswert?

Es mag Experten geben, die schreiben gleich richtigen Code.
Alle anderen müssen debuggen und da ist das einfachste die UART.

Man empfängt also einige Daten und guckt sie sich dann an, ob die was 
Sinnvolles ergeben oder nur Zufallszahlen.
Sehen die Daten o.k. aus, kann man dann die Auswertung programmieren.

von Firebird (Gast)


Lesenswert?

Hallo zusammen

Ich habe die SUSI Schnittstelle aufgebaut (mit MySmartControl), wobei 
eine grüne LED leuchten soll wenn ein gültiger Wert empfangen wurde und 
ausgehen soll wenn ein anderer Wert empfangen wurde:

LED an wenn addr = 0x60 und value = 0x04
LED aus wenn addr = 0x61 und value = 0x04

Die Logik habe ich mir so angedacht:

Empfange erster Byte und speichere in "addr".
Empfange zweiter Byte und speichere in "value1".
Prüfe ob "addr" ein 2 Byte Commando.

Wenn ja, dann "SUCCESS" und führe "susi_decode" aus.
Wenn nein, dann empfange dritter Byte und speichere in "value2".

Prüfe ob "addr" ein 3 Byte Commando ist.
Wenn ja, dann "SUCCESS" und führe "susi_decode" aus.

Die Timer Routine setzt TransmitState auf 0x00 zurück wenn innerhalb 7ms 
nichts empfangen wird.

Leider Funktioniert der Code nur teilweise.

Wenn ich in der "main" Funktion unter "case SUCCESS" ein LED leuchten 
lasse, dann leuchtet die LED auf.
Auch wenn ich in der Funktion "spi_check_data" anstelle von "return 
SUCCESS" ein LED leuchten lasse scheint es zu funktionieren.

Sobald "susi_decode" einbringe, dann leuchtet nur die rote LED.

Irgendwo ist der Wurm drin und ich kann den Fehler einfach nicht 
entdecken.
Ich bitte um Unterstützung.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define F_CPU 3686400UL
5
6
#define  FAIL 0x01
7
#define  SUCCESS 0x02
8
9
uint8_t TransmitState = 0x00;
10
uint8_t addr, value1, value2;
11
12
volatile uint8_t susi_F0F4 = 0;
13
volatile uint8_t susi_F5F12 = 0;
14
15
ISR (TIMER2_COMP_vect)
16
{
17
  TransmitState = 0x00;
18
  TCNT2 = 0;        // Clear timer
19
}
20
21
void init_timer2(void)
22
{  
23
  TCCR2 |= ((1<<CS21) | (1<<WGM21));    // Prescaler 1, CTC mode
24
  OCR2 = 100;                           // Set CTC compare value to 7ms
25
  TIMSK |= (1<<OCIE2);                  // Enable Compare Match Interrupt
26
}
27
28
void init_spi_slave(void)
29
{
30
  DDRB |= (1<<PB2) | (1<<PB3) | (1<<PB5);    // Set SS, MOSI and SCK input
31
  SPCR |= (1<<SPE) | (1<<DORD) | (1<<CPHA);  // SPI enable, LSB transmitted first,
32
                                             // sample at trailing edge 
33
}
34
35
uint8_t spi_receive_data(uint8_t temp)
36
{  
37
  while (!(SPSR & (1<<SPIF)))      // Wait until Char is received
38
  temp = SPDR;
39
  TCNT2 = 0;                       // Clear timer
40
  return temp;
41
}
42
43
uint8_t spi_check_data(void)
44
{
45
   addr = spi_receive_data(0x00);      // Read first Byte
46
  value1 = spi_receive_data(0x00);     // Read second Byte
47
48
  if ((addr & 0xF0) == 0x10)
49
  return SUCCESS;
50
  else if ((addr & 0xF0) == 0x20)
51
  return SUCCESS;
52
  else if ((addr & 0xF0) == 0x60)      // Check address if 2 bytes (0x60, 0x61) 
53
  return SUCCESS;
54
  
55
  value2 = spi_receive_data(0x00);     // Read third Byte
56
57
  if ((addr & 0xF0) == 0x70)           // Check address if 3 bytes 
58
  return SUCCESS;
59
  
60
  return FAIL;
61
}
62
63
void susi_decode(void)
64
{
65
  switch (addr)
66
  {
67
    case 96:
68
      susi_F0F4 = value1;              // 0x60
69
      
70
          if (susi_F0F4 & 0x01)        // Check if Bit 0 is set 
71
      PORTC = 0x04;                    // Green LED on
72
        break;
73
    case 97:
74
      susi_F5F12 = value1;             // 0x61
75
 
76
          if (susi_F5F12 & 0x02)       // Check if Bit 1 is set 
77
      PORTC &= 0x04;                   // Green LED off
78
      break;
79
    default:
80
      break;
81
  }
82
}
83
84
int main(void)
85
{
86
  init_timer2();
87
  init_spi_slave();
88
  
89
  DDRC |= 0xFF;                       // Set DDRC as output
90
  PORTC = 0x00;                       // Set PORTC is low
91
  
92
  sei();                              // Enable Global Interrupt
93
    
94
  while (1)   
95
  {
96
    TransmitState = spi_check_data();
97
    
98
    switch (TransmitState)            // Output TransmitState on Port C   
99
    {
100
      case 0x00:
101
        PORTC = 0x01;                 // LED Yellow
102
        break;   
103
      case FAIL:
104
        PORTC = 0x02;                 // LED Red
105
                      break;   
106
      case SUCCESS:
107
        susi_decode();
108
        break;
109
      default:
110
        break;
111
    }   
112
  }
113
}

von Karl H. (kbuchegg)


Lesenswert?

>
1
>   DDRB |= (1<<PB2) | (1<<PB3) | (1<<PB5);    // Set SS, MOSI and SCK input
2
>

Input?
Das ist aber nicht Input.

Alles in allem:
zuviel Code auf einmal und das ganze ohne vernünftige Debug-Möglichkeit. 
Eine einzelne LED ist da m.M. zu wenig. Ein Blick auf die konkreten 
Zahlenwerte, wie man ihn mit LCD oder UART bekommt, ist hingegen Gold 
wert.

von Firebird (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
>>>   DDRB |= (1<<PB2) | (1<<PB3) | (1<<PB5);    // Set SS, MOSI and SCK input
>>
> Input?
> Das ist aber nicht Input.
>
> Alles in allem:
> zuviel Code auf einmal und das ganze ohne vernünftige Debug-Möglichkeit.
> Eine einzelne LED ist da m.M. zu wenig. Ein Blick auf die konkreten
> Zahlenwerte, wie man ihn mit LCD oder UART bekommt, ist hingegen Gold
> wert.

Ich bekomme es nicht zum laufen. Auch der stark vereinfachte Code läuft 
nicht, d.h. die LED geht willkürlich an nach einigen Sekunden, teilweise 
gar nicht.

Ich befürchte, dass meine Kenntnisse auch nicht reichen um den Code 
hinzubekommen um die Zahlenwerte über UART an den PC zu senden.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define F_CPU 3686400UL
5
6
volatile uint8_t susi_F0F4 = 0;
7
8
uint8_t addr;
9
10
ISR (TIMER2_COMP_vect)
11
{
12
  TCNT2 = 0;                             // Clear timer
13
}
14
15
void init_timer2(void)
16
{  
17
  TCCR2 |= ((1<<CS20) | (1<<WGM21));     // Prescaler 1, CTC mode
18
  OCR2 = 100;                            // Set CTC compare value to 7ms
19
  TIMSK |= (1<<OCIE2);                   // Enable Compare Match Interrupt
20
}
21
22
void init_spi_slave(void)
23
{
24
  DDRB = (1<<PB2) | (1<<PB3) | (1<<PB5);       // Set SS, MOSI and SCK input
25
  SPCR = (1<<SPE) | (1<<DORD) | (1<<CPHA);     // SPI enable, LSB transmitted first,
26
                                               // sample at trailing edge 
27
}
28
29
uint8_t spi_receive_data(uint8_t temp)
30
{
31
  SPDR = temp;
32
  while (!(SPSR & (1<<SPIF)))                  // Wait until Char is received
33
  temp = SPDR;
34
  return temp;
35
}
36
37
int main(void)
38
{
39
  init_spi_slave();
40
  init_timer2();
41
  
42
  DDRC = 0xFF;                       // Set DDRC as output
43
  PORTC = 0x00;                      // Set PORTC is low
44
  
45
  sei();                             // Enable Global Interrupt
46
47
  while (1)   
48
  {
49
    addr = spi_receive_data(0x00);
50
    if (addr == 0x60)                // Green LED on
51
    PORTC = 0x04;
52
    TCNT2 = 0;
53
  }
54
}

von Kaj (Gast)


Lesenswert?

Erklär mir mal bitte was du da machst:
1
uint8_t spi_receive_data(uint8_t temp)
2
{
3
  SPDR = temp;
4
  while (!(SPSR & (1<<SPIF)))
5
  temp = SPDR;
6
  return temp;
7
}

1. Da fehlt ein ";" hinter der while()
2. Die funktion soll was empfangen... das schließe ich aus dem Kommentar 
und dem Namen der funktion. Warum gibts du da einen Wert rein? Zum 
zurücksetzen des Empfangs-Registers? Lies mal das Datenblatt!

Grüße

von Karl H. (kbuchegg)


Lesenswert?

Kaj schrieb:


> 1. Da fehlt ein ";" hinter der while()

Full ACK

> 2. Die funktion soll was empfangen... das schließe ich aus dem Kommentar
> und dem Namen der funktion. Warum gibts du da einen Wert rein? Zum
> zurücksetzen des Empfangs-Registers? Lies mal das Datenblatt!

Das kann schon Sinn machen. SPI ist ja eigentlich weniger ein Senden und 
Empfangen sondern mehr ein 'Austauschen von Bytes', wobei der Master die 
Kontrolle über Zeitpunkt und Geschwindigkeit behält. Irgendwie muss ja 
der Slave ja auch die Möglichkeit haben eine angeforderte Antwort an den 
Master zurückzugeben.

ok, bei ihm spielt das jetzt noch keine Rolle, soweit ich das sehen 
kann. Aber im allgemeinen Fall macht es schon Sinn, wenn der Slave einen 
Wert ins SPDR geben kann, welches sich dann der Master mit der nächsten 
Übertragung abholt.

von Kaj (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das kann schon Sinn machen

STOP! Ich nehm alles zurück! Hab nur die hälfte gelesen, hab das spi 
ignoriert. :(
Mein fehler. Bei SPI macht es natürlich sinn. war gedanklich irgendwie 
mehr bei u(s)art :/

von Firebird (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Unterdessen habe ich mir die u(s)art Routine zu Gemüte geführt und ein 
Programm aus dem Tutorial basierend zum laufen gebracht :-)

Als Terminal Programm habe ich HTerm verwendet.

Theoretisch müsste ich jetzt die Routine
1
// Zeichen empfangen
2
uint8_t uart_getc(void)
3
{
4
    while (!(UCSRA & (1<<RXC)));   // warten bis Zeichen verfuegbar
5
    return UDR;                    // Zeichen aus UDR an Aufrufer zurueckgeben
6
}

durch eine SPI Empfangsroutine ersetzen und den Code entsprechend 
ergänzen.

Ich hoffe ich bin auf dem richtigen Weg ;-)

von Firebird (Gast)


Lesenswert?

Hallo zusammen

Ich habe es fertig gebracht über die SUSI Schnittstelle Daten zu 
empfangen. Im Terminal Programm kommen die Daten reingeschossen und die 
grüne sowie rote LED gehen an wenn das entsprechende Byte empfangen 
wurde.

Leider macht die Reihenfolge der Bytes und der grösste Teil der Werte 
keinen Sinn. Mein Verdacht ist, dass die Synchronisation nicht 
sinngemäss funktioniert.

Habt ihr eine Idee woran es liegen könnte?

Anbei der aktuelle Code:
1
#include <avr/io.h>
2
#include <stdlib.h>
3
#include <avr/interrupt.h>
4
 
5
/* 
6
  UART-Init: 
7
  Berechnung des Wertes für das Baudratenregister 
8
  aus Taktrate und gewünschter Baudrate
9
*/
10
 
11
#ifndef F_CPU
12
/*
13
  In neueren Version der WinAVR/Mfile Makefile-Vorlage kann
14
  F_CPU im Makefile definiert werden, eine nochmalige Definition
15
  hier wuerde zu einer Compilerwarnung fuehren. Daher "Schutz" durch
16
  #ifndef/#endif 
17
 
18
  Dieser "Schutz" kann zu Debugsessions führen, wenn AVRStudio 
19
  verwendet wird und dort eine andere, nicht zur Hardware passende 
20
  Taktrate eingestellt ist: Dann wird die folgende Definition 
21
  nicht verwendet, sondern stattdessen der Defaultwert (8 MHz?) 
22
  von AVRStudio - daher Ausgabe einer Warnung falls F_CPU
23
  noch nicht definiert: */
24
#warning "F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000"
25
#define F_CPU 3686400UL  // Systemtakt in Hz - Definition als unsigned long beachten 
26
                         // Ohne ergeben sich unten Fehler in der Berechnung
27
#endif
28
 
29
#define BAUD 9600UL      // Baudrate
30
 
31
// Berechnungen
32
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)  // clever runden
33
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))    // Reale Baudrate
34
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD)     // Fehler in Promille, 1000 = kein Fehler.
35
 
36
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
37
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
38
#endif
39
40
uint8_t ReceivedByte;
41
42
ISR (TIMER2_COMP_vect)
43
{
44
  TCNT2 = 0;                             // Clear timer
45
}
46
47
void init_timer2(void)
48
{  
49
  TCCR2 |= ((1<<CS20) | (1<<WGM21));     // Prescaler 1, CTC mode
50
  OCR2 = 100;                            // Set CTC compare value to 7ms
51
  TIMSK |= (1<<OCIE2);                   // Enable Compare Match Interrupt
52
}
53
54
void init_spi_slave(void)
55
{
56
  DDRB = (1<<PB4);                              // Set MISO as output (SS, MOSI and SCK automatically input)
57
  SPCR = (1<<SPE) | (1<<DORD) | (1<<CPHA);      // SPI enable, LSB transmitted first,
58
                                                // sample at trailing edge 
59
}
60
 
61
void uart_init(void)
62
{
63
  UBRRH = UBRR_VAL >> 8;
64
  UBRRL = UBRR_VAL & 0xFF;
65
66
  UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);   // Asynchron 8N1 
67
  UCSRB |= (1<<RXEN)|(1<<TXEN);               // UART RX und TX einschalten
68
}
69
 
70
// Zeichen empfangen
71
uint8_t spi_receive_data(void)
72
{
73
    //SPDR = 0x00;
74
  while (!(SPSR & (1<<SPIF)));       // warten bis Zeichen verfuegbar
75
  TCNT2 = 0;                         // Clear timer
76
  return SPDR;                     // Zeichen aus SPDR an Aufrufer zurueckgeben
77
}
78
79
// Zeichen senden
80
uint8_t uart_putc(void)
81
{
82
  while (!(UCSRA & (1<<UDRE)));    // warten bis Senden moeglich
83
  
84
  UDR = ReceivedByte;
85
  return UDR;
86
}
87
 
88
int main(void)
89
{
90
  init_spi_slave();
91
  uart_init();
92
  init_timer2();
93
  
94
  sei();                               // Enable Global Interrupt
95
  
96
  while (1) 
97
  {  
98
    DDRC = 0xFF;                       // Set DDRC as output
99
        
100
    ReceivedByte = spi_receive_data();
101
  
102
    {
103
      if(ReceivedByte == 0x60)    // Grüne LED an
104
105
      PORTC |= (1<<PC2);
106
      
107
      if(ReceivedByte == 0x01)    // Rote LED an
108
109
      PORTC |= (1<<PC1);
110
    }
111
    
112
    uart_putc();
113
  }
114
  return 0; // never reached 
115
}

von Kaj (Gast)


Lesenswert?

So, da hab ich doch mal wieder direkt ne verständnis frage, nicht direkt 
an dich Firebird, sondern mehr so an die allgemeinheit:
Warum berechnest du/ihr die Baudrate? Im Datenblatt steht doch was man 
in UBRR reinschreiben muss um die gewünschte Baudrate einzustellen?!

Wozu dann sowas?
1
#define BAUD 9600UL      // Baudrate
2
 
3
// Berechnungen
4
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)  // clever runden
5
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))    // Reale Baudrate
6
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD)     // Fehler in Promille, 1000 = kein Fehler.
7
 
8
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
9
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
10
#endif

1
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)  // clever runden
da kommt bei mir 25,5416666.... raus.
Einmal ins Datenblat geschaut: Ohhh, welch wunder, da steht man muss 
eine 25 in UBRR reinschreiben wenn man bei 4MHz fosc eine baudrate von 
9600 haben möchte...

Wenn man ins Datenblatt schaut und sieht das Wert, den man in UBRR 
schreiben muss, nicht größer als 255 ist, kann man sich das Shiften
1
UBRRH = UBRR_VAL >> 8;
auch sparen und direkt ne 0 rein schreiben.
1
    {
2
      if(ReceivedByte == 0x60)    // Grüne LED an
3
4
      PORTC |= (1<<PC2);
5
      
6
      if(ReceivedByte == 0x01)    // Rote LED an
7
8
      PORTC |= (1<<PC1);
9
    }
Die geschweiften Klammern sind da völlig unnötig.

Ja, ich weiß, Dukatenscheißerei, aber mal ehrlich: die µC Hersteller 
werden das nicht ins Datenblatt schreiben damit man das nochmal 
ausrechnet, weil man es ja doch besser weiß.
Sind immerhin mind. 10 Zeilen unnötiger Code, meiner Meinung nach.


Zurück zum Thema:
Für was ist der Timer da? Ich sehe das du den Initialisierst und an 
allen möglichen Stellen zurück setzt... aber für was?

Was ich ebenfalls noch nicht verstehe:
1
uart_putc();
2
3
----------------------------------------
4
uint8_t uart_putc(void)
5
{
6
  while (!(UCSRA & (1<<UDRE)));    // warten bis Senden moeglich
7
  
8
  UDR = ReceivedByte;
9
  return UDR;
10
}

Vielleicht solltest du ReceivedByte auch an die Funktion übergeben.
Mit so "global/ über Funktionsgrenzen hinweg" usw, wäre ich immer 
vorsichtig. nur weil es so Funktionieren mag, heißt das nicht, das es 
auch richtig ist.
Und warum gibt die Funktion nach dem Senden den Wert von UDR zurück, den 
du aber nirgends auffängst?!

von Karl H. (kbuchegg)


Lesenswert?

Kaj schrieb:
> So, da hab ich doch mal wieder direkt ne verständnis frage, nicht direkt
> an dich Firebird, sondern mehr so an die allgemeinheit:
> Warum berechnest du/ihr die Baudrate? Im Datenblatt steht doch was man
> in UBRR reinschreiben muss um die gewünschte Baudrate einzustellen?!


Weil es wesentlich einfacher ist, bei einem Wechsel der Baudrate 
und/oder der Taktfrequenz ganz einfach die Werte anzugeben die man hat: 
die gewünschte Baudrate bzw. die tatsächliche Taktfrequenz.
Oder willst du bei jedem Wechsel bzw. bei jedem neuen Programm erneut 
wieder im Datenblatt dir den Wert raussuchen, der ins Register 
geschrieben werden muss? Da ist es doch viel einfacher, wenn der 
Compiler den Wert ganz einfach ausrechnet.


> Wenn man ins Datenblatt schaut und sieht das Wert, den man in UBRR
> schreiben muss, nicht größer als 255 ist, kann man sich das Shiften
>
1
> UBRRH = UBRR_VAL >> 8;
2
>
> auch sparen und direkt ne 0 rein schreiben.

UBRR_VAL ist letzten Endes eine Konstante, die der Compiler kennt. Halte 
Compilerbauer nicht für blöd, so etwas wegzuoptimieren ist eine triviale 
Aufgabe für jeden Studenten im ersten Semester Compilerbau.

> Die geschweiften Klammern sind da völlig unnötig.

Sie schaden aber auch nicht.


> Ja, ich weiß, Dukatenscheißerei, aber mal ehrlich: die µC Hersteller
> werden das nicht ins Datenblatt schreiben damit man das nochmal
> ausrechnet, weil man es ja doch besser weiß.

Es geht nicht ums besser wissen.
Die µC-Hersteller haben das auch nur ausgerechnet.
Die Tabellen haben ihre Berechtigung, wenn man eine 
Baudraten/Taktfrequenz Kombination sucht, die möglichst wenig 
prozentualen Fehler produziert.

> Sind immerhin mind. 10 Zeilen unnötiger Code, meiner Meinung nach.

Völliger Quatsch.
Nur weil du etwas in den Code schreibst, heißt das noch lange nicht, 
dass das dann auch im fertigen Programm auftaucht. Constant Folding ist 
für Compilerbauer eine Fingerübung.
Letzten Endes bleibt im Programm, welches auf den µC gebrannt wird, auch 
nur eine Zuweisung eines Zahlenwertes an das Register übrig. Einziger 
Unterschied: du hast dir als Programmierer das raussuchen aus dem 
Datenblatt erspart, weil der Compiler die Werte ausgerechnet hat. Mit 
genau den gleichen Formeln, mit der auch die Tabellen im Datenblatt 
erzeugt wurden.


> Für was ist der Timer da? Ich sehe das du den Initialisierst und an
> allen möglichen Stellen zurück setzt... aber für was?

Um ein Timeout zu realisieren.

: Bearbeitet durch User
von Firebird (Gast)


Lesenswert?

Kaj schrieb:
> Für was ist der Timer da? Ich sehe das du den Initialisierst und an
> allen möglichen Stellen zurück setzt... aber für was?

Gemäss SUSI Spezification müssen aufeinanderfolgende Bytes schneller als 
7ms gesendet werden. Eine Pause von 8ms +/- 1ms wird benutzt um zu 
Synchronisieren.

http://www.d-i-e-t-z.de/jd/plaene/SUSI_310.zip

Der Timer zählt bis 7ms hoch und sollte bei jedem empfangenem Byte 
zurüchgesetzt werden, vor 7ms erreicht wird. Wird 7ms erreicht so soll 
Synchronisiert werden, d.h. die Byte Reihenfoge wieder in Takt sein.

von Oliver (Gast)


Lesenswert?

Firebird schrieb:
> #warning "F_CPU war noch nicht definiert, wird nun nachgeholt mit
> 4000000"
> #define F_CPU 3686400UL

Das Leben steckt voller selbstgestellter Fallen...

Oliver

von Firebird (Gast)


Lesenswert?

Hallo zusammen

Ich habe die Timer2 und UART Einstellungen nochmals mit dem Atmega8 
Datenblatt verglichen und kann keine Fehler erkennen. Trotzdem erhalte 
ich nicht die Werte die von der Zentrale über den Decoder gesendet 
werden.

Für Adresse 0x60 ist der Wert 0x01 eingestellt, für Adresse 0x61 der 
Wert 0x00. Am Terminal (HTerm) kommen folgende Werte an:

Adresse 0x60, Wert 0x43 (falsch)
Adresse 0x60, Wert 0x01 (Zufällig korrekt)
Adresse 0x60, Wert 0xA5 (falsch)

Adresse 0x61, Wert 0x51 (falsch)
Adresse 0x61, Wert 0x53 (falsch)

Ich vermute, dass einige Bytes nicht erfasst werden oder irgenwie 
verloren gehen. Jetzt komme ich nicht mehr weiter und bitte um einen 
Hinweis damit ich einen Schritt weiter komme.

von Firebird (Gast)


Lesenswert?

Mit freude stelle ich fest, dass der Code tatsächlich funktionieren 
würde.

Per Zufall stellte ich fest, dass die Bytes schneller empfangen werden 
wenn ich mit einem Finger in die Nähe von PORTB komme. Wenn ich mit 
einer Pinzette PINB1 berühre kommen die Daten reingeschossen und werden 
am PC korrekt angezeigt. PINB0 und PINB2 haben keinen Einfluss beim 
Berühren.

Die Verdrahtung ist gemäss SUSI Spezifikation korrekt und auch im Code 
ist nichts drin das PINB1 (OC1A) beinflussen könnte.

Habt ihr eine Idee wo das Problem liegt?

von Firebird (Gast)


Lesenswert?

Jetzt hab ich's geschnallt, man muss PINB2 low halten damit SPI aktiv 
ist.

Schon wieder was gelernt :-)

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.