Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Système d'Acquisition Haute Fréquence pour Accéléromètre sur Arduino
- *
- * Ce programme implémente une architecture d'acquisition optimisée pour maximiser
- * la fréquence d'échantillonnage d'un accéléromètre ADXL335 sur Arduino Uno
- *
- * Principes architecturaux:
- * - Séparation stricte des phases d'acquisition et de sauvegarde
- * - Compression différentielle adaptative avec multiple formats d'encodage
- * - Accès direct aux registres matériels pour maximiser les performances
- *
- * Le programme utilise cinq formats de stockage différents selon les caractéristiques
- * du signal, avec un ratio de compression moyen attendu d'environ 35-40%.
- */
- #include <SPI.h>
- #include <SD.h>
- #include <avr/wdt.h>
- // Constantes système
- #define PIN_CAPTEUR A2
- #define PIN_CS_SD 4
- #define DUREE_ACQUISITION_S 7
- #define BAUD_RATE 115200
- // Constantes pour l'algorithme de compression
- #define MARQUEUR_VALEUR_COMPLETE 0xFF // Valeur complète avec temps (5 octets)
- #define MARQUEUR_DELTA_TEMPS_STD 0x7F // Delta avec temps fixe 1ms (2 octets)
- #define MARQUEUR_DELTA_TEMPS_VAR 0x7E // Delta avec temps variable (3 octets)
- #define MARQUEUR_RLE_DELTA 0x7D // Séquence répétitive (3 octets)
- #define MARQUEUR_BLOC_TEMPOREL 0x7C // Bloc multi-échantillons (7+ octets)
- // Paramètres de compression
- #define COMPTEUR_RLE_MIN 3 // Seuil minimal de rentabilité RLE
- #define COMPTEUR_RLE_MAX 255 // Valeur maximale encodable sur 1 octet
- #define TAILLE_BLOC_MAX 8 // Nombre max d'échantillons par bloc
- #define DELTA_MAX 126 // Valeur maximale encodable sur 8 bits signé
- #define DELTA_MIN -126 // Valeur minimale encodable (réserve -127,-128)
- #define INTERVALLE_TEMPS_STD 1 // Intervalle standard (ms)
- #define INTERVALLE_TEMPS_MAX 20 // Limite pour accumuler les échantillons
- // Macro de vérification pour uniformiser les contrôles
- #define VERIFIER_ESPACE_BUFFER(taille, actionEchec) \
- if (indexBuffer + (taille) > BUFFER_SIZE) { \
- Serial.println(F("Buffer plein")); \
- acquisitionTerminee = true; \
- actionEchec; \
- }
- // Constantes de conversion et calibration
- #define ZERO_G_VALUE 261 // Valeur ADC représentant 0g (à calibrer)
- #define SCALE_FACTOR 21 // Facteur de conversion ADC → mg (millièmes de g)
- // Optimisation mémoire critique
- #define BUFFER_SIZE 1024 // Buffer principal (50% de la RAM)
- // Buffer principal de stockage des données compressées
- uint8_t buffer[BUFFER_SIZE];
- uint16_t indexBuffer = 0; // Position courante dans le buffer
- uint16_t nbEchantillons = 0; // Compteur d'échantillons acquis
- int16_t dernierAccel = 0; // Dernière valeur d'accélération stockée
- uint16_t dernierTemps = 0; // Dernier horodatage relatif stocké
- // Variables de contrôle du timing
- volatile bool acquisitionTerminee = false;
- volatile uint8_t compteurSecondes = 0;
- uint16_t tempsDebutAcquisition = 0;
- // Interface d'accès à la carte SD
- File fichierSD;
- // Structure pour l'état de compression RLE
- typedef struct {
- int8_t dernierDeltaAccel; // Dernier delta d'accélération encodé
- uint8_t compteurRLE; // Compteur de répétitions en cours
- bool modeRLE; // Indicateur de séquence RLE active
- } EtatCompressionRLE;
- // Instance globale pour la compression RLE
- EtatCompressionRLE etatRLE = {0, 0, false};
- // Buffer temporaire pour compression multi-échantillons
- int16_t bufferAcceleration[TAILLE_BLOC_MAX];
- uint16_t bufferTemps[TAILLE_BLOC_MAX];
- uint8_t nbEchantillonsBuffer = 0;
- /**
- * Routine d'interruption du Watchdog Timer
- */
- ISR(WDT_vect) {
- if (++compteurSecondes >= DUREE_ACQUISITION_S) {
- acquisitionTerminee = true;
- wdt_disable();
- }
- }
- /**
- * Lecture ADC avec accès direct aux registres
- */
- inline uint16_t lectureADC(uint8_t pin) {
- ADMUX = (ADMUX & 0xF0) | (pin & 0x07);
- ADCSRA |= (1 << ADSC);
- while (ADCSRA & (1 << ADSC));
- uint8_t low = ADCL;
- uint8_t high = ADCH;
- return (high << 8) | low;
- }
- /**
- * Configuration du Watchdog comme base de temps précise
- */
- void configurerWatchdog() {
- cli();
- MCUSR &= ~(1 << WDRF);
- WDTCSR |= (1 << WDCE) | (1 << WDE);
- WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1); // ~1s
- sei();
- }
- /**
- * Calcul optimisé de la conversion ADC vers accélération
- */
- inline int16_t calcule_optimise(int32_t x) {
- int32_t tmp = ZERO_G_VALUE - x;
- int32_t result = (SCALE_FACTOR * tmp) >> 10;
- return (int16_t)result;
- }
- /**
- * Stockage d'une valeur complète avec horodatage
- */
- inline void stockerValeurComplete(int16_t valeur, uint16_t temps) {
- buffer[indexBuffer++] = MARQUEUR_VALEUR_COMPLETE;
- buffer[indexBuffer++] = (valeur >> 8) & 0xFF;
- buffer[indexBuffer++] = valeur & 0xFF;
- buffer[indexBuffer++] = (temps >> 8) & 0xFF;
- buffer[indexBuffer++] = temps & 0xFF;
- dernierAccel = valeur;
- dernierTemps = temps;
- nbEchantillons++;
- }
- /**
- * Finalisation d'une séquence RLE active
- */
- inline bool finaliserSequenceRLE() {
- if (!etatRLE.modeRLE || etatRLE.compteurRLE < COMPTEUR_RLE_MIN) {
- return false;
- }
- VERIFIER_ESPACE_BUFFER(3, return false)
- buffer[indexBuffer++] = MARQUEUR_RLE_DELTA;
- buffer[indexBuffer++] = etatRLE.compteurRLE;
- buffer[indexBuffer++] = etatRLE.dernierDeltaAccel;
- etatRLE.compteurRLE = 0;
- etatRLE.modeRLE = false;
- return true;
- }
- /**
- * Compression différentielle avec Run-Length Encoding
- */
- bool stockerDeltaValeurRLE(int16_t valeur, uint16_t temps) {
- int16_t deltaAccel = valeur - dernierAccel;
- uint16_t deltaTemps = temps - dernierTemps;
- if (deltaAccel < DELTA_MIN || deltaAccel > DELTA_MAX || deltaTemps > 255) {
- finaliserSequenceRLE();
- etatRLE.modeRLE = false;
- etatRLE.compteurRLE = 0;
- return false;
- }
- if (deltaAccel == etatRLE.dernierDeltaAccel && deltaTemps == INTERVALLE_TEMPS_STD) {
- if (!etatRLE.modeRLE) {
- etatRLE.modeRLE = true;
- etatRLE.compteurRLE = 1;
- } else {
- etatRLE.compteurRLE++;
- if (etatRLE.compteurRLE == COMPTEUR_RLE_MAX) {
- VERIFIER_ESPACE_BUFFER(3, return false)
- buffer[indexBuffer++] = MARQUEUR_RLE_DELTA;
- buffer[indexBuffer++] = etatRLE.compteurRLE;
- buffer[indexBuffer++] = etatRLE.dernierDeltaAccel;
- etatRLE.compteurRLE = 0;
- etatRLE.modeRLE = false;
- }
- }
- } else {
- if (etatRLE.modeRLE) {
- if (etatRLE.compteurRLE >= COMPTEUR_RLE_MIN) {
- VERIFIER_ESPACE_BUFFER(3, return false)
- buffer[indexBuffer++] = MARQUEUR_RLE_DELTA;
- buffer[indexBuffer++] = etatRLE.compteurRLE;
- buffer[indexBuffer++] = etatRLE.dernierDeltaAccel;
- } else {
- for (uint8_t i = 0; i < etatRLE.compteurRLE; i++) {
- VERIFIER_ESPACE_BUFFER(2, return false)
- buffer[indexBuffer++] = MARQUEUR_DELTA_TEMPS_STD;
- buffer[indexBuffer++] = etatRLE.dernierDeltaAccel;
- }
- }
- etatRLE.modeRLE = false;
- etatRLE.compteurRLE = 0;
- }
- if (deltaTemps == INTERVALLE_TEMPS_STD) {
- VERIFIER_ESPACE_BUFFER(2, return false)
- buffer[indexBuffer++] = MARQUEUR_DELTA_TEMPS_STD;
- buffer[indexBuffer++] = deltaAccel;
- etatRLE.dernierDeltaAccel = deltaAccel;
- } else {
- VERIFIER_ESPACE_BUFFER(3, return false)
- buffer[indexBuffer++] = MARQUEUR_DELTA_TEMPS_VAR;
- buffer[indexBuffer++] = deltaAccel;
- buffer[indexBuffer++] = deltaTemps;
- etatRLE.dernierDeltaAccel = deltaAccel;
- }
- }
- dernierAccel = valeur;
- dernierTemps = temps;
- nbEchantillons++;
- return true;
- }
- /**
- * Compression multi-échantillons avec alignement temporel
- *
- * Format: [MARQUEUR][TEMPS_MSB][TEMPS_LSB][NB_ECHANT][INTERVALLE][VAL_REF_MSB][VAL_REF_LSB][DELTA1]...[DELTAn]
- */
- bool stockerBlocTemporel(int16_t* valeurs, uint16_t temps, uint8_t nbEchantillons, uint8_t intervalle) {
- // Validation des paramètres
- if (nbEchantillons < 2 || nbEchantillons > TAILLE_BLOC_MAX) {
- return false;
- }
- // Calcul de l'espace requis: marqueur + temps(2) + nbEchant + intervalle + valRef(2) + deltas
- uint8_t espaceRequis = 1 + 2 + 1 + 1 + 2 + (nbEchantillons - 1);
- VERIFIER_ESPACE_BUFFER(espaceRequis, return false)
- // Écriture de l'en-tête du bloc
- buffer[indexBuffer++] = MARQUEUR_BLOC_TEMPOREL;
- buffer[indexBuffer++] = (temps >> 8) & 0xFF;
- buffer[indexBuffer++] = temps & 0xFF;
- buffer[indexBuffer++] = nbEchantillons;
- buffer[indexBuffer++] = intervalle;
- // Stockage de la valeur de référence
- buffer[indexBuffer++] = (valeurs[0] >> 8) & 0xFF;
- buffer[indexBuffer++] = valeurs[0] & 0xFF;
- // Calcul et stockage des deltas
- int16_t valeurPrecedente = valeurs[0];
- for (uint8_t i = 1; i < nbEchantillons; i++) {
- int16_t delta = valeurs[i] - valeurPrecedente;
- // Vérification des limites encodables
- if (delta < DELTA_MIN || delta > DELTA_MAX) {
- return false; // Delta hors limites, échec de compression
- }
- buffer[indexBuffer++] = (int8_t)delta;
- valeurPrecedente = valeurs[i];
- }
- // Mise à jour des références globales
- dernierAccel = valeurs[nbEchantillons - 1];
- dernierTemps = temps + (nbEchantillons - 1) * intervalle;
- // Incrémenter le compteur d'échantillons global
- ::nbEchantillons += nbEchantillons;
- return true;
- }
- /**
- * Formatage et écriture d'une mesure dans le fichier
- */
- inline void ecrireMesure(int16_t accel, uint16_t temps, char* buffer, File& fichier) {
- float accel_g = accel / 1000.0;
- dtostrf(accel_g, 6, 2, buffer);
- fichier.print(buffer);
- fichier.print(F(", "));
- fichier.println(temps);
- }
- /**
- * Décodage d'un bloc temporel compressé
- */
- void decoderBlocTemporel(uint16_t& i, int16_t& accel, uint16_t& temps, char* bufferFormatage, File& fichier) {
- uint16_t tempsBase = (buffer[i] << 8) | buffer[i+1];
- i += 2;
- uint8_t nbEchant = buffer[i++];
- uint8_t intervalle = buffer[i++];
- int16_t valeurReference = (buffer[i] << 8) | buffer[i+1];
- i += 2;
- accel = valeurReference;
- temps = tempsBase;
- ecrireMesure(accel, temps, bufferFormatage, fichier);
- int16_t valeurPrecedente = valeurReference;
- for (uint8_t j = 1; j < nbEchant; j++) {
- int8_t delta = buffer[i++];
- accel = valeurPrecedente + delta;
- temps = tempsBase + (j * intervalle);
- ecrireMesure(accel, temps, bufferFormatage, fichier);
- valeurPrecedente = accel;
- }
- }
- /**
- * Décompression et sauvegarde des données sur carte SD
- */
- void sauvegarderDonneesMesureRLE() {
- fichierSD = SD.open(F("MESURE.txt"), FILE_WRITE);
- if (!fichierSD) {
- Serial.println(F("Erreur création fichier MESURE.txt"));
- return;
- }
- fichierSD.println(F("Accel Z (g), Temps (ms)"));
- uint16_t i = 0;
- int16_t accel = 0;
- uint16_t temps = 0;
- char bufferFormatage[20];
- while (i < indexBuffer) {
- uint8_t marqueur = buffer[i++];
- if (marqueur == MARQUEUR_VALEUR_COMPLETE) {
- accel = (buffer[i] << 8) | buffer[i+1];
- i += 2;
- temps = (buffer[i] << 8) | buffer[i+1];
- i += 2;
- ecrireMesure(accel, temps, bufferFormatage, fichierSD);
- }
- else if (marqueur == MARQUEUR_DELTA_TEMPS_STD) {
- int8_t deltaAccel = buffer[i++];
- accel += deltaAccel;
- temps += INTERVALLE_TEMPS_STD;
- ecrireMesure(accel, temps, bufferFormatage, fichierSD);
- }
- else if (marqueur == MARQUEUR_DELTA_TEMPS_VAR) {
- int8_t deltaAccel = buffer[i++];
- uint8_t deltaTemps = buffer[i++];
- accel += deltaAccel;
- temps += deltaTemps;
- ecrireMesure(accel, temps, bufferFormatage, fichierSD);
- }
- else if (marqueur == MARQUEUR_RLE_DELTA) {
- uint8_t compteur = buffer[i++];
- int8_t deltaAccel = buffer[i++];
- for (uint8_t j = 0; j < compteur; j++) {
- accel += deltaAccel;
- temps += INTERVALLE_TEMPS_STD;
- ecrireMesure(accel, temps, bufferFormatage, fichierSD);
- }
- }
- else if (marqueur == MARQUEUR_BLOC_TEMPOREL) {
- decoderBlocTemporel(i, accel, temps, bufferFormatage, fichierSD);
- }
- }
- fichierSD.close();
- Serial.println(F("Fichier MESURE.txt généré"));
- // Génération du fichier de statistiques
- fichierSD = SD.open(F("INFO.txt"), FILE_WRITE);
- if (fichierSD) {
- fichierSD.println(F("STATISTIQUES ACQUISITION:"));
- fichierSD.println(F("------------------------"));
- fichierSD.print(F("Échantillons: "));
- fichierSD.println(nbEchantillons);
- fichierSD.print(F("Compression: "));
- uint8_t ratioCompression = (uint8_t)((float)indexBuffer / (nbEchantillons * 4.0) * 100.0);
- fichierSD.print(ratioCompression);
- fichierSD.println(F("%"));
- fichierSD.print(F("Durée: "));
- fichierSD.print(compteurSecondes);
- fichierSD.println(F(" sec"));
- fichierSD.print(F("Fréquence moyenne: "));
- uint16_t freqMoy = nbEchantillons / compteurSecondes;
- fichierSD.print(freqMoy);
- fichierSD.println(F(" Hz"));
- fichierSD.close();
- }
- }
- /**
- * Vérification de l'uniformité des intervalles temporels
- */
- bool verifierIntervallesUniformes(uint16_t* temps, uint8_t taille, uint8_t& intervalle) {
- if (taille < 2) return false;
- intervalle = temps[1] - temps[0];
- // Vérifier que tous les intervalles sont identiques
- for (uint8_t i = 2; i < taille; i++) {
- if (temps[i] - temps[i-1] != intervalle) {
- return false;
- }
- }
- return true;
- }
- /**
- * Initialisation du système
- */
- void setup() {
- Serial.begin(BAUD_RATE);
- Serial.println(F("Initialisation système"));
- // Configuration ADC optimisée (prescaler 128)
- ADCSRA = (ADCSRA & 0xF8) | 0x07;
- // Initialisation carte SD
- if (!SD.begin(PIN_CS_SD)) {
- Serial.println(F("Échec initialisation carte SD"));
- while (1);
- }
- // Configuration du Watchdog
- configurerWatchdog();
- // Initialisation des variables
- etatRLE.dernierDeltaAccel = 0;
- etatRLE.compteurRLE = 0;
- etatRLE.modeRLE = false;
- indexBuffer = 0;
- nbEchantillons = 0;
- nbEchantillonsBuffer = 0;
- tempsDebutAcquisition = millis();
- Serial.println(F("Début acquisition"));
- Serial.print(F("Capacité buffer: "));
- Serial.print(BUFFER_SIZE);
- Serial.println(F(" octets"));
- }
- /**
- * Boucle principale
- */
- void loop() {
- if (!acquisitionTerminee) {
- // Phase d'acquisition
- uint16_t valeurADC = lectureADC(PIN_CAPTEUR - A0);
- int16_t acceleration = calcule_optimise((int32_t)valeurADC);
- uint16_t tempsRelatif = millis() - tempsDebutAcquisition;
- // Ajout au buffer d'accumulation pour compression multi-échantillons
- bufferAcceleration[nbEchantillonsBuffer] = acceleration;
- bufferTemps[nbEchantillonsBuffer] = tempsRelatif;
- nbEchantillonsBuffer++;
- // Traitement du buffer quand il est plein ou après un délai maximum
- if (nbEchantillonsBuffer == TAILLE_BLOC_MAX ||
- (nbEchantillonsBuffer > 1 && (tempsRelatif - bufferTemps[0]) > INTERVALLE_TEMPS_MAX)) {
- // Vérifier si les intervalles sont uniformes
- uint8_t intervalleMesure;
- bool intervallesUniformes = verifierIntervallesUniformes(
- bufferTemps, nbEchantillonsBuffer, intervalleMesure);
- // Compression multi-échantillons si possible
- if (nbEchantillonsBuffer > 1 && intervallesUniformes && intervalleMesure <= 255) {
- if (stockerBlocTemporel(bufferAcceleration, bufferTemps[0],
- nbEchantillonsBuffer, intervalleMesure)) {
- // Compression réussie, réinitialisation du buffer
- nbEchantillonsBuffer = 0;
- return; // Continue to next sample acquisition
- }
- }
- // Fallback: stockage traditionnel
- for (uint8_t i = 0; i < nbEchantillonsBuffer; i++) {
- if (i == 0 || !stockerDeltaValeurRLE(bufferAcceleration[i], bufferTemps[i])) {
- VERIFIER_ESPACE_BUFFER(5, return)
- stockerValeurComplete(bufferAcceleration[i], bufferTemps[i]);
- }
- }
- nbEchantillonsBuffer = 0;
- }
- }
- else {
- // Phase de sauvegarde
- static bool sauvegardeFaite = false;
- if (!sauvegardeFaite) {
- // Traiter les échantillons restants dans le buffer
- if (nbEchantillonsBuffer > 0) {
- for (uint8_t i = 0; i < nbEchantillonsBuffer; i++) {
- if (i == 0 || !stockerDeltaValeurRLE(bufferAcceleration[i], bufferTemps[i])) {
- VERIFIER_ESPACE_BUFFER(5, break)
- stockerValeurComplete(bufferAcceleration[i], bufferTemps[i]);
- }
- }
- nbEchantillonsBuffer = 0;
- }
- // Finaliser toute séquence RLE en cours
- finaliserSequenceRLE();
- Serial.println(F("Acquisition terminée"));
- Serial.print(F("Échantillons acquis: "));
- Serial.println(nbEchantillons);
- // Sauvegarde des données
- sauvegarderDonneesMesureRLE();
- sauvegardeFaite = true;
- Serial.println(F("Traitement terminé"));
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement