Hallo Ich will einen langsamen Sinus erzeugen. Ich habe mir überlegt, das ganze mit PWM zu realisieren. Die Frequenz soll bei max. 100Hz liegen. Bei klassischem PWM, bei dem der Sinus mittels Filtern aus einem Rechteck erzeugt wird, wäre das Signal nicht schön und es werden große Filter benötigt. Daher habe ich mir überlegt, den Sinus mit einem deutlich schnelleren PWM Signal zu erzeugen, bei dem laufend das on/off Verhältnis verstellt wird. Die Ausgabe würde dann z.B. so aussehen: high: X X X X X X X X X X X X low:_____X____X__X_X[...] In diesem Fall würde ein kleiner Tiefpass als Integrator ausreichen, um ein schönes Signal zu erzeugen: high: -- --- ----- low:------ Die PWM wollte ich mit einem kleinen AVR erzeugen, bin da allerdings noch nicht so ganz fit. Als Controller verwende ich einen ATTiny462. Gesteuert wird die PWM über Timer0 und seine beiden Compare Match Register. In CompareMatchA ist dabei die Periode gespeichert. Zu beginn einer Periode wird der Pin auf low gelegt. In CompareMatchB wird dann der Schaltzeitpunkt geladen, zu dem der Pin auf high gelegt werden soll. Nach Ablauf der Periode wird der Pin wieder zurück auf low gelegt. Im AVR Studio Simulator funktioniert alles wie gewollt. Leider tut das nicht in der Realität. Verwende ich in der Tabelle durchgehend den selben Wert, z.B. 0x50, stimmt alles. Im nächsten Schritt wollte ich jeden 2. Wert der Tabelle mit 0x10 ersetzen. Ich hätte erwartet, einen Rechteck mit abwechselnd langer und kurzer off-Zeit zu erhalten. Statt dessen erhalte ich das ERgebnis, dass ich erwartet hätte, wenn in der Tabelle 16x 0x10 eingetragen wäre. Ich bin jetzt ein wenig ratlos. Habe ich etwas nicht bedacht? Die Periode für die IRQs sollte bei einem Prescaler von 8 ausreichen oder? Wäre das etwas für die Codesammlung wenn es läuft? Viele Grüße, Tilo
Deine Verwendung des Timers ist ....... unüblich. Allerdings sollte es funktionieren ich seh aber im Code das Problem nicht, warum es nicht geht. Kannst du eine Fehlinterpretation deiner Messung bzw. überhaupt eine fehlerhafte Messung (flascher Trigger am Oszi. etc) ausschliessen? Zum Code selber: Dir ist klar, dass du dir eine Menge Mühe für nichts machst, weil sich 90% deines Codes damit beschäftigen etwas nachzustellen, was die Hardware ganz von alleine machen kann? (Nimm wenigstens das Umschalten der Enable Bits für die beiden Compare Matches raus. Solange der Wert den du ins B-Register schreibst kleiner als der im A-Register ist, treten die schon schön im Wechsel auf) > Wäre das etwas für die Codesammlung wenn es läuft? http://www.mikrocontroller.net/articles/Digitaler_Funktionsgenerator Aber lass dich nicht aufhalten einen Artikel für die Codesammlung fertig zu machen.
Hallo :) Ich weiß. Ich denke gerne mit dem Finger von hinten ins Auge ;) Ich habe mir überlegt, das ganze mit Hardware-PWM zu machen. Allerdings habe ich das Handbuch so verstanden, dass damit das Tastverhältnis immer fest ist. DDS kenne ich, allerdings wird in dem Artikel ein DAC verwendet, den ich weglassen wollte. Viele Grüße, Tilo
Tilo schrieb: > Ich habe mir überlegt, das ganze mit Hardware-PWM zu machen. Allerdings > habe ich das Handbuch so verstanden, dass damit das Tastverhältnis immer > fest ist. Das ist aber ein komisches Handbuch ! Der Sinn und Zweck von PWM ist, dass man das Tastverhältniss verändern kann. Die Frequenz ist meist nur sekundär und wird nicht verändert sondern je nach Einsatzgebiert einmal ausgesucht (damit wird das PWM Modul initialisiert!) Wenn man z.B. LEDs dimmen will initialisiert man beispielsweise mit 5kHz und dimmen tut man dann mittels Tastverhältnis (meist 8Bit = 2^8 = 255 Schritte).
Als Beispiel vom PIC mit Microe Compiler: [alles mögliche Initialisieren] int i=0; PWM1_Init(5000); // Initialisiere PWM mit 5kHz PWM1_Start; for(i=0;i<256;i++){ PWM1_Set_Duty(i); Delay_ms(80); } for(i=255;i>0;i--){ PWM1_Set_Duty(i); Delay_ms(80); } // Ende Das Programm dimmt die LED von 0 bis maximum und wieder zurück. (Nonlinearität der LED-Dimmung mal ausgenommen). Siehst du was ich meine? Man arbeitet mit dem Duty-Cycle und nicht mit der Frequenz!
Danke. Ich hab das Handbuch nochmal genau gelesen. Ich war wohl beim ersten mal nicht so ganz auf der Höhe. Diesmal habe ich einige Dinge die keinen Sinn ergeben einfach ignoriert und etwas brauchbares hinbekommen.
OK. Natürlich wirst du noch das Weiterschalten von einem Datenbyte zum nächsten vom PWM Timer entkoppeln indem du zb dafür einen anderen Timer benutzt. Denn erstens willst du ja deinen Sinus mit einer bestimmten Frequenz erzeugen, die erst mal nichts mit der PWM Frequenz zu tun hat und zum anderen möchtest du ja die PWM im Interesse einer möglichst guten Ausgangsspannung nach der Filterung mit maximaler Frequenz (sprich einem Vorteiler von 1) laufen lassen. Noch ein Tip: Wenn du dir an das Ende deines Datenfeldes noch ein label machst, dann brauchst du in der Reloadroutine die Anzahl der Datenbytes nicht wissen, weil du dann einfach dieses Label für die Erkennung der letzten gültigen Adresse hernehmen kannst. Das spart einen möglichen Fehler, wenn du in das Datenfeld welches deine Schwingung beschreibt noch Bytes einfügst oder rausnimmst.
1 | Timer1_Reload: |
2 | LPM R16, Z+ ; Load new value from PWM table |
3 | OUT OCR1D, R16 |
4 | LDI R17, HIGH(2*sine_end) ; Copy end address of table |
5 | LDI R16, LOW(2*sine_end) ; Copy end address of table |
6 | |
7 | .... |
8 | |
9 | ; this table contais off values. period is 0xA3. the difference between 0xA3 and value is the on value |
10 | sine: |
11 | .db 0x52, 0x4F, 0x4E, 0x4C, 0x4A, 0x48, 0x46, 0x44, 0x42, 0x40, 0x3E, 0x3C, 0x3A, 0x38, 0x36, 0x34 |
12 | |
13 | .... |
14 | .db 0x71, 0x6F, 0x6D, 0x6B, 0x69, 0x67, 0x65, 0x63, 0x61, 0x5F, 0x5D, 0x5B, 0x59, 0x57, 0x55, 0x54 |
15 | |
16 | sine_end: |
Guten morgen Die Entkopplung ist eine gute Idee. Ich habe nur leider keinen freien Timer mehr, Timer0 brauche ich für etwas anderes. Da fällt mir aber vieleicht noch etwas ein, eventuell eine Zählvariable und gut. Der Timer kann mit einem höhrem asynchronen Takt betrieben werden. Damit könnte ich auch noch etwas raus holen, muss mir aber noch genau anschauen, wie das tut. Das Label am Ende der Tabelle ist eine gute Idee. Der Schwachpunkt ist mir auch aufgefallen. Ich habe zu erst an sizeof() gedacht. Allerdings kann der Assembler nicht wissen, wie groß die Tablle ist. Auf die Idee ein Label zu verwenden bin ich nicht gekommen. Klingt aber logisch, Labels sind auch nicht mehr dynamische Aliase für eine Adresse im Programmspeicher. Viele Grüße, Tilo
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.