PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Timer und PWM



tornado
16.02.2007, 17:12
Hallo.

Ich wollte jetzt als nächstes einen Timer für meinen PIC16F876 @ 10MHz machen um ein PWM Signal zu erzeugen um dann zwei Servos anzuschliessen.
Ich programiere in C und würde den Timer0 nehmen, der laut Sprut ein 8-Bit Timer ist und von 0 bis 255 zählen kann.

Im Internet habe ich verschiedene Code Fragmente oder Formeln gefunden, weiss aber nicht ob und wie ich sie verwenden kann.

Das erste wäre:

time = 1/frequenz * 4 * Timerwert * vorteiler

Jetzt habe ich aber auf einer anderen Seite diesen Code gefunden:




// Für Taktfrequenz des PIC = 4 MHz

void Delay1ms( uns16 ms) //verstehe nicht warum "16ms". Ist nur ein Name,
//da kann ich schreiben was ich will, oder?

{
while(ms) // Schleife verlassen wenn ms=0 ist
{
OPTION = 2; // Vorteiler auf 8 einstellen
OK, die Tabelle mit den Vorteilern habe ich
im Datenblatt auf Seite 47-48 vom PIC gefunden und bis da hin
vertehe ich es auch.


TMR0 = 131; // 125 * 8 = 1000 (= 1 ms)
while (TMR0); // abwarten einer Milisekunde
ms--; // "ms" mit jeder Milisekunde erniedrigen
}
}



Wenn ich die Formel von oben benutze um TMR0 auszurechnen kommt bei mir was anderes raus:
1/4M * 4 * 125 *8 = 1ms TMR= ist also 125 und nicht 131
Der Unterschied ist vieleicht nicht wichtig, aber ich verstehe nicht wie man auf 131 kommt.

Nächster Code:




//Für Taktfrequenz des PIC = 20 MHz
//Timerauflösung=13us
//1ms/13us=77
//delay<=255 !!
//Nicht sonderlich genau !!
void Delay1ms( uns16 time)
{
char next = 0;
OPTION = 6; // prescaler divide TMR0 rate by 128
TMR0 = 0; //
do {
next += 39; // 4992=128*39 ,4992+15=5007/5=1,0014ms
clrwdt(); // needed only if watchdog is enabled
while (TMR0 != next) // 125 * 8 = 1000 (= 1 ms)
;
} while ( -- time != 0);
}



Laut Formel:
1/20MHz *4 * 39 * 128 = 1ms Hm... OK, dann stimmt die Formel ja.

Wenn ich bei meinem PIC jetzt einen 10MHz Quarz benutze, gilt dann für mich:

1/10MHz * 4 * 20 *128 = 1ms ?

Falls das so stimmt, könnte der timer in C dann so sein?
Wie ihr seht habe ich den Code von oben genommen und verändert.



{
void Delay_1ms( uns16 ms)
while(ms)
{
OPTION = 6;
TMR0=20;
while (TMR0);
ms--;
}
}



Muss ich sonnst noch etwas in den Code schreiben oder könnte ich das so in den PIC brennen?

Wenn das funktioniert, habe ich dann noch eine Frage zum PWM zur Servo Steuerung, aber das später.

Viele GrÜsse,
Tornado

Mobius
16.02.2007, 17:49
Also, zu "uns16 ms". Das ist einfach eine unsigned (also ohne vorzeichen) Variable vom Typ Integer (von 0 bis 65535) mit dem Namen ms. Die Funktion lässt dein PIC die durch diese Zahl angegebenen Millisekunden schlafen.


1/4M * 4 * 125 *8 = 1ms TMR= ist also 125 und nicht 131 Nein, vorsicht, die 125 sind die Schritte die der Timer zählen muss, bis er überfläuft. Also muss er am Anfang auf den Wert 256-125=131 gesetzt werden, damit er 125 Schritt bis zu einem Überlauf braucht.

Und nein, dein Code geht wieder um einiges zu langsam, den du hast dir die Anzahl der Schritte errechnet. Der Startwert des Timers würde dann 256-20=236 sein.
MfG
Mobius

