Forum: Mikrocontroller und Digitale Elektronik AVR Interrupts TIMER/USART


von Marc B. (__marc)


Lesenswert?

Hallo zusammen,

ich habe folgendes Problem (ATMEGA164P):

Es wird alle 5ms eine Messung (Dauer ca 70µs) mittels externem 
AD-Wandler über die SPI - Schnittstelle durchgeführt. Dies habe ich 
mittels Timer (Compare Modus) gelöst. Die Messung funktioniert 
einwandfrei. Sie wird mittels Terminal-Programm vom PC aus gestartet. 
Nur mit dem Stoppen klappt es nicht so ganz...
Start und Stop - Befehl bestehen jeweils aus 5 Bytes. Die Bytes werden 
im USART-Register des Controllers mittels Interrupt-Routine empfangen. 
Durch Debuggen habe ich festgestellt, dass die Daten des Stop-Befehls 
nicht immer vollständig ankommen. Der Start Befehl funktioniert immer. 
Deshalb gehe ich davon aus, dass es etwas mit der Messung zu tun hat.

Folgende Dinge sind mir noch nicht ganz klar:
1. Wenn die Interruptroutine des Timers ausgeführt wird, was passiert 
mit dem Interrupt des USARTs?
2. Was passiert mit dem Daten die von dem Terminal-Programm des PCs 
gesendet werden, wenn das USART-Register noch nicht gelesen wurde.

Weitere Informationen:
AVRSTUDIO v4.13
AVRGCC v4.1.1

von Willi W. (williwacker)


Lesenswert?

zu 2. Wenn Du nicht mit Handshake-Signalen arbeitest, landen die zuviel 
gesendeten Signale vom PC im Nirvana. Du musst sie allerdings abholen. 
Bei 9600Bd hast Du dafür aber ca. 1ms Zeit. Der ATmega88 (Deinen kenne 
ich nicht, vermute aber, sie sind einigermaßen identisch) kann noch ein 
zusätzliches Byte puffern. Da sollten die 70us Deiner Wandlung nicht 
stören.

Aber: Welches PC-Programm setzt Du ein? Wir hatte große Schwierigkeiten 
mit dem Hyperterminal. Das lieber nicht verwenden, es gibt andere gute 
zum kostenlosen Download (ich habe "Hercules", kann noch einiges mehr, 
funktioniert aber sicher).

Allgemein: Wenn Du nicht besondere Maßnahmen triffst, kann ein Interrupt 
nicht von einem anderen (wie prior auch immer) unterbrochen werden. Das 
liegt daran, dass beim Einsprung in den Interrup das Globale 
Interruptflag gelöscht wird und erst am Ende des Interrupt wird es 
wieder gesetzt (RETI). Wenn Du es zu Beginn ( oder wo auch immer ) 
Deines Interrupt setzt (SEI), dann können andere Interrupts den aktuell 
laufenden unterbrechen (wie prior auch immer). Aber Vorsicht! Da kann 
man viel kaputt machen!

Deinen Wandlungs-Interrupt (ich gehe hierbei davon aus, dass Du im 
Interurpt auf das Ende der Wandlung wartest) könntest Du wahrscheinlich 
unterbrechen lassen, NACHDEM die Wandlung gestartet wurde. Du wartest ja 
ohnehin nur, etwas länger warten stört Deine Wandlung nicht.

Eine andere Alternative könnte sein, einen Interrupt am Ende der 
Wandlung zu generieren. Das scheint mir eine bessere Lösung zu sein.

Schöne Pfingsten noch
Willi Wacker

von Marc B. (__marc)


Lesenswert?

Hallo Willi,

danke für deine schnelle Antwort.

Ich verwende HTERM als Terminal-Programm, weil das binär, hex, dezimal 
und ASCII anzeigen kann.

> Wenn Du nicht mit Handshake-Signalen arbeitest, landen die zuviel
> gesendeten Signale vom PC im Nirvana.

Ich arbeite nicht mit Handshake-Signalen.
Aber das Start-Signal kommt ja ordnungsgemäß an. Nachdem das 
Start-Signal gesendet wurde, wird der Interrupt des TIMERS für die 
Messung aktiviert. Das Stop Signal kommt dann meistens nicht mehr 
richtig an. Wie sieht es denn aus, wenn die Interruptroutine von der 
Messung noch läuft, alle weiteren Interrupts gesperrt sind und dann der 
Interrupt des USARTs ausgelöst wird? Wird das Zeichen dann nach Beenden 
der Routine des TIMERS abgearbeitet?
Oder geht das Zeichen dann verloren?

> Eine andere Alternative könnte sein, einen Interrupt am Ende der
> Wandlung zu generieren. Das scheint mir eine bessere Lösung zu sein.

