www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Uart und Pwm gleichzeitig


Autor: mooo_ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe einen Assembler Code geschrieben, der per Uart daten empfängt 
und dann die daten per pwm an leds ausgibt. Die daten werden vom 
Programm Atmolight empfangen. Das ganze funktioniert auch, allerdings 
flackert die Led ziemlich stark, ich vermute es hängt damit zusammen, 
dass die Pwm durch den Uart empfang gestört wird.

Gibts es eine Möglichkeit Uart-Empfang und PWM gleichzeitig ablaufen zu 
lassen? Immoment habe ich zwei interrupts, die ausgelöst werden, eine 
für den Uart-Empfang und eine für die PWM.

Ich komm nicht wirklich weiter, wär nett, wenn jemand ne Idee hat. Ich 
verwende einen Atmega8.

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die PWM braucht doch keinen Interrupt.

Aber trotzden. Der Fehler liegt in Zeile 42 der main.c

Und der R42 ist auch zu groß.

Autor: mooo_ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
.include "m8def.inc"
 
.def temp  = r16
 
.def PWMCount = r17
 
.def ocr_1 = r18                      
.def ocr_2 = r19                      
.def ocr_3 = r20                      
.def ocr_4 = r21                      
.def ocr_5 = r22                      
.def ocr_6 = r23                      


.equ F_CPU = 12000000                            ; Systemtakt in Hz
.equ BAUD  = 38400                               ; Baudrate
 
.org 0x00
        rjmp main
 
.org OVF0addr
        rjmp    timer0_overflow       ; Timer Overflow Handler
.org URXCaddr                                   ; Interruptvektor für UART-Empfang
        rjmp int_rxc

main:
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren
        out     SPL, temp
        ldi     temp, HIGH(RAMEND)
        out     SPH, temp
  
        ldi     temp, 0xFF            ; Port C auf Ausgang
        out     DDRC, temp
  
        ldi     temp, 0b00000001     ; CS00 setzen: Teiler 1
        out     TCCR0, temp
 
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow
        out     TIMSK, temp

    ; Port D = Ausgang
 
    ldi     temp, 0xFF
    out     DDRD, temp
 
    ; Baudrate einstellen
 
    ldi     temp, HIGH(UBRR_VAL)
    out     UBRRH, temp
    ldi     temp, LOW(UBRR_VAL)
    out     UBRRL, temp
 
    ; Frame-Format: 8 Bit
 
    ldi     temp, (1<<URSEL)|(3<<UCSZ0)
    out     UCSRC, temp
 
    sbi     UCSRB, RXCIE                    ; Interrupt bei Empfang
    sbi     UCSRB, RXEN                     ; RX (Empfang) aktivieren
    
    sei                                     ; Interrupts global aktivieren
 
loop:
rjmp loop                             ; Endlosschleife
 
timer0_overflow:                      ; Timer 0 Overflow Handler
        inc     PWMCount              ; den PWM Zähler von 0 bis
        cpi     PWMCount, 255         ; 255 zählen lassen
        brne    WorkPWM
        clr     PWMCount
 
WorkPWM:
        ldi     temp, 0b11000000      ; 0 .. Led an, 1 .. Led aus
 
        cp      PWMCount, ocr_1       ; Ist der Grenzwert für Led 1 erreicht
        brlo    OneOn
        ori     temp, $01
 
OneOn:  cp      PWMCount, ocr_2       ; Ist der Grenzwert für Led 2 erreicht
        brlo    TwoOn
        ori     temp, $02
 
TwoOn:  cp      PWMCount, ocr_3       ; Ist der Grenzwert für Led 3 erreicht
        brlo    ThreeOn
        ori     temp, $04
 
ThreeOn:cp      PWMCount, ocr_4       ; Ist der Grenzwert für Led 4 erreicht
        brlo    FourOn
        ori     temp, $08
 
FourOn: cp      PWMCount, ocr_5       ; Ist der Grenzwert für Led 5 erreicht
        brlo    FiveOn
        ori     temp, $10
 
FiveOn: cp      PWMCount, ocr_6       ; Ist der Grenzwert für Led 6 erreicht
        brlo    SetBits
        ori     temp, $20
 
