Forum: Mikrocontroller und Digitale Elektronik Uart und Pwm gleichzeitig


von mooo_ (Gast)


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.

von Matthias L. (Gast)


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ß.

von mooo_ (Gast)


Lesenswert?

1
.include "m8def.inc"
2
 
3
.def temp  = r16
4
 
5
.def PWMCount = r17
6
 
7
.def ocr_1 = r18                      
8
.def ocr_2 = r19                      
9
.def ocr_3 = r20                      
10
.def ocr_4 = r21                      
11
.def ocr_5 = r22                      
12
.def ocr_6 = r23                      
13
14
15
.equ F_CPU = 12000000                            ; Systemtakt in Hz
16
.equ BAUD  = 38400                               ; Baudrate
17
 
18
.org 0x00
19
        rjmp main
20
 
21
.org OVF0addr
22
        rjmp    timer0_overflow       ; Timer Overflow Handler
23
.org URXCaddr                                   ; Interruptvektor für UART-Empfang
24
        rjmp int_rxc
25
26
main:
27
        ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren
28
        out     SPL, temp
29
        ldi     temp, HIGH(RAMEND)
30
        out     SPH, temp
31
  
32
        ldi     temp, 0xFF            ; Port C auf Ausgang
33
        out     DDRC, temp
34
  
35
        ldi     temp, 0b00000001     ; CS00 setzen: Teiler 1
36
        out     TCCR0, temp
37
 
38
        ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow
39
        out     TIMSK, temp
40
41
    ; Port D = Ausgang
42
 
43
    ldi     temp, 0xFF
44
    out     DDRD, temp
45
 
46
    ; Baudrate einstellen
47
 
48
    ldi     temp, HIGH(UBRR_VAL)
49
    out     UBRRH, temp
50
    ldi     temp, LOW(UBRR_VAL)
51
    out     UBRRL, temp
52
 
53
    ; Frame-Format: 8 Bit
54
 
55
    ldi     temp, (1<<URSEL)|(3<<UCSZ0)
56
    out     UCSRC, temp
57
 
58
    sbi     UCSRB, RXCIE                    ; Interrupt bei Empfang
59
    sbi     UCSRB, RXEN                     ; RX (Empfang) aktivieren
60
    
61
    sei                                     ; Interrupts global aktivieren
62
 
63
loop:
64
rjmp loop                             ; Endlosschleife
65
 
66
timer0_overflow:                      ; Timer 0 Overflow Handler
67
        inc     PWMCount              ; den PWM Zähler von 0 bis
68
        cpi     PWMCount, 255         ; 255 zählen lassen
69
        brne    WorkPWM
70
        clr     PWMCount
71
 
72
WorkPWM:
73
        ldi     temp, 0b11000000      ; 0 .. Led an, 1 .. Led aus
74
 
75
        cp      PWMCount, ocr_1       ; Ist der Grenzwert für Led 1 erreicht
76
        brlo    OneOn
77
        ori     temp, $01
78
 
79
OneOn:  cp      PWMCount, ocr_2       ; Ist der Grenzwert für Led 2 erreicht
80
        brlo    TwoOn
81
        ori     temp, $02
82
 
83
TwoOn:  cp      PWMCount, ocr_3       ; Ist der Grenzwert für Led 3 erreicht
84
        brlo    ThreeOn
85
        ori     temp, $04
86
 
87
ThreeOn:cp      PWMCount, ocr_4       ; Ist der Grenzwert für Led 4 erreicht
88
        brlo    FourOn
89
        ori     temp, $08
90
 
91
FourOn: cp      PWMCount, ocr_5       ; Ist der Grenzwert für Led 5 erreicht
92
        brlo    FiveOn
93
        ori     temp, $10
94
 
95
FiveOn: cp      PWMCount, ocr_6       ; Ist der Grenzwert für Led 6 erreicht
96
        brlo    SetBits
97
        ori     temp, $20
98
 
99
SetBits:                              ; Die neue Bitbelegung am Port ausgeben
100
        out     PORTC, temp
101
 
102
        reti
103
int_rxc:
104
bit1:
105
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
106
   rjmp     bit1
107
   in       r24, UDR                       ; empfangenes Byte nach temp kopieren
108
   cpi r24, 0xFF
109
   brne     bit1
110
bit2:
111
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
112
   rjmp     bit2
113
   in       r24, UDR                       ; empfangenes Byte nach temp kopieren
114
   cpi r24, 0x00
115
   brne     bit1
116
bit3:
117
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
118
   rjmp     bit3
119
   in       r24, UDR                       ; empfangenes Byte nach temp kopieren
120
   cpi r24, 0x00
121
   brne     bit1
