Forum: HF, Funk und Felder AT86RF230 - SRAM Read Access fehlerhaft..


von Joan P. (joan)


Lesenswert?

Ich empfange hier Frames mit 4 Byte Payload, wobei sich die Nachricht 
nach dem 2ten Byte wiederholt.. also doppelt vorhanden ist.
Das mach ich, weil ich recht schnell ohne großes Sicherheitsnetzt (nur 
CCA beim Senden) viele Statusmeldungen mit mehreren Modulen verschicke.. 
da kommen schon mal Fehler durch, trotz meiner kleinen CRC. Insgesamt 
kommt noch eine 16Bit CRC vom RF230 automatisch erzeugt über das ganze.
Aber genug der Vorrede..

Laut dem Datenblatt vom dem RF230 kann man während des Empfangs schon 
mit dem Lesen der empfangenen Bytes beginnen. Zuerst lese ich also 32µs 
nach dem RX_START Interrupt nur die PHR ein (1byte via 802.15.4 brauchte 
32µs), um zu entscheiden, ob die Nachricht überhaupt von mir ist (meine 
Nachrichten sind immer gleich lang) sonst kann ich das Empfangen gleich 
wieder beenden und auf das nächste SFD warten.
Das funktioniert problemlos.

Danach warte ich 64µs und lese dann byte 1&2 meiner eigentlichen 
Nachricht nochmal zusammen mit dem PHR-byte ein und schaue nach, ob mein 
selbstgebasteltes CRC stimmt (Bit2:0 von byte 2), sonst kann ich auch 
hier wieder den Empfang abbrechen. Ohne Störungen auch dies Problemlos. 
Keine Ausfälle.

Nun vergehen wieder 64µs bis byte 3&4 meiner Nachricht eintrudeln, 
welche genau das gleiche wie byte 1&2 enthalten sollten. Da ich jetzt 
aber nicht schon wieder PHR + byte 1&2 einlesen will (4µs/byte) habe ich 
es hier mit SRAM-Lesezugriff versucht.
Das verursacht aber sehr häufig Fehler.. byte 3&4 != byte 1&2.
Im Vergleich mit dem Lesen von byte 3&4 im Rahmen eines kompletten 
Lesevorganges mit PHR enstehen keine Fehler, dh. byte 3&4 == byte 1&2.

Selbst wenn ich mittels SRAM-Zugriff byte 1&2 (wie im Code aufgeführt) 
einlese werden Fehler angezeigt, obwohl ichs nur mit dem vorher per 
normalem Lesezugriff erhaltenen vergleiche.. was mache ich falsch?!

