Forum: Mikrocontroller und Digitale Elektronik AVR Studio und UART interrupt; wie?


von Johannes (Gast)


Lesenswert?

Hallo Forumsleute,

ich habe ein C Programm mit UART interrupts gemacht und moechte es in
AVR Studio simulieren. Ich sende das erste byte, dabei wird ein
ISR-Signal erzeugt und die naechsten Zeichen werden dann in der ISR
rausgeschickt.

while (!(UCSRA & (1<<UDRE))) ;
UDR = cmd[0];

Dieser Befehl (UDR = ...) erzeugt doch das ISR-Signal SIG_UART_TRANS.
Der Simulator springt aber nicht in diese ISR. Das TXCIE bit in UCSRB
ist gesetzt, der interrupt muesste doch funktionieren.

was mach ich falsch? oder kann man UART-Interrupts in AVR Studio nicht
simulieren.

ganz "nebenbei": meine Hilfe im AVR Studio funktioniert nicht. Click
auf "AVR-GCC plug-in Help" ruehrt sich ueberhaupt nicht, alle anderen
links  zu User Manual und User Guide funktionieren auch nicht. Ich hab
das SP2 zur Version 4.12 installiert, geht aber auch nicht.

vielen Dank fuer Tips,
Johannes

von johnny.m (Gast)


Lesenswert?

Du musst Dich schon entscheiden, ob Du nen Interrupt auslösen oder ob Du
die Flags im Polling-Verfahren abfragst. Beides gleichzeitig geht
garantiert in die Hose. BTW: hast Du die Interrupts auch global
freigegeben?

von Johannes (Gast)


Lesenswert?

Hi Johnny,

danke fuer die Antwort.

gobale interrupts sind enabled, sei();

Ich will nicht polling-Verfahren sondern ISR. Den Code, den ich
verwende hat bisher auf meinen AVRs funktioniert. Ich hab den Code
jetzt einfach kopiert und Variablen etc. angepasst.

sorry die Frage: was bedeutet "BTW" - bin kein AKS
(Abkuerzungsspezialist) :-)

mg,
Johannes

von Rahul (Gast)


Lesenswert?

btw = by the way
manche haben einen Aküfi...
Zeigt doch mal deinen Code.

von johnny.m (Gast)


Lesenswert?

BTW heißt 'By the way' (auf deutsch: 'übrigens'...:-)

Du fragst mit 'while (!(UCSRA & (1<<UDRE)));' das UDRE ab (Das nennt
man Polling). Wenn Du nen Interrupt aktiviert hast, kannst Du Dir das
aber sparen, weil das UDRE, sobald es gesetzt wird, eh den Interrupt
auslöst. Deshalb meine Vermutung, dass es da zu ner Kollision kommt.
Mit dem kurzen Codeschnipselchen kann ich aber nicht mehr anfangen, da
der Kontext fehlt.

Gruß

Johnny

von Johannes (Gast)


Angehängte Dateien:

Lesenswert?

hier ist der Source code.

von Johannes (Gast)


Angehängte Dateien:

Lesenswert?

hier ein screenshot

von johnny.m (Gast)


Lesenswert?

1.: Mach mal ein Update von Deinem WINAVR und benutze ISR anstatt
SIGNAL
2.: Du wertest einen Interrupt aus, der ausgelöst wird, sobald UDR leer
ist (also UDRE = 1)(Vermute ich jedenfalls, habe die alten
Vektorbezeichnungen von WINAVR nicht mehr parat). UDRE wird afaik beim
Einsprung in die ISR gelöscht, so dass der µC sich in der Abfrage
'while (!(UCSRA & (1<<UDRE)));' für immer und ewig aufhängt, weil
UDRE nie wieder gesetzt wird. Was soll diese Abfrage überhaupt
bewirken?

von johnny.m (Gast)


Lesenswert?

Sorry, hab grad gesehen, dass Du den normalen Transmit Interrupt
aktiviert hast. Also vergiss 2. von oben.

von Rahul (Gast)


Lesenswert?