tornado
17.02.2007, 10:26
Achso. Dann ist "Timerwert" also der Anfangswert um ab da x Schritte zu zählen (in meinem Fall 20).

Wenn ich dich richtig verstehe müsste das dann so aussehen:
1/10MHz * 4 * 236 *128 aber da kommen ja 12ms raus.

Dann halt so:
1000/128=7.8 also 8
256-8=248
1/10MHz * 4 * 248 * 128 = 12m Hmm.. das selbe wie eben. Ändern sich nur Zahlen hinter dem Komma.

Dann verstehe ich das doch nicht. Ich wollte doch 1ms und nicht 12ms.
Was mache ich falsch und wie mache ich es richtig?
Grüsse,
Tornado

kalledom
17.02.2007, 15:27
Ein Timer setzt immer dann sein (Interrupt-) Flag, wenn ein Überlauf von 255 nach 256 = 0 stattfindet (256 gibt es bei 8 Bit ja nicht).
Der Quarz hat eine Frequenz-Angabe, die in Zeit umgewandelt werden muß; das hast Du mit 1 / 1MHz schon gemacht; das ergibt 1 µs ! oder 0,001 ms !
Wenn Du als Beispiel einen Teiler 1 : 4 wählst, mit dem Du dann auf 0,004ms kommst, dann muß ein Zähler 250 mal diese 0,004ms zählen, damit sich 1ms ergibt (0,004 * 250 = 1). Genau das macht das Timer-Register. Du legst mit dem Teiler eine 'Schritt-Zeit' für den Timer fest und setzt im Timer die Anzahl der Schritte.
Allerdings wird dort nicht 250 eingesetzt, weil der Timer nicht runter zählt; er zählt rauf. Dann wäre der oben beschriebene Überlauf nach 6 Schritten (250 ... 256 = 6. Also mußt Du in den Timer die Zahl 6 reinschreiben (256 - 250 = 6), dann macht er genau 250 Schritte bis zum Überlauf bei 256, was 1ms entspricht.
Den Timer-Wert mußt Du dann wieder neu eintragen, sofern es nicht ein Reload-Register dafür gibt.

Edit: Hat noch keiner bemerkt, daß 10MHz keine 0,1µs = 0,001ms sind ?
Dann korrigiere ich mal den Quarz von 10MHz auf 1MHz (dann brauche ich nur den µs-Wert ändern).

tornado
18.02.2007, 17:37
Ich habe mir eure Antworten noch mal gründlich durchgelesen.
Ich hoffe, dass ich es jetzt kapiert habe. Mal sehen...

Für meine 10MHz müsste ja das hier gelten:
1/10M= 0,0000001

0.0000001 = 0.0001m

0.0001m * 128(Vorteiler) = 0.0128m

0.0128m*78=1m Ich brauche 78 Schritte a 0.0128m um auf 1m zu kommen

256-78= 178 Dann muss ich ab 178 zählen.



{
void Delay_1ms(uns16 ms)
while(ms)
{
OPTION=6; // Vorteiler auf 128
TMRO=178; // auf 178 um 78 Schritte zu zählen
while (TMR0);
ms--;
}
}

Ist das so richtig?

Was ist denn dann mit der "4" aus dieser Formel?
time = 1/frequenz * 4 * Timerwert * Vorteiler

kalledom
19.02.2007, 01:00
Auha, jetzt gibt es ein Problem; ich bin von Assembler ausgegangen (siehe Beispiele hier (http://www.domnick-elektronik.de/picasm.htm) und hier (http://www.domnick-elektronik.de/picpwm.htm) ).
Was bei C gemacht werden muß und was dort schon berücksichtigt wird, kann ich Dir leider nicht sagen. Ich mag kein C und kenne deshalb auch nicht die Bedeutung Deines Listings, zumindest nicht alles.

PICture
19.02.2007, 03:03
Hallo tornado!

Ich kenne C nicht und schreibe meine Programme für PIC´s nur im ASM.

Deswegen versuche das mit einem 16-bitigen Timer kurz und programmiersprachlos zu erklären. :)

Die Frequenz, die ein Timer zählt Fp ist gleich der Taktfrequenz F (Quarzt, oder änliches) geteilt durch Preskalerwert P (1,2,4,... u.s.w.), also Fp = F / P.

Die impulsenzahl, nach der er ein interrupt durch gesetztes interrupt flag signaliesiert beträgt (FFFF h + 1) - (TMRXH, TMRXL), das heisst 10000 h - Timerwert, wobei der Timerwert ist eine 16-bitige Hexzahl die in die Timerregister TMRXH und TMRXL vor dessen Start eingeschriben wird.

Die Zeit die vom Start des Timers bis zum von ihm ausgelösten interrupt vergeht beträgt T = 1/Fp * (10000 h - Timerwert).

MfG

Benji
19.02.2007, 12:50
Was ist denn dann mit der "4" aus dieser Formel?

Die interne Frequenz des PIC ist gleich der Frequenz des Quarz dividiert durch 4.
So stimmen auch deine Berechnungen nicht:

time = 1/f(quarz) * 4 * Presc * Zählwert
1ms = 1/10MHz * 4 * 16 * 156

==> Prescaler = 16
==> Zählwert = 156 (Einstellen musst du 256-156 = 100)

Jedes mal wenn das Interrupt ausgeführt wird, wird die Interruptfunktion ausgeführt.

Die Syntax würde in etwa so aussehen:


#INT_TIMER0
deine_funktion() {
; deine Befehle
}

PICture
20.02.2007, 06:24
Hallo Benji!

Du hast recht, der Systemtakt ist nicht gleich der Quarzfrequenz.

Bei meinem Beitrag muss man also als F=Fq/4 nehmen, wo Fq Quartzfrequenz ist.

Vielen Dank ! :)

MfG

tornado
20.02.2007, 18:46
Mittlerweile bin ich etwas durcheinander.
Ich werde mir morgen alles noch mal ganz in ruhe durchlesen.

tornado
22.02.2007, 15:45
Ich habe jetzt noch etwas rumprobiert und dank eurer Hilfe funktioniert der Timer jetzt!!!


In meinem C Programm rufe ich mit "pause_1(2000); //2 Sekunden pause " folgenden Code der oberhalb von void main{} steht




//timer
int ms;
void pause_1(ms)
{
while(ms){ // Schleife verlassen wenn ms=0 ist
OPTION = 3; // Vorteiler auf 16 einstellen
TMR0 = 100; // 1/10M *4 *16*x x=156 für 1ms
//256-156=100
while (TMR0); // abwarten einer Milisekunde
ms--; // "ms"-1
}
};//timer


Zum testen habe ich 2 LEDs zum leuchten gebracht und in 30 Sekunden Abständen ( pause_1(30000) ) mit meiner alten Stopuhr gemessen. Ich weiss, dass das verglichen mit dem PIC sehr ungenau ist (wenn man in ms rechnet). Es war nur um grob zu wissen ob das Ergebnis möglich ist oder total falsch. Zwichen PIC und Stopuhr ist ein Unterschied von rund 2/10 Sekunden. Wenn man meine Reaktionszeit und die genauigkeit der Stopuhr berücksichtig, sollte so weit alles in Ordnung sein. Was genaueres zum messen habe ich hier nicht aber ich will ja mit meinem PIC der Atomuhr auch keine Konkurenz machen ;-)