SetBits:                              ; Die neue Bitbelegung am Port ausgeben
        out     PORTC, temp
 
        reti
int_rxc:
bit1:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit1
   in       r24, UDR                       ; empfangenes Byte nach temp kopieren
   cpi r24, 0xFF
   brne     bit1
bit2:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit2
   in       r24, UDR                       ; empfangenes Byte nach temp kopieren
   cpi r24, 0x00
   brne     bit1
bit3:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit3
   in       r24, UDR                       ; empfangenes Byte nach temp kopieren
   cpi r24, 0x00
   brne     bit1
bit4:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit4
   in       r24, UDR                       ; empfangenes Byte nach temp kopieren
   cpi r24, 0x0F
   brne     bit1
bit5:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit5
   in       ocr_4, UDR                       ; empfangenes Byte nach temp kopieren
bit6:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit6
   in       ocr_2, UDR                       ; empfangenes Byte nach temp kopieren
bit7:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit7
   in       ocr_3, UDR                       ; empfangenes Byte nach temp kopieren
bit8:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit8
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit9:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit9
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit10:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit10
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit11:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit11
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit12:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit12
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit13:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit13
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit14:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit14
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit15:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit15
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit16:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit16
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren   
bit17:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit17
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit18:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit18
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
bit19:
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
   rjmp     bit19
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
reti

Das ist mein Quellcode, hab ihn nicht gleich geschickt, weil ich mich 
etwas dafür schäme:-(. Ist aus dem tutorial zusammen gebastelt. Die 
braucht doch aber einen Timer, und während ich per Uart empfange, 
funktioniert dieser Timer doch nicht oder?

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>int_rxc:
bit2:
   sbis     UCSRA, RXC              ; warten bis ein Byte angekommen ist
   rjmp bit2

In Interrupts wird grundsätzlich auf irgendwas gewartet!

>und während ich per Uart empfange, funktioniert dieser Timer doch nicht >oder?

Doch. Aber wenn du im uart-int wartest, bis weitere bytes eintrudeln, 
kannst du natürlich deine soft-pwm nicht bedienen!

Autor: mooo_ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja, das ist klar, aber wie kann ich sonst die 19 bytes, die ich 
hintereinander empfangen muss, bekommen?

Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OMG, wo wird nur solcher Programmierstil gelehrt...

Im UART-Interrupt wird der Inhalt von UDR ausgelesen und in einen Puffer 
gelegt, mehr nicht.

Die Mainloop überprüft zyklisch diesen Puffer, ob das darin enthaltene 
Telegramm vollständig und damit gültig ist. Ist es gültig, dann wird es 
ausgewertet (an die PWM-Sollwerte zugewiesen) und dabei gelöscht 
(RX-Pufferzeiger auf Start).

Die Timer-ISR lässt sich auch noch optimieren. Da ein Register (Byte) 
nur einen Wertebereich von 0 bis 255 hat, muss der Zählumfang nicht 
begrenzt werden, nach 255 kommt nunmal 0. Die Vergleichsergebnisse der 
einzelnen Kanäle landen im Carry-Flag. Durch Einrotieren in ein Register 
(ROR oder ROL) kann man die Carry-Zustände "einsammeln" und in einem 
Rutsch an den Port ausgeben. Das erspart eine Menge Branches.

...

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Die Vergleichsergebnisse der einzelnen Kanäle landen im Carry-Flag.

Das ist ne interessante Idee, aber mal so für mich gefragt: Wie macht 
man das in C ?


>OMG, wo wird nur solcher Programmierstil gelehrt...

Das ist die Frage. Nennt sich Spaghetti-Code.

Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wie macht man das in C ?

Hmmm, der AVR kann kein C...
Ich auch nicht...
Also stellt sich die Frage nicht. ;-D

Wurde C (ANSI-C) nicht für 16-Bit-Maschinen gemacht?
Läuft es auf 8-Bit-Controllern nur durch standardfremde Erweiterungen?
Sind diese Erweiterungen nicht auch von Version zu Version 
unterschiedlich?
Gibt es nicht andauernd Probleme, weil ein "gefundenes" C-Projekt mit 
dem heimischen Compiler nicht compilierbar ist?

Also lasse ich C C sein und werkele in ASM... ;-)

