Étiquette : AD9850

Code source ultra-simpliste pour Arduino/MSP430 d’un VFO à AD9850/AD9851

Prototype kit Balise WSPR DDS XV4Y (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2012/12/100_3339 NULL.jpg)Dans un commentaire Sylvain F6GGX m’a demandé après ce bout de code. Il était disponible gratuitement dans la boutique mais comme j’ai désactivé cette dernière il a disparu du site…
Voici donc le code source d’un VFO très simpliste à base de AD9850/AD9851 qui utilise les librairies que j’ai écrites ou adaptées (http://xv4y NULL.radioclub NULL.asia/docs/). Honnêtement je pense pas que ce code soit très utile, rien de particulièrement compliqué et l’ergonomie à 4 boutons n’est pas super. C’est utile en dépannage avec le kit balise WSPR que je produisais mais c’est tout. Le code est écrit pour Energia sur MSP430, le porter sur Arduino (ATMega328) ne devrait poser aucun problème.

/* Simple VFO with DDS for MSP430G2553
 * Code for Energia 009
 
 * By Yannick DEVOS - XV4Y - March 2013
    http://xv4y.radioclub.asia/

    Copyright 2012-2013 Yannick DEVOS under GPL 3.0 license
    Any commercial use or inclusion in a kit is subject to author approval

====
 * Agile Frequency generation using AD9850/9851 DDS
 * Output to Nokia 5110 compatible LCD
 * Check if P2_2 has changed state and switch VFO (like when PTT is pressed to operate split)

Usage for short press of buttons :
- Up / Down      increase or decrease the frequency following the choosen digit
- Left / Right   increase or decrease the digit
- OK             validate the frequency and send it to the DDS
Long press of buttons :
- Left           Set the current VFO to the next band bottom frequency
- Right          VFO A = VFO B
- OK             Switch between VFO A and VFO B

====
Revision history :
v1.00    2013-03-18
         First release

====
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You can download a copy of the GNU General Public License at <http://www.gnu.org/licenses/>
*/

// Here modify to your taste or needs

#define PTT_key          P2_2
#define ANALOG_BUTTONS   A5
#define AUTO_VALID       // Uncomment if you want the DDS frequency to be udapted automatically after each value change

#define DEFAULT_FREQ    8            // Value 8 in Frequencies array is 14000000
// Here under don't touch anything unless you know what you do

#include <legacymsp430.h>
#include <AD9850.h> // Library for AD9850 control by MSP430

#include <LCD_5110.h>

              // Frequencies (1 Hz precision) you can select
const unsigned long int frequencies[] = {
  137000, 471000, 501000, 1830000, 3500000, 5200000, 7000000, 10100000,
  14000000, 18068000, 21000000, 24890000, 28000000, 50000000, 70000000};
  
const byte Analog_Noise = 5;    // Margins for reading analog buttons

boolean   vfo=0, saved_PTT;  // VFO A or B and PTT input state
char      multiplier, aff_multiplier, freq_select=DEFAULT_FREQ;
unsigned  long int frequency_A=frequencies[DEFAULT_FREQ], frequency_B=frequencies[DEFAULT_FREQ], debounce;

char chaine_txt[6] = {' ', ' ', ' ', ' ', ' ', 0x00};


AD9850 myDDS (P1_0, P1_1, P1_2, P1_4);  // Call the AD9850 Library, AD9850 pins for CLOCK, LOAD, DATA and RESET
//AD9850 myDDS (P1_1, P1_2, P1_0, P1_4);  // Call the AD9850 Library, AD9851 pins for CLOCK, LOAD, DATA and RESET

LCD_5110 myScreen(P2_3,    // Chip Select *
         P1_6,    // Serial Clock *
         P2_5,    // Serial Data *
         P2_4,    // Data/Command *
         NULL,    // Reset *
         P1_7,    // Backlight
         NULL);  // Push Button 0

//******************************************************************
// Defining pins mode and initializing hardware

void setup() {
  pinMode(PTT_key, INPUT_PULLUP);

  myDDS.begin();
  
   
  pinMode(ANALOG_BUTTONS, INPUT);

  myScreen.begin();
  myScreen.setBacklight(1);
    
  myScreen.setFont(1);
  myScreen.text(0, 0, "sVFO");
  myScreen.text(0, 2, "AD9850");
  myScreen.setFont(0);
  myScreen.text(0, 4, "v1.00 - XV4Y");
  myScreen.text(0, 5, "Init...");
  
  delay(1000);
  
  myDDS.reset();
  
  myDDS.SetFrequency( frequency_A, 0, false );

  digitalWrite( PTT_key, LOW );

};

//******************************************************************
// Here starts the actual sequence sending

void loop() {
    myScreen.clear();
    myScreen.setBacklight(1);
    myScreen.setFont(1);
    if (vfo == 0) {
      myScreen.text(0, 0, "VFO A");
      myDDS.SetFrequency( frequency_A, 0, false );
    } else {
      myScreen.text(0, 0, "VFO B");
      myDDS.SetFrequency( frequency_B, 0, false );
    };

    myScreen.setFont(0);
    display_freq (frequency_A, 3);
    display_freq (frequency_B, 5);

    while (read_buttons()==5 || read_buttons()==4 || read_buttons()==2) delay(10); // Debounce except for UP/DWN

    while (1) {
      // Update the frequencies display
      if (multiplier > 5) {
        aff_multiplier = multiplier + 2;
      } else if (multiplier > 2) {
        aff_multiplier = multiplier + 1;
      } else {
        aff_multiplier = multiplier;
      };
      myScreen.text(0, 4, "          ");
      if (vfo == 0) {
        myScreen.text(9-aff_multiplier, 4, "^");
      } else {
        myScreen.text(9-aff_multiplier, 4, "v");
      }
      display_freq (frequency_A, 3);
      display_freq (frequency_B, 5);
  
      // Read the analog buttons input
      if(read_buttons()==1) {            // Up we increase frequency
        delay(200);  // Debounce
        if (vfo == 0) {
          frequency_A = frequency_A + powf(10,(float)multiplier);
        } else {
          frequency_B = frequency_B + powf(10,(float)multiplier);
        };
#if defined AUTO_VALID
        break;
#endif

      } else if (read_buttons()==3) {    // Down we decrease frequency
        delay(200);  // Debounce
        if (vfo == 0) {
          frequency_A = frequency_A - powf(10,(float)multiplier);
        } else {
          frequency_B = frequency_B - powf(10,(float)multiplier);
        };
#if defined AUTO_VALID
        break;
#endif

      } else if (read_buttons()==2) {    // Left we increase multiplier
        debounce = millis();
        while (read_buttons()==2) {  //Debounce
          if ((millis()-debounce)>1000) {  // Long press we do "Band UP"
            freq_select++;
            if (freq_select > 14) freq_select = 0;
            if (vfo == 1) frequency_B = frequencies[freq_select]; else frequency_A=frequencies[freq_select];
            multiplier--;
            break;
          };
        };
        multiplier++;
        if (multiplier > 7) multiplier = 7;

      } else if (read_buttons()==4) {    // Right we decrease multiplier
        debounce = millis();
        while (read_buttons()==4) {  //Debounce
          if ((millis()-debounce)>1000) {  // Long press we do VFO A=B
            if (vfo == 1) frequency_A = frequency_B; else frequency_B=frequency_A;
            multiplier++;
            break;
          };
        };
        multiplier--;
        if (multiplier < 0) multiplier = 0;

      } else if (read_buttons()==5) {    // OK we go out
        debounce = millis();
        while (read_buttons()==5) {  //Debounce
          if ((millis()-debounce)>1000) {  // Long press we switch VFO A/B
            if (vfo == 1) vfo=0; else vfo=1;
            break;
          };
        };
        break;   // Short press we just leave the loop so the frequency is transmitted to the AD9850
      }

      // Check if we are transmitting split (momentaneous VFO A->B switch)
      if (saved_PTT != digitalRead(PTT_key)) {
        saved_PTT = digitalRead(PTT_key);
        if (vfo == 1) vfo=0; else vfo=1;
        break;
      };
    }

};


//******************************************************************
// Display the frequency
void display_freq (unsigned long freq, char ligne) {
  myScreen.text(10, ligne, " Hz ");
  chaine_txt[5] = 0x00;
  chaine_txt[4] = 0x30 + (((freq)/1)%10);
  chaine_txt[3] = 0x30 + (((freq)/10)%10);
  chaine_txt[2] = 0x30 + (((freq)/100)%10);
  chaine_txt[1] = '.';
  chaine_txt[0] = 0x30 + (((freq)/1000)%10);
  myScreen.text(5, ligne, chaine_txt);
  chaine_txt[5] = 0x00;
  chaine_txt[4] = 0x30 + (((freq)/10000)%10);
  chaine_txt[3] = 0x30 + (((freq)/100000)%10);
  chaine_txt[2] = '.';
  chaine_txt[1] = 0x30 + (((freq)/1000000)%10);
  chaine_txt[0] = 0x30 + (((freq)/10000000)%10);
  myScreen.text(0, ligne, chaine_txt); 
}

//******************************************************************
// Display a 2 digits number
void display_number (byte number, char column, char ligne) {
  chaine_txt[2] = 0x00;
  chaine_txt[1] = 0x30 + (number%10);
  chaine_txt[0] = 0x30 + ((number/10)%10);
  myScreen.text(column, ligne, chaine_txt);
}

//******************************************************************
// Return a button value depending on the analog reading
byte read_buttons () {
  int value = analogRead(ANALOG_BUTTONS);
  if ( value<(1+Analog_Noise) ) {
    return 1;
  } else if ( value<(78+Analog_Noise) ) {
    return 2;
  } else if ( value<(146+Analog_Noise) ) {
    return 3;
  } else if ( value<(205+Analog_Noise) ) {
    return 4;
  } else if ( value<(255+Analog_Noise) ) {
    return 5;
  } else {
    return 0;
  }
}

Un analyseur de réseau très simple à base d’AD9850 et Arduino Nano

G4NQX Analyseur de réseau AD9850 Arduino Nano (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2015/10/ddstop NULL.jpg)Rob G4NQX a réalisé un analyseur de réseau scalaire très peu coûteux autour de quelques modules faciles à trouver : un DDS AD9850 et un Arduino Nano (http://rheslip NULL.blogspot NULL.co NULL.uk/2015/08/the-simple-scalar-network-analyser NULL.html). La détection de puissance est faite par un circuit à échelle logarythmique AD8307 et un petit ampli tampon est nécessaire pour augmenter un peu le signal de l’AD9850. La partie logiciel PC pour le contrôle et affichage logiciel est écrite en Python à partir d’une idée originale de PA2OHH.

La plupart des OM n’ayant pas besoin d’analyseur vectoriel, ce montage peut tout à fait faire l’affaire pour vos mesures jusque 30 MHz. Pour plus de détails reportez-vous à l’article en anglais.

 

Nouveautés chez QRP-Labs (G0UPL)

Synthetiseur Si5351 à OCXO QRP Labs G0UPL (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2014/09/Synthetiseur_OCXO_G0UPL NULL.jpg)L’annonce de ces nouveautés datent d’en fait il y a près d’une semaine mais je n’ai pas eu le temps d’en parler. Hans G0UPL va ajouter quelques produits à sa boutique dans les jours qui viennent.

  • Tout d’abord un boîtier en aluminium anodisé noir, pré-découpé pour le kit complet balise Ultimate III (http://www NULL.qrp-labs NULL.com/index NULL.php?route=product/product&product_id=77), avec la place pour toutes les options (filtre de bande, GPS…). Le prix de 20$ (plus port de 10$) est très correct.
  • Un module enfichable pour Arduino Uno qui reprend toutes les fonctionnalités du kit Ultimate III mais sans LCD et boutons (que vous pouvez ajouter vous-même). Cela permettra à ceux qui le veulent de développer eux-même leur logiciel de balise ou de générateur de fréquence en bénéficiant d’une plateforme matérielle performante et à prix raisonnable.
  • Prototype Synthetiseur OCXO G0UPL (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2014/09/Proto_Synthetiseur_OCXO_G0UPL NULL.jpg)Le plus important, c’est un kit synthétiseur de fréquence avec Si5351A et référence 10MHz à OCXO (http://www NULL.hanssummers NULL.com/ocxosynth NULL.html). Le but de ce kit est de remplacer avantageusement les modules chinois avec AD9850. Ceux-ci sont devenus impossibles à approvisionner et présentaient des limites en terme de performance et d’utilisation. Le nouveau kit devrait couvrir de 8 KHz à 160 MHz avec une stabilité de l’ordre de 0,1ppm à chaud, ce qui est excellent!

Le dernier kit est vraiment intéressant et pourra prendre la place dans les kits QRSS Ultimate III existant après une mise à jour du firmware. Avec le module Arduino, cela ouvre la possibilité de construire des générateurs de fréquence très stable jusque 160 MHz.

Un quatrième projet et quant à lui retardé, c’est celui d’un récepteur QRSS comme compagnon au kit balise. Hans cherchait une solution technique innovante mais ses tentatives autour de la démodulation méthode Weaver se sont avérées décevante.

MBDC par KD1JV : Mon premier récepteur à conversion directe

C’est toujours un peu émouvant d’écouter pour la première fois un nouveau transceiver. Durant ces derniers moi j’ai peu à peu assemblé mon kit MultiBand Direct-Conversion de KD1JV (http://xv4y NULL.radioclub NULL.asia/2014/04/12/le-transceiver-mbdc-multi-band-direct-conversion-de-kd1jv-bientot-disponible-chez-hendricks-qrp-kits/). Hier j’en étais aux dernières connexions de potentiomètres et autres “accessoires”. Vint le moment dé vérité avec la mise sous tension et les tests de fonctionnement…

MBDC composants passifs (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2014/06/DSCN3774 NULL.jpg)Prudemment, j’ai commencé par vérifier la présence de toutes les tensions d’alimentation avec une résistance de 50Ω en série afin de limiter le courant et les dégâts potentiels en cas de court-circuit. Tout semblant parfaitement en règle, j’ai installé les circuits intégrés, fini de câbler l’écran LCD et avec la même petite appréhension appliqué à nouveau du 12V sur l’alimentation.

Magie, l’écran LCD s’anime et le poste réagit aux commandes de l’encodeur rotatif et des boutons de MENU et de sélection de filtre. Pris par le temps j’essaie de le connecter rapidement à une antenne mais là, déception, pas moyen de recevoir un signal… Tant pis, je dois partir en ville et je verrais ça à mon retour.

KD1JV MBDC composants installés, photo XV4Y (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2014/06/DSCN3786 NULL.jpg)Quelques heures plus tard je reprends les choses méthodiquement et vérifie avec l’oscilloscope que le DDS AD9850 produit bien un signal (par ailleurs visuellement très distordu sur l’entrée du 74HC4053 utilisé comme mélangeur). J’utilise mon ANTAN comme générateur de signal de dépannage (http://xv4y NULL.radioclub NULL.asia/xv4tuj-station-radioamateur-en-ok20ua/analyseur-dantenne-antan-de-f6bqu/) et là surprise j’entends bien un signal! En fait j’avais juste oublié que le MBDC dispose de deux entrées antenne, une RX/TX après les filtres passe-bas, et une, non-filtrée, pour la réception seulement. Une petite modification dans le câblage et je peux maintenant connecter le récepteur à mon antenne pour écouter un peu les bandes.

La première impression est celle d’une réception très douce, parfois manquant d’ailleurs un peu de pêche sur les signaux faibles. Le filtrage audio offre 3 bandes passantes (WD non filtrée, NB1 environ 3 KHz et NB2 environ 1000 Hz) dont les deux dernières sont légèrement amplifiée, la réception de signaux AM avec le filtre médian (NB1) est très agréable pour la voix (mais un peu étroite pour la musique par exemple). C’est mon premier récepteur à conversion directe, et si je m’attendais à entendre les porteuses AM et écouter les deux bandes latérales (réception DSB), je suis surpris de l’effet de battement quand on est très légèrement décalé de la fréquence de réception. Le signal plutôt pur du DDS doit d’ailleurs accentuer cet effet, mais il est parfois difficile de se défaire de la voix chevrotante des émissions AM. Une bonne calibration du DDS minimisera cette effet.

Assemblage MBDC par KD1JV fini , photo XV4Y (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2014/06/DSCN3807 NULL.jpg)Il ne me reste plus qu’à arpenter les allées du “marché aux puces” locale pour lui trouver un boîtier adéquat. Je vais totalement inhibé la mise en émission du transceiver pour éviter des dommages, le comparateur du DDS (seul alignement à faire) a d’ailleurs été volontairement déréglé pour minimiser le signal transmis. Il faudra aussi que je fasse une mise à jour du firmware du transceiver, la première version livrée ayant un bug et ne comportant pas certaines améliorations comme la fréquence au démarrage programmable (important pour moi).

Article balises WSPR à DDS pour CQ Magazine : codes sources

Table des matières CQ Magazine Février 2014 (http://www NULL.cq-amateur-radio NULL.com/cq_highlights/2014_cq/2014_01_cq/PDF_NO_1_2014_02_CQ_TOC NULL.pdf)L’article que j’ai écrit pour CQ Magazine décrivant comment construire sa propre balise WSPR autonome avec DDS paraîtra le mois prochain. Afin d’alléger la pagination, les codes sources et autres documents volumineux sont publiés sur une page de ce blog. Les curieux peuvent donc en profiter pour jeter un oeil au code source et y faire leurs propres améliorations tant qu’ils respectent la licence GPL…

Ma pomme dans CQ Magazine de Décembre – Kits balise WSPR

Sommaire CQ Décembre 2013 (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2013/11/2013_12_cq_toc NULL.jpg)Le numéro “Spéciale technique” de décembre de CQ Magazine (http://www NULL.cq-amateur-radio NULL.com/) contiendra un article que j’ai écrit à propos des micro-contrôleurs MSP430 et du LaunchPad de Texas Instruments. En fait j’ai proposé cet article il y a plus d’un an mais ils ne trouvaient pas la place adéquate dans leurs colonnes.

Pour continuer sur ma lancée, je leur ai aussi proposé un article détaillant comment fabriquer sa propre balise WSPR ou QRSS avec un DDS AD9850 et un LaunchPad MSP430. C’est en fait la balise que je proposais en kit (http://xv4y NULL.radioclub NULL.asia/2013/03/21/la-balise-wspr-de-g4jvf/) dont je vais publier les schémas et code source. En principe l’article est accepté pour une publication au premier semestre 2014, j’attends leurs demandes de corrections.

Il y a quelques jours Hans G0UPL a lancé son kit Ultimate QRSS beacon 3 (U3) (http://hanssummers NULL.com/ultimate3 NULL.html)qui a maintenant atteint un bon niveau de fonctionnalité et surtout de stabilité (les premières versions ont essuyées pas mal de bogues). Il propose en option les kits module GPS et platine de filtre de bande à commutation par relais. C’est aujourd’hui l’offre la plus intéressante car il en produit des volumes de plusieurs centaines d’exemplaires. Son seul inconvénient c’est que Hans refuse d’en ouvrir le code source et qu’il est donc impossible de faire vos propres adaptations…

Au passage je suis content de voir que mon travail a porté ses fruits et que Hans a intégré dans son codes des idées issues du mien comme la mise en veille du DDS par le bit W34 et le mode WSPR-15 (pour les VLF). Je suis sûr que le côté “reprogrammable” de mes kits l’a aussi poussé à diffuser les binaires de ses firmwares en ligne pour que les utilisateurs puissent faire les mises à jour, ce qu’il ne voulait pas faire au début.

Un wobuloscope à partir d’un Raspberry Pi et d’un DDS

MI0IOU Wobuloscope Raspberry Pi (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2013/10/MI0IOU_wobby1 NULL.jpg)Aujourd’hui, les “vrais techniciens” ne jurent que par l’analyseur de spectre ou l’analyseur de réseau vectoriel (VNA). Ces équipements sont devenus relativement bon marché et des versions “simplifiées” du dernier sont même à la portée de toutes les bourses radioamateurs ou presque. Toutefois, il fut un temps où ces équipements étaient rares et où le Wobuloscope était l’équipement de choix pour les amateurs. Largement suffisant pour caractériser un filtre ou un circuit oscillant, j’ai mis les mains sur un appareil vintage lorsque je préparais ma licence au radio-club RCNEG de F6KKU (merci à F6GUB et F9ZS(SK) au passage).

Tom de MI0IOU a conçu un équipement équivalent mais en le remettant à la mode grâce au Raspberry Pi et aux modules DDS AD9850 d’origine chinoise (http://asliceofraspberrypi NULL.blogspot NULL.co NULL.uk/2013/10/raspberry-pi-wobbulator-introduction NULL.html). Le RPi pilote le DDS et en fait varier la fréquence, et il effectue la lecture d’un signal “redressé” par un circuit de détection simple. Le RPi n’ayant pas d’entrée analogique, un module convertisseur analogique-numérique (ADC) est nécessaire. La couverture est uniquement pour les bandes HF, mais c’est suffisant pour la plupart d’entre nous. Des exemples de mesures sont disponibles sur le blog de MI0IOU (http://asliceofraspberrypi NULL.blogspot NULL.co NULL.uk/2013/10/using-raspberry-pi-wobbulator-to-test_28 NULL.html). Tom met aussi à disposition le code Python à installer sur le Raspberry Pi.

Je vais vous avouer que j’avais commencé à travailler sur un circuit similaire en utilisant un LaunchPad MSP430. L’idée était de reprendre mon code de contrôle de l’AD9850 (http://xv4y NULL.radioclub NULL.asia/2013/03/19/code-source-vfo-avec-dds-ad9850ad9851-et-launchpad-msp430/) et de faire la lecture avec le port ADC du micro-contrôleur. L’affichage était en texte sur l’écran d’un micro-ordinateur connecté au MSP430 part le port USB. Faute de temps libre pour ce type de projet, celui-ci est passé en voie de garage tout comme celui de kit station météo complète (http://xv4y NULL.radioclub NULL.asia/2013/02/04/station-meteo-avec-serveur-web-code-source/) avec modules sans-fil et pression atmosphérique.

Nouveau transceiver QRP multibande par KD1JV : le MBDC [MAJ]

C’est la petite surprise du week-end. Steve de KD1JV a annoncé la disponibilité d’une première pré-série de 50 unités d’un nouveau kit transceiver HF QRP : le MBDC (Multi-Band Direct Conversion) (http://kd1jv NULL.qrpradio NULL.com/mbdc NULL.pdf). C’est un nouveau design conçu à partir de composants traditionnels sauf pour le module DDS AD9850 et l’écran LCD 16×2 avec contrôleur HD44780 du type de ceux vendus par les fournisseurs chinois.

Kit transceiver HF multi-bande KD1JV (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2013/08/KD1JV_MBDC NULL.jpg)Le récepteur est du type à conversion directe et possède une couverture générale tous modes de 100 KHz à 30 MHz. Son taux d’IP3 est élevé, mais dans la version de base l’absence de filtrage en entrée pourra parfois laisser passer les signaux forts sur l’harmonique 3. Le récepteur dispose de 3 positions de filtrage audio : un filtre large-bande, un filtre 600Hz (4-pôles) et les 2 filtres en cascade ce qui donne une bande-passante encore plus étroite pour la CW. Un circuit d’AGC est présent ainsi qu’un réglage du volume indépendant. La sélection de la fréquence se fait par encodeur rotatif, la sélection du pas se fait entre 1Hz et 10 KHz. Des mémoires de fréquences sont disponibles ainsi qu’un RIT.

L’émetteur est bi-bande pour le 160m et le 80m et produit 5 Watts en télégraphie. Un manipulateur électronique Iambic-B à mémoires est intégré. Le design est plutôt novateur en utilisant beaucoup de circuits courants et bon marchés un peu détournés de leur usage premier (74HC4053, 74HC4066, TL084 pour le récepteur, 74HC00 pour le buffer du PA).

Je vais avouer que je suis très très déçu de ne pas avoir pu commander un kit. J’étais absorbé dans un travail de développement sur des micro-contrôleurs ainsi que sur les dernières retouches pour nouveau site QScope.org (http://www NULL.qscope NULL.org/) (plus d’informations d’ici peu) et je n’ai pas lu mes e-mails… L’émetteur ne m’intéresse pas vraiment car il n’y a personne en local sur les bandes-basses. Par contre le récepteur multi-mode à couverture général aurait été parfait pour laisser à mon beau-père à la ferme ou plus tard pour mes enfants.

MAJ : Finalement Steve a décidé d’augmenter le nombre de kits à 100 unités, et j’aurai donc le mien! Le prix annoncé est de 50$ auxquels il faut ajouter le DDS et l’écran LCD que j’ai déjà dans mes tiroirs.

Promotions jusque -50% pour la fête des pères chez ICStation.com

DDS Signal Generator Module AD9850 0-40Mhz Sine Square Wave (http://www NULL.icstation NULL.com/product_info NULL.php?ref=5&products_id=1871&affiliate_banner_id=1)
Mon partenaire ICStation offre actuellement des promotions allant jusque -50% sur les kits, modules et composants électroniques. Sur les modules DDS AD9850 et AD9851 d’Analog Device cette remise atteint 30% par exemple, tous les prix étant port compris.

Cette offre est valable du 16 au 23 juin. Si vous achetez chez eux, merci de passer par ce lien (http://www NULL.ICStation NULL.com/index NULL.php?ref=5)

Code source VFO avec DDS AD9850/AD9851 et LaunchPad MSP430

sVFO DDS AD9850 MSP430 XV4Y (http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2013/03/100_3446 NULL.jpg)J’ai eu le temps de mettre la main à une première version d’un programme simple de VFO pour mon kit à base de DDS AD9850 ou AD9851 (http://xv4y NULL.radioclub NULL.asia/boutique/?slug=product_info NULL.php&products_id=33).

Cette première mouture reste assez simple avec juste un système VFO A et VFO B mais pas de mémoires. Les fonctionnalités des boutons suivent la description ci-dessous.

 

Pour une pression brève sur le bouton :

  • UP : Augmente la valeur du chiffre sélectionné (pour le VFO sélectionné)
  • DOWN : Diminue la valeur du chiffre sélectionné (pour le VFO sélectionné)
  • LEFT : Sélectionne le chiffre de rang supérieur (plus à gauche)
  • RIGHT : Sélectionne le chiffre de rang inférieur (plus à droite)
  • OK : Valide la fréquence et la transmet au DDS

Pour une pression longue (supérieure à 1 seconde) sur le bouton :

  • LEFT : Positionne la fréquence sur la limite basse de la bande suivante
  • RIGHT : Egalise les fréquences des deux VFO (VFO A = VFO B)
  • OK : Change le VFO sélectionné (VFO A/B)

A noté que la broche P2_2 du MSP430 (celle connectée à la broche 3 du connecteur 4 pins) est maintenant utilisée en entrée. Si son état change (passant à 0V par exemple), le VFO actuellement utilisé est momentanément interverti. Cela permet donc de trafiquer en split avec la fréquence transmise par le DDS qui change du VFO A vers VFO B (ou l’inverse) tant que la PTT est pressée.

/* Simple VFO with DDS for MSP430G2553
 * Code for Energia 009

 * By Yannick DEVOS - XV4Y - March 2013
    http://xv4y.radioclub.asia/

    Copyright 2012-2013 Yannick DEVOS under GPL 3.0 license
    Any commercial use or inclusion in a kit is subject to author approval

====
 * Agile Frequency generation using AD9850/9851 DDS
 * Output to Nokia 5110 compatible LCD
 * Check if P2_2 has changed state and switch VFO (like when PTT is pressed to operate split)

Usage for short press of buttons :
- Up / Down      increase or decrease the frequency following the choosen digit
- Left / Right   increase or decrease the digit
- OK             validate the frequency and send it to the DDS
Long press of buttons :
- Left           Set the current VFO to the next band bottom frequency
- Right          VFO A = VFO B
- OK             Switch between VFO A and VFO B

====
Revision history :
v1.00    2013-03-18
         First release

====
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You can download a copy of the GNU General Public License at <http://www.gnu.org/licenses/>
*/

// Here modify to your taste or needs

#define PTT_key          P2_2
#define ANALOG_BUTTONS   A5
//#define AUTO_VALID       // Uncomment if you want the DDS frequency to be udapted automatically after each value change

#define DEFAULT_FREQ    8            // Value 8 in Frequencies array is 14000000
// Here under don't touch anything unless you know what you do

#include <legacymsp430.h>
#include <AD9850.h> // Library for AD9850 control by MSP430

#include <LCD_5110.h>

              // Frequencies (1 Hz precision) you can select
const unsigned long int frequencies[] = {
  137000, 471000, 501000, 1830000, 3500000, 5200000, 7000000, 10100000,
  14000000, 18068000, 21000000, 24890000, 28000000, 50000000, 70000000};

const byte Analog_Noise = 5;    // Margins for reading analog buttons

boolean   vfo=0, saved_PTT;  // VFO A or B and PTT input state
char      multiplier, aff_multiplier, freq_select=DEFAULT_FREQ;
unsigned  long int frequency_A=frequencies[DEFAULT_FREQ], frequency_B=frequencies[DEFAULT_FREQ], debounce;

char chaine_txt[6] = {' ', ' ', ' ', ' ', ' ', 0x00};

//AD9850 myDDS (P1_0, P1_1, P1_2, P1_4);  // Call the AD9850 Library, AD9850 pins for CLOCK, LOAD, DATA and RESET
AD9850 myDDS (P1_1, P1_2, P1_0, P1_4);  // Call the AD9850 Library, AD9851 pins for CLOCK, LOAD, DATA and RESET

LCD_5110 myScreen(P2_3,    // Chip Select *
         P1_6,    // Serial Clock *
         P2_5,    // Serial Data *
         P2_4,    // Data/Command *
         NULL,    // Reset *
         P1_7,    // Backlight
         NULL);  // Push Button 0

//******************************************************************
// Defining pins mode
// Encoding the WSPR sequence

void setup() {
  pinMode(PTT_key, INPUT_PULLUP);

  myDDS.begin();

  pinMode(ANALOG_BUTTONS, INPUT);

  myScreen.begin();
  myScreen.setBacklight(1);

  myScreen.setFont(1);
  myScreen.text(0, 0, "sVFO");
  myScreen.text(0, 2, "AD9850");
  myScreen.setFont(0);
  myScreen.text(0, 4, "v1.00 - XV4Y");
  myScreen.text(0, 5, "Init...");

  delay(1000);

  myDDS.reset();

  myDDS.SetFrequency( frequency_A, 0, false );

  digitalWrite( PTT_key, LOW );

};

//******************************************************************
// Here starts the actual sequence sending

void loop() {
    myScreen.clear();
    myScreen.setBacklight(1);
    myScreen.setFont(1);
    if (vfo == 0) {
      myScreen.text(0, 0, "VFO A");
      myDDS.SetFrequency( frequency_A, 0, false );
    } else {
      myScreen.text(0, 0, "VFO B");
      myDDS.SetFrequency( frequency_B, 0, false );
    };

    myScreen.setFont(0);
    display_freq (frequency_A, 3);
    display_freq (frequency_B, 5);

    while (read_buttons()==5 || read_buttons()==4 || read_buttons()==2) delay(10); // Debounce except for UP/DWN

    while (1) {
      // Update the frequencies display
      if (multiplier > 5) {
        aff_multiplier = multiplier + 2;
      } else if (multiplier > 2) {
        aff_multiplier = multiplier + 1;
      } else {
        aff_multiplier = multiplier;
      };
      myScreen.text(0, 4, "          ");
      if (vfo == 0) {
        myScreen.text(9-aff_multiplier, 4, "^");
      } else {
        myScreen.text(9-aff_multiplier, 4, "v");
      }
      display_freq (frequency_A, 3);
      display_freq (frequency_B, 5);

      // Read the analog buttons input
      if(read_buttons()==1) {            // Up we increase frequency
        delay(200);  // Debounce
        if (vfo == 0) {
          frequency_A = frequency_A + powf(10,(float)multiplier);
        } else {
          frequency_B = frequency_B + powf(10,(float)multiplier);
        };
#if defined AUTO_VALID
        break;
#endif

      } else if (read_buttons()==3) {    // Down we decrease frequency
        delay(200);  // Debounce
        if (vfo == 0) {
          frequency_A = frequency_A - powf(10,(float)multiplier);
        } else {
          frequency_B = frequency_B - powf(10,(float)multiplier);
        };
#if defined AUTO_VALID
        break;
#endif

      } else if (read_buttons()==2) {    // Left we increase multiplier
        debounce = millis();
        while (read_buttons()==2) {  //Debounce
          if ((millis()-debounce)>1000) {  // Long press we do "Band UP"
            freq_select++;
            if (freq_select > 14) freq_select = 0;
            if (vfo == 1) frequency_B = frequencies[freq_select]; else frequency_A=frequencies[freq_select];
            multiplier--;
            break;
          };
        };
        multiplier++;
        if (multiplier > 8) multiplier = 8;

      } else if (read_buttons()==4) {    // Right we decrease multiplier
        debounce = millis();
        while (read_buttons()==4) {  //Debounce
          if ((millis()-debounce)>1000) {  // Long press we do VFO A=B
            if (vfo == 1) frequency_A = frequency_B; else frequency_B=frequency_A;
            multiplier++;
            break;
          };
        };
        multiplier--;
        if (multiplier < 0) multiplier = 0;

      } else if (read_buttons()==5) {    // OK we go out
        debounce = millis();
        while (read_buttons()==5) {  //Debounce
          if ((millis()-debounce)>1000) {  // Long press we switch VFO A/B
            if (vfo == 1) vfo=0; else vfo=1;
            break;
          };
        };
        break;   // Short press we just leave the loop so the frequency is transmitted to the AD9850
      }

      // Check if we are transmitting split (momentaneous VFO A->B switch)
      if (saved_PTT != digitalRead(PTT_key)) {
        saved_PTT = digitalRead(PTT_key);
        if (vfo == 1) vfo=0; else vfo=1;
        break;
      };
    }

};

//******************************************************************
// Display the frequency
void display_freq (unsigned long freq, char ligne) {
  myScreen.text(10, ligne, " Hz ");
  chaine_txt[5] = 0x00;
  chaine_txt[4] = 0x30 + (((freq)/1)%10);
  chaine_txt[3] = 0x30 + (((freq)/10)%10);
  chaine_txt[2] = 0x30 + (((freq)/100)%10);
  chaine_txt[1] = '.';
  chaine_txt[0] = 0x30 + (((freq)/1000)%10);
  myScreen.text(5, ligne, chaine_txt);
  chaine_txt[5] = 0x00;
  chaine_txt[4] = 0x30 + (((freq)/10000)%10);
  chaine_txt[3] = 0x30 + (((freq)/100000)%10);
  chaine_txt[2] = '.';
  chaine_txt[1] = 0x30 + (((freq)/1000000)%10);
  chaine_txt[0] = 0x30 + (((freq)/10000000)%10);
  myScreen.text(0, ligne, chaine_txt); 
}

//******************************************************************
// Display a 2 digits number
void display_number (byte number, char column, char ligne) {
  chaine_txt[2] = 0x00;
  chaine_txt[1] = 0x30 + (number%10);
  chaine_txt[0] = 0x30 + ((number/10)%10);
  myScreen.text(column, ligne, chaine_txt);
}

//******************************************************************
// Return a button value depending on the analog reading
byte read_buttons () {
  int value = analogRead(ANALOG_BUTTONS);
  if ( value<(1+Analog_Noise) ) {
    return 1;
  } else if ( value<(78+Analog_Noise) ) {
    return 2;
  } else if ( value<(146+Analog_Noise) ) {
    return 3;
  } else if ( value<(205+Analog_Noise) ) {
    return 4;
  } else if ( value<(255+Analog_Noise) ) {
    return 5;
  } else {
    return 0;
  }
}