Comment interfacer un CNA externe en SPI ?

NIVEAU 3

Objectifs

  • Mettre en oeuvre un convertisseur analogique-numérique externe SPI

Pré-requis

MCP4921 – DAC 12bits

Le composant MCP4921 de Microchip (documentation) est un convertisseur numérique analogique sur 12 bits. Il peut être piloté via une interface SPI jusqu’à 20~MHz.

Attention, la version MCP4921 n’est dotée que d’une seule sortie.

Étant donné qu’il s’agit d’un convertisseur numérique-analogique, la transmission se fait dans un seul sens, du maître vers l’esclave. Ce composant n’a donc pas de broche SDO (qui ne sert qu’à la transmission d’information de l’esclave vers le maître).

Transfert de données

Pour pouvoir appliquer une tension sur la sortie du convertisseur, il faut lui transmettre :

  • 4 bits de contrôle : tension de référence, voie à modifier…
  • 12 bits de données à convertir.

La documentation technique fournit les informations suivantes à propos des bits de contrôle :

Par exemple, pour convertir la donnée binaire 0b010010010000 ( = 1168 décimal = 3.3V * 1168 / 4096 = 0.94V) sur la voie A avec une référence standard, il faut transmettre :

  • octet 1 : 0b0011 ( 0100
  • octet 2 : 0b1001 0000 )

Connexion SPI entre la carte Nucleo L476RG et le DAC MCP4921

Le convertisseur MCP4921 attend des commandes sous forme d’une transmission de type SPI. Il faut donc avant tout déclarer les broches utiles à la communication.

SPI ad7303(D11,D12,D13); // mosi, miso, sck
DigitalOut CS(D8);
DigitalOut LDAC_MCP(D9);

La broche MOSI de la carte Nucléo (D11) sera reliée sur la broche SDI du composant, les broches SCK (D13 de la carte Nucléo) seront reliées ensemble, la broche CS (ici D8) de la carte Nucléo à la broche CS du composant, la broche LDAC du composant à la broche LDAC_MCP (D9) de la carte Nucléo.

Fonction de conversion

void MCP4921_update_A(int val){
     char cmd = 0b0011 << 4;
     cmd += (val >> 8);
     CS_MCP = 0;
     spi.write(cmd);
     cmd = val & 0xFF;
     spi.write(cmd);
     CS_MCP = 1;
     LDAC_MCP = 0;
     LDAC_MCP = 1;
}

DAC AD7303 – DAC 8bits

Le composant AD7303 de Analog Devices (documentation) est un convertisseur numérique analogique sur 8 bits à 2 sorties. Il peut être piloté via une interface SPI jusqu’à 30~MHz.

Diagramme Fonctionnel – AD7303

On peut remarquer sur le diagramme fonctionnel précédent que ce convertisseur est équipé de 2 sorties. Elles sont indépendantes.

Étant donné qu’il s’agit d’un convertisseur numérique-analogique, la transmission se fait dans un seul sens, du maître vers l’esclave. Ce composant n’a donc pas de broche SDO (qui ne sert qu’à la transmission d’information de l’esclave vers le maître).

Transfert de données

Pour pouvoir appliquer une tension sur l’une des deux sorties du convertisseur, il faut lui transmettre :

  • un octet de contrôle : tension de référence, voie à modifier, utilisation des registres de stockage…
  • un octet de données à convertir.

La documentation technique fournit les informations suivantes à propos des bits de contrôle :

Transmission de données en SPI – AD7303

Il s’agit d’envoyer une série de bits qui correspondent à l’utilisation souhaitée du convertisseur. Les différentes valeurs et actions associées sont présentées dans la suite de la documentation constructeur.

Bits de contrôle (partie 1) – AD7303
Bits de contrôle (partie 2) – AD7303

Par exemple, si l’on souhaite convertir la valeur 0x54 vers la voie A directement par le registre du DAC, en utilisant la référence interne, il faudra transmettre l’information suivante : 0b 0 0 1 0 0 0 1 1 et 0x54.

Modes de fonctionnement / Références

La tension de sortie, notée \(V_{OUT}\), vaut : \(V_{OUT} = 2 \cdot V_{REF} \frac{N}{256}\) où \(V_{REF}\) est une tension de référence et \(N\) la valeur numérique (sur 8 bits) à convertir en tension.

