Forum: Mikrocontroller und Digitale Elektronik AD9850-Modul mit AtMega steuern


von KLS (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,


ich habe mir neulich ein AD9850-Modul wie im Anhang zugelegt.

Es soll als Rechteckgenerator verwendet und mit einem M88 o.ä. 
angesteuert werden (plus LC-Display für die Frequenz-Anzeige).


Weiß jemand, ob es hierzu schon irgendwo ein fertiges Projekt gibt?

Habe beim Googeln zwar einige Treffer erhalten, aber leider ohne 
verwertbaren Inhalt.

von KLS (Gast)


Lesenswert?

Wenn niemand ein entsprechendes Projekt kennt, werde ich mich wohl 
selber ans Proggen machen müssen...

von Andreas B. (andreasb)


Lesenswert?

Hallo

ggf. bringt dich das ein wenig weiter: 
http://alhin.de/arduino/index.php?n=7
Ist aber alles andere als ein fertiges Projekt.

Du darfst aber auch der erste sein der dann ein fertiges Projekt in die 
Codesammlung stellt;-)



mfg Andreas

von KLS (Gast)


Angehängte Dateien:

Lesenswert?

Danke für den Link!

Zu Arduino habe ich das hier gefunden:
http://de.wikipedia.org/wiki/Arduino-Plattform

Will mich da aber nicht weiter einarbeiten und das ganze lieber direkt 
mit dem AVR-Studio umsetzen.

In dem EF_AD8950.cpp-File (s. Anhang) findet man unter anderem folgenden 
Programminhalt:
1
void EF_AD9850::wr_serial(unsigned char w0,double frequence)
2
  {
3
    unsigned char i,w;
4
    long int y;
5
    double x;
6
    
7
    //Calculate the frequency of the HEX value
8
    x=4294967295/125;//Suitable for 125M Crystal 
9
    frequence=frequence/1000000;
10
    frequence=frequence*x;
11
    y=frequence;

Momentan überlege ich, wo die Variable frequence ihren Wert zugewiesen 
bekommt...

Ich vermute, vor der Zeile
1
frequence=frequence/1000000;

sollte "von Hand" die Frequenz in [Hz] zugewiesen werden, z.B. für 
10MHz:
1
frequence=10000000; //für 10MHz, in der Praxis natürlich als Variable ;)

!!?

y wäre dann der Wert, der an den AD9850 übertragen wird, um die 
vorgegebene Frequenz zu generieren...

von B e r n d W. (smiley46)


Lesenswert?

Im Prinzip so:
1
void AD9850_wr_serial(unsigned char w0, double freq)
2
{
3
  unsigned char i, w;
4
  long y;
5
  double x;
6
    
7
  // 4294967295 = 0xFFFFFFFF
8
  // x = 0xFFFFFFFF/125;    // 125 MHz Crystal 
9
  x = (double)34359738.36;  // 
10
  freq /= 1000000L;         // das MHz kürzt sich weg
11
  y = freq * x;
12
  ...
13
}
14
15
//Aufruf:
16
void main()
17
{
18
  P0 = 0x00;
19
  P1 = 0x00;
20
  P2 = 0x00;
21
  P3 = 0x00;
22
23
  ad9850_reset_serial();
24
  ad9850_wr_serial(0x00, 1e7);
25
26
  while(1) {
27
    // Do something else
28
  }
29
}

> wo die Variable frequence ihren Wert zugewiesen
Durch den Aufruf aus main.

Aber IMHO werden double im AVR-Studio als float behandelt. Also 8Bit 
Exponent, Vorzeichen und 23Bit Mantisse. Deshalb gehen hier relevante 
Stellen verloren. So wie ich das verstanden habe, hat das Uwe mit dem 
AD9834 etwas anders umgesetzt, also mit reduzierter Auflösung, aber 
immer noch gut genug:

Beitrag "Re: Ladderfilter berechnen und bauen"

von B e r n d W. (smiley46)


Lesenswert?

Es gibt hier schon einiges auch mit ATmega:
http://www.mikrocontroller.net/articles/DDS

von fiffi (Gast)


Lesenswert?

steht det nicht im Datenblatt, wie die Frequenz berechnet wird?

von B e r n d W. (smiley46)


Lesenswert?

> steht det nicht im Datenblatt, wie die Frequenz berechnet wird?
Schon, aber wenn einem double nicht zur Verfügung steht, fangen die 
Klimmzüge an. Wieso soll man jedesmal das Rad neu erfinden, wenn das 
schon mal jemand gemacht hat.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich hab's mit 64-bit-Integern gelöst.  Langsam, aber genau. ;-)
1
#define MASTERCLOCK 99999200ull  /* gemessen ;) */
2
3
4
static
5
uint32_t val_to_dds(uint32_t v)
6
{
7
  uint64_t x;
8
9
  x = v * 0x100000000ull;
10
  x /= MASTERCLOCK;
11
12
  return x;
13
}
14
15
static void
16
fq_ud(uint32_t fq)
17
{
18
  union
19
  {
20
    uint32_t l;
21
    uint8_t b[5];
22
  }
23
  u;
24
  uint8_t i;
25
26
  u.l = fq;
27
  /*
28
   * W32..W39: no phase angle, no power-down, control bits = 0
29
   */
30
  u.b[4] = 0;
31
32
  for (i = 0; i < 5; i++)
33
    {
34
      SPDR = u.b[i];
35
      while ((SPSR & _BV(SPIF)) == 0)
36
        /* wait */;
37
    }
38
39
  /* Update now. */
40
  PORTC |= _BV(1);
41
  PORTC &= ~_BV(1);
42
}
43
44
//...
45
int
46
main(void)
47
{
48
  uint32_t ddsval = 1000000ul;
49
  //...
50
51
    fq_ud(val_to_dds(ddsval));
52
  //...
53
}

von Uwe (de0508)


Lesenswert?

Hallo,

B e r n d W. schrieb:
> Aber IMHO werden double im AVR-Studio als float behandelt. Also 8Bit
> Exponent, Vorzeichen und 23Bit Mantisse. Deshalb gehen hier relevante
> Stellen verloren. So wie ich das verstanden habe, hat das Uwe mit dem
> AD9834 etwas anders umgesetzt, also mit reduzierter Auflösung, aber
> immer noch gut genug:

ich muss anmerken, die Berechnung läuft nur mit 32 Bit ab und das FTW 
wird über einen Kettenbruch berechnet. Was aber fest ist, ist die DDS 
Taktfrequenz von 75MHz!
Nach zusammenfassen der Terme erhält man diese einfache Berechnung.

http://www.qrpforum.de/index.php?page=Thread&threadID=6976&highlight=ad9834

In meinem Beispiel ist die Frequenz-Auflösung ist nicht reduziert.
Das FTW kann sich um 1Bit vom exakt gerechneten unterscheiden, ist aber 
vernachlässigbar.

von KLS (Gast)


Lesenswert?

Hallo,

Danke für eure Postings.

Habe heute endlich das LCD für die Frequenzanzeige am AVR zum Laufen 
gebracht und schon mal ein kleines Menü zur Bedienung erstellt.


Gibt es eigentlich keinen einfachen linearen Zusammenhang wie

f = bw / c

wobei

f = Frequenz in Hz
bw= 32-bit-Wort
c = 687,12  (oder ähnlicher Faktor)

?

Im Datenblatt finde ich nichts in der Richtung (jedenfalls nichts 
Prägnantes).

@ Bernd:
wenn man uint32 Variablen verwendet, umschifft man das Problem mit 
double und float... (hoffe ich jedenfalls ;-))

von B e r n d W. (smiley46)


Lesenswert?

Hallo KLS

> Gibt es eigentlich keinen einfachen linearen Zusammenhang?
Natürlich ist der Zusammenhang linear. Aber bei einer 32 Bit Variablen 
gibt es ohne weitere Maßnahmen bei der ersten Multiplikation einen 
Überlauf. Deshalb würde ich Jörgs Variante vorziehen.
1
#define MASTERCLOCK 125000000ULL
2
3
static void
4
AD9850_wr_serial(uint32_t freq)
5
{
6
  union {
7
    uint32_t l;
8
    uint8_t b[5];
9
  }u;
10
  uint8_t i;
11
12
  u.l = (uint64_t)freq * 0x100000000ULL / MASTERCLOCK;
13
14
  // W32..W39: no phase angle
15
  //           no power-down
16
  //           control bits = 0
17
  u.b[4] = 0;
18
19
  // send 5 Bytes per SPI
20
  for (i = 0; i < 5; i++) {
21
    SPDR = u.b[i];     // send 1 byte
22
    while ((SPSR & _BV(SPIF)) == 0) {
23
      // wait while busy
24
    }
25
  }
26
27
  // Update now.
28
  PORTC |= _BV(1);     // set PORTC.Bit1
29
  PORTC &= ~_BV(1);    // res PORTC.Bit1
30
}
31
32
void 
33
main(void)
34
{
35
  uint32_t ddsval = 1000000ul;
36
  AD9850_wr_serial(ddsval);   // 1MHz
37
38
  while(1) {
39
    // Do something else
40
  }
41
}

von richie (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> 99999200ull

> 125000000ULL

> 1000000ul

Welche Bedeutung haben die Buchstaben hinter den Zahlen?

von B e r n d W. (smiley46)


Lesenswert?

> Welche Bedeutung haben die Buchstaben hinter den Zahlen?
Die bestimmen, in welchem Zahlenformat, die Konstante im 
Programmspeicher angelegt wird:

L = long = int32_t
UL = unsigned long = uint32_t
ULL = unsigned long long = uint64_t
usw.

von richie (Gast)


Lesenswert?

B e r n d W. schrieb:
> L = long = int32_t
> UL = unsigned long = uint32_t
> ULL = unsigned long long = uint64_t
> usw.

braucht man einen speziellen Header, damit die Schreibweise 
funktioniert?

Wieso hat dieses IC-Ding keine anschlüsse für "inc frequenz" oder "dec 
frequenz", dann könnte man es über eine Schleife steuern.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

richie schrieb:

> braucht man einen speziellen Header, damit die Schreibweise
> funktioniert?

Nein.  Ein C-Standard genügt. ;-)

Im Gegensatz zum Rest von C ist bei den Suffixen übrigens die Groß-
und Kleinschreibung egal.

> Wieso hat dieses IC-Ding keine anschlüsse für "inc frequenz" oder "dec
> frequenz", dann könnte man es über eine Schleife steuern.

Weil du dann morgen noch nicht fertig wärst mit dem QSY.

von Karl H. (kbuchegg)


Lesenswert?

richie schrieb:
> B e r n d W. schrieb:
>> L = long = int32_t
>> UL = unsigned long = uint32_t
>> ULL = unsigned long long = uint64_t
>> usw.
>
> braucht man einen speziellen Header, damit die Schreibweise
> funktioniert?

Nein.

> Wieso hat dieses IC-Ding keine anschlüsse für "inc frequenz" oder "dec
> frequenz", dann könnte man es über eine Schleife steuern.

Schon mal nachgerechnet, wie lang das bei größeren Zahlen dauern würde?
Ausserdem hindert dich ja keiner daran, so was in Software zu machen.

von Uwe (de0508)


Lesenswert?

richie Du brauchst ein C Buch !

Einige würden schreiben, Ende der ersten Klasse der "C" Programmierung.

Der AD9850 und der AD9851 sind so einfach zu steuern, man kann doch noch 
eine Software-Ebene einführen, die genau deine Anforderungen erfüllt.

Dem "C" Programmierer sind doch kaum Grenzen gegeben !

Im Datenblatt und den Application Notes ist die Programmierung sehr 
ausführlich dargestellt.

PeDa, Peter hat einen Beitrag zu schnellen 64-Bit Routinen verfasst, der 
ist lesenswert:

- 
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=113673

Ich habe mir diese Idee auch mal angesehen:

- Beitrag "Re: Rechnen mit AVR"

Aus alle dem ist eine, für mich, schnelle 64-Bit Multiplikation und 
Division 'abgefallen'.


richie schrieb:
> B e r n d W. schrieb:
>> L = long = int32_t
>> UL = unsigned long = uint32_t
>> ULL = unsigned long long = uint64_t
>> usw.
>
> braucht man einen speziellen Header, damit die Schreibweise
> funktioniert?
>
> Wieso hat dieses IC-Ding keine anschlüsse für "inc frequenz" oder "dec
> frequenz", dann könnte man es über eine Schleife steuern.

von richie (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
>> Wieso hat dieses IC-Ding keine anschlüsse für "inc frequenz" oder "dec
>> frequenz", dann könnte man es über eine Schleife steuern.
>
> Schon mal nachgerechnet, wie lang das bei größeren Zahlen dauern würde?
> Ausserdem hindert dich ja keiner daran, so was in Software zu machen.

hätte der Hersteller ja benutzerfreundlich in Hz-Schritten 
implementieren können.

Wenn man es als Software macht, dauert es wirklich lange! ;v)

von Uwe (de0508)


Lesenswert?

richie schrieb:
> Wenn man es als Software macht, dauert es wirklich lange! ;v)

Nein, dann hast Du das "System" DDS nicht verstanden !

Hier läuft alles direkt und hat direkte Auswirkungen auf die Hardware 
(DDS).

Die nächste Ebene / Schnittstelle ist dann deine.

von richie (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Im Gegensatz zum Rest von C ist bei den Suffixen übrigens die Groß-
> und Kleinschreibung egal.

wie werden Variablen mit Vorzeichen als Suffix abgekürzt?


> uint32_t ddsval = 1000000ul;

Ist das doppelt gemoppelt?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

richie schrieb:
> Jörg Wunsch schrieb:
>> Im Gegensatz zum Rest von C ist bei den Suffixen übrigens die Groß-
>> und Kleinschreibung egal.
>
> wie werden Variablen mit Vorzeichen als Suffix abgekürzt?

Bahnhof?

>> uint32_t ddsval = 1000000ul;
>
> Ist das doppelt gemoppelt?

Zur Hälfte.  Den Typ "long" würde die Konstante 10000000 bereits von
sich aus bekommen (statt "int"), aber erst durch die Zuweisung wird
daraus ein "unsigned long", implizit wäre sie als Dezimalkonstante
erst einmal vorzeichenbehaftet.

Wie Uwe schon schrieb, du brauchst ein C-Buch (und danach eins über
DDS).

von ./. (Gast)


Lesenswert?

LOL.
Wenn man die Klimmzüge hier sieht.
Mit 8 32 bit-Konstanten die man nur passend aufsummieren muß,
kann man jede Frequenz zwischen 1 Hz und 99999999 Hz einstellen.
Ganz ohne Gleitkomma und solches Teufelszeug.

Ihr solltet vielleicht mal auf Brain 1.0 upgraden.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

./. schrieb:
> Mit 8 32 bit-Konstanten

Schön.  Für jede neue Frequenz eine neue Firmware?

von Uwe (de0508)


Lesenswert?

Hallo Jörg,

er meint, vorher einmal 8x FTW für 10MHz, 1MHz, 100kHz usw. zu berechnen 
und dann, wie beim Abzählen eines Geldbetrages, aufsummieren.

von ./. (Gast)


Lesenswert?

Uwe S. schrieb:
> aufsummieren

Endlich mal einer der mitdenkt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

./. schrieb:
> Endlich mal einer der mitdenkt.

Gut, dass wir dich haben.  Keinen Namen, keinen Code, aber zumindest
den nötigen Durchblick.  Hilft bloß keinem was.

von KLS (Gast)


Lesenswert?

So, ich habe jetzt einen Bresenham-Algo umgebaut für die 
Frequenzberechnung.
Das funktioniert ganz gut und kommt auch ohne komplizierte Berechnungen 
aus (und ich komme gleichzeitig mit meinen eher kleinen C-Kenntnissen 
aus ;-)).

