PWM mit Duty-Cycle 0%?

Gast #2708872
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!
#2708916
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
Gast #2708931
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.
Gast #2708951
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 ;)
Persönliche Seite #2708961
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
Gast #2709294
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... ;-)
Gast #2710981
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
#2711095
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
Gast #2715509
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?
Gast #2715540
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
Gast #2717538
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren