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.
Die PWM braucht doch keinen Interrupt. Aber trotzden. Der Fehler liegt in Zeile 42 der main.c Und der R42 ist auch zu groß.
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?
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!
ja, das ist klar, aber wie kann ich sonst die 19 bytes, die ich hintereinander empfangen muss, bekommen?
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. ...
>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.
> 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... ...
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. ...
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
@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... ...
@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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.