DAC MCP4922

Bonjour a tous,

Pour réaliser une conversion “Digital-to-Analog”, je me suis doté d’un DAC MCP4922 utilisant le Bus SPI pour communiquer.

Problème: c’est la première fois que j’utilise du SPI, je me suis donc lancer à la recherche de tuto mais cela reste peu évident à comprendre pour l’instant.

je suis tombé sur un code qui me parait fonctionnelle, et je souhaiterai que l’on m’explique ( Si possible :slight_smile: ) quelques points afin que je puisse identifier ceux qui m’intéresse ( pour me focaliser dessus).

voici le code entier:

#define DATAOUT 11//MOSI
//#define DATAIN 12//MISO - not used, but part of builtin SPI
#define SPICLOCK  13//sck
#define SLAVESELECT 10//ss

void setup()
{
  byte clr;
  pinMode(DATAOUT, OUTPUT);
// pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device
  
  SPCR = (1<<SPE)|(1<<MSTR);
  clr=SPSR;
  clr=SPDR;
  //delay(10);


}

void write_value(uint16_t sample)
{
  uint8_t dacSPI0 = 0;
  uint8_t dacSPI1 = 0;
  dacSPI0 = (sample >> 8) & 0x00FF;
  dacSPI0 |= 0x10;
  dacSPI1 = sample & 0x00FF;
  digitalWrite(SLAVESELECT,LOW);
  SPDR = dacSPI0;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  
  SPDR = dacSPI1;
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };  
  digitalWrite(SLAVESELECT,HIGH);
  //delay(5);
}

void playNote(int incomingByte)
{
  int diff = 0;
  
  
  //---------
  for (diff=0; diff <= 4095; diff++){
   // write_value (405) ; // 0 --->4095 12 bits  405=1.00V  
   write_value (diff) ; // 0 --->4095 12 bits  405=1.00V  
      //delay(1); run OK avec Voltmetre ana et num , signal à tester à l'oscillo
      delayMicroseconds(200); //run ok mais met les Voltmetres ana et digital dans les choux, pas etonnant = oscillo a faire 
   } 


}
//void noteOff()
//{
//  write_value(0);
//}
int incomingByte = 0;

void loop()
{
  
         playNote(incomingByte);
   
}

j’ai compris ce que réalise le code mais j’ai quelques questions sur les fonctions suivantes:

void write_value(uint16_t sample)
{
  uint8_t dacSPI0 = 0;
  uint8_t dacSPI1 = 0;
  dacSPI0 = (sample >> 8) & 0x00FF;
  dacSPI0 |= 0x10;
  dacSPI1 = sample & 0x00FF;
  digitalWrite(SLAVESELECT,LOW);
  SPDR = dacSPI0;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };

“sample” est sur 16bits, le DAC étant de 12 bits, on coupe le mot en deux. mais comment procéder si la valeur de sample est déjà sur 12 bits ?

En suite dans cette fonction :

void playNote(int incomingByte)
{
  int diff = 0;
  
  
  //---------
  for (diff=0; diff <= 4095; diff++){
   // write_value (405) ; // 0 --->4095 12 bits  405=1.00V  
   write_value (diff) ; // 0 --->4095 12 bits  405=1.00V  
      //delay(1); run OK avec Voltmetre ana et num , signal à tester à l'oscillo
      delayMicroseconds(200); //run ok mais met les Voltmetres ana et digital dans les choux, pas etonnant = oscillo a faire 
   } 

}

int incomingByte = 0;

pourquoi créer une variable “int incomingByte”? elle reste toujours a zero !! :confused:

Enfin, je voudrais que le DAC convertisse un valeur renvoyer par un capteur:
le code suivant me permet de décoder la réponse de mon capteur ( au format numérique ):

void setup()
{
 
  
  Serial.begin (115200);
  noInterrupts();           // disable all interrupts

  /* initialisation timer3 mode CaptureToCompare*/

  TCCR3A = 0;
  TCCR3B = 0;
  TCNT3  = 0;

  OCR3A = 125; // compare match register 16MHz/256/2Hz // interrupt every 1 ms
  TCCR3B |= (1 << WGM32);   // CTC mode
  TCCR3B |= (1 << CS32);    // 256 prescaler
  TIMSK3 |= (1 << OCIE3A);  // enable timer compare interrupt

  /*Initialisation timer5 mode InputCompare*/

  TCCR5A = 0;
  TCCR5B = 0;
  TCNT5  = 0;
  TCCR5B |= (1 << CS50);// 8 prescaler
  TCCR5B |= (1 << ICES5);// Rising


  interrupts();             // enable all interrupts
}

/*Trigger*/

ISR(TIMER3_COMPA_vect)          // interrupt fonction
{
  TIMSK5 |= (0 << ICIE5);
  DDRB = (1 << DDB4); //Pin 10 en mode OUTPUT
  PORTB = (0 << PORTB4); // Pin 10 à 0
  delayMicroseconds(9); // temps a l'état bas du trigger
  PORTB = (1 << PORTB4); // Pin 10 à 1

  // PASSEAGE EN INPUT POUR LIRE LA REPONSE DU CAPTEUR
  DDRB = ( 0 << DDB4); // Pin 10 en entrée et attente de la réponse capteur
  TIMSK5 |= (1 << ICIE5);//enable Input Capture interrupt
}


ISR(TIMER5_CAPT_vect)
{
  TCNT5 = 0;
  Buffer[ buff_index ] = (ICR5); //(48 = 3*16) 3 = prédiv capteur, et 16 clock/µs
  Buffer[ buff_index ] = ((ICR5 / 43) - 11); //(48 = 3*16) 3 = prédiv capteur, et 16 clock/µs
  buff_index ++;
  if (buff_index == size_buff)
  {
    buff_index = 0;
  }
}

void loop()
{
  Serial.println("New trame");
  for ( i = 4, j = 0; i < 8, j < 4; i++, j++)
  {
    Nibble [j] = Buffer [i];
    val_Num_16 = (Nibble[3] + (Nibble[2] << 4) + (Nibble[1] << 8) + (Nibble[0] << 12));
  }
  //Serial.println (val_Num_16); // buffer reader
  val_Num_12 = map(val_Num_16, 0, 65535, 0, 4095);
  Serial.println (val_Num_12); // buffer reader
}

comment doit-je m’y prendre pour réaliser la conversion ?

cordialement

Ausmoz1:
"sample" est sur 16bits, le DAC étant de 12 bits, on coupe le mot en deux. mais comment procéder si la valeur de sample est déjà sur 12 bits ?

C'est normal. La valeur de sample doit être sur 12bits, mais la variable sample est sur 16 bits (il n'existe pas de variable 12 bits en C).

Ausmoz1:
pourquoi créer une variable "int incomingByte"? elle reste toujours a zero !!

Le programme n'est visiblement pas terminé ou pas fonctionnel

Ausmoz1:
comment doit-je m'y prendre pour réaliser la conversion ?

write_value(val_Num_12);

Merci Kamil, j’ai donc réduis le code au minimum sa donne sa:

#define DATAOUT 51//MOSI
#define SPICLOCK  52//sck
#define SLAVESELECT 53//ss

void setup()
{
  byte clr;
  pinMode(DATAOUT, OUTPUT);
// pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device
  
  SPCR = (1<<SPE)|(1<<MSTR);
  clr=SPSR;
  clr=SPDR;
  //delay(10);


}

void write_value(uint16_t sample)
{
  uint8_t dacSPI0 = 0;
  uint8_t dacSPI1 = 0;
  dacSPI0 = (sample >> 8) & 0x00FF;
  dacSPI0 |= 0x10;
  dacSPI1 = sample & 0x00FF;
  digitalWrite(SLAVESELECT,LOW);
  SPDR = dacSPI0;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  
  SPDR = dacSPI1;
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };  
  digitalWrite(SLAVESELECT,HIGH);
  //delay(5);
}


