Hallo an alle.
Bei der suche nach einer PWM Lüftersteuerung im netz, bin ich auf diese
seite gestossen:
http://www.mikrocontroller.net/articles/Soft-PWM#Intelligenter_L.C3.B6sungsansatz
. Ich habe mich ganz besonderes auf die 8 Kanäle gefreut und dachte mir,
"ok, so schwer kann es nicht sein, einwenig haste ja druff". Ich habe
den Code für ein Atmega16 compiliert ohne etwas zu verändern aber, es
geht nicht. Ich schreibe ab und zu selber kleine programchen, das ABC
habe ich drauf, im AVR Studio bleibt der Zeiger nach click auf pause
immer bei
1
// auf Sync warten
2
3
pwm_sync=0;// Sync wird im Interrupt gesetzt
4
->while(pwm_sync==0);
und das gibt mir zu denken, dass das interrupt nicht ausgelöst wird.
Für jede hilfe bin ich dankbar, und so werde ich auch etwas neues
lernen.
MFG
>und das gibt mir zu denken, dass das interrupt nicht ausgelöst wird.
Wie lange hast du denn gewartet? Schliesslich muß der Interrupt nicht
nur einmal aufgerufen werden, bis deine while Bedingung erfüllt wird,
und im Studio vergeht die "Echtzeit" arg langsam. Um zu prüfen, ob der
Interrupt ausgelöst wird, empfiehlt es sich, mal auf dessen erste Zeile
eine breakpoint zu setzen. Und dann warten...
Oliver
Hallo, danke für die Antwort erst mall.
Ich habe ein JTAG ICE und da leuft ja alles so ziemlich in echtzeit,
also keine simulation.
Ich habe gemacht was du vor geschlagen hast und, leider zind meine
vermutungen falsch gewesen, nach jedem RUN befehl stoppt das program bei
der ISR.
Hmm....Jemand noch eine idee?
Ich habe gerade festgestelld, dass die Definition die #Define für
PWM_PORT und PWM_DDR nicht funktionieren. Im AVR Studio ist zu sehen das
die Port richtungs register nicht richtig gesetzt werden. Was könte die
ursache dfür sein?
MFG
Rudolf Bremer wrote:
> vermutungen falsch gewesen, nach jedem RUN befehl stoppt das program bei> der ISR.
Das sieht doch nicht schlecht aus.
Wenn du einen Breakpoint am Begin der ISR hast und sich der
Debugger nach einem Run am Begin der ISR wieder meldet, dann
ist doch alles so wie es sein soll.
Ab dort gehst du dann mit Einzelschrittbefehlen weiter durch
die ISR.
Ist die ISR fertig abgearbeitet, dann müsste nach dem Rücksprung
aus der ISR die while-Schleife verlassen werden.
Ich denke auch, dass hier erst mal ein Bedienungsproblem
des Debuggers vorliegt und kein Programmfehler.
Rudolf Bremer wrote:
> Ich habe gerade festgestelld, dass die Definition die #Define für> PWM_PORT und PWM_DDR nicht funktionieren. Im AVR Studio ist zu sehen das> die Port richtungs register nicht richtig gesetzt werden. Was könte die> ursache dfür sein?
Ev. hast du im Simulator einen anderen Prozessor eingestellt
als der für den du compiliert hast.
DAs ging aber schnell... Im Studio ist der richtige procesor
eingestellt, die testplatine leuft wunderbar, schon zig sachen dmit
getestet, da ist alles in ordnung... könte mein makefile irgendwie
schuld daran sein?
Nach dem in main PWM_DDR = 0xFF; aufgerufen wird wird R24 0xFF. R24 ist
aber kein portregister, oder? Nach dem nächsten schritt wir R24 0x01 und
nach dem dritten wird 0x10
Rudolf Bremer wrote:
> Nach dem in main PWM_DDR = 0xFF; aufgerufen wird wird R24 0xFF. R24 ist> aber kein portregister, oder? Nach dem nächsten schritt wir R24 0x01 und> nach dem dritten wird 0x10
R24 interessiert keinen.
Was passiert im I/O View mit dem Port-Register?
Nur das ist interessant.
Ich muss mich veguckt haben, der Portrichtungsregister wird richtig
gesetzt im I/O View wird der DDRB auf 0xFF gesetzt. Also werden die
Register richtig definiert.
HEULLL
Grundsatzfrage:
Wird die ISR jetzt angesprungen oder wird sie es nicht?
Man kann in eine ISR genauso einen Breakpoint setzen, wie
in jede andere Funktion auch. Wird die ISR angesprungen,
dann läuft sie auch auf den Breakpoint auf.
Ab dort musst du dann mit Singlesteps rausfinden was da abgeht.
Im Moment hab ich das Gefühl du spekulierst ...
> kommt garnicht dazu pwm_sync auf 1 zu setzen.
... und Spekulation ist kein guter Ratgeber beim Debuggen.
Auch wenn ein JTAG was feines ist, am allerschönsten für solche
Fehlersuchen ist VMLAB. Damit sieht man dann direkt, was abgeht.
Ansonsten setzt mal Breakpoints auf jede der folgend Zeilen der ISR:
if (pwm_cnt == pwm_cnt_max) {
pwm_cnt++;
if (pwm_cnt == pwm_cnt_max) pwm_sync = 1;
und dann noch einen auf deine while-Bedingung,
und stepp dann jedes mal im Einzelschrittbetrieb durch die ISR. Dann
siehst du, ob pwm_cnt inkremetiert wird, wenn nicht, warum nicht, und
wenn ja, wann pwm_sync zu 1 wird.
Oliver
Die ISR wird angesprungen, ich habe die Braekpoints markiert aber, es
ist so, dass nur der erste IF angesprungen wird (in der ISR)und
pwm_cnt/pwm_cnt_max immer 0 sind. Ist es richtig, dass pwm_cnt in der
ISR mit 0 init. wird? Was passiert damit wenn die ISR aufgerufen wird,
ich meine, wird sie jedesmall neu init.?
Rudolf Bremer wrote:
> Die ISR wird angesprungen, ich habe die Braekpoints markiert aber, es> ist so, dass nur der erste IF angesprungen wird (in der ISR)und> pwm_cnt/pwm_cnt_max immer 0 sind.
Nein.
pwm_cnt ist static, wird also nur beim ersten Betreten der ISR
initialisiert.
pwm_cnt_max wird in pwm_update berechnet.
Bevor also diese Funktion nicht aufgerufen wird, hat es keinen
Sinn die ISR zu Debuggen.
Daher ist auch das vorzeitige Freigeben des Interrupts mit
sei() nicht unbedingt toll. So wie ich das sehe, macht das
die pwm_update() Funktion ja sowieso -> sollte aus der main
herausgenommen werden.
Was mich stutzig macht: Im main() gibt es keine Hauptschleife.
ammm, ind der pwm_update wird sei(); erst nach while(pwm_sync==0); und
da pwm_sync erst in der ISR bearbeitet wird muss, meiner meinung nach,
sei(); in main sein...oder nicht?!? Was die hauptschleife angeht... wie
weiter oben gesagt, wenn ich Studio anhalte, steht der Zeiger auf:
while(pwm_sync==0); und, wenn ich dann zum nächsten befehl springe (
cli(); )
1
sei();
2
pwm_sync=0;// Sync wird im Interrupt gesetzt
3
--<-while(pwm_sync==0);
4
|
5
|// neue PWM-Daten kopieren mit maximaler Geschwindigkeit
6
|->cli();
7
pwm_timing[8]=pwm_timing_tmp[8];
8
pwm_mask[8]=pwm_mask_tmp[8];
Funktioniert es, also 8 verschiedene PW werte am den port pins und, der
Zeiger, nach dem ich Studio angehalten habe, ist ganz am ende der main.
Anscheinend reicht ab dem pwm_update(); die ISR aus.
MFG
Rudolf Bremer wrote:
> ammm, ind der pwm_update wird sei(); erst nach while(pwm_sync==0); und> da pwm_sync erst in der ISR bearbeitet wird muss, meiner meinung nach,> sei(); in main sein...oder nicht?!?
Ooops.
Mein Fehler. Da hast du absoult recht, hab ich übersehen.
Hat jemand im forum schon mall das erwehnte Program benutzt oder, weiss
jemand wer das geschrieben hat? Das Program ist zwar etwas gross aber,
laut Entwikler braucht es nur 0,1 bis 1% cpu leistung für 8 Kanäle, ist
schon was, da bleibt noch eine menge leistung für LCD, tastenentprelung
und weiss der geier was noch... Es ist das beste Soft PWM das ich bis
jetzt gesehen habe und, es wäre traurig wenn es nicht geht. Hat denn
jemand von euch die Möglichkeit es zu testen? Am besten gleich mit ein
Atmega16. Ich habe es nicht so drauf mit Programieren und, des wegen
Schreie ich hier noch mall nach Hilfe.
P.S: Es wird denke ich, auch anderen Anfänger helfen mit der materie
klar zu kommen. Ich würde natürlich auch gerne wissen warum das nicht
geht, ich zerbreche mir damit den kopf seit etwa 1 Woche, ich kriege
langsam ´ne krise.
Für jede hilfe bin ich dankbar. MFG
Also, ich habs mal in vmlab getestet.
So, wie das das steht, ist das Programm noch nie gelaufen. Neben deinem
while-Problem, das daran liegt, daß pwm_cnt_max zu Beginn nirgends
initialisiert wird, und deshalb das erste
1
if(pwm_cnt==pwm_cnt_max)
in der ISR immer wahr bleibt, ist da noch mehr im argen. pwm_cnt wird
größer als pwn_cnt_max, und damit gehen alle folgende array-Zugrife in
die Hose. pwm_cnt_max wird zunächst auf 8 gesetzt, wenn dann pwn_cnt bis
8 hochgezählt hat, wird pwm_cnt_max auf 7 gesetzt (warum auch immer) -
ohne pwm_cnt zurückzusetzen, das immer noch auf 8 steht, und welches im
folgenden fröhlich weiter erhöht wird, weil pwm_cnt == pwm_cnt_max erst
wieder wahr wird, wenn der Zähler einmal "rum" ist.
An der Stelle habe ich dann aufgehört. Die Grundidee des Codes mag ja
brauchbar sein, aber was da steht, ist Schrott. Da musst du noch einiges
an Gehirnschmalz reinstecken, und direkt selbsterklärend ist der Code
auch nicht.
Viel Spaß (!)
Oliver
@ Oliver
Danke für die mühe die du dir gemacht hast. Schade eigentlich. Es war
wieder "zu gut um war zu sein :D". Ich werde noch ein wenig damit
rumprobieren (wie gesagt, wen ich pwm_cnt mit anderen wert als 0
initialisiere, geht es). Und wen es nicht so geht, nehme ich ein 2ten
contr. die kosten ja nix, die 3,50 für´n Atmega16 sind ein Witz. Und
trotz dem, bin ich der meinung, dass wen so ein artikel veröfentlicht
wird, dann müste auch, das was da drin steht funktionieren, denke ich.
MFG
>Und trotz dem, bin ich der meinung, dass wen so ein artikel veröfentlicht>wird, dann müste auch, das was da drin steht funktionieren, denke ich.
Denke ich auch. Tut es aber nicht. So what.
>(wie gesagt, wen ich pwm_cnt mit anderen wert als 0>initialisiere, geht es).
Nun ja, irgendwie zappeln die Ausgänge, aber ob das das gewollte Ergenis
ist, bezweifele ich mal.
Ganz aufgeben würde ich das Thema aber nicht - die Fehler im Programm
lassen sich sicherlich beheben.
Oliver
@ Rudolf Bremer (Gast)
>Hat jemand im forum schon mall das erwehnte Program benutzt oder, weiss>jemand wer das geschrieben hat? Das Program ist zwar etwas gross aber,
Ich kenn den Autor. Seh ich jede Tag im Spiegel ;-)
>jetzt gesehen habe und, es wäre traurig wenn es nicht geht. Hat denn
Nun, ich hab sie nur im AVR-Studio getestet, kann aber nciht wirklich
nachvollziehen, warum die nicht gehen soll. Was aber möglicherweise das
problem ist, dass pwm_cnt == pwm_cnt_max nie rreicht wird, wenn nach dem
Programmstart bei de Variablen auf Null stehen. Also pwm_cnt_max einfach
mal auf 1 setzen.
@ Oliver (Gast)
>So, wie das das steht, ist das Programm noch nie gelaufen.
Nur im AVR-Studio Simulator.
>while-Problem, das daran liegt, daß pwm_cnt_max zu Beginn nirgends>initialisiert wird, und deshalb das erste
Doch sind globale/statische Variablen. Aber man sollte pwm_cnt_max
tatsächlich anders initialisieren ;-)
>die Hose. pwm_cnt_max wird zunächst auf 8 gesetzt, wenn dann pwn_cnt bis
Wo?
>8 hochgezählt hat, wird pwm_cnt_max auf 7 gesetzt (warum auch immer) ->ohne pwm_cnt zurückzusetzen, das immer noch auf 8 steht, und welches im>folgenden fröhlich weiter erhöht wird, weil pwm_cnt == pwm_cnt_max erst>wieder wahr wird, wenn der Zähler einmal "rum" ist.
Nein. pwm_cnt_max wird nur von pwm_update() geschrieben.
>brauchbar sein, aber was da steht, ist Schrott. Da musst du noch einiges
Nanana, das hab ich mal jetzt nicht gehört.
>an Gehirnschmalz reinstecken, und direkt selbsterklärend ist der Code>auch nicht.
Soll er auch gar nicht bzw. ist wohl ein wenig viel verlangt.
MfG
Falk
>So, wie das das steht, ist das Programm noch nie gelaufen.>Nur im AVR-Studio Simulator.>Einfach in der Definition von pwm_cnt_max>eine =1 eintragen und schon läuft alles bestens.
:-)
Oliver
>pwm_cnt_max wird nur von pwm_update() geschrieben.
Richtig.
1
memcpy(pwm_setting,t1,8);
2
pwm_update();
setzt pwm_cnt_max am Ende von pwm_update()auf 8. Soweit, sogut.
Das darauffolgende
1
memcpy(pwm_setting,t2,8);
2
pwm_update();
setzt pwm_cnt_max am Ende von pwm_update()auf 7, wahrscheinlich, weil in
t2 ein Wert 0 ist. Da pwm_cnt aber noch auf 8 steht, zählt das jetzt
einmal rum. Das mag zwar in Echtzeit gar nicht auffallen, ist aber so.
Über
1
OCR1A+=pwm_timing[pwm_cnt];
wird OCR1A x-mal mit Speicherschrott geladen (der allerdings die meiste
Zeit aus Nullen besteht), und die ISR entsprechend erratisch aufgerufen.
Ist pwm_cnt einmal rum, gehts wieder richtig weiter.
Ok, das mit dem (Voll-)Schrott nehme ich zurück, aber das solltest du
dir nochmals ansehen.
Oliver
@ Oliver (Gast)
>Ok, das mit dem (Voll-)Schrott nehme ich zurück, aber das solltest du>dir nochmals ansehen.
Akzeptiert. Danke für die Kritik und Fehlersuche.
MfG
Falk
OK, der Quelltext im Artikel Soft-PWM ist korrigiert. Er läuft jetzt
sauber an und schaltet auch sauber zwischen verschiedenen Einstellungen
um.
In realer Hardware getestet.
MFG
Falk
geht irgendwas schief.
min wird in der inneren while-Schleife mehrfach dekrementiert, und da es
ein unsigned ist, geht es da leider durch Null nach 255. Das fällt
wieder beim zweiten Datensatz auf.
Ich habs zwar nicht 100% verstanden, aber vermutlich ist
1
// Datensatz entfernen
2
for(j=i;j<k;j++)
falsch, und müsste
1
// Datensatz entfernen
2
for(j=i;j<=k;j++)
heissen.
Mit der Änderung läuft es auch in vmlab ohne Fehler.
Oliver
@ Oliver (Gast)
>min wird in der inneren while-Schleife mehrfach dekrementiert, und da es
Das soll auch so sein. Mit welchem Testdatensatz hast du das
festgestellt?
>Ich habs zwar nicht 100% verstanden, aber vermutlich ist>// Datensatz entfernen>for(j=i; j<k; j++)>falsch, und müsste
Nein, das ist OK. k startet mit 8. Wenn der erste Datensatz entfernt
wird, werden die Indizes 2..8 auf 1..7 kopiert, also j<k. Danach wird
Index 8 gelöscht. min sollte immer genau acht mal decrementiert werden.
Hast du den Code komplett neu übernommen? Das musst du, da ich einiges
geändert habe.
MfG
Falk
for(j=i;j<k;j++)// k=7, die Schleife läuft bis 6, j ist danach 7. Das ist ein Durchlauf zu wenig
8
{
9
pwm_setting_tmp[j]=pwm_setting_tmp[j+1];
10
pwm_mask_tmp[j]=pwm_mask_tmp[j+1];
11
}
12
pwm_setting_tmp[j]=0;// hier wird jetzt pwm_setting_tmp[7] auf 0 gesetzt, es müsste aber pwm_setting_tmp[8] sein.
Der Underrun von min passiert dann später, wenn i = 7 ist, und
pwm_setting_tmp[7]==0 wahr ist. Dann läuft die while-Schleife so lange
durch, bis k=0 ist, und dekrementiert dabei min ein paar mal zu oft.
Oliver
@ Oliver (Gast)
Deine Version sieht gut aus. Aber was macht sie wenn alle PWM Werte Null
sind?
Dann wird der letzte Datensatz nicht gelöscht, weil (pwm_cnt_max_tmp >i)
nicht erfüllt ist.
MFG
Falk
pwm_cnt_max_tmp >i wird immer erfüllt :-)
Wenn alle Werte 0 sind, bleibt i auf 1, und pwm_cnt_max_tmp zählt runter
bis 1. Dann endet die while-Schleife.
Dein alter Code
if(pwm_setting_tmp[0]==0)pwm_timing_tmp[0]=(uint16_t)T_PWM;// Sonderfall, wenn alle Kanäle 0 sind
ist damit wunderbar klar gekommen. Den neuen hab ich nicht probiert.
>#define PWM_PRESCALER 8 // Vorteiler für den Timer
:-)
Mit der alten Version (mit Precaler =1) gab es ein paar nette Effekte,
aber so gehts besser.
Eine letzte Kleinigkeit gibts noch: 255 als Wert funktioniert nicht. Da
kommt die Sortierroutine durcheinander.
Oliver
@ Oliver (Gast)
>Eine letzte Kleinigkeit gibts noch: 255 als Wert funktioniert nicht. Da>kommt die Sortierroutine durcheinander.
Kann ich nicht feststellen? Mit welchem Datensatz?
MfG
Falk
@ Oliver (Gast)
Eieiei, das ist wirklich kniffelig. Naja, aber jetzt sollte es OK
sein. Ich hab auch deine Idee mit der etwas verbesserten Aufräumroutine
eingebaut. Spart eine lokale Variable uund ist übersichtlicher.
MfG
Falk
Ich bekomm den intelligenten Ansatz einfach nicht zum Laufen. Alles, was
in VMLAB passiert, ist dass die Pegel alle auf Null gehen, da bleiben
sie dann aber auch die restliche Zeit. VMLAB gibt folgende Warnungen
aus, ich glaub
1
[PC = $0316, Time = 0.37 ms, {MEM}]: Indexed write to a I/O space register? Address = $0037 (PWM_DDR = 0xFF;)
2
[PC = $031A, Time = 0.37 ms, {MEM}]: Indexed write to a I/O space register? Address = $004E (TCCR1B = 2;)
3
[PC = $02AD, Time = 65.92 ms, {MEM}]: Indexed read from a I/O space register? Address = $004A (OCR1A += isr_ptr_time[pwm_cnt];)
Vielleicht steh ich ja grad gewaltig aufm Schlauch, aber bisher hat
eigentlich immer alles geklappt, was ich getestet hab.
In AVR Studio will das Programm auch nicht so recht, es ist sau langsam,
aber immerhin gehen die Ports an und aus. Auf einem echten uC passiert
dagegen gar nichts :-(
Christian