www.mikrocontroller.net

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


Autor: Joan P. (joan)
Datum:

Bewertung
0 lesenswert
nicht 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:
ISR (INT5_vect) // kommt 8µs nachdem das PHR-Octet begonnen hat (per Octet 32µs)
{
initialize_timer4B_us (32); // Timer 4 Compare Match in 32us
// 32µs warten, dann PHR einlesen
poll_timer4B_CompareMatch();
initialize_timer4B_us (64); // Timer 4 Compare Match in 62ms
uint8_t rec_PHR = spi_read_framePHR(); // PHR einlesen
if (rec_PHR == 6) // 1. Test ob's zu uns gehört: ((byte1 + byte2)* 2) + (16Bit-CRC) <=> 6)
{
  // 64µs warten, dann 1st Frame einlesen
  poll_timer4B_CompareMatch(); // Timer 4 Output Compare B Flag pollen
  initialize_timer4B_us (62+16); // Timer 4 Compare Match in 62ms
  uint16_t rec_frame = spi_read_frame(); // die ersten 2 byte einlesen
  if (crc_calc (crc_poly, (rec_frame>>8), rec_frame) != 0) goto Abbruch; // CRC-Check
  // wenn CRC check sagt 'recFrame' ist defekt oder nicht unseres -> Abbruch
  // 64µs warten, dann das 2nd Frame einlesen
  poll_timer4B_CompareMatch(); // Timer 4 Output Compare B Flag pollen

  // Compare 1st mit 2nd Frame
/* abwechselnd mit spi_read_SRAMframe(addr) oder spi_read_2ndframe() */
/* die folgende Art des Einlesens verursacht übermässig Fehler, egal ob 0x02 oder 0x00 als Adresse genutzt wird */
  if (rec_frame != spi_read_SRAMframe(0x00)) goto Abbruch; // 2nd Frame einlesen und Vergleichen
/* diese Art des Einlesens verursacht keine Fehler */
  if (rec_frame != spi_read_2ndframe()) goto Abbruch; // 2nd Frame einlesen und Vergleichen

....
}

static void initialize_timer4B_us (uint16_t time_us)
{
  uint16_t timer4 = TCNT4; // Timer/Counter4 Zähler auslesen
  OCR4B = timer4 + time_us; // Timer 4B mit ...µs bis Output Compare B Flag starten
  TIFR4 |= (1<<OCF4B); // Timer 4 Output Compare B Flag reset
}

static void poll_timer4B_CompareMatch (void)
{
  while (!((1<<OCF4B) == (TIFR4 & (1<<OCF4B)))) {} // Timer 4 Output Compare B Flag pollen
}

Und hier die beiden Funktionen, um die es geht:
// 2 byte Payload bei Adresse aus den Speicher des RF230 lesen ================
static uint16_t spi_read_SRAMframe (uint8_t addr)
{
  PORTB &= ~(1<<PB0); // SEL low
  uint16_t two_bytes_read=0;
  SPDR = 0x00; // SPI read SRAMframe
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  SPDR = addr; // Startadresse fürs lesen der bytes
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  SPDR = 0x00; // SPI create SCK
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  two_bytes_read |= (SPDR << 8); // lies & schieb 'first_byte' um 8 Stellen nach links
  SPDR = 0x00; // SPI create SCK
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  two_bytes_read |= SPDR; // lies 'second_byte'
  PORTB |= (1<<PB0); // SEL high
  return (two_bytes_read); // give back 2 bytes
} // ======================================================================= */


// 1 Frame mit 2 byte Payload aus den Speicher des RF230 lesen ================
static uint16_t spi_read_2ndframe (void)
{
  PORTB &= ~(1<<PB0); // SEL low
  uint16_t two_bytes_read=0;
  SPDR = 0x20; // SPI read Frame
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  SPDR = 0x00; // SPI create SCK, skip PHR
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  SPDR = 0x00; // SPI create SCK, skip 1st byte
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  SPDR = 0x00; // SPI create SCK, skip 2nd byte
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  SPDR = 0x00; // SPI create SCK
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  two_bytes_read |= (SPDR << 8); // lies & schieb 'first_byte' um 8 Stellen nach links
  SPDR = 0x00; // SPI create SCK
    // do 17 clock-cycles nothing
    __asm__ __volatile__ (" nop \n nop \n nop \n nop \n nop \n nop \n nop \n"
    "nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n nop \n":: );
  two_bytes_read |= SPDR; // lies 'second_byte'
  PORTB |= (1<<PB0); // SEL high
  return (two_bytes_read); // give back 2 bytes
} // ======================================================================= */

Grüsse und Danke fürs Lesen

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Joan P. (joan)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Axel Wachtler (awachtler)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Joan P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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. :-)

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.