Da der Timer jetzt geschafft ist, würde ich gerne auch ein oder zwei Servos (PWM) ansteuen können.

So viel wie ich weiss, müsste ein Servo bei 1,5 ms stehen bleiben und bei 2ms sich drehen.
Reicht es dann wenn ich die Servos an + und - anschliesse und das dritte Kabel an einen Pin den ich dann mit meinem timer(1,5
oder 2ms) immer auf High und Low setze?

Grüsse,
Tornado

Mobius
23.02.2007, 14:38
Hum, also ein Servosignal ist schon ein wenig raffinierter ;). Also, schon richtig, + kommt an VCC (5V gehen, nur ist der Servo da net am stärksten, glaub dass die ne Versorgung von so 6V haben wollen, aber net schlagen, wenn es abraucht :) ), - an GND und die Signal-Leitung (meist die weiße) an einen Ausgang des PICs.

Das war also der Anschluss. Die Ansteuerung erfolg durch Frequenz mit einer Periodenlänge von 20ms. Von diesem sind 1 - 2ms High, der rest Low. Die High-Time bestimmt, _welche_ Position der Servo anfahren soll. Also bei 1ms fährt er auf die rechte Seite, bei 1,5ms bleibt er genau mittig stehen und bei 2ms schlägt er nach links aus. Je höher nun du die Auflösung von diesem 2ms-Pulsen gestalten kannst, desto genauer kannst den den Servo in die richtige Positon steuern. Also wirst du mit deinem TImer, der, wenn ich es richtig sehe, gerade nur ms-Auflösung kann den Sero in zwei Positionen ansteuern können (ganz links oder ganz rechts). Würdest du nun, sagen wir 500µs Auflösung haben, könntest du 3 Positionen anfahren, bei 250µs 5, bei 125µs 7, etc.
(Quelle: erkenntnishorizont.de (http://www.erkenntnishorizont.de/robotik/effektoren/servos.c.php?screen=800)).

MfG
Mobius

tornado
23.02.2007, 17:49
Ich werde dann mal versuchen, dass sich dieses Servo erst mal nur nach rechts und links dreht und wenn dass klappt werde ich den Timer so verändern, dass ich verschiedene Positionen anfahren kann (Geschwindigkeiten)

tornado
24.02.2007, 15:12
Hmm... der erste Versuch ist wohl gescheitert...
Ich habe nur mal versucht mit meinem Timer einen Pin 2ms auf High und 18 auf Low zu setzen. Alles mit 5V (Auf einer INternetseite stand, dass Servos mit 5V betrieben werden können).
Es hat sich aber nichts getan. Beim anschliessen macht der Motor einen kleinen "Sprung" und das war es dann auch schon.

Hat jemand noch einen Tip? Kennt ihr eine Internetseite wo das mit Beispielen in C erklärt ist? RN Wissen habe ich mir schon durchgelesen, da sind aber leider keine Beispiele.

tornado
26.02.2007, 11:12
Wie steuert Ihr denn eure Servos an?
Ich verstehe nicht warum das bei mir nicht funktioniert.

tornado
03.03.2007, 16:34
Kann mir wirklich keiner sagen, wie ihr eure Servos ansteuert?
Vieleicht ist meine Frage etwas blöd, aber es funktioniert wirklich bei mir nicht.
WÄre über hilfe wirklich sehr dankbar.
Tornado

Mobius
03.03.2007, 20:51
didum, didum...

ka, wieso dieses Fragment gepostet wurde, also bitte löschen, wenn es keine Umstände macht...

:)
Mobius

Mobius
03.03.2007, 21:00
shit... Sorry, das ganze Subforum wurd bei mir als "kein neues Thema" anzegeit, sodass ich deine beiden Posts irgendwie voll +bersehen habe.

Also, zur Ansteuerung, hast du ein Osci um zumindest eine grobe Idee zu bekommen, wasür ein Signal du aussendest? Ohne dem wird die Fehlersuche ein wenig schwierig.
Zur Ansteuerung, wenn du dem Servo ein 2 ms high, 18 ms low schickst sagst du ihm, dass er voll in die eine Richtung ausschlagen soll. Wenn nun die Periodendauern nicht ganz stimmen, sagen wir 2,7 oder ,28 ms, dann wird er dem Befehl versuchen "treu" zu folgen und das ist das characteristische Knacken des Servos, wenn er versucht weiter als den mechanisch möglichen Weg zu drehen (es exisitiert ein kleines Plastikstösel in dem Zahnrad, der ein überdrehen verhindert). Versuch es einmal mit 1 ms 19 ms (also Mittelstellung) und dreh vor dem Anschließen den Servo bis zu einem Anschlag auf die eine Seite. Wenn er dann nach dem Anschließen an den PIC fast in die Mittelstellung zurückfährt, dann funktioniert dein Code halbwegs.

Wenn du interesse am Sourcecode hast, ich hab eines (nur Assembler), welches die Ansteuerung von theoreitsch bis zu 9 Servos wenn ich mich richtig erinnere (nicht 10, da ich noch ein wenig rechenzeit brauche und sich deswegen der 10. nicht mehr ganz ausgehen würde).
Dieser benutzt aber 2 Timer (also sehr verschwenderisch ;)) und ist für einen 16F877(A) geschrieben. Die Ansteuerung hat also nur einen eher darstellenden Wert, dass man es auch so lösen kann :).
Auch kann ich dir auszüge aus dem Protokoll von der Laborübung zur Verfügung stellen, in dem die grundlegende Logik hinter der Schaltung erläutert wird, wenn ich sie in meinen Archiven wiederfinde.
MfG
Mobius