Die entsprechenden Codeschnipsel:
1
ISR (INT5_vect) // kommt 8µs nachdem das PHR-Octet begonnen hat (per Octet 32µs)
2
{
3
initialize_timer4B_us (32); // Timer 4 Compare Match in 32us
4
// 32µs warten, dann PHR einlesen
5
poll_timer4B_CompareMatch();
6
initialize_timer4B_us (64); // Timer 4 Compare Match in 62ms
7
uint8_t rec_PHR = spi_read_framePHR(); // PHR einlesen
8
if (rec_PHR == 6) // 1. Test ob's zu uns gehört: ((byte1 + byte2)* 2) + (16Bit-CRC) <=> 6)
9
{
10
  // 64µs warten, dann 1st Frame einlesen
11
  poll_timer4B_CompareMatch(); // Timer 4 Output Compare B Flag pollen
12
  initialize_timer4B_us (62+16); // Timer 4 Compare Match in 62ms
13
  uint16_t rec_frame = spi_read_frame(); // die ersten 2 byte einlesen
14
  if (crc_calc (crc_poly, (rec_frame>>8), rec_frame) != 0) goto Abbruch; // CRC-Check
15
  // wenn CRC check sagt 'recFrame' ist defekt oder nicht unseres -> Abbruch
16
  // 64µs warten, dann das 2nd Frame einlesen
17
  poll_timer4B_CompareMatch(); // Timer 4 Output Compare B Flag pollen
18
19
  // Compare 1st mit 2nd Frame
20
/* abwechselnd mit spi_read_SRAMframe(addr) oder spi_read_2ndframe() */
21
/* die folgende Art des Einlesens verursacht übermässig Fehler, egal ob 0x02 oder 0x00 als Adresse genutzt wird */
22
  if (rec_frame != spi_read_SRAMframe(0x00)) goto Abbruch; // 2nd Frame einlesen und Vergleichen
23
/* diese Art des Einlesens verursacht keine Fehler */
24
  if (rec_frame != spi_read_2ndframe()) goto Abbruch; // 2nd Frame einlesen und Vergleichen
25
26
....
27
}
28
29
static void initialize_timer4B_us (uint16_t time_us)
30
{
31
  uint16_t timer4 = TCNT4; // Timer/Counter4 Zähler auslesen
32
  OCR4B = timer4 + time_us; // Timer 4B mit ...µs bis Output Compare B Flag starten
33
  TIFR4 |= (1<<OCF4B); // Timer 4 Output Compare B Flag reset
34
}
35
36
static void poll_timer4B_CompareMatch (void)
37
{
38
  while (!((1<<OCF4B) == (TIFR4 & (1<<OCF4B)))) {} // Timer 4 Output Compare B Flag pollen
39
}

Und hier die beiden Funktionen, um die es geht:
1
// 2 byte Payload bei Adresse aus den Speicher des RF230 lesen ================
2
static uint16_t spi_read_SRAMframe (uint8_t addr)
3
{
4
  PORTB &= ~(1<<PB0); // SEL low
5
  uint16_t two_bytes_read=0;
6
  SPDR = 0x00; // SPI read SRAMframe
7
    // do 17 clock-cycles nothing
8
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
9
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
10
  SPDR = addr; // Startadresse fürs lesen der bytes
11
    // do 17 clock-cycles nothing
12
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
13
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
14
  SPDR = 0x00; // SPI create SCK
15
    // do 17 clock-cycles nothing
16
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
17
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
18
  two_bytes_read |= (SPDR << 8); // lies & schieb 'first_byte' um 8 Stellen nach links
19
  SPDR = 0x00; // SPI create SCK
20
    // do 17 clock-cycles nothing
21
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
22
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
23
  two_bytes_read |= SPDR; // lies 'second_byte'
24
  PORTB |= (1<<PB0); // SEL high
25
  return (two_bytes_read); // give back 2 bytes
26
} // ======================================================================= */
27
28
29
// 1 Frame mit 2 byte Payload aus den Speicher des RF230 lesen ================
30
static uint16_t spi_read_2ndframe (void)
31
{
32
  PORTB &= ~(1<<PB0); // SEL low
33
  uint16_t two_bytes_read=0;
34
  SPDR = 0x20; // SPI read Frame
35
    // do 17 clock-cycles nothing
36
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
37
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
38
  SPDR = 0x00; // SPI create SCK, skip PHR
39
    // do 17 clock-cycles nothing
40
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
41
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
42
  SPDR = 0x00; // SPI create SCK, skip 1st byte
43
    // do 17 clock-cycles nothing
44
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
45
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
46
  SPDR = 0x00; // SPI create SCK, skip 2nd byte
47
    // do 17 clock-cycles nothing
48
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
49
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
50
  SPDR = 0x00; // SPI create SCK
51
    // do 17 clock-cycles nothing
52
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
53
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
54
  two_bytes_read |= (SPDR << 8); // lies & schieb 'first_byte' um 8 Stellen nach links
55
  SPDR = 0x00; // SPI create SCK
56
    // do 17 clock-cycles nothing
57
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
58
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
59
  two_bytes_read |= SPDR; // lies 'second_byte'
60
  PORTB |= (1<<PB0); // SEL high
61
  return (two_bytes_read); // give back 2 bytes
62
} // ======================================================================= */

Grüsse und Danke fürs Lesen

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Hmm, naja, einen offensichtlichen Fehler sehe ich erstmal nicht.

Aber nur so: richtig gewinnen kannst du durch den SRAM-Zugriff bei
derart kurzen Frames eigentlich nicht.  Die beiden zusätzlichen
SPI-Transfers für das neue Kommando und die SRAM-Adresse lohnen
nicht.  Warum startest du nicht einfach einen frame read und
lässt diesen solange aktiv, solange es ein möglicher, dich
interessierender Frame ist?  D. h. /SEL auf low, frame read Kommando,
PHR lesen.  Wenn Länge != 4 => Abbruch.  Zwei weitere Bytes
abwarten und einlesen, CRC überprüfen, wenn falsch => Abbruch.
Noch zwei Bytes abwarten und lesen, gegen die vorigen vergleichen,
wenn falsch => Abbruch.  CRC abwarten und lesen, /SEL auf high.
CRC-16 noch vergleichen (wenn's dich interessieren sollte).

von Joan P. (joan)


Lesenswert?

Oh, das ist auch ne Idee.. stimmt, da wär ich nicht drauf gekommen.

Das werd ich gleich mal umsetzen und probieren.
So richtig wild ist das Problem mit dem SRAM für mich ja eigentlich 
nicht, weil wie du schon sagtes ich so viel Zeit ja nicht verlustiere 
wenn ich von vorn lese.
Bin auch nur wegen dem CRC16 Problem drauf gekommen, was in dem neuen 
Thread drin ist.

Danke schon mal für die Idee :-)

von Axel W. (awachtler)


Lesenswert?

Die Lösung mit dem Lesen der 4 Byte Frames als 2x2 Bytes nach dem 
RX_START IRQ scheint mir doch etwas speziell auf das Problem optimiert 
zu sein.

Wenn ein Byte 4us zum Upload braucht, dann sind die
o.g. Rahmen inkl. SPI-Command, PHR und CRC in 8 * 4us = 32us im 
Controller. Wenn die Verarbeitung der Empfangsdaten um diese Dauer 
verzoegert werden kann, reicht also der TRX_END Interrupt aus, da der 
nächste Frame frühestens nach ca. 6*32us (5 Byte Preambel + 1 Byte SFD) 
in den SRAM geschrieben wird. Ein Vorteil dieser Lösung wäre außerdem 
eine kurze Verweildauer in der ISR.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Hier einfach noch so paar Ideen zum ursprünglichen Problem (auch wenn
ich nicht denke, dass du mit dem SRAM-Zugriff wirklich was gewinnst):

Den Sinn der NOPs verstehe ich nicht ganz: das Pollen des SPIF kostet
doch am Ende auch nicht mehr Zeit, und man kann sich zumindest 100%ig
sicher sein, dass das SPI auch wirklich schon fertig ist.

Das Timing könnte knapp sein.  Wenn du dich irgendwo um 1 oder 2 µs
vertust, versuchst du ein Byte zu lesen, das noch gar nicht im Puffer
steht.  Ich würde ,,für alle Fälle'' erstmal 1...2 µs ,,drauflegen'',
um auf der sicheren Seite zu sein.  Da du mittels SRAM zugreifst,
bekommst du ja nicht einmal einen underrun-Interrupt, den gibt's nur
beim Frame-Lesen.

Die Frage ist noch, was du denn eigentlich als falsche Bytes liest.
Steht das in irgendeinem Zusammenhang mit den gesendeten Daten, oder
ist das einfach nur der vorherige Inhalt des SRAMs?  Ggf. kannst du
ja zum Debuggen den SRAM mal vor dem Empfang mit einer bekannten
Bytefolge füllen?

von Joan P. (Gast)


Lesenswert?

Danke für eure Ideen.. ich hab mittlerweile die Version am laufen, wo 
ich per SPI immer dann das byte lese wenn ich es brauche, so wie das 
Jörg zuerst vorgeschlagen hat. SRAM Zugriff hab ich nicht genutzt jetzt.

Ich muss zwar zu Anfang 160µs warten, bis ich mit dem einlesen des PHR 
anfangen kann nachdem das RX-START Interrupt kam, aber das reicht für 
mein Timing locker aus.

Es läuft nun alles so wie ich mir das vorgestellt hab, zwar bisschen 
anders, aber es läuft und stabil. Habe dank der CRC16, die der Chip 
macht und der doppelten Nachrichtenversendung 2 Modulpaare bisher über 
20h ungestört am laufen (je Paar wechselseitig Sender und Emfpänger bei 
den Paaren, Umschaltzeit ca 43ms).. und keine Ausfälle, obwohl die sich 
gegenseitig beharken. Ich bin sowas von glücklich im Moment :-)

Zum Schluss hat sich dann lustigerweise noch der im Datenblatt 
beschriebene Fehler mit dem CCA-Rückmeldungsausfall gemeldet, was bisher 
nie der Fall bei mir war.. da hab ich nicht schlecht geguckt.
Dort werkelt jetzt die von Jörg empfohlene harte Abfrage nach 140µs nach 
CCA-START.

Wenn nun nix mehr ausfällt auf Sicht von 5 Tagen Dauereinsatz ändere ich 
nix mehr am Code. :-)

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.