Forum: Mikrocontroller und Digitale Elektronik PWM mit Duty-Cycle 0%?


von David (Gast)


Lesenswert?

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!

von Jonathan S. (joni-st) Benutzerseite


Lesenswert?

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

von Dominik S. (dasd)


Lesenswert?

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

von ich (Gast)


Lesenswert?

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.

von David (Gast)


Lesenswert?

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 ;)

von Jonathan S. (joni-st) Benutzerseite


Lesenswert?

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

von Hinz (Gast)


Lesenswert?

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.

von David (Gast)


Lesenswert?

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

von Jim M. (turboj)


Lesenswert?

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

von David (Gast)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Jobst M. (jobstens-de)


Lesenswert?

spess53 schrieb:
> An dieser Stelle sollte ein Sprung zur ISR stehen. Oder ist das beim
> 8051 anders.

Anders als wo?


Gruß

Jobst

von spess53 (Gast)


Lesenswert?

Hi

>Anders als wo?

Bei anderen µCs?

MfG Spess

von Jobst M. (jobstens-de)


Lesenswert?

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

von Route_66 H. (route_66)


Lesenswert?

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?

von David (Gast)


Lesenswert?

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?

von spess53 (Gast)


Lesenswert?

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

von David (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.