Ce convertisseur numérique-analogique possède sa propre référence de tension interne, qui vaut \(V_{REFINT} = \frac{V_{DD}}{2}\). Lorsque cette référence est utilisée, on a alors une tension de sortie qui vaut : \(V_{OUT} = V_{DD} \frac{N}{256}\). La dynamique va alors de 0 à \(V_{DD}\) en sortie.

Il est également possible d’appliquer une référence externe de tension, sur la broche notée REF du composant. Dans ce cas, la tension de sortie vaudra : \(V_{OUT} = REF \cdot \frac{N}{256}\) où \(N\) est la valeur à convertir en tension et \(REF\) le potentiel appliqué sur la broche REF.

Attention ! La tension que l’on peut appliquer sur la broche REF est limitée par la tension d’alimentation du composant.

Utilisation du DAC avec la carte Nucleo L476RG

Connexion SPI entre la carte Nucleo L476RG et le DAC AD7303

Le convertisseur AD7303 attend des commandes sous forme d’une transmission de type SPI. Il faut donc avant tout déclarer les broches utiles à la communication.

SPI ad7303(D11,D12,D13); // mosi, miso, sck
DigitalOut CS(D10);

Le câblage se fera alors de la façon suivante :

Une fois cette étape faite, il faut à présent initialiser la liaison, dans la fonction main par exemple, avec les paramètres adéquats : 8 bits par paquet, mode 0 et fréquence inférieure à 30 MHz.

ad7303.format(8,0);          
ad7303.frequency(10000);     

Bibliothèque de fonctions

Afin de simplifier l’utilisation de le DAC externe, il peut être intéressant d’écrire quelques fonctions de base. En particulier, il peut être intéressant d’avoir des fonctions qui permettent :

  • d’initialiser le DAC – void initDAC(void)
  • de convertir la valeur passée en paramètre sur la voie A, en utilisant la référence de tension interne – void changeDACA_IntRef(char val)
  • de convertir la valeur passée en paramètre sur la voie B, en utilisant la référence de tension interne – void changeDACB_IntRef(char val)
  •  

Cette bibliothèque peut aussi contenir les constantes suivantes :

#define     INT_REF         0x00        // Internal reference voltage Vref ? VDD/2
#define     EXT_REF         0x80        // External reference voltage

#define     POWER_DOWN_DACA 0b00001000  // Switch off DACA
#define     POWER_DOWN_DACB 0b00010000  // Switch off DACB

#define     UPDATE_DACA     0b00000011  // Update DACA from shift register
#define     UPDATE_DACB     0b00100111  // Update DACB from shitf register

Initialisation de la communication

Pour initialiser la communication entre le DAC et la carte Nucleo L476RG, on peut suivre les étapes suivantes. Ces différentes instructions peuvent être mises dans une fonction nommée void initDAC(void).

CS_DAC = 1;
wait_ms(10);

Ces premières lignes permettent d’initialiser la broche CS en sortie et de lui affecter la valeur ‘1’.

CS_DAC = 0;

Pour démarrer ensuite une communication SPI avec le DAC, la sortie CS est passée à ‘0’.

ad7303.write(INT_REF | UPDATE_DACA);
ad7303.write(0x80);

Ensuite on transmet les données nécessaires à la mise à jour du DAC :

  • la première donnée correspond à une mise à jour de la voie A, en utilisant une tension de référence interne
  • la seconde à la donnée à convertir, ici la valeur 128 (correspondant sur 8 bits à la tension VREF/2).
CS_DAC = 1;
wait_ms(10);
CS_DAC = 0;

On stoppe ensuite la discussion avec le DAC en remettant à ‘1’ la broche CS. Après un délai de 10 ms, on relance une nouvelle transmission.

ad7303.write(INT_REF | UPDATE_DACB);
ad7303.write(0x80);

On envoie cette fois-ci le même type d’informations mais sur la voie B.

CS_DAC = 1;
wait_ms(10);
return;

Enfin, on stoppe la communication SPI avec le DAC.

L’ensemble des commandes sont transmises à l’aide de la fonction void write(char data) de la classe SPI de la bibliothèque mbed.h.

Fonction d’initialisation complète

void initDAC(void){
    CS_DAC = 1;
    wait_ms(10);
    CS_DAC = 0;
    ad7303.write(INT_REF | UPDATE_DACA);
    ad7303.write(0x80);
    CS_DAC = 1;
    wait_ms(10);
    CS_DAC = 0;
    ad7303.write(INT_REF | UPDATE_DACB);
    ad7303.write(0x80);
    CS_DAC = 1;
    wait_ms(10);
    return;
}