Ich würde das eher mit dem UDRE-Interrupt lösen.
Das TXC-Interrupt wird erst ausgelöst, wenn ein Byte komplett gesendet
wurde. Das nutzt aber nicht den Doppelpuffer der USART aus...
Das UDRE-Interrupt darf man natürlich erst freigeben, wenn man wirklich
senden will...
Man könnte send_cmd auch als void deklarieren...
In die ISR springt der Controller sobald das Byte komplett gesendet
wurde... und das kann einen Moment dauern (breakpoint setzen und dann
den Simulator laufen lassen).
TXC benutze ich, um RS485-halbduplex-Kommunikation zu realsisieren (bei
TXC wird der Bus wieder freiggeben bzw. der TRansceiver auf Empfang
geschaltet).

von johnny.m (Gast)


Lesenswert?

>  bytecountSen==0;
Das dürfte nix bringen. Ein '=' muss vermutlich weg...

Abgesehen davon wird der Sende-Interrupt nicht beim Schreiben des UDR
ausgelöst, sondern dann, wenn der Sendevorgang abgeschlossen ist.

von Johannes (Gast)


Lesenswert?

Hallo,

Danke fuer die Muehe.

UDRE ist laut Datenblatt nur lesbar, nicht beschreibbar. Sobald das UDR
leer ist - also bereit neue Daten zu empfangen, die abgeschickt werden
sollen, wird dieses Bit automatisch gesetzt. Die Abfrage stammt aus dem
Tutorial hier auf der Seite und bewirkt, dass solange gewartet wird bis
das UDRE bit gesetzt ist, also bis man neue Daten losschicken kann,
indem man sie ins UDR schreibt. Diese Sachen funktionieren seit Jahren.


Auch meine Struktur mit den ISRs haben bisher immer gut funktioniert.
Ich simuliere allerdings zum erstenmal mit AVR Studio, hier will es
offenbar nicht so wie ich...

ich vermute das AVR Studio hinter dem Problem, dass es nicht jedwelche
interrupts Interrupts simulieren kann. Wie kann man pruefen ob ein
Interrupt tatsaechlich erzeugt wird? Offensichtlich ist das nicht der
Fall und das TXC flag wird auch nicht gesetzt wenn ich etwas ins UDR
schreib...

Johannes

von Johannes (Gast)


Lesenswert?

sorry! es geht ja schlag auf schlag :-) ich muss etliches
zuruecknehmen...


Johannes

von Rahul (Gast)


Lesenswert?

Bis jetzt hatte ich bei der Simulation von seriellen Interrupts im
AVR-Studio noch keine Probleme. Wenn ich heute abend dazu komme,
probiere ich deinen Code mal zu simulieren.

von Johannes (Gast)


Lesenswert?

Ich bin die Sache Schritt fuer Schritt durchgegangen. nachdem "UDR =
cmd[0]" Befehl geht das Programm einfach weiter ohne in die ISR zu
springen. Das TXC flag, das den Interrupt ausloest, wird nie danach
gesetzt.

Fuers Ausprobieren danke! Falls ich dahinter komm meld ich mich
natuerlich.

Gruss,
Johannes

von johnny.m (Gast)


Lesenswert?

> UDRE ist laut Datenblatt nur lesbar, nicht beschreibbar.
Dem hat hier auch niemand widersprochen.

AVRStudio hat zwar tatsächlich einige Macken was die Simulation
bestimmter Hardware-Ereignisse angeht, aber UART ist afaik davon nicht
betroffen.

(Ach ja, bevor Du fragst: 'afaik' heißt 'as far as I know', auf
deutsch 'soweit ich weiß'. Sorry, abkürzen ist so ne Gewohnheit;-)

von johnny.m (Gast)


Lesenswert?

Hast Du die falsche Zuweisung korrigiert?

Andere Frage: Denkst Du daran, dass die Simulation in AVRStudio nicht
in Echtzeit ist? Bei 9600 Baud dauert eine Übertragung ne Weile. Wenn
Du da ungeduldig wirst...

