Hallo! Bin gerade am Experimentieren mit einem 8051-Mikrocontroller-Board. Möchte eine LED per PWM mit wechselndem Tastgrad an und aus "dimmen". Klappt so weit eigentlich auch, nur gibt es an dem Punkt, an dem die LED kurz ausgeschaltet sein sollte, einen kurzen, sehr hellen Impuls. Ich schätze der µC hat einfach Probleme, eine PWM mit Duty-Cycle nahe 0% zu liefern? Gibt es einen Trick um dieses kurze Aufblitzen der LED zu umgehen? Für die PWM benutze ich das PCA in 16bit-PWM-Betrieb. Danke schonmal für eure Hilfe!
Ein µC kann mit der PWM komplett an 0% und 100% rangehen. Kommt dann halt Gleichstrom raus. Bestimmt hast Du in deinem Programm irgendwo einen Überlauf, sodass Du deine PWM versehentlich auf 100% fährst. Ohne Programm und Schaltplan können wir aber nur raten. Gruß Jonathan
Jonathan Strobl schrieb: > Kommt dann > halt Gleichstrom raus So eine PWM erzeugt immer Gleichspannung, die Polarität ändert sich ja nicht. David schrieb: > Ich schätze der µC hat einfach Probleme, eine PWM mit Duty-Cycle nahe 0% > zu liefern? Wenn dann hat dein Code ein Problem damit :) Duty-Cycle 0 heißt für den Controller einfach PINx=0, für den µC ist das das kleinste Problem. > Gibt es einen Trick um dieses kurze Aufblitzen der LED zu > umgehen? Wie Jonathan sagt: Mehr Infos rausrücken xD
David schrieb: > Gibt es einen Trick um dieses kurze Aufblitzen der LED zu > umgehen? PWM abschalten. Ich denke mal, das funktioniert wie bei einem AVR: beim Hochzählen bis zum maximalen Wert ist der Ausgang 0 - Beim Erreichen umschalten auf 1 - jetzt weiterzählen bis zum eingestellten 'PWM-Wert' - wieder ausschalten. Und das dauert eben von 'Max' bis '0' einen Timertakt.
Danke euch schonmal! Kompletten Code hier jetzt reinposten wäre denk ich übertrieben... Also das ganze funktioniert so: Das PCA zählt permanent von 0x0000 bis 0xffff hoch (mit 3MHz), dazu gebe ich im Programm einen Compare-Wert vor, am Anfang soll dieser 0 sein. Sobald der Counter über diesen Wert läuft schaltet der Port auf high, LED ist dann aus. Dachte auch erst ich hab nen Fehler in der Zählschleife drin (dass ich den Compare-Wert öfter verringere als ich ihn vorher erhöht habe). Allerdings hab ichs in Einzelschritt durchprobiert und komme da immer wieder bei 0 raus, dann beginnt die Zählschleife von vorne. Das störende Aufblinken ist auch ab einem Compare-Startwert von ~0x40 nicht mehr da, ich erhöhe Compare aber bei jedem PCA-Interrupt um 0xfa, kann also nicht einen ganzen Schritt zu viel runterzählen... Hoffe das war einigermaßen verständlich was ich meine, fällt mir bisschen schwer das in Worte zu fassen ;)
Dominik S. schrieb: > So eine PWM erzeugt immer Gleichspannung, die Polarität ändert sich ja > nicht. Doch: Das ist eine Wechselspannung, die mit einer Gleichspannung überlagert ist. Wenn Du auf 12V Welchselspannung 20V Gleichspannung drauflegst, ändert sich die Polarität ja auch nicht mehr, aber die Wechselspannung ist trotzdem noch vorhanden. Wenn Du vom PWM-Signal den Gleichanteil wegfilterst, bleibt auch nur noch die Wechselspannung übrig. Aber ich glaube, das wird jetzt ein Bisschen Off-Topic und ist auch eine Definitionsfrage ;) David schrieb: > Kompletten Code hier jetzt reinposten wäre denk ich übertrieben... Nein, ohne Quellcode vor der Nase kann man keine Fehler finden. Zeig schon her ;) Gruß Jonathan
Dominik S. schrieb: > So eine PWM erzeugt immer Gleichspannung, die Polarität ändert sich ja > nicht. Da würde dich eine FFT eines belehren. Eine Gleichspannung hat nur einen Beitrag bei 0 Hz, d.h. sie ändert sich zeitlich nicht.
Also, dann hier mal der Code...
1 | CSEG AT 0H ;Legt absolute Codesegmentadresse auf 0h |
2 | call Init_Device ;Aufruf zur Initialisierung der Controller Funktionen |
3 | jmp INIT |
4 | ; |
5 | ;------------------------------------------------------------------------ |
6 | ;Interrupt-Vektoren |
7 | ;------------------------------------------------------------------------ |
8 | ORG 05Bh ;PCA Interrupt-Adresse |
9 | clr CF ;Interrupt-Flag rücksetzen |
10 | call PCAINT ;springe in Interrupt-Serviceroutine |
11 | |
12 | ;------------------------------------------------------------------------ |
13 | ;Initialisierungsteil fuer On-Chip Peripherie |
14 | ;------------------------------------------------------------------------ |
15 | ORG 100H ;Programmstart bei 100H |
16 | INIT: |
17 | setb P3.2 ; LED ausschalten |
18 | clr 20h.0 ; Bits (Flags) rücksetzen |
19 | clr 20h.1 |
20 | clr 20h.2 |
21 | clr 20h.3 |
22 | SCHRITTE EQU 255 ; Anzahl der Gesamtschritte bei PWM (so oft wird Compare erhöht...) |
23 | SCHRITTW EQU 255; Wert, um den Compare bei jedem Interrupt erhöht wird |
24 | mov R2, #SCHRITTE ; R2 mit Anzahl d. Schritte laden |
25 | |
26 | |
27 | |
28 | ;------------------------------------------------------------------------ |
29 | ;Programmschleife |
30 | ;------------------------------------------------------------------------ |
31 | |
32 | ABFRAGE: |
33 | jnb P1.1,SETF1 ; Abfrage Taster T1 |
34 | jmp ABFRAGE |
35 | |
36 | SETF1: |
37 | setb 20h.0 ;Merken, dass T1 bereits gedrückt wurde... |
38 | |
39 | jmp ABFRAGE |
40 | |
41 | |
42 | PCAINT: ; Interrupt_Routine |
43 | jb 20h.0, PWM ;falls T1 bereits gedrückt wurde: PWM starten... |
44 | reti |
45 | |
46 | PWM: |
47 | jb 20h.2, DUNKLER |
48 | djnz R2, HELLER |
49 | setb 20h.2 ; falls R2=null: Ab nächstem Durchlauf in "DUNKLER" springen... |
50 | mov R2, #SCHRITTE ; R2 neu laden |
51 | reti |
52 | |
53 | |
54 | HELLER: |
55 | mov A, PCA0CPL0 ; Compare-Lowbyte in Akku schreiben |
56 | clr C ; evtl. noch vorhandenes Übertragscarry nullen |
57 | add A, #SCHRITTW ;Compare-Lowbyte erhöhen (Duty-Cycle wird kleiner /LED heller) |
58 | mov PCA0CPL0, A ; zurückschreiben |
59 | mov A, PCA0CPH0 ;Highbyte in Akku |
60 | addc A, #0h ; evtl. aufgetretenes Carry zu Highbyte addieren |
61 | mov PCA0CPH0, A ; Highbyte zurückschreiben |
62 | |
63 | reti |
64 | |
65 | |
66 | |
67 | DUNKLER: |
68 | djnz R2, DUNKLER_SCHL ; Wenn R2 nicht 0 springen... |
69 | clr 20h.2 ; wieder erhöhen |
70 | mov R2, #SCHRITTE |
71 | reti |
72 | |
73 | DUNKLER_SCHL: ; LED abdunkeln |
74 | mov A, PCA0CPL0 |
75 | clr C |
76 | subb A, #SCHRITTW ;Compare-Wert Lowbyte verringern -> Duty-Cycle wird größer /LED dunkler |
77 | mov PCA0CPL0, A |
78 | mov A, PCA0CPH0 |
79 | subb A, #0h ; evtl. aufgetretenes Carry von Compare-Highbyte abziehen |
80 | mov PCA0CPH0, A |
81 | reti |
82 | |
83 | |
84 | |
85 | ;------------------------------------ |
86 | ;- Generated Initialization File -- |
87 | ;------------------------------------ |
88 | |
89 | |
90 | |
91 | ; Peripheral specific initialization functions, |
92 | ; Called from the Init_Device label |
93 | PCA_Init: |
94 | mov PCA0CN, #040h |
95 | anl PCA0MD, #0BFh |
96 | mov PCA0MD, #009h |
97 | mov PCA0CPM0, #0C2h |
98 | mov PCA0CPL0, #0h ; Startwert für Compare-Lowbyte |
99 | mov PCA0CPH0, #0h ; Startwert für Compare-Highbyte |
100 | ret |
101 | |
102 | Timer_Init: |
103 | mov TCON, #010h |
104 | mov TMOD, #001h |
105 | ret |
106 | |
107 | Port_IO_Init: |
108 | ; P0.0 - Skipped, Push-Pull, Digital |
109 | ; P0.1 - Skipped, Push-Pull, Digital |
110 | ; P0.2 - Skipped, Push-Pull, Digital |
111 | ; P0.3 - Skipped, Push-Pull, Digital |
112 | ; P0.4 - TX0 (UART0), Push-Pull, Digital |
113 | ; P0.5 - RX0 (UART0), Push-Pull, Digital |
114 | ; P0.6 - Skipped, Push-Pull, Digital |
115 | ; P0.7 - Skipped, Push-Pull, Digital |
116 | |
117 | ; P1.0 - Skipped, Push-Pull, Digital |
118 | ; P1.1 - Skipped, Push-Pull, Digital |
119 | ; P1.2 - Skipped, Push-Pull, Digital |
120 | ; P1.3 - Skipped, Push-Pull, Digital |
121 | ; P1.4 - Skipped, Push-Pull, Digital |
122 | ; P1.5 - Skipped, Push-Pull, Digital |
123 | ; P1.6 - Skipped, Push-Pull, Digital |
124 | ; P1.7 - Skipped, Push-Pull, Digital |
125 | |
126 | ; P2.0 - Skipped, Push-Pull, Digital |
127 | ; P2.1 - Skipped, Push-Pull, Digital |
128 | ; P2.2 - Skipped, Push-Pull, Digital |
129 | ; P2.3 - Skipped, Push-Pull, Digital |
130 | ; P2.4 - Skipped, Push-Pull, Digital |
131 | ; P2.5 - Skipped, Push-Pull, Digital |
132 | ; P2.6 - Skipped, Push-Pull, Digital |
133 | ; P2.7 - Skipped, Push-Pull, Digital |
134 | |
135 | ; P3.0 - Skipped, Push-Pull, Digital |
136 | ; P3.1 - Skipped, Push-Pull, Digital |
137 | ; P3.2 - CEX0 (PCA), Push-Pull, Digital |
138 | ; P3.3 - CEX1 (PCA), Push-Pull, Digital |
139 | ; P3.4 - CEX2 (PCA), Push-Pull, Digital |
140 | ; P3.5 - CEX3 (PCA), Push-Pull, Digital |
141 | ; P3.6 - CEX4 (PCA), Push-Pull, Digital |
142 | ; P3.7 - T0 (Timr0), Push-Pull, Digital |
143 | |
144 | mov P0MDOUT, #0FFh |
145 | mov P1MDOUT, #0FFh |
146 | mov P2MDOUT, #0FFh |
147 | mov P3MDOUT, #0FFh |
148 | mov P0SKIP, #0CFh |
149 | mov P1SKIP, #0FFh |
150 | mov P2SKIP, #0FFh |
151 | mov P3SKIP, #003h |
152 | mov XBR0, #001h |
153 | mov XBR1, #055h |
154 | ret |
155 | |
156 | Oscillator_Init: |
157 | mov OSCICN, #083h |
158 | ret |
159 | |
160 | Interrupts_Init: |
161 | mov EIE1, #030h |
162 | mov IT01CF, #010h |
163 | mov IE, #080h |
164 | ret |
165 | |
166 | ; Initialization function for device, |
167 | ; Call Init_Device from your main program |
168 | Init_Device: |
169 | lcall PCA_Init |
170 | lcall Timer_Init |
171 | lcall Port_IO_Init |
172 | lcall Oscillator_Init |
173 | lcall Interrupts_Init |
174 | ret |
175 | |
176 | end |
Hoffe es ist nachzuvollziehen wie das funktionieren soll... ;-)
> Hoffe es ist nachzuvollziehen wie das funktionieren soll Wenn man wissen würde welchen SiLabs 8051 Du denn verwendest, wäre es einfacher. Ich tippe mal auf 8051F020 o.ä.
Jim Meba schrieb: >> Hoffe es ist nachzuvollziehen wie das funktionieren soll > > Wenn man wissen würde welchen SiLabs 8051 Du denn verwendest, wäre es > einfacher. Ich tippe mal auf 8051F020 o.ä. Sorry, wusste nicht dass das wichtig ist...Ist ein 8051F340... Gruß David
Hi >;---------------------------------------------------------------------- -- >;Interrupt-Vektoren >;---------------------------------------------------------------------- -- >ORG 05Bh ;PCA Interrupt-Adresse >clr CF ;Interrupt-Flag rücksetzen >call PCAINT ;springe in Interrupt-Serviceroutine An dieser Stelle sollte ein Sprung zur ISR stehen. Oder ist das beim 8051 anders. MfG Spess
David schrieb: > Sorry, wusste nicht dass das wichtig ist...Ist ein 8051F340... Naja, es gibt ja nur über 500 Typen des 8051 von etwa 50 Herstellern. Das die alle irgendwelche Unterschiede haben, sollte klar sein. Auch muß man ja zu Anfang das entsprechende Include reinschreiben, damit der Assembler/Compiler überhaupt die richtigen Adressen findet. Geht daraus nicht eindeutig das verwendete Target hervor, schreibt man es als Kommentar mit rein. In die Vektortabelle schreibt man immer nur den Sprung zur Behandlungsroutine rein, keinen sonstigen Code! Dein Sprung zum Main kollidiert mit dem ersten Interruptvektor. Vielleicht willst Du ihn später mal verwenden und dann krachts. Peter
spess53 schrieb: > An dieser Stelle sollte ein Sprung zur ISR stehen. Oder ist das beim > 8051 anders. Anders als wo? Gruß Jobst
Also bei den Z80 basierten Controllern im IRQ-Mode 2 ist es anders :-) Nein, der 8051 hat vorne eine Vektortabelle, wie AVRs z.B. auch. Gruß Jobst
Hallo!
Ohne jetzt den Code komplett analysiert zu haben fällt auf den ersten
Blick dieses auf:
> call PCAINT ;springe in Interrupt-Serviceroutine
Das ist kein Sprung, sondern ein Unterprogrammaufruf. Wenn jetzt in der
Service-Routine das RETI kommt, was macht dann der Controller?
Route 66 schrieb: >> call PCAINT ;springe in Interrupt-Serviceroutine > Das ist kein Sprung, sondern ein Unterprogrammaufruf. Wenn jetzt in der > Service-Routine das RETI kommt, was macht dann der Controller? Ich bin davon ausgegangen er springt wieder dahin im Programm wo er war, als der Interrupt aufgetreten ist...was ist daran falsch?
Hi >Ich bin davon ausgegangen er springt wieder dahin im Programm wo er war, >als der Interrupt aufgetreten ist...was ist daran falsch? Normalerweise ja. Wenn ein Interrupt auftritt wird die aktuelle Adresse auf den Stack gelegt und der Sprung zur ISR, der an der zum Interrupt gehörenden Stelle in der Interrupttabelle steht, ausgeführt. Bei dir steht aber dort kein Sprung, sondern ein Call. Und bei einem Call wird automatisch die Adresse des Befehls nach dem Call automatisch auf den Stack gelegt. Beim RETI der ISR wird dann die Rückkehradresse vom Stack geholt. Dank deinem Call ist das aber nicht die Adresse an der Stelle des Interrupts sondern die nach 'call PCAINT'. Und damit geht es bei dem, was dort zufällig steht weiter. MfG Spess
spess53 schrieb: > Hi > >>Ich bin davon ausgegangen er springt wieder dahin im Programm wo er war, >>als der Interrupt aufgetreten ist...was ist daran falsch? > > Normalerweise ja. Wenn ein Interrupt auftritt wird die aktuelle Adresse > auf den Stack gelegt und der Sprung zur ISR, der an der zum Interrupt > gehörenden Stelle in der Interrupttabelle steht, ausgeführt. Bei dir > steht aber dort kein Sprung, sondern ein Call. Und bei einem Call wird > automatisch die Adresse des Befehls nach dem Call automatisch auf den > Stack gelegt. Beim RETI der ISR wird dann die Rückkehradresse vom Stack > geholt. Dank deinem Call ist das aber nicht die Adresse an der Stelle > des Interrupts sondern die nach 'call PCAINT'. Und damit geht es bei > dem, was dort zufällig steht weiter. > > MfG Spess Danke, jetzt hab ich das Problem erkannt...d.h. ich müsste entweder das "call" durch ein "jmp" ersetzen, oder das reti in die Zeile unterhalb von "call PCAINT" schreiben, richtig? So im Nachhinein wundert es mich aber, dass das Programm überhaupt funktioniert - er müsste doch jetzt wenn er nach dem call wieder zurückspringt wieder weitergehen im Code, also wieder zu "Init:" und den darauf folgenden Befehlen oder? LG David
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.