Wie macht man das?

von Hannes L. (hannes)


Lesenswert?

> Es wird alle 5ms eine Messung (Dauer ca 70µs) mittels externem
> AD-Wandler über die SPI - Schnittstelle durchgeführt.

Auch der SPI-Bus kann im Hintergrund laufen und einen Interrupt 
auslösen, wenn ein Byte fertig ist.

Ich würde zur Realisierung folgenden Ansatz machen (in ASM gedacht):

- Timer-Interrupt startet alle 5ms die SPI-Sequenz und kehrt sofort zur
  Mainloop zurück.

- SPI-Interrupt führt diese SPI-Sequenz weiter (State-machine), sendet
  ggf. Ergebnis über UART-TX oder legt Ergebnis in Buffer und kehrt
  unverzüglich zur Mainloop zurück.

- UART-RX-Interrupt empfängt ein Byte, legt es in RX-Buffer, setzt
  Semaphore für Mainloop und kehrt unverzüglich zur Mainloop zurück.

- Mainloop parst Befehle im Buffer, falls Semaphore gesetzt war und
  führt Befehl aus, falls er gültig war. Geht dann ggf. in Sleep.

- Mainloop kann jederzeit durch Interrupt unterbrochen werden, wobei
  sich jeder Interrupt extrem kurz fasst, um anderen Interrupts auch
  eine Chance zu geben.

...

von Wolfram Q. (quehl)


Lesenswert?

@MarcB

Der Fehler liegt vermutlich daran, daß der Timerinterrupt dauernd aktiv 
wird und damit dem Rx Interrupt keine Chance zur Abarbeitung läßt. Tx 
funktioniert, weil da der Timerinterrupt noch nicht aktiv ist.

@hannes Lux

- SPI-Interrupt führt diese SPI-Sequenz weiter (State-machine), sendet
  ggf. Ergebnis über UART-TX oder legt Ergebnis in Buffer und kehrt
  unverzüglich zur Mainloop zurück.

Ich habe bisher den 486er in assembler programmiert und darum denke ich 
in ns statt in µs, wie es beim AVR notwendig ist. Und weil ich beim 486 
hauptsächlich mit Berechnungen zu tun habe, fallen dort keine 
Wartezeiten an. Oder kaum.

Wenn Ergebnis über Uart gesendet werden soll, muß erst geprüft werden, 
ob der Sendepuffer leer ist. Da entsteht ggf. eine Wartezeit. Der Buffer 
ist aber recht aufwendig, unabhängig von der Größe. Bei einem Byte 
Puffer könnte man vielleicht noch 1 Register nehmen. Aber schon ab 2 
Byte muß man den Puffer im RAM haben. 2 Zeiger für Tx und 2 Zeiger für 
Rx. 3 Zeiger stehen nur zur Verfügung und werden vielleicht auch noch 
anders gebraucht, daher müssen die Zeiger auch im Ram stehen. Mindestens 
die 2 Zeigerregister im Interrupt müssen gepusht und gepopt werden. Ggf. 
muß festgestellt werden, ob der Puffer voll ist und was dann zu tun ist 
und ob der Puffer leer ist. Wegen des Ringpuffers muß auch das Ende des 
Pufferspeichers abgefragt werden.
Um da ein Byte abzulegen und wieder zu lesen, ist das ein ganz schön 
großer Aufwand.
Beim Rx entsteht der gleiche Aufwand.
Dabei muß auch noch berücksichtigt werden, daß schnellere Abläufe durch 
einen Buffer nicht an langsamere angepaßt werden, sondern es werden 
damit nur unregelmäßige Zeiten ausgeglichen. Dies könnte auch ein Timer 
überbrücken, jedoch entstehen dann zwangsweise Pausen, die evtl. 
unerwünscht sind.



von Hannes L. (hannes)


Lesenswert?

Wolfram, ich weiß, wie Ringbuffer in AVR-ASM funktionieren. Das Arbeiten 
damit ist aber allemal effizienter als das Verweilen in einer Timer-ISR 
während einer kompletten externen ADC-Wandlung incl. 
SPI-Datenübertragung.

Eine andere Vorgehensweise könnte bei Nutzung externer OWI-Chips 
sinnvoll sein, während des (relativ schnellen) Auslesens darf man keine 
Interrupts zulassen, da diese das Timing stören. Allerdings kann man den 
Zeitpunkt des Auslesens so organisieren, dass er am wenigsten stört.

Meine Hinweise richteten sich aber auf einen externen Chip, der über SPI 
angesprochen wird. Und dies ist beim AVR recht effizient per 
Hardware-SPI möglich.

...

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.