void loop()
{
  
         write_value(1800);
   
}

le problème est que la sortie de mon DAC s’atture a 5V quand “Sample = 2000” alors que la valeur de mon “Val_Num_12” varie entre 0 et 4095 :confused:
je pourrai simplement la mapper dans cette intervalle mais j’aimerai comprendre pourquoi, “sample” ne va pas juska 4095 ( 12 bits)

bonjour
perso j'utilise cette lib

Merci Artouste, tu pourrai me donner ( si possible ) un exemple en l’appliquant a mon code pour la convertion de “val_Num”?

void setup()
{
 
  
  Serial.begin (115200);
  noInterrupts();           // disable all interrupts

  /* initialisation timer3 mode CaptureToCompare*/

  TCCR3A = 0;
  TCCR3B = 0;
  TCNT3  = 0;

  OCR3A = 125; // compare match register 16MHz/256/2Hz // interrupt every 1 ms
  TCCR3B |= (1 << WGM32);   // CTC mode
  TCCR3B |= (1 << CS32);    // 256 prescaler
  TIMSK3 |= (1 << OCIE3A);  // enable timer compare interrupt

  /*Initialisation timer5 mode InputCompare*/

  TCCR5A = 0;
  TCCR5B = 0;
  TCNT5  = 0;
  TCCR5B |= (1 << CS50);// 8 prescaler
  TCCR5B |= (1 << ICES5);// Rising


  interrupts();             // enable all interrupts
}

/*Trigger*/

ISR(TIMER3_COMPA_vect)          // interrupt fonction
{
  TIMSK5 |= (0 << ICIE5);
  DDRB = (1 << DDB4); //Pin 10 en mode OUTPUT
  PORTB = (0 << PORTB4); // Pin 10 à 0
  delayMicroseconds(9); // temps a l'état bas du trigger
  PORTB = (1 << PORTB4); // Pin 10 à 1

  // PASSEAGE EN INPUT POUR LIRE LA REPONSE DU CAPTEUR
  DDRB = ( 0 << DDB4); // Pin 10 en entrée et attente de la réponse capteur
  TIMSK5 |= (1 << ICIE5);//enable Input Capture interrupt
}


ISR(TIMER5_CAPT_vect)
{
  TCNT5 = 0;
  Buffer[ buff_index ] = (ICR5); //(48 = 3*16) 3 = prédiv capteur, et 16 clock/µs
  Buffer[ buff_index ] = ((ICR5 / 43) - 11); //(48 = 3*16) 3 = prédiv capteur, et 16 clock/µs
  buff_index ++;
  if (buff_index == size_buff)
  {
    buff_index = 0;
  }
}

void loop()
{
  Serial.println("New trame");
  for ( i = 4, j = 0; i < 8, j < 4; i++, j++)
  {
    Nibble [j] = Buffer [i];
    val_Num_16 = (Nibble[3] + (Nibble[2] << 4) + (Nibble[1] << 8) + (Nibble[0] << 12));
  }
  //Serial.println (val_Num_16); // buffer reader
  val_Num_12 = map(val_Num_16, 0, 65535, 0, 4095);
  Serial.println (val_Num_12); // buffer reader
}

cordialement.

Ausmoz1:
je pourrai simplement la mapper dans cette intervalle mais j'aimerai comprendre pourquoi, "sample" ne va pas juska 4095 ( 12 bits)

Je ne connais pas ce dac, mais est ce qu'il n'y a pas des registres à configurer (gain, offset, ...) ?

Ausmoz1:
Merci Artouste, tu pourrai me donner ( si possible ) un exemple en l'appliquant a mon code pour la convertion de "val_Num"?

il y a un exemple MCP49X2_dual-demo fourni avec la lib qui est facilement adaptale