Conversion de données

Référence interne

Pour pouvoir convertir une donnée sur la sortie A (ou B) en utilisant une référence de tension interne, il faut suivre les étapes suivantes, que l’on peut écrire dans une fonction nommée changeDACA_IntRef(char val).

void changeDACA_IntRef(char val) {
        CS_DAC = 0;
        ad7303.write(INT_REF | UPDATE_DACA);
        ad7303.write(val);
        CS_DAC = 1;
        return;
}

Cette fonction permet de lancer la transmission sur la liaison SPI puis d’envoyer la commande permettant de configurer le DAC sur sa sortie A en utilisant la référence interne de tension. Le second octet transmis correspond à la donnée à convertir passée en paramètre de la fonction (ici sur 1 octet car le convertisseur est sur 8 bits).

Référence externe

Pour pouvoir convertir une donnée sur la sortie A (ou B) en utilisant une référence de tension externe, il faut suivre les étapes suivantes, que l’on peut écrire dans une fonction nommée changeDACA_ExtRef(char val).

void changeDACA_ExtRef(char val) {
        CS_DAC = 0;
        ad7303.write(EXT_REF | UPDATE_DACA);
        ad7303.write(val);
        CS_DAC = 1;
        return;
}

Cette fonction permet de lancer la transmission sur la liaison SPI puis d’envoyer la commande permettant de configurer le DAC sur sa sortie A en utilisant la référence externe de tension. Le second octet transmis correspond à la donnée à convertir passée en paramètre de la fonction (ici sur 1 octet car le convertisseur est sur 8 bits).

Exemples d’utilisation

Transmission d’une rampe de tension à l’aide d’un DAC AD7303

Méthode standard

#include "mbed.h"

SPI ad7303(D11,D12,D13); // mosi, miso, sck
DigitalOut CS(D10);

int main(){
    int i;

    ad7303.format(8,0);          
    ad7303.frequency(100000);
    initDAC(); 

    while(1){
        for(i = 0; i < 256; i++){
            changeDACA_IntRef(i);
            wait_ms(1);
        }
    }
}   

Méthode par interruption / timer

#include "mbed.h"

SPI ad7303(D11,D12,D13); // mosi, miso, sck
DigitalOut CS(D10);
Ticker tik;
int i;

void action(void){
    if(i > 256)  i = 0;
    else i++;
    changeDACA_IntRef(i);
}

int main(){
    i = 0;
    ad7303.format(8,0);          
    ad7303.frequency(100000);
    initDAC(); 
    tik.attach(&action, 0.001);

    while(1){}
}   

Transmission d’un signal sinusoïdal à l’aide d’un DAC AD7303

#include "mbed.h"

/* Tableau contenant les points du sinus */
const int sinu[64] =  { 2048, 2149, 2250, 2349, 2445, 2537, 2624, 2706,
                        2781, 2848, 2908, 2959, 3001, 3033, 3056, 3069,
                        3071, 3064, 3046, 3018, 2981, 2934, 2879, 2815,
                        2744, 2666, 2581, 2492, 2398, 2300, 2200, 2099,
                        1996, 1895, 1795, 1697, 1603, 1514, 1429, 1351,
                        1280, 1216, 1161, 1114, 1077, 1049, 1031, 1024,
                        1026, 1039, 1062, 1094, 1136, 1187, 1247, 1314,
                        1389, 1471, 1558, 1650, 1746, 1845, 1946, 2048};

SPI ad7303(D11,D12,D13); // mosi, miso, sck
DigitalOut CS(D10);

/* Programme principal */
void main(void){
    int i;

    ad7303.format(8,0);          
    ad7303.frequency(100000);
    initDAC(); 

    while(1){
        for(i = 0; i < 64; i++){
            changeDACA_IntRef(sinu[i]/16.0);
            wait_ms(1);
        }
    }
}   

Dans cet exemple, une période du signal sinusoïdal a été numérisée sur 64 points sur 12 bits. Il faut alors diviser par 16 pour obtenir le signal sur 8 bits.

MInE Prototyper Prototyper avec Nucleo et MBED

Nucléo – Interfacer un convertisseur numérique-analogique externe en SPI