tornado
05.03.2007, 17:16
@Mobius
Die Servos die ich habe sind schon gehackt und funktionieren (bei einem anderen Bot). Den alten Bot habe ich in der Uni gemacht, und die haben uns nur gesagt was wir eintippen müssen ohne zu erklären was wir eigendlich machen. Mein neuer Bot (den ich von 0 aufbaue um richtig zu lernen wie das funktioniert) hat aber einen anderen Quwartz. Daher kann ich die Formel die ich damals benutzt habe nicht gebrauchen.
Lange Rede, kurzer Sinn...
Das Problem ist die Programierung (in C).
Ach ja, und ein Osci habe ich nicht :-(
Grüsse,
Tornado

tornado
18.03.2007, 14:58
Es hat zwar etwas gedauert, aber ich habe jetzt einen Servo zum laufen gebracht.

Diese 1ms Auflösung war wohl nicht genau genug. Mit einem Timer mit 50us geht es jetzt.
Ich kann ein Servo jetzt vor- und rückwÄrts drehen lassen.
Zum stehen kann ich es aber noch nicht bringen. Wenn er eigendlich stehen müsste, fährt er rückwärts.

Muss ich beim Servo anschliessen noch etwas beachten? Ich habe den Servo einfach an einen Ausgang von Port A gehangen. Dazwichen ist noch ein 270Ohm Wiederstand (weil ich dort vorher eine LED angesteuert hatte).

Also, der Servo dreht. Aber es gibt ein Problem...
der Pic wird heiss, sehr heiss. Ich habe mir eben die Pfoten verbrannt.
Löschen oder umprogramieren kann ich den auch nicht mehr. Der scheint hinüber zu sein. Wie habe ich das hingekriegt? Das würde ich gerne wissen, um nicht noch einen PIC zu verbrennen.

Hier kommt der fertige Code:


#include <pic1687x.h>
#define PORTBIT(adr, bit) ((unsigned)(&adr)*8+(bit))
//-------------------------------------------------------------------------

int cont=0;

//-------------------------------------------------------------
void initial(){
ADCON1=6; // PortA Pins als digital definiert
TRISA = 0x00; //Port A = Ausgang mit LEDs
TRISB = 0b00011111; //RB0 - RB4 = Eingänge, RB5 - RB7 =
//Ausgänge
//RB0 brauchen wir im Moment nicht
PORTA = 0b00000000;
PORTB = 0b00011110; // RB5-RB7 auf Low und RB1-RB4 auf High
//RB1 - RB4 auch mit Pull-up Wiederständen auf High
}//initial

//timer us=50us
int us;
void pause_1(us){
while(us){ // Schleife verlassen wenn ms=0 ist
OPTION = 3; // Vorteiler auf 16 einstellen
// 1/10M * 4 * 16 * x x=8 für 50us
TMR0=248; //256-8=248
while (TMR0); // abwarten von 50us
us--; // "50us"-1
}
};//timer

void job (){ // Servo fährt vorwärts
if(cont==0){
PORTA = 0b00000001;
pause_1(20); //timer 20 *50us = 1ms
cont=1;
}
else{
PORTA = 0b00000010;
pause_1(380); // 380 * 50us = 19ms
cont=0;
}
}//job
//-------------------------------------------------------

void main (void)
{
initial();

do{
job();

}while (1); //immer wiederholen

}//()





Viele Grüsse,
Tornado