Besoin d'aide interfaçage ESP32, ADC, Barrette CCD

Bonjour à tous et toutes,

C'est mon premier post ici donc je tâcherai d'être le plus clair possible.
Pour un projet perso je souhaite interfacer un Arduino esp32 nano avec le capteur CCd toshiba TCD1304.

J'y suis arrivé, malgré la datasheet plutôt obscure j'obtiens mon signal de TCD1304.

Cependant mon signal capteur est évidemment analogique et au vue de la fréquence l'ADC intégrée à l'esp elle n'est pas suffisante

Avec le temps d'intégration le plus lent le capteurs envoie les signaux des pixels en 14ms. Compte tenu du nombre de pixel (~3700) cela me fais un fréquence de 264 000Hz minimum pour mon ADC.

J'ai donc dû utiliser un convertisseur externe, dans mon cas le ADS7818.
J'utilise le protocole SPI afin d'interfacer l'ADC, cependant les valeurs numérique ressemble plus à du bruit qu'autre chose. J'ai pourtant vérifier que la période de capture se déroule bien lorsque la barrette CCD envoie la valeurs des pixels.
(j'ai vérifier que mon signal analogique du CCD est bien en entré de broche +In)

Je ne sais plus où chercher si vous pourriez m'aider ou me donner des pistes cela m'aiderai grandement !

Questions:

  1. Appeler les fonction ADC_setup ainsi que TCD_setup dans void setup(), ne donne pas le résultat attendu : mon signal SH ne se lance pas. Mais lorsque j'appelle les fonctions dans void loop() SH se lance correctement.

  2. Mon signal ICG qui permet de "réinitialiser" la barette CCD est très irrégulier et se lance deux fois à 1.5 microseconde d'écarts (voire image 1).

Mais l'écart en plus de ces 2 impulsions sont irrégulier (voire image 2 signal jaune).

  1. Mon ADC convertie une sorte de bruit:
    Figure 2024-07-22 140712
    Je vérifie pourtant la période de conversion à l'aide du pin 20 dans la fonction ADC_converter qui reste à l'état HIGH du début jusqu'à la fin de la conversion.

Voici une image des signaux SCK en vert et CS en jaune:

  1. Afin d'avoir un réel délais entre chaque capture de 600ms nécessaire à l'envoie des données via le port série je dois mettre un délais de 200ms et non 600ms (lorsque je renseigne 600ms le délais entre chaque capture est >1s).

J'espère avoir été le plus clair possible ! Si des informations importantes manque n'hésitez pas à me le faire remarquer.

main.cpp


/* Problème capture de bruit par ADC environs 20% du temps
Peux aussi être problème de synchro TCD avec ICG
*/

#include "Arduino.h"
#include "ADC.h" //Inclus les fonctions concernant l'ADC
#include "TCD.h" //Inclus les fonctions concernant le TCD
#include "LED.h" //Inclus les fonctions de controle de LED
unsigned long currentTime = 0; 
unsigned long previousTime = 0;
void setup() {
  //Unique set-up de tous les sous-programmes
  Serial.begin(115200); //Initialise la fréquence de communication série
  delay(1);
  LED_setup();//Initialise les pin et la config des LEDS
  //ADC_setup(); 
  //TCD_setup();//Ne fonctionne pas correctement dans setup
}

void loop() {

  //LED_select(1); //Selection de la LED
  //LED_power(250);// selection de l'intensité

  currentTime= millis();
  if (currentTime- previousTime >= 200){//L'affichage des valeurs via le port série prend environs 600ms
    previousTime= currentTime;
    ADC_setup();
    TCD_setup();
    ADC_converter();
    TCD_clock();
    
  }
  delay(1);
  
}

TCD.cpp

//TCD13  04
#include "TCD.h"
const short pinSH = 2;
const short pinmasterclock = 3;  // Pin de sortie
const short pinICG = 4;

const int frequency_SH = 20000; //Tint min 20kHz
const int frequency_master = 925926;  // 1 MHz /925926

const short pwmmasterclock = 2;
const short pwmSH = 1;

const short resolution = 1;  // Résolution en bits des signaux d'horloges utilisant le PWM

int currentTime_TCD;
int lastSampleTime_TCD = 0;
const short samplingInterval_TCD = 8;

void TCD_setup() {
  pinMode(pinmasterclock, OUTPUT);
  pinMode(pinSH, OUTPUT);
  pinMode(pinICG, OUTPUT);

  // Configurer les canaux PWM
  
  ledcSetup(pwmmasterclock, frequency_master, resolution);
  // Attacher le canal PWM au pin spécifié
  ledcAttachPin(pinmasterclock, pwmmasterclock);
  // En résolution 1 bit, 1 signifie 50%
  

  ledcSetup(pwmSH, frequency_SH, resolution);
  // Attacher le canal PWM au pin spécifié
  ledcAttachPin(pinSH, pwmSH);
}

void TCD_clock() {
  ledcWrite(pwmmasterclock, resolution);
  digitalWrite(pinICG, LOW);
  ledcWrite(pwmSH, resolution);  // En résolution 1 bit, 1 signifie 50%
  delayMicroseconds(6); 
  digitalWrite(pinICG, HIGH);

}

ADC.cpp

//ADC ADS7818
#include "ADC.h"

// Définir les broches SPI personnalisées
#define SCK_PIN 13
#define MISO_PIN 12  //ESP32 nano pin pour SPI définis
#define MOSI_PIN 11
#define CS_PIN 10 
#define SPI_FREQUENCY 8000000

const unsigned long samplingInterval_ADC = 0.1;  //intervalle échantillonage en microsecondes
const unsigned int numSamples = 4000;            //nombre d'échantillons attendu
uint16_t samples[numSamples];                    //Tableau stockage échantillons
short tempsTot;
float volt;
int sampleIndex;
//Défini les différents state de la machine à état
enum State { INI,
             CAPTURE,
             AFFICHAGE };
State adcState = INI; //Défini l'état initiale de la machine à état

//Sous-fonction d'initialisation de l'interfaçage avec l'ADC
void ADC_setup() {
  pinMode(20, OUTPUT); //Permet le controle de la période de conversion
  pinMode(CS_PIN, OUTPUT);// Initialisation de la broche CS
  digitalWrite(CS_PIN, HIGH);
  // Initialisation du SPI avec les broches définis
  SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS_PIN);
}