Außerdem: Warum benutzt Du unten nicht cli() und sei()?

von Johannes (Gast)


Lesenswert?

Jawohl Herr Kapitaen. ... genau das meinte ich mit "zuruecknehmen":
meine belehrenden Ausfuehrungen mit leicht pedagogischen touch.

danke fuer die Erklaerung zum "afaik"!

BTW "mg" bedeutet mit Gruss ;)

mg,
Johannes

von johnny.m (Gast)


Lesenswert?

...Mal eben nachgerechnet: Bei 9600 Baud und einem Oszillatortakt von 16
MHz dauert die Übertragung eines Bytes über 13000 Taktzyklen...

von johnny.m (Gast)


Lesenswert?

Hey, ein bisschen Spaß muss doch sein, oder nicht;-?

von Rahul (Gast)


Lesenswert?

sach ich doch:
Breakpoint in die ISR setzen, F5 drücken und warten, bis der Simulator
am Breakpoint stehen bleibt. Wenn er das nach einer Weile nicht gemacht
hat, kann man ihn mit STRG-F5 anhalten, um zu sehen, womit sich der
"Controller" gerade beschäftigt.
Wenn man dann noch ein paar sinnvolle watches setzt, kann man sich
angucken, wo der Hund begraben ist.

von Johannes (Gast)


Lesenswert?

Jawohl, Spass muss sein. Deswegen bist du wohl auf die 13 gekommen.

Tatsache ist aber, dass ich noch immer nicht check, warum mein
Simulator nicht funzt. Ich werd mich mal ein wenig bilden =
"Datenblatt lesen", vielleicht gibts dort einen tip.

mg,
Johannes

von johnny.m (Gast)


Lesenswert?

Nö, die 13(tausend) sind mathematisch fundiert (zumindest in grober
Näherung). Ist nur so'n Zahlenwert, damit Du ne Ahnung davon hast, was
der µC bis zum Auftreten eines einzigen Sende-Interrupts fürn Unfug
machen kann (in 13000 Zyklen kann der ne Menge Mist bauen. Ist für den
ne halbe Ewigkeit.) Und dass Du nicht auf die Idee kommst, das ganze
mit Einzelschritten durchzuspielen. Könnte sein, dass danach Deine Maus
bzw. Tastatur ihre Nenn-Lebensdauer erreicht haben und Du immer noch
keinen Interrupt hast...;-)

mFg

Johnny

von Johannes (Gast)


Lesenswert?

Was hast du denn gerechnet?
1/16Mhz*13000*9600 = 7.4 bei 1MHz=1/(1024hoch2). Die 13000 Zuklen sind
die Zeit zwischen zwei zu sendenden bits. Woher weist du, dass das
Senden selber tatsaechlich diese Zeit in Anspruch nimmt? das glaube ich
naemlich nicht. Ich denke eher das senden eines bits dauert wenige
zuklen und alle anderen bis zu 13000 werden vom uC (hoffentlich
vernuenftig) verwendet.

Allerdings stimmt es, dass beim zweitenmal in der Schleife das UDRE bit
nicht mehr gesetzt ist, offensichtlich also Daten im UDR sind, die noch
nicht gesendet wurden. Diese Situation aendert sich allerdings auch
nach ueber 500000 Takten nicht. Der uC springt nicht in die ISR; dort
incrementier ich ein Variable um das zu testen, aber die bleibt ewig
Null...

von johnny.m (Gast)


Lesenswert?

Wenn Du das UDR schreibst, werden die 8 Bits aus UDR der Reihe nach mit
der eingestellten Baudrate (9600 Baud, also 9600 Bit/s) ausgegeben. Da
der µC pro Sekunde 16000000 Taktzyklen hinlegt, macht er während der
Übertragung (16000000 / 9600) * 8 Zyklen (ungefähr). Vom Moment des
sendens (durch Schreiben von UDR) bis zum Auslösen des Transmit
Complete-Interrupt vergehen also ungefähr 13000 Taktzyklen, die der µC
großenteils mit Warten verbringt. Diese Taktzyklen müssen aber trotzdem
in der Simulation berücksichtigt werden, auch wenn der µC in einer
Endlosschleife rumeiert...

von johnny.m (Gast)


Lesenswert?

Was mir grad noch auffällt: Du rufst in der Endlosschleife am Ende des
Hauptprogramms immer wieder die Funktion 'send_cmd(AS_nr);' auf. So
wie ich das verstehe (korrigiere mich, wenn ich daneben liege) soll
diese Funktion aus dem Programm nur am Anfang einmal aufgerufen werden,
um die ganze Sache anzustoßen. Den Rest der Übertragung macht doch die
ISR, oder nicht???? So wie es da steht kann es eigentlich nur Probleme
geben.

Gruß

Johnny

von Rahul (Gast)


Lesenswert?

Naja, wenn man erst die Werte mit get...(); vom Sensor holt, sollte man
sie danach auch verschicken.

von johnny.m (Gast)


Lesenswert?

Das ist schon korrekt, aber für das Versenden muss man sich schon auf
eine Methode einigen. Die beiden im Programm kollidieren garantiert...

von Johannes (Gast)


Lesenswert?

...hmmm. Ich bin mir nicht sicher, ob der uC die Zeit nicht doch
ausnuetzen kann. Ist der UART-Block nicht unabhaengig? Sprich er wird
konfiguriert/initialisiert und erhaelt dann seine Daten und verrichtet
sich damit ohne, dass der uC warten muss - quasi parallel. Ganz
unabhaengig natuerlich nicht, zB Takt nimmt das UART Modul schon ueber
den uC. Sprich der uC kann 1+2=3 rechnen und speichern waehrend zB bit
6 und 7 am USART mit der Geschwindigkeit von 9600buad abgeschickt
werden. stimmt das nicht?

mg,
Johannes

von Rahul (Gast)


Lesenswert?

Da liegst du richtig.
Deswegen ist es auch so schön, mit Interrupts zu arbeiten...

von Johannes (Gast)


Lesenswert?

Hallo Rahul, hallo Johnny,

also meine Software ist noch in der Testphase, wer haette das gedacht
:)...
Ich will jetzt am Anfang erst einmal den Sensor auslesen und dann die
Daten abschicken wie Rahul richtig erkannt hat. Spaeter einmal werd ich
nur dann senden, wenn es erwuenscht ist, zB auf Knopfdruck oder Commando
vom host etc...

Ich hab mal mein UART Modul in die Codesammlung gestellt. Das Programm
ist getestet. Es lief auf meinem ATMega16 richtig und ohne Probleme.
der Link:
http://www.mikrocontroller.net/forum/read-4-274151.html#new

Ich hab dieses kleine Programm auch ausprobiert und es tritt das
gleiche Phaenomen auf wie in meinem jetzigen Programm: Beim ersten
Sprung in die send_cmd() Funktion ist das UDRE bit gesetzt. Ich
schreibe in UDR, das bit bleibt gesetzt (?!). Das Programm lauft weiter
ohne in die ISR zu springen. Beim zweitenmal komm ich auch dazu ins UDR
zu schreiben, aber diesmal wird das UDRE bit geloescht, und das
Programm lauft weiter. Da das TXC flag nie gesetzt wird, geht meine ISR
nie los. Aber dieses Programm hatte auf dem uC funktioniert.

Ich vermute immer noch, dass der Bug nicht bei mir ist. Kann es aber
auch nicht ausschliessen.

mg,
Johannes

von johnny.m (Gast)


Lesenswert?

