Forum: Mikrocontroller und Digitale Elektronik Frequenzmodulation mit dem Timer


von Robert (Gast)


Lesenswert?

Hallo zusammen,
ich würde gerne ein Frequenzmoduliertes Signal mit einem ATxmega32A4U 
erzeugen und weiß nicht so recht wie ich da herangehen soll.
Das Signal soll wie folgt aussehen:
Die Periodendauer der Grundrechteckfrequenz ist T = 105ms (ca. 9,5Hz). 
Tein beträgt 5ms und Taus 100ms. Nun würde ich gerne in Tein, also in 
den 5ms eine Frequenz von 39kHz bis 40kHz (Tastgrad 50%) hochlaufen 
lassen. Dann 100ms Taus und in dem nächsten Tein die Frequenz von 40kHz 
wieder auf 39kHz runterlaufen lassen. Das dann immer im Wechsel.
Ich denke ich brauche hierfür zwei Timer. Einen für die Grundfrequenz 
und einen für die Frequenz die in Tein der Grundfrequenz herauf- bzw. 
herunterläuft.
Den Ersten Timer würde ich im „phase correct PWM mode“ laufen lassen um 
den Tastgrad zu erreichen.
Den zweiten im „fast PWM mode“ da ich ja keinen Tastgrad benötige“.
Die Nächste Überlegung von mir wäre, dass ich die Reloadwerte für die 
39kHz-40kHz in eine Tabelle lege und diese dann immer im Timerinterrupt 
nachlade um so die 100kHz von 39kHz bis 40kHz zu erreichen.
Bei einer Oszillatorfrequenz von 16MHz und einem Prescaler von 1, komme 
ich auf Reloadwerte (OCR1A) für 39kHz auf 204 und für 40kHz auf 199. Das 
wären ja dann in viel zu große Schritte. Ich würde gerne die 100kHz 
möglichst klein in den 5ms herauf- bzw. herunterlaufen.
Ich wäre sehr dankbar für jegliche Tipps wie ich so etwas realisieren 
könnte.

Grüße
Robert

von Jacko (Gast)


Lesenswert?

39 kHz ~= 410 Clk-Zyklen bei 16 MHz
40 kHz ~= 400 Clk-Zyklen bei 16 MHz
das sind 10 Steps

Wenn es symmetrisch sein soll:
39 kHz ~= 2 * 205 Clk-Zyklen bei 16 MHz
40 kHz ~= 2 * 200 Clk-Zyklen bei 16 MHz

Zwischenwerte sind noch möglich:
205 - 205
204 - 205
204 - 204
   ...
200 - 200
Das sind 10 Schritte.

Zwischen- Zwischenwerte sind aber auch noch möglich:
8 * 205
7 * 205 + 1 * 204 = 204,875
6 * 205 + 2 * 204 = 204,750
5 * 205 + 3 * 204 = ... usw.
Kämen schon mal 40 Zwischenschritte bei raus.

Lässt sich aber noch verfeinern mit:
16 * 205, 15 * 205 + 1 * 204, ...

DDS macht es auch nicht viel besser. ;-)

von Wolfgang (Gast)


Lesenswert?

Jacko schrieb:
> DDS macht es auch nicht viel besser. ;-)

Gutes Stichwort. Ein DDS "mischt" die unterschiedlich langen Intervalle 
per Prinzip gut durch und man hat damit im uC keine Scherereien.
Für die 3€, die ein Modul mit einem AD9833 kostet, hat man das nicht 
selber gestrickt. Per Timer braucht man dem nur die Werte auf der 
Frequenzrampen zuzuschieben, muss sich aber keine Sorgen um 
Phasenstetigkeit und Periodenmittelwert mehr machen. Bei 5ms Rampenlänge 
wird man allerdings sowieso nicht beliebig viele 39..40kHz Schwingungen 
unter kriegen und die Anwendung muss mit dem Phasenjitter leben können.

von Robert (Gast)


Lesenswert?

Hallo,

