PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Freuqenz erzeugen



hagbart06
16.11.2009, 18:31
Hallo,

möchte mit einem PIC (16F628A bzw 12F675) eine Frequenz an einem PIN erzeugen. Sie soll von ca. 20 Hz bis 1,6 kHz in 10 Hz Schritten mittels zweier Taster einstellbar sein. Eine Frequenz zu erzeugen war auch nicht das Problem, nur ist es so das es keine Lineare Funktion ist. Es lassen sich die niedrigen Werte sehr genau, die hohen Werte sehr ungenau einstellen.

Um das zu verdeutlichen hab ich eine Wertetabelle im Anhang. Die Werte passen zwar nicht mehr zu dem Programm, verdeutlichen das Problem aber.

Hier noch das Programm:


#pragma config |= 0b.0011.0101.0000.0010 //Prozessor Konfiguration
void pause1(uns16 ms)
{
while(ms)
{
OPTION = 2;
TMR0 = 131;
while (TMR0);
ms--;
}
}
void pause(void)
{
OPTION = 2;
TMR0 = 131;
while (TMR0);
}
void main(void) // Hier beginnt das Hauptprogramm
{
TRISA = 0b.0000.0000;
TRISB = 0b.1111.1111;
bit Plus @ PORTB.1 ; // Pin RB1 erhält Name "Plus"
bit Minus @ PORTB.2 ; // Pin RB2 erhält Name "Minus"
char w; // W für Tasterentprellung
unsigned int Zahler; // Zähler für Taster
Zahler=100; // Zähler-Startwert = 100
Sprungmarke:
if (Plus) // Wenn "Plus"-Taster gedrückt ist, dann
{
Zahler = Zahler + 10; // Zähler + 10

}
for (w=0;w<50;w++)
{
pause(); // Hier wird zum Entprellen gewartet
}
if (Minus) // Wenn "Minus"-Taster gedrückt ist, dann
{
Zahler = Zahler - 10; // Zähler - 10

}
for (w=0;w<50;w++)
{
pause(); // Hier wird zum Entprellen gewartet
}
PORTA.0 = 1; // PIN wird high
pause1(Zahler); // Warte durch Zähler definierte Zeit
PORTA.0 = 0; // PIN wird low
pause1(Zahler); // Warte durch Zähler definierte Zeit
goto Sprungmarke;
}


Ich hatte auch schon versucht das ganze mittels Switch und case umzusetzen, also jedem Wert eine Frequenz zuzuordnen. Das hat auch funktioniert allerdings wird das Programm dadurch natürlich viel zu groß. Deshalb hab es es dann nach 10 Werten gelassen, abgesehen davon wird es bei den hohen Frequenzen auch wieder unmöglich die 10er Schritte beizubehalten. Wenn jemand noch eine andere Idee hat, wie man es hinbekomm eine Frequenz zu erzeugen die man in 10er Schritten hoch bzw. runter stellen kann wäre ihm sehr dankbar.

Gruß
Kevin

Siro
16.11.2009, 20:45
Das Hauptproblem liegt darin, daß Du die Zeiten nicht berücksichtigst, welche für die eigentlichen Programmzeilen benötigt werden.
Der Timer selbst läuft schon absolut exakt.
Das ist bei tiefen Freuqenzen nicht ganz so tragisch, je höher aber deine Frequenz wird, desto mehr
geht diese zusätzliche Zeit mit ein, bzw. macht sich negativ bemerkbar.
Zwischen deiner "Sprungmarke" und dem Setzen des Ports auf Low, liegt ja deine Tastenabfrage, während dieser
Zeit ist der Portpin weiterhin auf LOW. Während deiner Entprellzeiten ebenfalls.


Um eine exakte Frequenz zu erzeugen gäbe es folgende Möglichkeit.
Ich hoffe, daß ich es nicht zu schwierig erkläre, weil das ist nicht ganz so einfach zu verstehen.
mit dem PIC16F628:

Der RB3/CCP1 Pin ist dein Ausgang für die Frequenz.
Timer 1 wird ganz normale als aufwärtszähler benutzt. Also von 0..65535 mit internem Takt
Das Capture Compare Register CCP1L sowie CCP1H wird als Vergleichsregister benutzt,
also der "Compare Mode" hier muss ein Wert rein, welcher irgendwie deiner Frequenz entspricht.
Wenn der Timer 1 und das Vergleichsregister übereinstimmen, wird etwas ausgelöst.
Hier gibt es einen sogenannten "Special Event Trigger"
Der kann dann den PIN RB3 automatisch entweder auf High oder auf Low setzen.
Das Timer Register wird dann automatisch wieder gelöscht und der Timer fängt wieder an von 0 zu zählen.
So bekommst Du ein einwandfreies Timing. Wenn dann deine Tasten betätigt werden, muss der Wert in den
Registern CCP1L und CCP1H verändert werden.

Prinzipeller Ablauf:
Timer 1 wird auf Asynchron Counter Mode gesetzt.
Du setzt im CCP1M Register den Wert so, daß bei einer Übereinstimmung der Ausgangspin RB3/CCP1 auf High gesetzt wird.
also CCPM BITS = 1000
Wenn ein Interrupt ausgelöst wird, stellt sich der Timer automatisch wieder auf 0 zurück, darum brauchst Du Dich nicht kümmern.
nun stellst Du in aller Ruhe das CCP1M Register so ein, daß bei der nächsten Übereinstimmung der Ausgangspin RB3/CCP1
auf Low geschaltet wird. Also CCPM Bits = 1001
Wenn der nächste Interrupt kommt, stellst Du die CCPM Bits wieder auf 1000 wie am Anfang.
So bekommst Du ein "einwandfreies Timing" für deinen Pin.

Info: Das geht nur mit Timer 1.
Der Inhalt des Zählerregisters vom Timer 1 ist in diesem Falle völlig unwichtig.
In den Registern CCP1L und CCP1H muss der Vergleichswert stehen, wann der PIN umschalten soll.
Also im Prinzip die halbe Frequenz, da der Pin mit jedem Ablauf des Timers umgeschaltet wird.

nicht einfach, gebe ich zu.
ich hoffe, ich konnte Dir damit etwas helfen.

mfg Siro

hagbart06
17.11.2009, 13:59
Danke erstmal für die Antwort.
Das Prinzip habe ich verstanden, allerdings hackts an der Umsetzung. Habe von Assembler Null Ahnung und mit C kann man nicht in Register schreiben, oder? (außer Inline Assembler).

Siro
17.11.2009, 15:29
Hallo Kevin,

natürlich kannst Du in "C" auch in die Register schreiben, das hast Du doch schon getan:

TMR0 = 131;
TRISA = 0b.0000.0000
PORTA.0 = 0;

das sind alles direkte Registerzugriffe.

Das Problem bei mir liegt darin, daß ich noch nie die PICs in "C" programmiert habe. Gibts denn da überhaupt einen "Free C Compiler ?"

mfg. Siro

hagbart06
17.11.2009, 18:22
natürlich kannst Du in "C" auch in die Register schreiben, das hast Du doch schon getan:


](*,) , Alles klar, Danke!



Das Problem bei mir liegt darin, daß ich noch nie die PICs in "C" programmiert habe. Gibts denn da überhaupt einen "Free C Compiler ?"


Jo, es gibt cc5x, die kostenlose Version erlaubt zwar nur bis 1k Code, dass kann man aber legal umgehen. http://cc5x.de/

Gruß Kevin