122
bit4:
123
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
124
   rjmp     bit4
125
   in       r24, UDR                       ; empfangenes Byte nach temp kopieren
126
   cpi r24, 0x0F
127
   brne     bit1
128
bit5:
129
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
130
   rjmp     bit5
131
   in       ocr_4, UDR                       ; empfangenes Byte nach temp kopieren
132
bit6:
133
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
134
   rjmp     bit6
135
   in       ocr_2, UDR                       ; empfangenes Byte nach temp kopieren
136
bit7:
137
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
138
   rjmp     bit7
139
   in       ocr_3, UDR                       ; empfangenes Byte nach temp kopieren
140
bit8:
141
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
142
   rjmp     bit8
143
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
144
bit9:
145
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
146
   rjmp     bit9
147
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
148
bit10:
149
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
150
   rjmp     bit10
151
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
152
bit11:
153
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
154
   rjmp     bit11
155
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
156
bit12:
157
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
158
   rjmp     bit12
159
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
160
bit13:
161
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
162
   rjmp     bit13
163
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
164
bit14:
165
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
166
   rjmp     bit14
167
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
168
bit15:
169
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
170
   rjmp     bit15
171
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
172
bit16:
173
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
174
   rjmp     bit16
175
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren   
176
bit17:
177
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
178
   rjmp     bit17
179
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
180
bit18:
181
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
182
   rjmp     bit18
183
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
184
bit19:
185
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist
186
   rjmp     bit19
187
   in       r25, UDR                       ; empfangenes Byte nach temp kopieren
188
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?

von Matthias L. (Gast)


Lesenswert?

1
>int_rxc:
2
bit2:
3
   sbis     UCSRA, RXC              ; warten bis ein Byte angekommen ist
4
   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!

von mooo_ (Gast)


Lesenswert?

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

von Hannes Lux (Gast)


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.

...

von Matthias L. (Gast)


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.

von Hannes Lux (Gast)


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

...

von Hannes Lux (Gast)


Lesenswert?

Achja, in ASM sieht es estwa so aus (uraltes Beispiel):
1
TIM0_OVF:       ;ISR Timer0-Überlauf
2
 in srsk,sreg          ;SREG sichern, falls in Main mal ein Hauptprogramm entsteht
3
 out tcnt0,tsw         ;Timer0-Startwert setzen
4
 inc pwz               ;PWM-Treppenzähler erhöhen
5
 cpi pwz,pwu           ;Endwert (Zählumfang) erreicht?
6
 brne tim0b            ;nein...
7
 clr pwz               ;ja, von 0 beginnen
8
 in wl,adch            ;ADC einlesen
9
 lsr wl                ;durch 4
10
 lsr wl                ;teilen
11
 st y,wl               ;und ins Sollwert-Register
12
 dec yl                ;nächst niedrigerer PWM-Kanal
13
 brpl tim0a            ;unter Null? - nein...
14
 ldi yl,7              ;ja, wieder mit 7 beginnen
15
tim0a:
16
 ldi wl,(1<<adlar)     ;linksbündige ADC-Ausgabe
17
 add wl,yl             ;und ADC-Quelle
18
 out admux,wl          ;umschalten
19
tim0b:
20
 cp pwz,soll0          ;Sollwert0 erreicht?
21
 ror wl                ;Ergebnis (Carry) sichern
22
 cp pwz,soll1          ;Sollwert1 erreicht?
23
 ror wl                ;Ergebnis (Carry) sichern
24
 cp pwz,soll2          ;Sollwert2 erreicht?
25
 ror wl                ;Ergebnis (Carry) sichern
26
 cp pwz,soll3          ;Sollwert3 erreicht?
27
 ror wl                ;Ergebnis (Carry) sichern
28
 cp pwz,soll4          ;Sollwert4 erreicht?
29
 ror wl                ;Ergebnis (Carry) sichern
30
 cp pwz,soll5          ;Sollwert5 erreicht?
31
 ror wl                ;Ergebnis (Carry) sichern
32
 cp pwz,soll6          ;Sollwert6 erreicht?
33
 ror wl                ;Ergebnis (Carry) sichern
34
 cp pwz,soll7          ;Sollwert7 erreicht?
35
 ror wl                ;Ergebnis (Carry) sichern
36
 com wl                ;invertieren wegen L-aktiven Ausgängen
37
 out aus,wl            ;Ergebnisse ausgeben
38
 out sreg,srsk         ;SREG wiederherstellen
39
 reti                  ;fertig...

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

...

von schobbe (Gast)


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

von Hannes Lux (Gast)


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

...

von schobbe (Gast)


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

von mooo_ (Gast)


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

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.