vielen Dank für die Antworten. Ich habe nun mal etwas programmiert, was 
mir in den 5ms die Frequenzen erzeugt. Wie gesagt eben leider nur in 200 
Hz Schritten, da 16MHz. Bei 32MHz könnte ich 100Hz Schritte realisieren. 
Den AD9833 muss ich mir noch genau anschauen. Hier müsste ich ja denn 
immer über die serielle Schnittsctelle mit diesem kommunizieren. Geht da 
nicht zu viel Zeit verloren?
Hier ist mein Code in C für einen Atmega256. Bin dankbar für jede Hilfe 
wie ich das Program besser bzw. effizienter mache. Funktionieren tuts.
1
/*
2
 * MEGA2560_R3.c
3
 *
4
 * Created: ...
5
 * Author : ...
6
 */
7
8
#define F_CPU 16000000UL // 16Mhz
9
10
#include <avr/io.h>
11
#include <util/delay.h>
12
#include <avr/interrupt.h>
13
14
15
//================================================================================
16
// pin- and portdefinition
17
//================================================================================
18
#define CLK_STOP_HI    TCCR1A &= ~(1<<COM1A0);PORTB |= (1<<PORTB5)    // Portpin von Timer abgekoppelt => high
19
#define CLK_STOP_LO    TCCR1A &= ~(1<<COM1A0);PORTB &= ~(1<<PORTB5)  // Portpin von Timer abgekoppelt => low
20
#define CLK_TOGGLE    PINB |= (1<<PINB5)                // Portpin von Timer abgekoppelt => toggle
21
#define CLK_START    TCCR1A |= (1<<COM1A0)              // Portpin an Timer angekoppelt  => fclk
22
23
//================================================================================
24
// frequenzen
25
//================================================================================
26
#define  OCR_39000Hz 204     //39024,39024 Hz
27
#define  OCR_39200Hz  203      //39215,68627 Hz
28
#define  OCR_39400Hz  202      //39408,867 Hz
29
#define  OCR_39600Hz  201      //39603,9604 Hz
30
#define  OCR_39800Hz  200      //39800,99502 Hz
31
#define  OCR_40000Hz  199      //40000 Hz
32
33
34
uint16_t count_100us = 0;
35
36
void clk_init (void)
37
{
38
  unsigned char reg_help;
39
  
40
  DDRB |= (1<<DDB5);                  // CLK-Pin Port B, Bit 5 -> OUTPUT
41
  CLK_STOP_LO;
42
  
43
  TCCR1A = 0x00;
44
  TCCR1B = 0x00;
45
  
46
  reg_help  = TCCR1A;
47
  reg_help |= ((1<<WGM11) | (1<<WGM10));        // Timer 1: Fast PWM
48
  TCCR1A    = reg_help;
49
  
50
  reg_help  = TCCR1B;
51
  reg_help |= ((1<<WGM13) | (1<<WGM12) | (1<<CS12));  // Timer 1: Fast PWM / Prescale: 256
52
  TCCR1B    = reg_help;
53
  
54
  TCCR1B &= ~(1<<CS12);                // Timer 1: Fast PWM / Prescale: 1
55
  TCCR1B |= (1<<CS10);
56
}
57
58
void timer3_init (void)
59
{  
60
  TCCR3A = 0x00;
61
  TCCR3B = 0x00;
62
  
63
  TIMSK3 |= (1<<OCIE3A);
64
  
65
  OCR3A = 1600;  
66
  
67
  TCCR3A |= ((1<<WGM31) | (1<<WGM30));        // Timer 3: Fast PWM
68
  TCCR3B |= ((1<<WGM33) | (1<<WGM32) | (1<<CS30));  // Timer 3: Fast PWM / Prescale: 1
69
}
70
71
// 100us
72
ISR (TIMER3_COMPA_vect)          
73
{
74
  count_100us++;
75
  
76
  if (count_100us == 10)
77
  {
78
    OCR1A = OCR_39800Hz;
79
  }
80
  else if (count_100us == 20)
81
  {
82
    OCR1A = OCR_39600Hz;
83
  }
84
  else if (count_100us == 30)
85
  {
86
    OCR1A = OCR_39400Hz;
87
  }
88
  else if (count_100us == 40)
89
  {
90
    OCR1A = OCR_39200Hz;
91
  }
92
  else if (count_100us == 50)
93
  {
94
    OCR1A = OCR_39000Hz;
95
    CLK_STOP_LO;
96
  }    
97
  else if (count_100us == 1000)
98
  {
99
    OCR1A = OCR_40000Hz;
100
    CLK_START;
101
    count_100us = 0;
102
  } 
103
}
104
105
int main (void)
106
{    
107
  
108
  clk_init ();
109
  timer3_init ();
110
  
111
  OCR1A = OCR_40000Hz;
112
  
113
  sei();
114
  
115
  while(1) 
116
    {
117
    ;
118
    }
119
}