Die ausgegebenen Frequenzen scheinen so weit zu stimmen (zumindest lt. 
rundfunksenderkalibriertem Weltempfänger).

Werde das aber noch eingehender prüfen, wenn ich wieder aus dem Urlaub 
zurück bin.
Dann auch mit Filter(n) vor dem Rechteck usw.

Derweilen vielen Dank an alle Poster und schöne Zeit!

von Uwe (de0508)


Lesenswert?

Hallo KLS,

jetzt wäre es noch nett, wenn Du hier deinen Code mal einstellen 
könntest.

_

von Andreas B. (andreasb)


Lesenswert?

Uwe S. schrieb:
> Hallo KLS,
>
> jetzt wäre es noch nett, wenn Du hier deinen Code mal einstellen
> könntest.
>
> _

Nachdem nun 14 Tage vergangen sind pushe ich das hier nochmals...

Ich wäre auch interessiert am Code...



mfg Andreas

von Andreas B. (andreasb)


Angehängte Dateien:

Lesenswert?

Dann eben nicht...

Habe nochmals gegoogelt, und nichts schlaues gefunden. Habe dann auf 
Basis des nicht schlauen Codes selbst etwas geschrieben...

Beispiel im Anhang. Es wird kein Double verwendet, zum Vergleich mit 
Double ist dieser Code ca. 1k kleiner.

Ich habe meine Rechnungen verifiziert, und ich weiss das diese nicht 
korrekt sind. Mein Oszilloskop zeigt aber "nur" 4 Nachkommastellen an 
bei 20MHz und diese haben sich im Vergleich zu Double nichts geändert.

Ich lads jetzt mal hier hoch, sobald ich dann weiss, dass das ganze 
läuft etc. werde ich den Code noch in der Codesammlung hochladen.

Wer den Code testet soll doch kurz ein Feedback hinterlassen obs passt 
oder nicht. Danke.


Der Code zum prüfen meiner Lösung (nicht für AVR!)
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <stdint.h>
4
5
int main(void) {
6
  const uint64_t frequency = 4000000;
7
8
  uint64_t tmp1 = frequency;
9
  tmp1 <<= 32;
10
  tmp1 /= 125; // 125 MHz
11
  tmp1 /= 1000000;
12
  uint32_t sollResultat = (uint32_t) tmp1;
13
14
  uint32_t tmp = frequency;
15
  // Faktor: 34.359'738'368
16
17
  tmp = frequency * 35; //35
18
  tmp -= frequency / 10 * 6; //34.4
19
  tmp -= frequency / 100 * 4; //34.36
20
  tmp -= frequency / 10000 * 2; //34.359'8
21
  tmp -= frequency / 100000 * 6; //34.359'74
22
  tmp -= frequency / 1000000 * 2; //34.359'738
23
24
  uint32_t istResultat = tmp;
25
26
  if (istResultat == sollResultat) {
27
    printf("Stimmt!\n");
28
  } else {
29
    printf("Strimmt nicht! soll: %d; ist: %d\n", sollResultat, istResultat);
30
  }
31
32
  return EXIT_SUCCESS;
33
}


mfg Andreas

von Alex (Gast)


Lesenswert?

Ist zwar schon etwas länger her, aber nur ein kleiner Hinweis an 
Andreas' code im Anhang. An der markierten Stelle hat er wohl vergessen, 
das erste Byte vom 32bit-Frequenzwort zum AD9850 zu übertragen:

void AD9850_write(...)
{
...
AD9850_writeByte(tmp);  <<<< Dieses erste Frequenzbyte fehlt in Andreas 
Code!
AD9850_writeByte(tmp >> 8);
AD9850_writeByte(tmp >> 16);
AD9850_writeByte(tmp >> 24);
AD9850_writeByte(w0);
...
}


Habe heute den ganzen Tag gebraucht, um den Fehler zu finden. Die 
Frequenz stimmte nicht mit der berechneten überein. Jetzt passt es 
wunderbar. Mein Dank geht an Andreas' und seine Vorarbeit.


Mfg Alex

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.