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
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
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?
> 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.
...
@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.
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.
...