Gruß
Robert

von c-hater (Gast)


Lesenswert?

Robert schrieb:

> Hier ist mein Code in C für einen Atmega256. Bin dankbar für jede Hilfe
> wie ich das Program besser bzw. effizienter mache.

Jacko hat dir die Grundidee für eine bessere Lösung quasi auf dem 
Silbertablett präsentiert, sogar die wesentliche Einschränkung mit 
aufgeführt und damit gezeigt, dass er definitiv weiss, wovon er redet.
Diese Einschränkung betrifft im übrigen zumindest prinzipiell den Ansatz 
mit dem AD9833 ganz genauso.

Warum nutzt du also nicht zumindest erst einmal Jackos Grundidee? Die 
läßt sich doch wirklich noch recht einfach umsetzen.

Wenn das läuft und der sowieso unvermeidliche Phasenjitter ein wenig zu 
regulär ist, kann man die Grundidee variieren und dann tatsächlich das 
erreichen, was eine mit 16MHz getaktete DDS überhaupt zu erreichen in 
der Lage ist. Sprich: den unvermeidlichen Phasenjitter so stochastisch 
wie möglich zu machen, damit der Beschiss weniger auffällt. Das genau 
ist die Grundidee auch des AD9833, ja das Konzept einer DDS überhaupt...

von Robert (Gast)


Lesenswert?

Hallo,

ich würde die Grundidee von Jacko (vielen Dank Jacko!) sehr gerne 
versuchen programmtechnisch umzusetzen. Leider weiß ich nicht genau wie. 
Benötige ich Timer und ISR? Soll ich es irgendwie mit Schleifen nur im 
Hauptprogramm laufen lassen. Einzelne Befehle brauchen dann ja auch 
wieder eine bestimmte zeit. Vielleicht könnte mir jemand erklären wie 
das Program dann aussehen soll bzw. wie es aufgebaut sein soll.

Grüße

von c-hater (Gast)


Lesenswert?

Robert schrieb:

> Benötige ich Timer und ISR?

Ja, genau das. Allerdings genügt ein Timer. Der wird dann halt in der 
ISR dynamisch umprogrammiert und kann so sowohl den 5ms-Sweep erzeugen 
als auch die 100ms Wartezeit zwischen den Sweeps.
Der sinnvollste Timermodus für den Sweep ist übrigens Fast-PWM. Die 
Ablaufsteuerung kann man mittels Tabelle machen oder algorithmisch. 
Tabelle ist einfacher umzusetzen, braucht aber halt auch mehr Flash, die 
algorithmische Umsetzung braucht dafür mehr Rechenzeit in der ISR. 
Könnte in C umgesetzt eventuell schon etwas knapp werden. Die 
Tabellenlösung hingegen sollte auf jeden Fall ganz entspannt möglich 
sein.

> Soll ich es irgendwie mit Schleifen nur im
> Hauptprogramm laufen lassen. Einzelne Befehle brauchen dann ja auch
> wieder eine bestimmte zeit.

Kann man auch machen, aber nur dann, wenn das die einzige Aufgabe des µC 
sein soll. Ich würde es hier nicht empfehlen. Es macht hier nichts 
einfacher, eher ist das Gegenteil der Fall.

von Robert (Gast)


Lesenswert?

Hallo,

ich glaube weitestgehend habe ich das so in meinem Program realisiert. 
Der Timer mit der ISR erzeugt mein 5ms tein und meine 100ms taus. Der 
zweite Timer erzeugt meine 5 verschiedenen Frequenzen welche immer in 
1ms gewechselt werden.
Wie funktioniert das aber nur mit einem Timer? Wie genau erzeuge ich 
dann den Sweep?
Grüße

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
Noch kein Account? Hier anmelden.