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)