From 888723abc7b2580e4bc9bfaca6a2178d05fa8fb8 Mon Sep 17 00:00:00 2001 From: nicoboy Date: Mon, 3 Aug 2026 18:50:21 +0200 Subject: [PATCH] Etape 5 : measure.c et constantes de calibration reelles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report des mesures de docs/mesure-cartepuissance.md dans calib.h, avec une structure par voie (gain, offset) plutot que des constantes isolees. calib.h Tensions : VIN 11,11 / V1 31,25 / VOUT 181,82 V/V (mesures) IIN : 1,2019 A/V -- un seul point mesure, offset suppose nul IOUT : 0,016667 A/V -- NON MESURE, valeur theorique de conception Shunts : I1 offset 47,3 mV gain 0,631 V/A I2 offset 34,0 mV gain 0,637 V/A Deux jeux separes : les voies different d'environ 1 % en gain et n'ont pas le meme offset, ne jamais moyenner. Seuil de protection : le code DAC est maintenant calcule par macro a partir de SAFETY_ISHUNT_THRESHOLD_A et des gain/offset mesures, et il est PROPRE A CHAQUE ETAGE (601 et 603 au lieu d'un 558 commun). Correction d'analyse : le 558 precedent supposait un gain de 30 pile. Le gain reel etant plus eleve (~31,5), le seuil de 1,8 V etait atteint a 2,78 A et non a 3 A -- la protection declenchait donc TROP TOT, et non trop tard comme indique au §5 du document de mesures. Conservatif, sans danger, mais corrige. measure.c/h (etape 5) measure_vin/iin/v1/vout/iout/i1/i2/temp, signature float f(uint16_t raw). Deux fonctions distinctes pour les shunts, puisque gain ET offset different. measure_i1/i2 peuvent renvoyer une valeur negative a courant nul : volontaire, une derive d'offset apres changement d'ampli devient ainsi visible en telemetrie au lieu d'etre masquee par une saturation. NTC : la relation du PROMPT §5 est INVERSEE. Avec la NTC cote 3,3 V c'est R_ntc = R_fixe * (VREF - Vadc) / Vadc. Verifie sur les points de repere du PROMPT lui-meme : 0,75 V a 0 degC donne 34,2 kOhm, ce que beta=4000 predit, alors que la forme inversee donnerait 2,94 kOhm (donc du chaud). Telle qu'ecrite, la protection thermique aurait ete inexploitable. La telemetrie transporte desormais de vraies grandeurs physiques. Reste en suspens (voir docs/mesure-cartepuissance.md §6) : IOUT jamais mesure, second point pour IIN, valeur reelle de la resistance fixe des ponts NTC, et surtout gain/offset des shunts A REMESURER apres passage du MCP6001 au TLV9151 -- l'offset actuel est domine par le Vos de l'ampli. Note : l'"auto-zero logiciel au boot" evoque dans le document de mesures n'existe pas dans le firmware et n'a pas ete ajoute ; mesurer un offset au boot figerait une valeur fausse si du courant circule deja. Co-Authored-By: Claude Opus 5 --- src/calib.h | 72 +++++++++++++++++++++++++++++++++++++++++++++--- src/main.c | 29 ++++++++++---------- src/measure.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/measure.h | 30 ++++++++++++++++++++ src/safety.c | 6 ++-- 5 files changed, 193 insertions(+), 20 deletions(-) create mode 100644 src/measure.c create mode 100644 src/measure.h diff --git a/src/calib.h b/src/calib.h index 2d7aeb9..58da0ae 100644 --- a/src/calib.h +++ b/src/calib.h @@ -17,12 +17,76 @@ // Valeur de champ AIOMUX1 pour basculer une broche AIOx en mode analogique #define GPIO_ANALOG_MODE 2 -// Seuil de protection courant shunt (identique etage 1 et 2) -// Shunt 0.02 ohm, gain x30 -> Vadc = I * 0.02 * 30 = I * 0.6 +// ===================================================================== +// Constantes de calibration des chaines de mesure. +// Source : docs/mesure-cartepuissance.md (mesures reelles sur la carte +// elevateur). Aucune de ces valeurs ne doit apparaitre ailleurs. +// ===================================================================== + +// ---- Chaines de tension (doc §1) ------------------------------------ +// Gain = Vreel / Vadc, soit l'inverse du rapport du pont diviseur. +// VIN et V1 tombent sur les valeurs theoriques ; VOUT s'en ecarte de +// 1,8 % (tolerance des resistances), sans consequence : la pleine +// echelle reelle (600 V) reste au-dessus des 400 V vises. +#define MEAS_VIN_GAIN_V_PER_V 11.11f // broche 10, coef mesure 0,09 +#define MEAS_V1_GAIN_V_PER_V 31.25f // broche 16, coef mesure 0,032 +#define MEAS_VOUT_GAIN_V_PER_V 181.82f // broche 14, coef mesure 0,0055 + +// ---- Courant d'entree (doc §2) -------------------------------------- +// Un seul point mesure (250 mA -> 0,208 V) : la caracteristique est +// SUPPOSEE passer par zero, offset non verifie. Un second point reste +// a faire. +#define MEAS_IIN_A_PER_V 1.2019f + +// ---- Courant de sortie (doc §4) ------------------------------------- +// NON MESURE : valeur theorique de conception (3,0 V @ 50 mA). +// A remplacer des qu'une mesure reelle sera disponible. +#define MEAS_IOUT_A_PER_V 0.016667f + +// ---- Shunts MOSFET 0,02 ohm (doc §3) -------------------------------- +// PROVISOIRE : mesures faites avec les MCP6001 de banc. Le gain, fixe +// par le reseau RF/RG, doit rester valable apres passage au TLV9151 ; +// l'OFFSET en revanche est domine par le Vos de l'ampli et devra etre +// ENTIEREMENT REMESURE. Les deux voies different d'environ 1 % : +// garder deux jeux de constantes, ne jamais moyenner. +#define MEAS_I1_OFFSET_V 0.0473f +#define MEAS_I1_GAIN_V_PER_A 0.631f +#define MEAS_I2_OFFSET_V 0.0340f +#define MEAS_I2_GAIN_V_PER_A 0.637f + +// ---- NTC B57451V5103J062 (PROMPT §5) -------------------------------- +// Montage : 3,3 V -- NTC -- R_fixe -- 0 V, mesure au point milieu. +// La NTC etant du cote 3,3 V, la tension MONTE avec la temperature : +// R_ntc = R_fixe * (VREF - Vadc) / Vadc +// (le PROMPT §5 donne cette relation inversee ; verifie sur ses propres +// points de repere : 0,75 V a 0 C -> 34,2 kOhm, ce que beta=4000 predit, +// alors que la forme inversee donnerait 2,94 kOhm, donc du chaud.) +// R_fixe suppose a 10 kOhm : reste a confirmer sur la carte (doc §6). +#define MEAS_NTC_R_FIXED_OHM 10000.0f +#define MEAS_NTC_R25_OHM 10000.0f +#define MEAS_NTC_BETA_K 4000.0f +#define MEAS_NTC_T25_K 298.15f +#define MEAS_KELVIN_OFFSET 273.15f + +// ---- Seuil de protection rapide (doc §5) ---------------------------- +// Le seuil est defini en AMPERES ; le code DAC est calcule par macro a +// partir du gain et de l'offset mesures, jamais ecrit en dur. Ainsi le +// futur passage au TLV9151 ne touchera que les constantes ci-dessus. +// +// Formule TI : V = DACVAL * (VDDA - VSSA) / 1023 -> 1023, pas 4096. #define SAFETY_ISHUNT_THRESHOLD_A 3.0f #define SAFETY_DAC_VREF_V 3.3f -#define SAFETY_DAC_CODE \ - ((uint16_t)((SAFETY_ISHUNT_THRESHOLD_A * 0.6f) / SAFETY_DAC_VREF_V * 1023.0f + 0.5f)) + +#define SAFETY_DAC_CODE_FROM_V(v_) \ + ((uint16_t)((v_) / SAFETY_DAC_VREF_V * 1023.0f + 0.5f)) + +// Seuil ramene a la sortie de l'ampli : Vadc = offset + I * gain. +#define SAFETY_DAC_CODE_STAGE1 \ + SAFETY_DAC_CODE_FROM_V(MEAS_I1_OFFSET_V \ + + SAFETY_ISHUNT_THRESHOLD_A * MEAS_I1_GAIN_V_PER_A) +#define SAFETY_DAC_CODE_STAGE2 \ + SAFETY_DAC_CODE_FROM_V(MEAS_I2_OFFSET_V \ + + SAFETY_ISHUNT_THRESHOLD_A * MEAS_I2_GAIN_V_PER_A) // ADC : reference interne obligatoire (VREFHI partage avec ADCINA0/VIN), // pleine echelle 3,3 V. Conversion brut -> volts : V = raw * 3.3 / 4096. diff --git a/src/main.c b/src/main.c index ba18d9f..10b5ba8 100644 --- a/src/main.c +++ b/src/main.c @@ -6,6 +6,7 @@ #include "protocol.h" #include "pwm.h" #include "adc.h" +#include "measure.h" #include "calib.h" extern uint16_t RamfuncsLoadStart; @@ -106,24 +107,24 @@ void main(void) { telemetry_t t; - // BRING-UP : chaque champ transporte la TENSION BRUTE de la - // broche ADC correspondante, en volts (0..3,3) -- et non la - // grandeur physique. Permet de verifier les 9 voies d'un coup - // sur l'IHM ESP32 apres ressoudure. measure.c fera la vraie - // conversion (ponts diviseurs, gain de shunt, NTC) a l'etape 5. + // Grandeurs physiques reelles (etape 5) : conversion par + // measure.c a partir des coefficients mesures sur la carte + // (docs/mesure-cartepuissance.md). + // FREQ/DUTY restent les valeurs de consigne du bloc de bring-up + // ci-dessus, pas encore une mesure. t.freq1_hz = (float)PWM_STAGE1_FREQ_HZ; t.freq2_hz = (float)PWM_STAGE2_FREQ_HZ; t.duty1_pct = 50.0f; t.duty2_pct = 50.0f; - t.vin_v = adc_get_volts(ADC_CH_VIN); - t.iin_a = adc_get_volts(ADC_CH_IIN); - t.v1_v = adc_get_volts(ADC_CH_V1); - t.i1_a = adc_get_volts(ADC_CH_I1); - t.t1_c = adc_get_volts(ADC_CH_T1); - t.vout_v = adc_get_volts(ADC_CH_VOUT); - t.i2_a = adc_get_volts(ADC_CH_I2); - t.t2_c = adc_get_volts(ADC_CH_T2); - t.iout_a = adc_get_volts(ADC_CH_IOUT); + t.vin_v = measure_vin(adc_get_raw(ADC_CH_VIN)); + t.iin_a = measure_iin(adc_get_raw(ADC_CH_IIN)); + t.v1_v = measure_v1(adc_get_raw(ADC_CH_V1)); + t.i1_a = measure_i1(adc_get_raw(ADC_CH_I1)); + t.t1_c = measure_temp(adc_get_raw(ADC_CH_T1)); + t.vout_v = measure_vout(adc_get_raw(ADC_CH_VOUT)); + t.i2_a = measure_i2(adc_get_raw(ADC_CH_I2)); + t.t2_c = measure_temp(adc_get_raw(ADC_CH_T2)); + t.iout_a = measure_iout(adc_get_raw(ADC_CH_IOUT)); uart_link_send_telemetry(&t); s_send_telemetry = false; diff --git a/src/measure.c b/src/measure.c new file mode 100644 index 0000000..3737d0f --- /dev/null +++ b/src/measure.c @@ -0,0 +1,76 @@ +#include +#include "measure.h" +#include "calib.h" + +// Brut -> tension a la broche. Reference interne, pleine echelle 3,3 V. +static float raw_to_volts(uint16_t raw) +{ + return (float)raw * ADC_VREF_V / 4096.0f; +} + +float measure_vin(uint16_t raw) +{ + return raw_to_volts(raw) * MEAS_VIN_GAIN_V_PER_V; +} + +float measure_iin(uint16_t raw) +{ + return raw_to_volts(raw) * MEAS_IIN_A_PER_V; +} + +float measure_v1(uint16_t raw) +{ + return raw_to_volts(raw) * MEAS_V1_GAIN_V_PER_V; +} + +float measure_vout(uint16_t raw) +{ + return raw_to_volts(raw) * MEAS_VOUT_GAIN_V_PER_V; +} + +float measure_iout(uint16_t raw) +{ + return raw_to_volts(raw) * MEAS_IOUT_A_PER_V; +} + +// Vadc = offset + I * gain -> I = (Vadc - offset) / gain. +// Le resultat peut etre legerement negatif a courant nul (bruit autour de +// l'offset) : on ne le sature pas, une valeur negative en telemetrie est un +// indice utile de derive de l'offset apres changement d'ampli. +float measure_i1(uint16_t raw) +{ + return (raw_to_volts(raw) - MEAS_I1_OFFSET_V) / MEAS_I1_GAIN_V_PER_A; +} + +float measure_i2(uint16_t raw) +{ + return (raw_to_volts(raw) - MEAS_I2_OFFSET_V) / MEAS_I2_GAIN_V_PER_A; +} + +// Pont 3,3 V -- NTC -- R_fixe -- 0 V, mesure au point milieu. +// R_ntc = R_fixe * (VREF - Vadc) / Vadc +// T(K) = 1 / ( 1/T25 + ln(R_ntc / R25) / beta ) +// +// Precision attendue : la tolerance de +/-3 % sur beta donne environ +// +/-2 degC vers 100 degC sans calibration individuelle. Suffisant pour de +// la protection thermique, pas pour de la mesure fine (PROMPT §5). +float measure_temp(uint16_t raw) +{ + float vadc = raw_to_volts(raw); + float r_ntc; + float inv_t; + + // Bornes : hors de cette plage le pont est en butee (broche en l'air, + // NTC en court-circuit ou coupee) et le calcul n'a plus de sens. + if (vadc <= 0.01f || vadc >= (ADC_VREF_V - 0.01f)) + { + return 0.0f; + } + + r_ntc = MEAS_NTC_R_FIXED_OHM * (ADC_VREF_V - vadc) / vadc; + + inv_t = 1.0f / MEAS_NTC_T25_K + + logf(r_ntc / MEAS_NTC_R25_OHM) / MEAS_NTC_BETA_K; + + return (1.0f / inv_t) - MEAS_KELVIN_OFFSET; +} diff --git a/src/measure.h b/src/measure.h new file mode 100644 index 0000000..22b2d01 --- /dev/null +++ b/src/measure.h @@ -0,0 +1,30 @@ +#ifndef MEASURE_H +#define MEASURE_H + +#include + +// Conversion des valeurs brutes de l'ADC (0..4095) vers les grandeurs +// physiques, a partir des seules constantes de calib.h (elles-memes issues +// de docs/mesure-cartepuissance.md). +// +// Ces fonctions utilisent du flottant et, pour les NTC, un logarithme : +// a n'appeler QUE depuis la boucle principale, jamais depuis une ISR +// (PROMPT §7). La protection rapide ne passe pas par ici. + +float measure_vin(uint16_t raw); // V - tension d'entree batterie +float measure_iin(uint16_t raw); // A - courant d'entree +float measure_v1(uint16_t raw); // V - tension intermediaire (etage 1) +float measure_vout(uint16_t raw); // V - tension de sortie HT +float measure_iout(uint16_t raw); // A - courant de sortie HT + +// Courant shunt MOSFET. Deux fonctions distinctes et non une seule +// parametree : les deux voies ont leur propre gain ET leur propre offset, +// mesures separement. +float measure_i1(uint16_t raw); // A - shunt etage 1 +float measure_i2(uint16_t raw); // A - shunt etage 2 + +// Temperature NTC, en degres Celsius. Meme chaine de mesure sur les deux +// etages, donc une seule fonction. +float measure_temp(uint16_t raw); // degC + +#endif diff --git a/src/safety.c b/src/safety.c index 0e1124a..fe59cb3 100644 --- a/src/safety.c +++ b/src/safety.c @@ -31,7 +31,9 @@ void safety_init(void) Comp1Regs.COMPCTL.bit.SYNCSEL = 0; // asynchrone -> ~30 ns vers le Trip Zone Comp1Regs.COMPCTL.bit.CMPINV = 1; // driver actif haut : 1=OK, 0=defaut Comp1Regs.COMPCTL.bit.QUALSEL = 0; // pas de qualification pour l'instant - Comp1Regs.DACVAL.bit.DACVAL = SAFETY_DAC_CODE; + // Seuil propre a l'etage 1 : les deux chaines de mesure different d'environ + // 1 % en gain et n'ont pas le meme offset (docs/mesure-cartepuissance.md). + Comp1Regs.DACVAL.bit.DACVAL = SAFETY_DAC_CODE_STAGE1; GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 3; // COMP1OUT // Comparateur 2 : idem, shunt etage 2 sur COMP2A. @@ -40,7 +42,7 @@ void safety_init(void) Comp2Regs.COMPCTL.bit.SYNCSEL = 0; Comp2Regs.COMPCTL.bit.CMPINV = 1; Comp2Regs.COMPCTL.bit.QUALSEL = 0; - Comp2Regs.DACVAL.bit.DACVAL = SAFETY_DAC_CODE; + Comp2Regs.DACVAL.bit.DACVAL = SAFETY_DAC_CODE_STAGE2; GpioCtrlRegs.GPAMUX1.bit.GPIO3 = 3; // COMP2OUT // Digital Compare : COMPxOUT -> DCAEVT1 -> Trip Zone one-shot (latche)