//Sous-fonction permettant le transfert des données via la liaison série
//On attends des données sur 16bits
void ADC_display(uint16_t* data) {
  for (unsigned int i = 0; i < numSamples; i++) {
    volt = samples[i];
    Serial.print(volt);
    Serial.print(" ");
  }
  Serial.println("end");
}

//Lance le protocole de communication ainsi que les signaux/horloges permettant la conversion des données
//Stock les données dans la matrice "samples[]" jusqu'à atteindre numSamples définie
void ADC_converter() {
  switch (adcState) {
    case INI:
      SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE2));  // Définir les paramètres SPI (fréquence, ordre des bits, mode SPI)
      adcState = CAPTURE; //Une fois les settings effectués change d'état
      break;

    case CAPTURE:
      digitalWrite(20, HIGH);  //VERIFIE DECALLAGE ENTRE SH ET ADC
      for (sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) {
        digitalWrite(CS_PIN, LOW);
        //On attends un entier de 12bits mais 16bits est envoyé
        //Décallage de 3 bits de poids faibles
        samples[sampleIndex] = SPI.transfer16(0x0000) >> 3;
        digitalWrite(CS_PIN, HIGH);
        delayMicroseconds(samplingInterval_ADC);
      }
      
      SPI.endTransaction(); //Termine la communication via SPI une fois la matrice complété
      digitalWrite(20, LOW);  //VERIFIE DECALLAGE ENTRE SH ET ADC
      //ADC_display(samples); //Appel la fonction d'envoie sur la liaison série les données
      adcState = AFFICHAGE; //Une fois les données récupérées change d'état
      break;

    case AFFICHAGE:
      ADC_display(samples);  //l'affiche prend environs 600ms a être effectué ce cette manière
      adcState = INI; //Reinitialise l'état afin de convertir des données
      break;
  }
}

Je n'ai pas encore tout regardé mais ça ce n'est pas bon

Un unsigned long ne peut pas valoir 0.1 puisque c'est un entier.

De plus, la doc indique

Notes and Warnings
This function works very accurately in the range 3 microseconds and up to 16383. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times. Larger delay times may actually delay for an extremely brief time.


Tu as bien vérifié la synchronisation des signaux SH, ICG avec ΦM ainsi qu'avec le signal d'échantillonnage de ton ADC.

Il y a un timing un peut serré entre le front descendant de ICG et le front montant de SH.
L'as-tu vérifié?


