Hallo zusammen, ich grüble gerade seit mehreren Tagen über ein merkwürdiges Problem - das ich zwar isolieren aber bis jetzt nicht nachvollziehen konnte. Zur Umgebung: Ein Atmega328P bei 3,3 V und 12 MHz (Quarz) soll als SPI-Slave agieren. Zur Synchronisation kann leider nicht das CS-Signal verwendet (daher mit GND verbunden) werden, stattdessen soll das Timing der Kommunikation genutzt werden: Wir für etwa 250 µs kein SPI-Interrupt ausgelöst, werden die empfangenen Daten ausgewertet (momentan aus dem Code entfernt) und SPIE deaktiviert und aktiviert, um möglichen Versatz der Daten zum Clock zu umschiffen. Dazu wird im SPI-Interrupt ticks auf 0 gesetzt und über einen Timer-Interrupt der Wert (sofern kleiner 255) inkrementiert. Über einen Task, der im main-loop ausgeführt wird, wird das Timing und die Anzahl der empfangenen Bytes überprüft. Die Variable ist selbstverständlich volatile und im Timer-Interrupt habe ich aus Verzweiflung schon einen Atomic-Block gesetzt, welcher aber keine Besserung bringt. Zusätzlich lasse ich, um Überlappung der Interrupts besser erkennen zu können, in den jeweiligen Funktionen Pins wackeln und konnte soweit keine Auffälligkeit erkennen. Das Problem: Wie im Screenshot vom Logic Analyzer zu sehen ist (Aufzeichnung mit Saleae Logic 1.2.12 in tickerror.logicdata.zip), hätte vor dem SPI IRQ (kurz nach der +30 µs-Marke, Ch06) tick inkrementiert, kurz danach (kurz vor der +40 µs-Marke, Ch05) aber im SPI-Interrupt wieder auf 0 zurückgesetzt werden sollen. Im darauf folgenden Task-Aufruf (+40 µs-Marke, Ch07) steht in Ticks allerdings 255 (Format auf dem UART: Ticks und Anzahl der der empfangenen Bytes roh) - die Nachricht hätte aber nicht auftreten dürfen, sondern erst nachdem alle Bytes empfangen wurden (nicht im Bild, 4,333 ms später). main.c enthält das isolierte Problem, nicht den gesamten Code. Zusätzlich habe ich das Atmel Studio 7-Projekt angehängt. Die mit dem LA erfassten Daten wurden mit dem angehängten Code erzeugt (Stimuli erfolgte mit einem "rückwärts" betriebenen Logic8, Ch00-Ch02), wobei der Fehler reproduzierbar aber nicht immer an der selben Stelle auftritt. Vielleicht bin ich mittlerweile blind und habe eine Kleinigkeit übersehen oder "fressen" sich die Interrupts gegenseitig? Viele Grüße & Vielen Dank! Chris
hat vermutlich nicht mit deinem Problem zu tun aber ATOMIC_BLOCK(ATOMIC_FORCEON) gehört nicht in eine ISR. Bei dem Atmel sind alles ISR gesperrt wenn eine ISR aufgerufen wird, es macht damit gar keine sinn.
Hallo Peter, Danke für den Hinweis. Der Atomic-Block war wie schon geschrieben eine Verzweiflungstat. Was ich vergessen habe zu schreiben: das Problem besteht unabhängig von dem Block. Habe beides getestet und kam auf das gleiche Ergebnis. Viele Grüße Chris
Moin, du führst das volatile-Konzept selbst ad absurdum, indem du in "task" mit einer Kopie von "ticks" arbeitest und nicht mit "ticks" selbst. Da dein Programm zu 99,9% aus wiederholter Ausführung von "task" besteht, schlägt jeder Interrupt auch innerhalb der Ausführung von "task" zu und beschert dir inkonsistente Zustände ( dh unterschiedliche Werte von dem lokalen "t" in "task" und "ticks" ). Ändere mal "task" so ab, dass du direkt mit "ticks" arbeitest. Weiterhin würde ich "task" dann auch nicht permanent aufrufen, sondern nur dann, wenn du tatsächlich rücksetzen willst. Diese Rücksetzentscheidung kannst du per volatile flag-Variable direkt in der Timer-ISR setzen und diese Flag-Variable in der main-Loop abfragen:
1 | volatile uint8_t reset_flag = 0; |
2 | #define TICK_LIMIT 200
|
3 | ISR(TIMER0_COMPA_vect) |
4 | {
|
5 | PORTD |= (1<<PORTD7); |
6 | if(ticks < 255) ticks++; |
7 | if ( bytepos > 0 && ticks > TICK_LIMIT ) reset_flag = 1; |
8 | PORTD &= ~(1<<PORTD7); |
9 | }
|
10 | |
11 | void do_reset(void) |
12 | {
|
13 | PORTD |= (1<<PORTD6); |
14 | spi_disable(); |
15 | spi_enable(); |
16 | |
17 | uart_debug_putc(ticks); |
18 | uart_debug_putc(bytepos); |
19 | bytepos = 0; |
20 | PORTD &= ~(1<<PORTD6); |
21 | }
|
22 | |
23 | |
24 | ...
|
25 | ...
|
26 | ...
|
27 | while ( 1 ) { |
28 | if ( reset_flag ) { |
29 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { |
30 | reset_flag=0; |
31 | do_reset(); |
32 | }
|
33 | }
|
34 | }
|
Wie soll denn ueberhaupt eine Synchronisation passieren ? Einfach etwas reinclocken ? Nee. Ohne CS laeuft nichts. Du kannst es natuerlich ohne CS probieren. .. bis es irgendwann nicht mehr geht.
Rainer B. schrieb: > du führst das volatile-Konzept selbst ad absurdum, indem du in "task" > mit einer Kopie von "ticks" arbeitest und nicht mit "ticks" selbst. damit wollte ich bezwecken, dass der Wert in ticks in der Ausgabe derselbe wie bei der Abfrage ist. Ich habe deine Codeänderungen übernommen und habe den gleichen Effekt wie zuvor. :( Ich habe lediglich TICK_LIMIT angepasst, da ich kürzere Zeiten für den Sync brauche... Glitches würde ich ausschließen, die Versorgung vom Chip sieht sauber aus. Ich weiß nicht, ob mein Verdacht berechtigt ist, aber der AVR stammt von eBay und mir kam die Laserung etwas ungewöhnlich vor (der linke Teil vom Atmel-A ist nur ein Stroke, der Rest ist doppelt) hatte bis jetzt aber noch keine anderen neuen Atmels auf dem Tisch um vergleichen zu können... Viele Grüße und abermals vielen dank für die Hilfe! Chris
Hi, für mich sieht es so aus, dass das Programm genau das macht, was es soll: Anhand des Abschnittes, den du getraced hast kann man folgendes sehen: - Ticks wird mit jedem Aufruf der OCR0A-Match ISR um 1 hochgezählt ( am Anfang 4, am Ende 8, das passt zu den Peaks auf PD7 - bytepos wird in do_reset auf 0 gesetzt und mit jedem empfangenen Zeichen um 1 hochgezäht - "ticks" wird im SPI-Interrupt auf 0 gesetzt - Leider endet der Trace, bevor ticks > TICK_LIMIT, daher lässt sich hierzu nichts aussagen. Frage: Was hättest du denn erwartet?
:
Bearbeitet durch User
Hi, wenn der Code so funktionieren würde, wie ich gewollt hatte, müsste ticks im SPI-Interrupt auf 0 gesetzt werden, was aber nicht der Fall ist. Stattdessen bleibt die 4 in ticks. Der Code produziert aber auch Verhalten wie es gewollt ist, wie in tickerror2_ok.png zu sehen ist. Das ist eben, was mich komplett stutzig macht... Viele Grüße Chris
Ah, ok. Was mich stutzig macht: Die Ausführung von do_reset, angezeigt durch PD6, wird in jedem Fall durch eine Ausführung der Timer-ISR, angezeigt durch PD7, angestossen. Am Beginn des Diagramms passt es: Hier folgt der Puls von PD6 direkt auf den Puls von PD7. Am Ende des Diagramms ist aber eine ca 20 µs lange Lücke zwischen der fallenden Flanke von PD6 und der steigenden Flanke von PD7. Das kann ich mir nicht erklären: Wodurch wird an dieser Stelle do_reset aufgerufen? Hast du irgendwo einen weiteren Aufruf von do_reset drin? Oder setzt du ausser in der Timer-ISR noch irgendwo noch "reset_flag"?
Ich kann es mir zwar nicht zu 100 % erklären, aber nach einem Reboot vom PC und einem Clean Build funktionierte der Code. So etwas ist mir bis jetzt noch nie untergekommen. Um sicher zu gehen, gibt der AVR jetzt beim Start _DATE__ und __TIME_ aus. Vielen Dank für deine Unterstützung, Rainer! Viele Grüße und noch ein schönes Restwochenende Chris
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.