Mögen mich die Fachleute dafür steinigen, ich werde es überleben...

...

Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achja, in ASM sieht es estwa so aus (uraltes Beispiel):
TIM0_OVF:       ;ISR Timer0-Überlauf
 in srsk,sreg          ;SREG sichern, falls in Main mal ein Hauptprogramm entsteht
 out tcnt0,tsw         ;Timer0-Startwert setzen
 inc pwz               ;PWM-Treppenzähler erhöhen
 cpi pwz,pwu           ;Endwert (Zählumfang) erreicht?
 brne tim0b            ;nein...
 clr pwz               ;ja, von 0 beginnen
 in wl,adch            ;ADC einlesen
 lsr wl                ;durch 4
 lsr wl                ;teilen
 st y,wl               ;und ins Sollwert-Register
 dec yl                ;nächst niedrigerer PWM-Kanal
 brpl tim0a            ;unter Null? - nein...
 ldi yl,7              ;ja, wieder mit 7 beginnen
tim0a:
 ldi wl,(1<<adlar)     ;linksbündige ADC-Ausgabe
 add wl,yl             ;und ADC-Quelle
 out admux,wl          ;umschalten
tim0b:
 cp pwz,soll0          ;Sollwert0 erreicht?
 ror wl                ;Ergebnis (Carry) sichern
 cp pwz,soll1          ;Sollwert1 erreicht?
 ror wl                ;Ergebnis (Carry) sichern
 cp pwz,soll2          ;Sollwert2 erreicht?
 ror wl                ;Ergebnis (Carry) sichern
 cp pwz,soll3          ;Sollwert3 erreicht?
 ror wl                ;Ergebnis (Carry) sichern
 cp pwz,soll4          ;Sollwert4 erreicht?
 ror wl                ;Ergebnis (Carry) sichern
 cp pwz,soll5          ;Sollwert5 erreicht?
 ror wl                ;Ergebnis (Carry) sichern
 cp pwz,soll6          ;Sollwert6 erreicht?
 ror wl                ;Ergebnis (Carry) sichern
 cp pwz,soll7          ;Sollwert7 erreicht?
 ror wl                ;Ergebnis (Carry) sichern
 com wl                ;invertieren wegen L-aktiven Ausgängen
 out aus,wl            ;Ergebnisse ausgeben
 out sreg,srsk         ;SREG wiederherstellen
 reti                  ;fertig...

Die hier nicht benötigte ADC-Behandlung habe ich mal drin gelassen.

...

Autor: schobbe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hannes lux wrote:

>Wurde C (ANSI-C) nicht für 16-Bit-Maschinen gemacht?
Nein. Das hängt wohl eher vom Compiler ab.
C funktioniert auf 8, 16, 32, 64, ... Bit System oder was immer es auch 
noch gibt.

>Läuft es auf 8-Bit-Controllern nur durch standardfremde Erweiterungen?
Was meinst du mit "stanardfremd"? Ja, es gibt Compiler abhängige C 
Direktiven, die dem Programmierer z.B. erlauben einen Variable an eine 
bestimmte Speicheradresse zu legen.

>Sind diese Erweiterungen nicht auch von Version zu Version
>unterschiedlich?
Von Compiler zu Compiler unterschiedlich.

Aber du kannst ja auch den Assembler für AVRs nicht auf einem Mircochip 
PIC verwenden.
Bei C kannst du den C Code von einem Controller auf den anderen 
portieren, natürlich muss man die Startup-Funktionen, die Register 
Initialisierungen und die Port Konfiguration anpassen, aber das dürfte 
ja klar sein. Das schönste ist aber, dass man z.B. seine funktionierende 
Display-Ansteuerung nicht neu schreiben muss. Selbst, wenn dein 
Assembler Code super abstrahiert sein sollte.. wenn einfach der 
Assembler ein anderer ist, ist er für einen anderen Controller 
"unbrauchbar".

>Gibt es nicht andauernd Probleme, weil ein "gefundenes" C-Projekt mit
>dem heimischen Compiler nicht compilierbar ist?

Siehe oben. Diese Probleme gibt es eher bei Assembler, das ist kein 
"C-Problem".

