Advertisement
bipping

FreehareV3.cpp

Mar 19th, 2025
134
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.17 KB | None | 0 0
  1. #include <SPI.h>
  2. #include <SD.h>
  3. #include <avr/wdt.h>
  4.  
  5. // Constantes système
  6. #define PIN_CAPTEUR A2
  7. #define PIN_CS_SD 4
  8. #define DUREE_ACQUISITION_S 7
  9. #define BAUD_RATE 115200
  10.  
  11. // Constantes pour l'algorithme de compression
  12. #define MARQUEUR_VALEUR_COMPLETE 0xFF
  13. #define MARQUEUR_DELTA_TEMPS_STD 0x7F
  14. #define MARQUEUR_DELTA_TEMPS_VAR 0x7E
  15. #define DELTA_MAX 126
  16. #define DELTA_MIN -126
  17. #define INTERVALLE_TEMPS_STD 1  // Intervalle standard (ms)
  18.  
  19. // Constantes de conversion et calibration
  20. /**
  21. * Valeur ADC représentant 0g (position neutre du capteur)
  22. * Cette valeur doit être calibrée pour chaque capteur ADXL335
  23. */
  24. #define ZERO_G_VALUE 512
  25.  
  26. /**
  27. * Facteur de conversion ADC vers mg (1g = 1000mg)
  28. * Déterminé par la sensibilité du capteur (~300mV/g) et la résolution ADC
  29. */
  30. #define SCALE_FACTOR 102
  31.  
  32. // Optimisation mémoire critique
  33. /**
  34.  * Réduit à ~1KB pour respecter contraintes RAM
  35.  * Allocation calculée: 2048 - 917 - 100 (marge) = 1031
  36.  * Valeur choisie: 1024 (pour garantir ~1% marge)
  37.  */
  38. #define BUFFER_SIZE 1024
  39.  
  40. // Buffer d'acquisition statique réduit
  41. uint8_t buffer[BUFFER_SIZE];
  42. uint16_t indexBuffer = 0;
  43. uint16_t nbEchantillons = 0;    // Réduit à uint16_t pour économiser 2 octets
  44.  
  45. // Références pour la compression différentielle
  46. int16_t dernierAccel = 0;
  47. uint16_t dernierTemps = 0;      // Réduit à uint16_t (restriction à 65.5s max d'acquisition)
  48.  
  49. // Variables de contrôle du timing
  50. volatile bool acquisitionTerminee = false;
  51. volatile uint8_t compteurSecondes = 0;
  52. uint16_t tempsDebutAcquisition = 0;  // Réduit à uint16_t
  53.  
  54. // Interface carte SD
  55. File fichierSD;
  56.  
  57. // Routine d'interruption du Watchdog
  58. ISR(WDT_vect) {
  59.     if (++compteurSecondes >= DUREE_ACQUISITION_S) {
  60.         acquisitionTerminee = true;
  61.         wdt_disable();
  62.     }
  63. }
  64.  
  65. /**
  66.  * Lecture ADC optimisée avec accès direct aux registres
  67.  *
  68.  * Cette fonction contourne la couche d'abstraction Arduino (analogRead) pour
  69.  * accéder directement aux registres du convertisseur analogique-numérique.
  70.  * Le prescaler ADC est maintenu à 128 pour privilégier la précision sur la vitesse,
  71.  * conformément aux spécifications du projet.
  72.  *
  73.  * @param pin Numéro du canal ADC (0-7, correspondant aux broches A0-A7)
  74.  * @return Valeur de conversion ADC 10 bits (0-1023)
  75.  */
  76. inline uint16_t lectureADC(uint8_t pin) {
  77.     ADMUX = (ADMUX & 0xF0) | (pin & 0x07);
  78.     ADCSRA |= (1 << ADSC);
  79.     while (ADCSRA & (1 << ADSC));
  80.     return ADC;
  81. }
  82.  
  83. /**
  84.  * Configuration du Watchdog comme base de temps précise pour l'acquisition
  85.  *
  86.  * Cette fonction configure le timer Watchdog en mode interruption (sans reset)
  87.  * pour générer des interruptions périodiques d'environ 1 seconde, utilisées
  88.  * comme base de temps pour contrôler la durée totale d'acquisition.
  89.  *
  90.  * La précision temporelle est d'environ ±10%, ce qui est suffisant pour
  91.  * respecter les spécifications du projet (7 secondes ±10%).
  92.  */
  93. void configurerWatchdog() {
  94.     cli();  // Désactiver les interruptions
  95.     MCUSR &= ~(1 << WDRF);
  96.     WDTCSR |= (1 << WDCE) | (1 << WDE);
  97.     WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1);  // ~1s
  98.     sei();  // Réactiver les interruptions
  99. }
  100.  
  101. /**
  102.  * Stocke une valeur d'accélération complète avec son horodatage
  103.  *
  104.  * Cette fonction est utilisée pour l'initialisation et lorsque les deltas
  105.  * ne peuvent pas être encodés efficacement (variations brusques ou intervalles
  106.  * temporels importants). Utilise 5 octets de stockage.
  107.  *
  108.  * @param valeur Valeur d'accélération en mg (millièmes de g)
  109.  * @param temps Temps relatif en ms depuis le début de l'acquisition
  110.  */
  111. void stockerValeurComplete(int16_t valeur, uint16_t temps) {
  112.     // Vérifier l'espace disponible (5 octets)
  113.     if (indexBuffer + 5 > BUFFER_SIZE) {
  114.         Serial.println(F("Buffer plein"));
  115.         acquisitionTerminee = true;
  116.         return;
  117.     }
  118.    
  119.     // Format: [MARQUEUR][VAL_MSB][VAL_LSB][TEMPS_MSB][TEMPS_LSB]
  120.     buffer[indexBuffer++] = MARQUEUR_VALEUR_COMPLETE;
  121.     buffer[indexBuffer++] = (valeur >> 8) & 0xFF;
  122.     buffer[indexBuffer++] = valeur & 0xFF;
  123.     buffer[indexBuffer++] = (temps >> 8) & 0xFF;
  124.     buffer[indexBuffer++] = temps & 0xFF;
  125.    
  126.     // Mettre à jour les références
  127.     dernierAccel = valeur;
  128.     dernierTemps = temps;
  129.    
  130.     nbEchantillons++;
  131. }
  132.  
  133. /**
  134.  * Stockage d'une valeur delta compressée
  135.  *
  136.  * Cette fonction implémente l'algorithme de compression différentielle adaptatif
  137.  * qui permet de réduire l'empreinte mémoire des échantillons. Trois formats sont
  138.  * disponibles selon les caractéristiques du signal:
  139.  * 1. Format standard (2 octets): delta d'accélération + intervalle fixe 1ms
  140.  * 2. Format variable (3 octets): delta d'accélération + delta temps explicite
  141.  * 3. Échec compression: utilisation d'une valeur complète (via stockerValeurComplete)
  142.  *
  143.  * @param valeur Valeur d'accélération actuelle en mg
  144.  * @param temps Horodatage relatif actuel en ms
  145.  * @return true si compression réussie, false si valeur complète nécessaire
  146.  */
  147. bool stockerDeltaValeur(int16_t valeur, uint16_t temps) {
  148.     // Calculer les deltas
  149.     int16_t deltaAccel = valeur - dernierAccel;
  150.     uint16_t deltaTemps = temps - dernierTemps;
  151.    
  152.     // Vérifier si le delta est dans les limites encodables
  153.     if (deltaAccel < DELTA_MIN || deltaAccel > DELTA_MAX) {
  154.         return false;  // Nécessite une valeur complète
  155.     }
  156.    
  157.     // Déterminer le format optimal selon l'intervalle temporel
  158.     uint8_t espaceRequis;
  159.     uint8_t typeMarqueur;
  160.    
  161.     if (deltaTemps == INTERVALLE_TEMPS_STD) {
  162.         // Format compact (delta avec intervalle implicite)
  163.         espaceRequis = 2;  // 1 marqueur + 1 delta
  164.         typeMarqueur = MARQUEUR_DELTA_TEMPS_STD;
  165.     } else if (deltaTemps <= 255) {
  166.         // Format avec temps explicite
  167.         espaceRequis = 3;  // 1 marqueur + 1 delta + 1 temps
  168.         typeMarqueur = MARQUEUR_DELTA_TEMPS_VAR;
  169.     } else {
  170.         return false;  // Delta temps trop grand, nécessite valeur complète
  171.     }
  172.    
  173.     // Vérifier l'espace disponible
  174.     if (indexBuffer + espaceRequis > BUFFER_SIZE) {
  175.         Serial.println(F("Buffer plein"));
  176.         acquisitionTerminee = true;
  177.         return false;
  178.     }
  179.    
  180.     // Écriture selon le format déterminé
  181.     buffer[indexBuffer++] = typeMarqueur;
  182.     buffer[indexBuffer++] = deltaAccel & 0xFF;  // Delta signé 8 bits
  183.    
  184.     if (typeMarqueur == MARQUEUR_DELTA_TEMPS_VAR) {
  185.         buffer[indexBuffer++] = deltaTemps & 0xFF;
  186.     }
  187.    
  188.     // Mettre à jour les références
  189.     dernierAccel = valeur;
  190.     dernierTemps = temps;
  191.    
  192.     nbEchantillons++;
  193.     return true;
  194. }
  195.  
  196. /**
  197.  * Décompression et sauvegarde des données au format texte final
  198.  *
  199.  * Cette fonction parcourt le buffer de données compressées, reconstruit les
  200.  * valeurs originales d'accélération et de temps, puis les écrit directement
  201.  * dans le fichier MESURE.txt avec le format attendu par l'utilisateur final.
  202.  *
  203.  * Le processus de décompression reconstruite séquentiellement les valeurs en
  204.  * appliquant l'algorithme inverse de compression, selon les marqueurs rencontrés.
  205.  * Une validation de format est appliquée pour éviter les corruptions de données.
  206.  *
  207.  * Un fichier de métadonnées INFO.txt est également généré pour documenter
  208.  * les statistiques d'acquisition et l'efficacité de la compression.
  209.  */
  210. void sauvegarderDonneesMesure() {
  211.     // Création du fichier de mesure au format attendu
  212.     fichierSD = SD.open(F("MESURE.txt"), FILE_WRITE);
  213.     if (fichierSD) {
  214.         // En-tête standardisé
  215.         fichierSD.println(F("Accel Z (g), Temps (ms)"));
  216.        
  217.         // Variables pour la reconstruction des données
  218.         uint16_t i = 0;
  219.         int16_t accel = 0;
  220.         uint16_t temps = 0;
  221.         char bufferFormatage[20];  // Buffer temporaire pour formatage des valeurs
  222.        
  223.         // Parcours séquentiel et décodage du buffer compressé
  224.         while (i < indexBuffer) {
  225.             if (buffer[i] == MARQUEUR_VALEUR_COMPLETE) {
  226.                 // Décodage de la valeur complète (16 bits)
  227.                 i++;  // Avancer après le marqueur
  228.                 accel = (buffer[i] << 8) | buffer[i+1];
  229.                 i += 2;
  230.                 temps = (buffer[i] << 8) | buffer[i+1];
  231.                 i += 2;
  232.             }
  233.             else if (buffer[i] == MARQUEUR_DELTA_TEMPS_STD) {
  234.                 // Décodage du delta standard (variation fixe)
  235.                 i++;
  236.                 int8_t deltaAccel = buffer[i];
  237.                 i++;
  238.                
  239.                 accel += deltaAccel;
  240.                 temps += INTERVALLE_TEMPS_STD;
  241.             }
  242.             else if (buffer[i] == MARQUEUR_DELTA_TEMPS_VAR) {
  243.                 // Décodage du delta variable (intervalle temporel explicite)
  244.                 i++;
  245.                 int8_t deltaAccel = buffer[i];
  246.                 i++;
  247.                 uint8_t deltaTemps = buffer[i];
  248.                 i++;
  249.                
  250.                 accel += deltaAccel;
  251.                 temps += deltaTemps;
  252.             }
  253.             else {
  254.                 // Protection contre les formats non reconnus
  255.                 i++;
  256.                 continue;
  257.             }
  258.            
  259.             // Conversion en valeur d'accélération en g (précision 2 décimales)
  260.             float accel_g = accel / 1000.0;  // Conversion mg → g
  261.             dtostrf(accel_g, 6, 2, bufferFormatage);
  262.            
  263.             // Écriture formatée dans le fichier
  264.             fichierSD.print(bufferFormatage);
  265.             fichierSD.print(F(", "));
  266.             fichierSD.println(temps);
  267.         }
  268.        
  269.         fichierSD.close();
  270.         Serial.println(F("Fichier MESURE.txt généré"));
  271.     } else {
  272.         Serial.println(F("Erreur création fichier MESURE.txt"));
  273.     }
  274.    
  275.     // Génération optionnelle d'un fichier de métadonnées
  276.     fichierSD = SD.open(F("INFO.txt"), FILE_WRITE);
  277.     if (fichierSD) {
  278.         fichierSD.println(F("STATISTIQUES ACQUISITION:"));
  279.         fichierSD.println(F("------------------------"));
  280.         fichierSD.print(F("Échantillons: ")); fichierSD.println(nbEchantillons);
  281.         fichierSD.print(F("Compression: "));
  282.         uint8_t ratioCompression = (uint8_t)((float)indexBuffer / (nbEchantillons * 4.0) * 100.0);
  283.         fichierSD.print(ratioCompression); fichierSD.println(F("%"));
  284.         fichierSD.print(F("Durée: ")); fichierSD.print(compteurSecondes); fichierSD.println(F(" sec"));
  285.         fichierSD.close();
  286.     }
  287. }
  288.  
  289. void setup() {
  290.     Serial.begin(BAUD_RATE);
  291.     Serial.println(F("Initialisation système"));
  292.    
  293.     /**
  294.     * Configuration ADC optimisée
  295.     * Maintien du prescaler à 128 (valeur par défaut) pour privilégier
  296.     * la précision sur la vitesse, conformément aux recommandations.
  297.     * La valeur 0x07 correspond au diviseur 128 dans les registres ADCSRA.
  298.     */
  299.     ADCSRA = (ADCSRA & 0xF8) | 0x07;
  300.    
  301.     // Initialisation carte SD
  302.     if (!SD.begin(PIN_CS_SD)) {
  303.         Serial.println(F("Échec initialisation carte SD"));
  304.         while (1);  // Arrêt du système
  305.     }
  306.    
  307.     // Suppression des fichiers existants
  308.     if (SD.exists(F("DATA.BIN"))) SD.remove(F("DATA.BIN"));
  309.     if (SD.exists(F("META.TXT"))) SD.remove(F("META.TXT"));
  310.     if (SD.exists(F("ACCEL.CSV"))) SD.remove(F("ACCEL.CSV"));
  311.    
  312.     // Configuration du Watchdog
  313.     configurerWatchdog();
  314.    
  315.     // Initialisation des variables
  316.     indexBuffer = 0;
  317.     nbEchantillons = 0;
  318.     tempsDebutAcquisition = millis();
  319.    
  320.     Serial.println(F("Début acquisition de données"));
  321.     Serial.print(F("Capacité buffer: "));
  322.     Serial.print(BUFFER_SIZE);
  323.     Serial.println(F(" octets"));
  324. }
  325.  
  326. void loop() {
  327.     if (!acquisitionTerminee) {
  328.          /**
  329.          * PHASE 1: ACQUISITION PURE
  330.          *
  331.          * Cette phase privilégie la vitesse d'échantillonnage en concentrant
  332.          * toutes les ressources système sur l'acquisition et la compression.
  333.          * Aucune opération de sauvegarde n'est effectuée pendant cette phase
  334.          * pour éviter les latences variables associées aux accès SD.
  335.          *
  336.          * La fréquence d'échantillonnage théorique maximale est d'environ 8-10kHz,
  337.          * limitée principalement par le temps de conversion ADC et les calculs
  338.          * de compression.
  339.          */
  340.  
  341.         // Lecture optimisée de l'accéléromètre
  342.         uint16_t valeurADC = lectureADC(PIN_CAPTEUR - A0);
  343.        
  344.         // Conversion en accélération (mg)
  345.         int16_t acceleration = ((int32_t)valeurADC - ZERO_G_VALUE) * SCALE_FACTOR / 100;
  346.        
  347.         // Horodatage relatif (ms)
  348.         uint16_t tempsRelatif = millis() - tempsDebutAcquisition;
  349.        
  350.         /**
  351.         * Sélection intelligente du mode de stockage
  352.         * Première valeur toujours stockée en format complet (référence initiale)
  353.         * Sinon tentative de compression, avec fallback vers format complet
  354.         * si les deltas calculés sont hors plage ou si le temps est trop grand
  355.         */
  356.         if (nbEchantillons == 0 || !stockerDeltaValeur(acceleration, tempsRelatif)) {
  357.             stockerValeurComplete(acceleration, tempsRelatif);
  358.         }
  359.        
  360.     } else {
  361.         /**
  362.         * PHASE 2: SAUVEGARDE FINALE
  363.         *
  364.         * Cette phase se déclenche après la période d'acquisition définie (7s)
  365.         * et s'occupe exclusivement du traitement et de la sauvegarde des données.
  366.         *
  367.         * Le processus inclut:
  368.         * - Décompression des données depuis le buffer mémoire
  369.         * - Conversion au format attendu (accélération en g, temps en ms)
  370.         * - Génération du fichier MESURE.txt pour l'utilisateur final
  371.         * - Enregistrement des statistiques d'acquisition
  372.         */
  373.         static bool sauvegardeFaite = false;
  374.        
  375.         if (!sauvegardeFaite) {
  376.             Serial.println(F("Acquisition terminée"));
  377.             Serial.print(F("Échantillons acquis: "));
  378.             Serial.println(nbEchantillons);
  379.            
  380.             // Génération directe du fichier MESURE.txt
  381.             sauvegarderDonneesMesure();
  382.            
  383.             sauvegardeFaite = true;
  384.             Serial.println(F("Traitement terminé"));
  385.         }
  386.     }
  387. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement