Hallo Leute,
ich versuche, mit einem FPGA eine 3phasen PWM zu bewerkstelligen. Am
Schluss soll man damit eine Raumzeigermodulation machen können, oder
eine FOC oder was auch immer - halt alles, wozu man eine 3phasen PWM
benutzen kann.
Ich will, dass die PWM's 'center aligned' sind. also nicht so:
___________
Phase U ________| |______
_____
Phase V _______________| |______
sondern so:
___________
Phase U ________| |______
_____
Phase V ___________| |__________
dies wird mit einem up/down Counter erreicht. Er zählt von 0 hoch bis
zum erreichen des maximalwerts und zählt dann wieder runter bis auf 0.
Gleichzeitig wird dieser Counter mit dem PWM-Value verglichen, und wenn
dieser grösser ist, dann soll der Ausgangspin eingeschaltet werden.
Soweit die Theorie.
Warum funktioniert mein Code:
1
libraryieee;
2
useieee.std_logic_1164.all;
3
useieee.numeric_std.all;
4
5
entitysimple_pwmis
6
port(
7
clk:instd_logic;--clock
8
9
rel:inintegerrange0to255;--reload value
10
pre:inintegerrange0to255;--prescaler value
11
12
dcu:inintegerrange0to255;--duty cycle u phase
13
dcv:inintegerrange0to255;--duty cycle v phase
14
dcw:inintegerrange0to255;--duty cycle w phase
15
16
hsu:outstd_logic;--high side u output
17
lsu:outstd_logic;--low side u output
18
hsv:outstd_logic;--high side v output
19
lsv:outstd_logic;--low side v output
20
hsw:outstd_logic;--high side w output
21
lsw:outstd_logic--low side w output
22
);
23
endsimple_pwm;
24
25
architecturebehvofsimple_pwmis
26
signalctr:integerrange0to255;
27
signalps:integerrange0to255;
28
29
signaldcu_reg:integerrange0to255;
30
signaldcv_reg:integerrange0to255;
31
signaldcw_reg:integerrange0to255;
32
33
signalctr_nxt:integerrange0to255;
34
35
signaldir:std_logic;
36
37
begin
38
39
processbegin
40
waituntilrising_edge(clk);
41
ifps>0then
42
ps<=ps-1;
43
else
44
ps<=pre;
45
ctr<=ctr_nxt;
46
endif;
47
48
ifctr=0then
49
dcu_reg<=dcu;
50
dcv_reg<=dcv;
51
dcw_reg<=dcw;
52
dir<='0';
53
elsifctr=relthen
54
dcu_reg<=dcu;
55
dcv_reg<=dcv;
56
dcw_reg<=dcw;
57
dir<='1';
58
endif;
59
60
ifdcu_reg<ctrthen
61
lsu<='1';
62
hsu<='0';
63
else
64
lsu<='0';
65
hsu<='1';
66
endif;
67
68
ifdcv_reg<ctrthen
69
lsv<='1';
70
hsv<='0';
71
else
72
lsv<='0';
73
hsv<='1';
74
endif;
75
76
ifdcw_reg<ctrthen
77
lsw<='1';
78
hsw<='0';
79
else
80
lsw<='0';
81
hsw<='1';
82
endif;
83
84
endprocess;
85
86
ctr_nxt<=ctr+1whendir='0'elsectr-1;
87
88
endarchitecture;
nicht?
Da ich den FPGA mit einem Microcontroller verbunden habe, kann dieser
gleich die Werte in die Register schreiben, wie etwa den duty cycle usw.
Ich möchte mit dem FPGA am Schluss eine Art Peripheriebaustein
realisieren, ähnlich der Capture/Compare Unit die der 80C517 von Siemens
hatte. Ich muss nämlich noch andere Timingaufgaben erledigen, und dafür
ist der FPGA (denke ich) wie geschaffen.
Es handelt sich übrigens um einen Altera Cyclone II EP2C8T144C8N, falls
dies von Belang sein sollte.
Der Zähler zählt nicht bis zum Wert rel hoch, sondern bis zu rel + 1,
und er zählt nicht bis 0 runter, sondern er zählt noch eins mehr runter,
nämlich auf 255.
Beispiel: ich setze den rel - Eingang auf 10, dann zählt der Zähler so:
0 1 2 3 4 5 6 7 8 9 10 11 10 9 8 7 6 5 4 3 2 1 0 255 0 1 ...
Warum?
Und kann man das überhaupt so machen, oder ginge das noch eleganter? Und
wie könnte ich möglichst ressourcenschonend eine dead time einbauen?
Wie ich festgestellt habe, dass das Teil nicht funktioniert:
Ich habe es im Simulator simuliert, da ich den FPGA grade nicht zur Hand
habe.
Mein Glückwunsch... ;-)
Du hast das entdeckt, was andere Latency nennen. Das tritt gerne im
Zusammenhang mit getakteten Prozessen und Signalen auf...
Überleg dir mal, wie das hier zustande und wie du damit fertig werden
könntest...
Probiers mal so in dieser Art (ungetestet als Denkanstoss):
1
ifps>0then
2
ps<=ps-1;
3
else
4
ps<=pre;
5
if(dir='1')then--- runter
6
if(ctr>0)then
7
ctr<=ctr-1;
8
else
9
ctr<=1;
10
dir<='1';
11
endif;
12
else--- hoch
13
if(ctr<rel)then
14
ctr<=ctr+1;
15
else
16
ctr<=rel-1;
17
dir<='0';
18
endif;
19
endif;
20
endif;
> Wie ich festgestellt habe, dass das Teil nicht funktioniert:> Ich habe es im Simulator simuliert
Das ist schon mal der richtige Weg ;-)
Okay. Das nächste mal bitte Kommentare oder nur Kommentare dann siehst
du es auch selbst !
Dein Fehler:
Überlauf:
; wenn ctr = 0 dann dir auf 0
if ctr = 0 then
dcu_reg <= dcu;
dcv_reg <= dcv;
dcw_reg <= dcw;
dir <= '0';
; wenn dir = 0 dann ctr_nxt auf ctr-1 bedeutet 255
ctr_nxt <= ctr + 1 when dir = '0' else ctr - 1;
Unterlauf:
; wenn ctr = rel dann dir auf 1
elsif ctr = rel then
dcu_reg <= dcu;
dcv_reg <= dcv;
dcw_reg <= dcw;
dir <= '1';
end if;
; wenn dir = 1 dann ctr_nxt auf ctr+1 bedeutet rel+1
ctr_nxt <= ctr + 1 when dir = '0' else ctr - 1;
Hi Lothar, Hi keinproblem,
danke für eure ausführlichen Antworten. Besonderen Dank an dich, Lothar.
Ich werde deinen Code morgen Abend mal ausprobieren.
Aber Grundsätzlich: ist mein Ansatz, diese PWMs zu machen, richtig? das
wird in uCs intern ja auch irgendwie so gemacht, nicht?
gibts noch bessere Lösungen? Wie machst du das jeweils?
Dann:
Wie könnte ich auf möglichst einfache Weise Frequenz und Pulsbreite
eines Signals messen? Hast du dazu auch grade eine schlaue Idee?
Das Signal ist natürlich ein digitales Signal, welches bereits auf
3.3Volt Pegel umgesetzt ist. Frequenz liegt zwischen einigen 100 Hz und
ca. 100 kHz. Messen möchte ich, wie gesagt, die Frequenz, sowie die
Dauer der Positiven Impulse.
Gruss Tobias
PS: ich habe meinen ursprünglichen Code, der ja nicht korrekt
funktioniert, vorhin mal kurz auf den FPGA runtergeladen. Wie zu
erwarten war, kommen auch PWM Signale raus, soweit funktioniert die
Sache also schon. Nur halt dass der Zähler (noch) falsch zählt :-)
Tobias Plüss schrieb:> Aber Grundsätzlich: ist mein Ansatz, diese PWMs zu machen, richtig?
Tausend Wege führen nach Rom, deiner ist einer davon...
> Messen möchte ich, wie gesagt, die Frequenz,
Mit welcher Auflösung/Genauigkeit?
Wie schnell willst du das Ergebnis?
Zu den Grundlagen: eine Frequenz wird üblicherweise so gemessen, dass
während einer Gate-Zeit (z.B. 1 sec) die ankommenden steigenden Flanken
des Signals gezählt werden.
> sowie die Dauer der Positiven Impulse.
Ja, das ist einfach: eine steigende Flanke startet einen Zähler, die
fallende Flanke übernimmt den aktuellen Zählerwert. Wobei mit steigender
und fallender Flanke aber nicht rising_edge() und falling_edge() gemeint
ist, sondern eine traditionelle Flankenerkennung auf ein
einsynchronisiertes Signal. So wie das hier:
http://www.lothar-miller.de/s9y/categories/18-Flankenerkennung
Hi Lothar,
Als Auflösung wären ca. 10 Hz genügend.
In welcher Geschwindigkeit ich das Ergebnis will, kann ich aus dem
Stegreif so nicht sagen, ich muss mir das erst überlegen.
Evtl. wäre es einfacher, die Periodendauer zu messen?
Wie wähle ich eine sinnvolle Gate-Zeit für die Frequenzmessung? Die soll
ja einerseits möglichst lang sein, damit die Messung genau wird, aber
andererseits muss sie ja möglichst kurz sein, damit ich den Messwert
schnell bekomme.
Kann ich das mit der Flankenerkennung auch so machen?
Tobias Plüss schrieb:> Evtl. wäre es einfacher, die Periodendauer zu messen?
Und dann in VHDL einen Kehrwert bilden?
Viel Spass mit der Division. Aber auch da hätte ich was ;-)
http://www.lothar-miller.de/s9y/archives/29-Division-in-VHDL.html> Evtl. wäre es einfacher, die Periodendauer zu messen?
Fazit: garantiert nicht.
> Als Auflösung wären ca. 10 Hz genügend.
Dann nimm eine Gate-Zeit von 100ms und du bekommst 10 mal pro Sekunde
eine aktuelle Frequenz.
> Kann ich das mit der Flankenerkennung auch so machen?
Prinzipiell schon,...
aber dein count hat so Multiple Drivers aus 2 Prozessen heraus... :-o
Du brauchst die Flanke eigentlich nur für die Datenübernahme. Den Zähler
kannst du auch einfach auf das einsynchronisierte Signal laufen lassen:
Hi Lothar,
> Und dann in VHDL einen Kehrwert bilden?
Nöö, ohne Kehrwert. Einfach die Periodendauer.
Die Messung der positiven Pulsbreite habe ich jetzt wie folgt
realisiert:
1
signalct:integerrange0to255;
2
signals:integerrange0to255;
3
signalch0_old:std_logic;
4
signalch0_new:std_logic;
5
6
processbegin
7
waituntilrising_edge(clk);
8
ch0_old<=ch0_new;
9
ch0_new<=ch0;
10
if(ch0_old='0')and(ch0_new='1')then
11
ct<=0;
12
endif;
13
ifch0_old='1'then
14
ifs=prethen
15
ct<=ct+1;
16
s<=0;
17
else
18
s<=s+1;
19
endif;
20
endif;
21
if(ch0_old='1')and(ch0_new='0')then
22
ctr<=ct;
23
endif;
24
endprocess;
das kann man schon so machen, oder? es scheint jedenfalls zu
funktionieren. Nur wie man die Periode misst, weiss ich noch nicht so
genau.
Tobias Plüss schrieb:> Nur wie man die Periode misst, weiss ich noch nicht so genau.
Einfach mal zählen, wie lange es von einer steigenden Flanke des Signals
zu nächsten steigenden Flanke dauert.
>> Und dann in VHDL einen Kehrwert bilden?> Nöö, ohne Kehrwert. Einfach die Periodendauer.
Aber wenn du eine Frequenz willst, dann ist das eben der Kehrwert der
Periodendauer...