Das eigentliche Problem ist ja gerade, dass der µC beschäftigt werden
will, während er auf den Interrupt wartet, der ihm mitteilt, dass der
letzte Wert weg ist und der nächste gesendet werden kann. Wenn Du aber,
während er noch am senden ist (was, wie gesagt, mehr als 13000
Taktzyklen pro Byte beansprucht) schon wieder was neues senden willst
und die entsprechende Funktion aufrufst, dann gibts Komplikationen.
Dann wartet er nämlich in der Funktion send_cmd(), bis UDRE gesetzt
wird (also bis der letzte Sendevorgang abgeschlossen ist). Sobald UDRE
1 wird, springt er aber auch schon in die ISR und sendet von dort aus
den nächsten Wert, dann wieder in die Funktion. Dort wird dann (diesmal
ohne zu warten, denn die Warteschleife wurde ja bereits abgebrochen)
nochmal gesendet. Das ist dann der Punkt, an dem es kollidiert.... Das
eigentliche Problem Deines Programms ist, dass Du die Funktion
send_cmd() sowohl aus der Funktion als auch aus der ISR heraus
aufrufst. Da dürfte nicht viel Sinnvolles beim Empfänger ankommen.

Warum in der Simulation kein Interrupt auftritt, kann ich nicht sagen,
solange Du nicht sagst, wie Du das simulierst. Wie gesagt, die
Simulation ist nicht in Echtzeit...

von Rahul (Gast)


Lesenswert?

>Ich schreibe in UDR, das bit bleibt gesetzt (?!).
Das liegt daran, dass die meisten AVR ein doppelt gepuffertes UART
haben. Es ist also noch ein UDR (unter der gleichen Adresse) frei.

>Sobald UDRE 1 wird, springt er aber auch schon in die ISR und sendet
>von dort aus den nächsten Wert, dann wieder in die Funktion
Nee, die ISR reagiert auf das Ende eines gesendeten Bytes, wenn also
ein Byte komplett gesendet wurde. Das kann man (wie oben schon erwähnt)
hervorragend für halbduplex-Übertragungen benutzen (RS485-2wire).
Um interrupt-gesteuert eine Reihe von Bytes über das UART zu den
senden, benutzt man (z.B. ich) das UDRE-Interrupt.
Dieses Interrupt wird immer dann ausgelöst, wenn das (mindestens ein)
UDR leer ist. Deswegen sollte man das Interrupt-flag erst dann setzen,
wenn man wirklich senden will.


SIGNAL(SIG_UART_EMPTY)   // ich gehe mal davon aus, dass der UDRE-Vektor
so heisst...nachgucken!
//ISR for transmitting via serial Interface
{
  if (bytecountSen >= PLENGTH)  //bytes 0 bis PLEGNTH sind gesendet
  {
      bytecountSen=0;
      UCSRB &= ~(1<<UDRIE); // UDRE-Interrupt wieder sperren
  }
  else
    {
      UDR = cmd[bytecountSen++]; // Daten senden
    }
}


void send_cmd(uint8_t AS_nr)
{
 cmd[0] = STX;        // start of text
 cmd[1] = AS_nr;      // sensor number
 cmd[2] = position >> 8;    // position high byte
 cmd[3] = position & 0xFF;  // position low byte
 cmd[4] = flags;      // flags
 cmd[5] = 0;                // Error code; 0 --> OK
 cmd[6] = ETX;        // end of text

 bytecountSen = 0;          // start sending
 UCSRB |= (1<<UDRIE);       // UDRE-Interrupt freigeben
  //  UDR = cmd[0]; // Das kann man sich durch die Freigabe des
Interrupts sparen...

}

von Johannes (Gast)


Lesenswert?

Aha! danke Rahul, verstehe das Prinzip. Ehrlich gesagt scheint mir das
logischer als meines. Ich hatte das in meinen Anfaengen bei einem
Freund so gesehen und es uebernommen. Bisher hat es immer seinen Dienst
getan. Aber man kann ja auch mal was aendern, was jahrelang funzte.

Der USART vector heisst: SIG_USART_DATA (falls hier mal jemand
nachliest).

Johannes

von Wolfram (Gast)


Lesenswert?

an den Anfang von send_cmd sollte noch
while (bytecountSen>0){}

sonst könnte das Paket verstümmelt werden

von Johannes (Gast)


Lesenswert?

Hallo,

also die Sache mit dem Interrupt funktioniert jetzt. Um ein byte zu
senden braucht er ca. 16000 Zyklen (bei 9600baud)!! Allerdings muss ich
jetzt noch ueberpruefen ob er die richtigen bytes sendet...