Ce serait bien de mettre cette voie en liaison continue.
Pour ce qui concerne la régularité des impulsions, je ne suis pas loin de penser que ton oscillo est un peu dans ces limites ici. Je ne sais pas trop quel est sa fréquence d'échantillonnage mais en 20ms par division elle ne doit pas être très élevée et il doit probablement rater des impulsions.

Tout d'abord merci pour ta réponse !

Un unsigned long ne peut pas valoir 0.1 puisque c'est un entier.

Oui tu as raison j'avais besoin d'un délais plus court, est-ce que tu connaitrais un moyen d'avoir des delay très court ? Quitte à effectuer des actions inutiles à la place d'une fonction delayMicroseconds.

Tu as bien vérifié la synchronisation des signaux SH, ICG avec ΦM ainsi qu'avec le signal d'échantillonnage de ton ADC.
Il y a un timing un peut serré entre le front descendant de ICG et le front montant de SH.
L'as-tu vérifié?

J'ai vérifié au début de ma recherche mais une fois l'interfaçage effectué cela jamais semblé poser de problème. Une fois SH et ΦM lancé il me suffit de lancer ICG avec le bonne intervalle pour obtenir le signal du CCD.
Tu penses que cela peux poser des problèmes malgré que cela fonctionne ?

Ce serait bien de mettre cette voie en liaison continue.
Pour ce qui concerne la régularité des impulsions, je ne suis pas loin de penser que ton oscillo est un peu dans ces limites ici. Je ne sais pas trop quel est sa fréquence d'échantillonnage mais en 20ms par division elle ne doit pas être très élevée et il doit probablement rater des impulsions.

Que veut tu dire par liaison continue ?
J'aurai l’oscilloscope demain matin je suis sur que c'est 100MHz ou 200Mhz.
Sur l'image 1 tu peux aussi observer ICG avec 500µs/div.
J'ai du mal l'expliquer, pour ICG j'ai 2 impulsion à 1.5ms qu'une autre irrégularité sur l'image 2. Je n'arrive à expliquer aucune des deux, elles ne sont pas désirées.

Merci beaucoup et bonne soirée !

Il semblerait que mon pulse ICG est perturbé par une partie de mon code ADC_converter. Lorsque que je le commente ICG cesse les irrégularités.
Je n'arrive pas à identifier la partie qui pose problème par contre :confused:

Liaison AC
image

Liaison DC
image

En liaison AC, les fronts peuvent être déformés, La ligne de base du signal bouge en fonction de la valeur moyenne du signal et cela peut masquer des problèmes. De plus cela peut aussi perturber le déclenchement puisque la valeur moyenne du signal bouge.


Oui, c'est vrai sur les fréquences de balayage les plus rapides mais ce n'est plus vrai sur les plus lentes.
On va prendre un exemple numérique simple.
Admettons que la mémoire d'échantillonnage de l'oscillo soit de 1000 mots.
A 100MHz (donc un échantillon toutes les 10ns) tu peux capturer au maximum 10μs
de signal.
Comme la quantité de mémoire est fixe, si tu veux acquérir 100μs de signal il faudra réduire la fréquence d'échantillonnage d'un facteur 10.
Les conséquences de cette réduction de la fréquence d'échantillonnage sont multiples

  • perte de certains signaux (les impulsions d'une durée inférieure à la fréquence d'échantillonnage sont perdues ou présentes de temps en temps)
  • perte de précision sur la mesure de la durée d'impulsions, les fronts d'un signal peuvent sembler instables

C'est pour cela que l'on trouve sur les oscillo une fonction "base de temps retardée" ou "zoom" qui permet de visualiser plus précisément une portion du signal avec une fréquence d'échantillonnage plus élevée.

J'ai effectué de nouveau la mesure en liaison continue, le résultat semble le même. Mon oscilloscope est un Tektronix TDS2012C, 100MHz 2GS/s.

Comme mentionné lorsque j'exécute uniquement TDC_clock et commente ADC_converter, mon signal ICG est bien régulier. Il doit y avoir un élément perturbateur dans ADC_converter, mais ayant assez peu d'expérience je n'arrive pas à identifier les causes possibles.
Voici pour illustrer:
Signal ICG sans ADC_converter, il corresponds à mes attentes délais de 20ms entre chaque signal:

Signal ICG avec ADC_converter, on observe ici 2 signaux à 2.5ms d'intervalle qui n'ont pas lieu d'exister.

Signal ICG avec ADC_converter, on observe bien ici les signaux à 2.5ms d'intervalle, le signal tout à gauche est à environs 40ms ce qui ne fais aussi aucun sens.