C ist eine sogenannte "Hochsprache". C ist zwar Maschinen NAH aber es 
ist keine konkrete Maschinensprache wie Assembler. Deshalb kann man 
funktionalen C-Code auf vielen verschiedenen Plattformen einsetzen.

Diesen Vorteil solltest du dir mal klar machen.

Gruss

Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Schobbe, es tut mir leid, dass Du auf meinen Spruch angesprungen bist, 
er war für Lippi gedacht, es war ja eine (humorige) Antwort auf seine 
(humorige) Frage. Es ging darum, die für einfachste ASM-Aufgaben in C 
erforderlichen Verrenkungen ins (scherzhaft-)Lächerliche zu ziehen. Das 
C eine Hochsprache ist, ist mir auch schon aufgefallen, der K&R liegt 
hier auch irgendwo in Reichweite.

Wenn ich 40 Jahre jünger wär und mit Programmierung meine Brötchen 
verdienen müsste, dann würde ich Dir recht geben, wobei mir Deine 
Argumente alle bekannt sind, Du hast mir also nichts Neues gesagt. Das 
ist aber nicht der Fall (40 Jahre jünger und Profi), daher kann ich mir 
fürs Hobby die Controllerfamilie selbst aussuchen, es sind nunmal AVRs. 
PICs, 8051er oder Motorolas (nur um einige andere 8-Bitter zu nennen) 
und die ganzen 16/32-Bitter interessieren mich nicht, denn ich gehöre 
nicht zu den Nachbauern, sondern zu den Selbermachern, da bin ich 
ungebunden...

Ich hatte nunmal Kontakt zu BASIC und 6502-Assembler, bevor ich 
irgendwelche Infos über C bekam, daher programmiere ich PCs in BASIC und 
AVRs in ASM. Die paar Jahre, die mir noch bleiben, werde ich auch noch 
auf C verzichten können.

Also, nix für ungut...

...

Autor: schobbe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@hannes

Das war auch nicht böse gemeint.
Ich hatte nur nicht den ironischen Kontext deines Beitrags raus gelesen. 
;-)

Du hast natürlich vollkommen Recht für Hobby / Bastler ist die 
Programmierung ein Werkzeug. Auch der Mikrocontroller ist nur ein 
Werkzeug um eine Aufgabe zu erfüllen. Deshalb ist es auch vollkommen 
legitim, dass jeder das Werkzeug benutzt das er am besten beherrscht.

Und wenn man mal ehrlich ist, dann ist für die meisten kleineren 
Anwendungen zu Hause ein 8 Bit Controller vollkommen ausreichend.

Bei mir ist es fast andersrum, ich habe zwar mal Assembler gelernt 8051 
und PIC. Aber mittlerweile mache ich alles in C. Manchmal wäre es aber 
ganz gut nochmal etwas in Assembler umzusetzen, weil, wie du schon 
geschrieben hast, da hat man alles in der Hand. Da funkt dir kein 
Compiler dazwischen.

Ab einer gewissen Größe würde ich es mir einfach gar nicht mehr zu 
trauen, etwas in Assembler zu schreiben, ganz einfach weil mir da die 
Erfahrung fehlt.

Aber so oder so. Man kann in Assembler schlecht programmieren und in C, 
auch in C++, Java, C#, Pascal oder Delphi.. schlechter Code lässt sich 
mit jeder Programmiersprache erzeugen.

Um mal wieder zu diesem Thread zurück zu kommen :) (der ja in Assembler 
verfasst ist...)

Aktiv in einem Interrupt auf etwas zu warten, lässt sich auch in C 
realisieren :-) aber ist da natürlich genauso böse..

Wie schon geschrieben wurde: Im UART Rx Interrupt das Rx Register 
auslesen und in einen Puffer schreiben. Dann raus aus der Interrupt 
Routine.

Gruss

Autor: mooo_ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
habe mich jetzt doch dafür entschieden die hardware pwm zu benutzen. 
OCR1A und OCR1B funktionieren auch schon. Jetzt bin ich grade dran den 
Timer 2 für OCR2 zu initialisieren und kriege dass irgendwie nicht 
richtig hin. kann mir jemand die init für Timer 2 in assembler schreiben 
oder sagen was ich da machen muss?
Vielen Dank

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.