mg,
Johannes

von Rahul (Gast)


Lesenswert?

>Allerdings muss ich jetzt noch ueberpruefen ob er die richtigen >bytes
sendet...

Kann schon sein...
Wo liegt das Problem?

von Johannes (Gast)


Lesenswert?

Das Problem ist, dass ich die sendeFunktion aufruf, bevor die 7 byte des
Protokolls gesendet sind, und dabei wird byteCountSen zu Null gesetzt.
Das problem liegt mal wieder beim Programmierer :) "trozdem ist
natuerlich der Computer schuld, dass es nicht funktioniert hat"

Ich habs geschnallt, jetzt funktioniert es sowie ich es will, und wie
es mir logisch vorkommt. Vorher hats wahrscheinlich auch logisch
funktioniert, aber ich hatte es nicht verstanden, also war der Computer
schuld...

Ich schreib gleich noch einmal das Programm in der funktionierenden
Version. Ich musste eine Variable sendComplete einfuegen um zu testen
ob  alles gesendet war oder nicht. Aber es funktioniert damit. Im
Schnitt werden 16639 Zyklen benoetigt um 1 byte bei 9600
rauszuschieben. In dieser Zeit warte ich einfach (vorlaeufig, spaeter
werd ich was sinnvolles damit machen)

bis gleich,
Johannes

von Rahul (Gast)


Lesenswert?

Du könntest auch per

"if (UCSRB & (1<<UDRIE)) ..."

abfragen, ob die Schnittstelle "noch belegt ist".

Eine andere Möglichkeit hast du ja schon beschrieben.

von johnny.m (Gast)


Lesenswert?

> >Sobald UDRE 1 wird, springt er aber auch schon in die ISR und sendet
> >von dort aus den nächsten Wert, dann wieder in die Funktion
> Nee, die ISR reagiert auf das Ende eines gesendeten Bytes
Eben. Das ist (oder besser: war) ja das eine Problem. Er löst auf TXC
einen Interrupt aus, fragt aber GLEICHZEITIG in einer anderen Routine
das UDRE ab. Und genau da hats ja auch geknallt. Wenn er UDRE in einer
Warteschleife abfragt und es '1' wird, wird u.U. quasi im selben
Moment auch TXC '1' und er springt in die ISR. Danach wird
möglicherweise alles Mögliche gesendet, nur nicht in der richtigen
Reihenfolge...

Aber hat sich ja anscheinend erledigt...

von Johannes (Gast)


Angehängte Dateien:

Lesenswert?

hallo,

hier also das Funktionierende Programm.
BTW: Die Auslesefunktion liest die Daten eines Sensors aus: Es handelt
sich um den AS5045 von www.austriamicrosystems.com. Nicht das jemand
denkt, ich wuerde es nicht sagen wollen. Hab die Funktion nur
herausgeholt um ein wenig uebersichtlicher zu bleiben.  Wer daran
interessiert ist schreibt ne email an
johannesphilippi{bei}web{punkt}de.

Danke an Johnny und Rahul!

mg,
Johannes

von Rahul (Gast)


Lesenswert?

Das "sendComplete = 0" würde ich schon in die "send_cdm()" packen.
Dann greift es schon früher und verhindert einen neuen Zugriff auf die
Messung und die Datensendung. Ist aber eher eine Schönheitskorrektur.
In der ISR würde ich sowenig wie möglich machen.

von Wolfram (Gast)


Lesenswert?

solange du keine RX-Interruptroutine hast solltest du diese nicht
aktivieren.

von Johannes (Gast)


Lesenswert?

Hallo,

unter folgendem Link ist eine Version des Programms mit einer groben
Zusammenfassung der Ergebnisse(im Header) aus diesem Thread zu finden.

http://www.mikrocontroller.net/forum/read-4-358508.html?reload=yes#358508

Korrekturen natuerlich weiterhin gerne willkommen falls notwendig.

von fern der Heimat gruesst,
Johannes

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.