www.mikrocontroller.net

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

Autor: Joan P. (joan)
Datum: 16.05.2008 16:31

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: 16.05.2008 17:43

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: 16.05.2008 17:53

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: 16.05.2008 20:51

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: 19.05.2008 11:41

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: 19.05.2008 22:50

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 Email-Adresse ist freiwillig. Wenn Sie automatisch per Email über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Suchfunktion und Betreffsuche benutzen - vielleicht gibt es schon einen ähnlichen Beitrag
  • Aussagekräftigen Betreff wählen
  • Im Betreff angeben um welchen Controllertyp es geht (AVR, PIC, ...)
  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
  • JPEG-Dateien (.jpg) nur für Fotos und Scans verwenden
  • Schaltpläne, Screenshots usw. als PNG oder GIF anhängen

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [pre]vorformatierter Text (z.B. Code in anderen Sprachen)[/pre]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel






webmaster@mikrocontroller.netImpressumWerbung auf Mikrocontroller.net