Hallo zusammen, habe davor nur STM32 Prozessoren programmiert und wollte jetzt für einen Controllino MINI über Microchip Studio einen ATmega programmieren. Jedoch bekomme ich die USB Schnittstelle einfach nicht zum Laufen. Alle anderen Ein- und Ausgänge + ADC funktionieren. Selbst wenn ich alle anderen Libs auskommentiere und nur die UART lib drin habe, schmiert er ab, sobald ich ein Zeichen sende. Vielleicht sieht ja jemand einen groben Schnitzer oder kann mir weiterhelfen ... Da ich nur den Controllino da habe, habe ich leider keine debug Schnittstelle und kann das fertige Programm nur über USB aufspielen und testen. Vielen Dank. Schöne Grüße noobsen
Warum dein Programm "abschmiert" kann ich nicht erkennen. Mir ist aber dies aufgefallen: Die Methode rund um UART_ZEICHEN_BUFFER_LEER erscheint mir äußerst fragwürdig. Schau dir mal diesen Artikel an, dort ist ein erprobtes Konzept beschrieben, das zuverlässig funktioniert: https://www.mikrocontroller.net/articles/FIFO Alle Variablen, die sowohl innerhalb als auch außerhalb der ISR benutzt werden, würde ich als volatile kennzeichnen. In deinem Fall betrifft das die Variable UART_Flag_senden_aktiv. Im konkreten Code vernute ich, dass es keinen Unterschied macht, aber ich würde das volatile trotzdem einsetzen um Probleme bei künftigen Änderungen zu vermeiden. https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Puffern_von_volatile-Variablen Du könntest eine Reihe LED's anschließen, die anzeigen wie weit dein Code ausgeführt wird.
Hallo Stefan, danke für dein Feedback. Volatile hatte ich auch schon probiert, leider ohne Erfolg. Den Ringbuffer verwende ich eigentlich sehr erfolgreich beim ST. Mit dem Lampen-Spiel hatte ich es auch versucht, jedoch komme ich nur zu dem Ergebnis, dass er einmal in die Funktion USART_UDRE_vect() kommt und ein Zeichen reinschiebt zum Senden. Mit einem Terminalprogramm sieht man auch, dass das eine Zeichen auch übertragen wird. Danach jedoch im Nirvana hängt ... Nach meinem Verständnis müsste die Funktion USART_UDRE_vect() ein zweites Mal aufgerufen werden, sobald das eine Zeichen übertragen worden ist, jedoch passiert das nicht. Da nichts weiter im Code bzw. main() vorhanden ist, bin ich überfragt, wo ich noch eine Lampe ein oder ausschalten soll, um etwas zu sehen ...
1 | void USART_UDRE_vect(void) |
2 | {
|
3 | if( !(UART_TX_Buffer[UART_TX_Buffer_Register_Senden]==UART_ZEICHEN_BUFFER_LEER) ) |
4 | {
|
5 | UDR0 = UART_TX_Buffer[UART_TX_Buffer_Register_Senden]; |
6 | UART_TX_Buffer[UART_TX_Buffer_Register_Senden] = UART_ZEICHEN_BUFFER_LEER; |
7 | |
8 | if( (UART_TX_Buffer_Register_Senden+1) < UART_TX_BUFFER_SIZE ) |
9 | UART_TX_Buffer_Register_Senden++; |
10 | else
|
11 | UART_TX_Buffer_Register_Senden = 0; |
12 | }
|
13 | // Alle Zeichen gesendet, ISR abschalten
|
14 | else
|
15 | {
|
16 | UART_Flag_senden_aktiv = AUS; |
17 | UCSR0B &= ~(1<<UDRIE0); |
18 | }
|
19 | }
|
Andreas R. schrieb: > UCSR0B &= ~(1<<UDRIE0); Ich vermute, dass der Interrupt erst dann ausgelöst wird, wenn das UDR Register danach wieder leer wird. Wenn es zu dem Zeitpunkt aber schon leer ist, gibt es keinen Grund für einen erneuten Interrupt. Einen hattest du ja schon vorher aber da war dein Sendepuffer leer. Das ist ein genereller Unterschied zwischen STM32 und AVR an ganz vielen (nicht allen) Stellen. Bei STM32 musst du jeden Interrupt selbst Quittieren oder Deaktivieren, sonst wird die selbe ISR bei ihrem Ende direkt wieder aufgerufen. Bei AVR werden die Interrupt-Flags beim Eintritt in die ISR automatisch gelöscht, so dass ein Ereignis immer nur zu maximal einem ISR Aufruf führt. Oder anders formuliert: Ein STM32 würde würde die ISR immer wieder aufrufen, solange du das "UDR ist leer geworden" Flag nicht löschst. Ein AVR ruft die ISR nur einmal auf, wenn das Register leer wird. In meinen AVR Programmen funktioniert die UART_send() Funktion so: Wenn der UART sender aktiv ist, dann schreibe ich das Zeichen in den Puffer. Wenn er nicht aktiv ist, schreibe ich es direkt ins UDR Register. Dadurch entfällt auch die Notwendigkeit, das UDRIE0 immer wieder umzuschalten. Ich schalte es einmal bei der Initialisierung ein, und dann bleibt es dabei. Probiere das mal.
Ohne Interrupt funktioniert es, wenn man einfach ein Zeichen in UDR schreibt. Jedoch möchte ich bei vielen Zeichen den Mikrocontroller nicht in einer while() warten lassen, bis das nächste Zeichen gesendet werden kann. Später sollen auch größere Informationspakete an die Schnittstelle gesendet werden, während die Hauptstatemaschine läuft. Von dem her würde ich es gerne mit Interrupt gesteuert realisieren.
Arduino F. schrieb: >> void USART_UDRE_vect(void) > Das ist doch keine ISR! > Zumindest nicht in der AVR GCC Welt. Wie sollte er korrekt heißen?
Arduino F. schrieb: > Das ist doch keine ISR! Facepalm, das hätte ich erkennen müssen. Habe ich aber nicht. Andreas R. schrieb: > Wie sollte er korrekt heißen? Ich empfehle dazu diese Doku https://www.nongnu.org/avr-libc/user-manual/index.html In diesem Fall konkret die Unterseite https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html Dort steht als Beispiel:
1 | ISR(USART_UDRE_vect) |
2 | {
|
3 | // user code here
|
4 | }
|
Andreas R. schrieb: > Ohne Interrupt funktioniert es, wenn man einfach ein Zeichen in UDR > schreibt. Jedoch möchte ich bei vielen Zeichen den Mikrocontroller nicht > in einer while() warten lassen, bis das nächste Zeichen gesendet werden > kann. > Später sollen auch größere Informationspakete an die Schnittstelle > gesendet werden, während die Hauptstatemaschine läuft. > Von dem her würde ich es gerne mit Interrupt gesteuert realisieren. Ich glaube, du hast meinen Vorschlag (direkt ins UDR zu schreiben) nicht verstanden. Du sollst doch nur direkt ins Register schreiben, wenn der Sender nicht aktiv ist. Wenn er hingegen aktiv ist, sollst du nicht warten, sondern wie gehabt den Puffer und die ISR nutzen.
Das klingt vielversprechend! Bin gerade unterwegs und werde es morgen früh gleich mal testen und berichten. Danke Leute!!
:
Bearbeitet durch User
Es gibt 2 interrupts. Der eine : UartDataRegisterEmpty bedeutet das UartDataRegister is leer, ist ins Shieberegister kopiert worden und wird raus geschoben. In diesem Interrupt wird das naechste Datenbyte in das TxRegister geladen. der Andere : TxReady bedeutet das Effektive schieberegister ist leer. Die Uebertragung ist beendet. Also erst mal : X mal UartDataRegisterEmpty interrupt laufen lassen und zum Schluss den TxReady. Bedeutet beim vorherigen, dem letzten UartDataRegisterEmpty macht man nichts, weil man nichts zum Laden mehr hat. Eine Library mit Ringbuffer benotigt man eigentlich nicht, laesst grad eine Statemachine das Protokol abspulen. Ich arbeite immer mit linearen buffern, die sind so lange wie die Meldung ist. Natuerlich nicht dynamisch alloziert, sondern fest.
:
Bearbeitet durch User
Purzel H. schrieb: > Also erst mal : X mal UartDataRegisterEmpty interrupt laufen lassen und > zum Schluss den TxReady. Und wozu? Sofern man nicht einen RS485-Treiber bespaßen will, braucht man TXC nicht, und kann sich ausschließlich auf UDRE beschränken.
Hallo zusammen, der Fehler lag wirklich an der Bennenung des Namen für den ISR. Jetzt funktioniert Tx & Rx über Interrupt Steuerung wunderbar. Vielen Dank für eure Hilfe! Hier noch die fertige Version:
1 | ISR(USART_UDRE_vect) |
2 | {
|
3 | char *TXpointer = UART_TX_Buffer; |
4 | |
5 | if( !(TXpointer[UART_TX_Buffer_Register_Senden]==UART_ZEICHEN_BUFFER_LEER) ) |
6 | {
|
7 | UDR0 = TXpointer[UART_TX_Buffer_Register_Senden]; |
8 | TXpointer[UART_TX_Buffer_Register_Senden] = UART_ZEICHEN_BUFFER_LEER; |
9 | |
10 | if( (UART_TX_Buffer_Register_Senden+1) < UART_TX_BUFFER_SIZE ) |
11 | UART_TX_Buffer_Register_Senden++; |
12 | else
|
13 | UART_TX_Buffer_Register_Senden = 0; |
14 | }
|
15 | else
|
16 | {
|
17 | UART_Flag_senden_aktiv = AUS; |
18 | UCSR0B &= ~(1<<UDRIE0); |
19 | }
|
20 | }
|
21 | |
22 | void UART_send_string(char *data) |
23 | {
|
24 | char *TXpointer = UART_TX_Buffer; |
25 | |
26 | while(*data) |
27 | {
|
28 | TXpointer[UART_TX_Buffer_Register_Schreiben] = *data++; |
29 | |
30 | if( (UART_TX_Buffer_Register_Schreiben+1) < UART_TX_BUFFER_SIZE ) |
31 | UART_TX_Buffer_Register_Schreiben++; |
32 | else
|
33 | UART_TX_Buffer_Register_Schreiben = 0; |
34 | }
|
35 | |
36 | // starten, wenn Buffer noch nicht bereits abgearbeitet wird
|
37 | if(UART_Flag_senden_aktiv==AUS) |
38 | {
|
39 | UART_Flag_senden_aktiv = AN; |
40 | UCSR0B |= (1<<UDRIE0); |
41 | }
|
42 | }
|
Andreas R. schrieb: > UART_TX_Buffer_Register_Schreiben Andreas R. schrieb: > UART_TX_Buffer_Register_Senden - Zu lange Namen für Variablen - ähnliche, fast gleichlautende Variablennamen, OMG! - missverständliche Variablennamen, das sind Pointer, Indexe in einem Array oder Ringbuffer
Andreas R. schrieb: > der Fehler lag wirklich an der Bennenung des Namen für den ISR. Der Bezeichner war eigentlich schon OK so... Es fehlten nur ein paar Attribute, die die Funktion zu einer ISR machen.
1 | /* extern "C" */ void USART_UDRE_vect() __attribute__ ((__signal__,__used__, __externally_visible__)); |
2 | void USART_UDRE_vect() |
3 | {
|
4 | // tuwas
|
5 | }
|
:
Bearbeitet durch User
Harald K. schrieb: > Purzel H. schrieb: >> Also erst mal : X mal UartDataRegisterEmpty interrupt laufen lassen und >> zum Schluss den TxReady. > > Und wozu? Sofern man nicht einen RS485-Treiber bespaßen will, braucht > man TXC nicht, und kann sich ausschließlich auf UDRE beschränken. Man braucht es bei jeder halbduplex Kommunikation mit dem UART. Wenn man zwischen TX und RX umschalten will.
:
Bearbeitet durch User
Cyblord -. schrieb: > Man braucht es bei jeder halbduplex Kommunikation mit dem UART. Wenn man > zwischen TX und RX umschalten will. Diesen Fall habe ich mit "rs485-Treiber bespaßen" gemeint. Sonst braucht man das aber nicht; wer macht schon freiwillig Halbduplex, wenn's nicht not tut?
Harald K. schrieb: > Cyblord -. schrieb: >> Man braucht es bei jeder halbduplex Kommunikation mit dem UART. Wenn man >> zwischen TX und RX umschalten will. > > Diesen Fall habe ich mit "rs485-Treiber bespaßen" gemeint. Sonst braucht > man das aber nicht; wer macht schon freiwillig Halbduplex, wenn's nicht > not tut? Dann wenn man a.) nur 3 Leitungen haben kann oder b.) wenn man mit etwas Kommunizieren will was halbduplex macht. z.B. Telemetrie-Sensoren im RC Modellbau. Merke: Dein beschränkter Horizont ist nicht das Maß aller Dinge.
Cyblord -. schrieb: > Dann wenn man a.) nur 3 Leitungen haben kann Rx, Tx und Gnd. Klar, da muss man Halbduplex einsetzen. Was auch sonst? Cyblord -. schrieb: > Merke: Dein beschränkter Horizont ist nicht das Maß aller Dinge. Welchen Teil von "wenn's nicht not tut" hast Du jetzt wieder überlesen? Bist Du intellektuell von so einer Formulierung überfordert?
:
Bearbeitet durch User
Harald K. schrieb: > Cyblord -. schrieb: >> Dann wenn man a.) nur 3 Leitungen haben kann > > Rx, Tx und Gnd. Tja manchmal ist es GND,VCC,Signal. > Welchen Teil von "wenn's nicht not tut" hast Du jetzt wieder überlesen? Habe nichts überlesen aber dein Posts suggerierte sowas würde nur für RS485 gebraucht und das ist halt quatsch. > Bist Du intellektuell von so einer Formulierung überfordert? Ich kann nichts für deine falschen Aussagen.
Cyblord -. schrieb: > Ich kann nichts für deine falschen Aussagen. Und ich nichts für Deine kategorische Weigerung, sinnerfassend zu lesen. Schönes Wochenende.
Wastl schrieb: > Andreas R. schrieb: >> UART_TX_Buffer_Register_Schreiben > > Andreas R. schrieb: >> UART_TX_Buffer_Register_Senden > > - Zu lange Namen für Variablen > - ähnliche, fast gleichlautende Variablennamen, OMG! > - missverständliche Variablennamen, das sind Pointer, Indexe > in einem Array oder Ringbuffer Hallo Wastl, ich glaube, das ist Ansichtssache. Mir persönlich gefällt es besser etwas zu beschreiben, als einfach mit x, cnt oder gar cryptischen Zeichen so kurze Zeilen wie möglich zu produzieren. Der Code muss auch nur von mir gelesen werden, also denke ich, kann man da ein Auge zudrücken. Schöne Grüße
:
Bearbeitet durch User
Andreas R. schrieb: > Der Code muss auch nur von mir gelesen werden, also denke ich, kann man > da ein Auge zudrücken. Du irrst. Du hast deinen unleserliche Code hier reingestellt weil du um Hilfe gebeten hast. Daher "müssen" sich andere Leute mit deinem Kauderwelsch auseinandersetzen. In einer Team von Programmierern in dem du teilnimmst würde man dich auspeitschen. Andreas R. schrieb: > als einfach mit x, cnt oder gar cryptischen > Zeichen so kurze Zeilen wie möglich zu produzieren. Es gibt durchaus Möglichkeiten sich in einer Programmiersprache prägnant auszudrücken ohne Variablen-Namen mit zwei oder drei Buchstaben anzuwenden.
Wastl schrieb: > In einer Team von Programmierern in dem du > teilnimmst würde man dich auspeitschen. Das muss aber ein schlechtes Team sein, wenn es sich an solchen Kleinigkeiten schon aufreibt. Diskutiert ihr auch über die Anzahl von Leerzeilen und ob geschweifte Klammern hinter "if (...)" in eine neue Zeile gehören?
Stefan F. schrieb: > Wastl schrieb: >> In einer Team von Programmierern in dem du >> teilnimmst würde man dich auspeitschen. > > Das muss aber ein schlechtes Team sein, wenn es sich an solchen > Kleinigkeiten schon aufreibt. Ich würde das für ein gutes Team halten: https://www.youtube.com/watch?v=MBRoCdtZOYg
Andreas R. schrieb: > Mir persönlich gefällt es besser > etwas zu beschreiben, als einfach mit x, cnt oder gar cryptischen > Zeichen so kurze Zeilen wie möglich zu produzieren. Das ist legitim. Aber "UART_TX_Buffer_Register_Senden" ist irreführend, da das mit "Register" nichts zu tun hat. Das ist ein Index. Die Kombination aus "TX" und "Senden" ist dazu noch redundant, denn "TX" und "Empfangen" kann es nicht geben, ebensowenig wie es "RX" und "Senden" geben kann. "UART_TX_Buffer_Index" würde genügen.
Harald K. schrieb: > "UART_TX_Buffer_Index" würde genügen. Noch so eine Unsitte ist es Variablen-Namen GROSS zu schreiben. Grosschreibung ist in einem anständigen Sourcen-Gefüge für Konstanten (Defines) reserviert. Konstante Variablen dürfen (sollen) dagegen auch klein geschrieben werden.
Wastl schrieb: > Noch so eine Unsitte ist es Variablen-Namen GROSS zu schreiben. Ich sehe hier gemischte Groß- und Kleinschreibung. Ja, als an gewisse Standards gewöhnter Mensch denkt man am Anfang automatisch an ein Macro (#define), aber wenn man das zehnte Zeichen (u) sieht, ändert sich was. Schön finde ich das nicht; ich wollte in meinem Beitrag aber in erster Linie auf die irreführende und redundante Begriffswahl hinweisen, und nicht auf den fünfhundertdrölfzigten Coding-Richtlinien-Flamewar.
Harald K. schrieb: > ich wollte in meinem Beitrag aber in erster > Linie auf die irreführende und redundante Begriffswahl hinweisen Du warst ja nicht als Urheber der Schreibweise angesprochen.
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.