Reprise controlee apres EMUSTOP, sans lever le verrouillage des defauts
Le verrouillage total decide precedemment (aucun effacement, cycle
d'alimentation obligatoire) rendait le developpement intenable : chaque
breakpoint tuait le PWM jusqu'a debranchement. EMUSTOP n'est pourtant pas
un defaut de puissance mais un artefact du debogueur.
La distinction est maintenant explicite dans la boucle principale :
overcurrent / overtemp -> enter_safe_state(), VERROUILLE jusqu'au cycle
d'alimentation (PROMPT §8), aucun acquittement
emustop seul -> reprise controlee
La reprise ne redemarre PAS la conversion la ou elle s'etait arretee :
elle repasse par start_conversion(), le meme chemin qu'au boot, duty remis
a zero d'abord. Quand la rampe de soft-start existera, la reprise apres
breakpoint en beneficiera automatiquement.
uart_link_restart()
Reset logiciel du SCI, vidage des FIFO, abandon de la ligne en cours et
de la trame en attente. Pendant la halte l'ESP32 a continue d'emettre :
l'anneau de reception a deborde et la ligne assemblee est tronquee.
Le vidage avance seulement la QUEUE de l'anneau ; s_rx_head appartient a
l'ISR et n'est pas touche, ce qui exclut toute course.
TZCLR au demarrage dans safety_init()
TZFLG.OST est latche dans le materiel : il survit a un reset logiciel
comme a un rechargement de programme, et sans ce clear le PWM restait
coupe sans raison visible.
Sans danger, pour une raison precise : a ce stade pwm_init() a deja
force les deux sorties a l'etat bas (AQCSFRC), donc rien ne demarre ; et
si la condition de defaut est toujours presente le trip se re-verrouille
immediatement. On n'efface qu'un etat perime, jamais un defaut reel.
start_conversion()
Sequence de demarrage factorisee, appelee au boot et a la reprise.
Inhibe la decharge AVANT d'autoriser les etages -- jamais l'inverse,
sinon la conversion travaillerait contre le circuit de decharge.
Valide sur cible apres cycle pause/reprise/pause :
TZEINT.OST rearme a 1 (l'ISR l'avait mis a 0), AQCSFRC.CSFA a 0,
GPIO5 a 1 (decharge inhibee), GPIO16 a 1.
LIMITE CONNUE, non resolue ici : a la reprise l'effacement est immediat
alors que la telemetrie ne part que toutes les 300 ms. FAULT=1 ne sera
donc quasiment jamais emis, et l'ESP32 ignorera qu'un EMUSTOP a eu lieu
(sans meme voir de timeout si la halte a dure moins de 2 s). Transmettre
FAULT=1 AVANT la halte est structurellement impossible : la halte est
asynchrone, imposee par la logique de debug, sans point d'accroche
prealable -- c'est justement pourquoi TZ6 est un chemin materiel. La
solution sera un drapeau collant maintenu quelques secondes apres la
reprise.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
67
src/main.c
67
src/main.c
@ -50,6 +50,36 @@ static void update_overtemp(void)
|
||||
update_one_overtemp(s_overtemp_t2, measure_temp(adc_get_raw(ADC_CH_T2)));
|
||||
}
|
||||
|
||||
// Sequence de demarrage de la conversion. Appelee au boot ET apres un
|
||||
// EMUSTOP : dans les deux cas on repart du meme etat connu, on ne reprend
|
||||
// jamais la conversion la ou elle s'etait arretee.
|
||||
//
|
||||
// Ordre impose : la decharge est inhibee AVANT d'autoriser les etages,
|
||||
// sinon la conversion travaillerait contre le circuit de decharge.
|
||||
static void start_conversion(void)
|
||||
{
|
||||
hv_discharge_set(false);
|
||||
|
||||
// Duty a zero d'abord : c'est le point de depart obligatoire, un
|
||||
// condensateur de sortie vide provoquerait sinon un appel de courant
|
||||
// destructeur (PROMPT §6 etape 6).
|
||||
pwm_set_duty(STAGE_1, 0.0f);
|
||||
pwm_set_duty(STAGE_2, 0.0f);
|
||||
|
||||
pwm_enable(STAGE_1, true);
|
||||
pwm_enable(STAGE_2, true);
|
||||
stage_enable_set(STAGE_1, true);
|
||||
stage_enable_set(STAGE_2, true);
|
||||
|
||||
// ---- BRING-UP : duty applique d'un coup, SANS rampe de soft-start. --
|
||||
// A REMPLACER par la rampe avant toute mise sous tension de la
|
||||
// puissance, et a supprimer entierement quand control.c pilotera le
|
||||
// duty (etape 8).
|
||||
pwm_set_duty(STAGE_1, 0.5f);
|
||||
pwm_set_duty(STAGE_2, 0.5f);
|
||||
// ---------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// Priorite 2 > 3 > 4 > 5 > 1 : EMUSTOP est le code le moins prioritaire
|
||||
// malgre son numero, pour ne jamais masquer une surintensite reelle.
|
||||
static fault_code_t compute_fault_code(const safety_faults_t *f)
|
||||
@ -161,14 +191,7 @@ void main(void)
|
||||
adc_init();
|
||||
uart_link_init();
|
||||
|
||||
// ---- BRING-UP : retest PWM + ADC, puissance DECONNECTEE ---------------
|
||||
// A RETIRER avant toute mise sous tension de la carte de conversion.
|
||||
// HV_EN et Stagex-EN restent a 0 (exclus de ce test).
|
||||
pwm_set_duty(STAGE_1, 0.5f);
|
||||
pwm_set_duty(STAGE_2, 0.5f);
|
||||
pwm_enable(STAGE_1, true);
|
||||
pwm_enable(STAGE_2, true);
|
||||
// -----------------------------------------------------------------------
|
||||
start_conversion();
|
||||
|
||||
EALLOW;
|
||||
PieVectTable.TINT0 = &cpu_timer0_isr;
|
||||
@ -212,19 +235,29 @@ void main(void)
|
||||
// Aucun redemarrage automatique : rien ne reactive le PWM ensuite,
|
||||
// meme si la temperature redescend sous l'hysteresis ou si le defaut
|
||||
// disparait. Il faut un reset, ou une commande explicite (etape 8).
|
||||
if (faults.stage1_fault || faults.stage2_fault
|
||||
|| s_overtemp_t1 || s_overtemp_t2)
|
||||
if (faults.overcurrent || s_overtemp_t1 || s_overtemp_t2)
|
||||
{
|
||||
// Defaut de PUISSANCE : verrouille jusqu'au cycle d'alimentation
|
||||
// (PROMPT §8). Aucun acquittement, ni automatique ni par UART.
|
||||
enter_safe_state();
|
||||
}
|
||||
else
|
||||
else if (faults.emustop)
|
||||
{
|
||||
// Inhiber la decharge AVANT d'autoriser les etages : jamais
|
||||
// l'inverse, sinon on ferait travailler la conversion contre le
|
||||
// circuit de decharge.
|
||||
hv_discharge_set(false);
|
||||
stage_enable_set(STAGE_1, true);
|
||||
stage_enable_set(STAGE_2, true);
|
||||
// EMUSTOP seul : ce n'est pas un defaut de puissance mais un
|
||||
// artefact du debogueur. On autorise donc la reprise -- mais en
|
||||
// repassant par la sequence de demarrage complete, jamais en
|
||||
// reprenant la conversion la ou elle s'etait arretee.
|
||||
//
|
||||
// L'effacement ne peut aboutir qu'ici, CPU en marche : tant que
|
||||
// le coeur est halte, EMUSTOP reste asserte et le drapeau se
|
||||
// re-verrouille aussitot.
|
||||
//
|
||||
// La liaison est relancee aussi : pendant la halte l'ESP32 a
|
||||
// continue d'emettre, l'anneau de reception a deborde et la
|
||||
// ligne en cours est tronquee.
|
||||
uart_link_restart();
|
||||
safety_clear_faults();
|
||||
start_conversion();
|
||||
}
|
||||
|
||||
status_led_set_state(compute_led_state(&faults));
|
||||
|
||||
16
src/safety.c
16
src/safety.c
@ -68,6 +68,22 @@ void safety_init(void)
|
||||
EPwm2Regs.TZSEL.bit.OSHT6 = 1;
|
||||
EPwm2Regs.TBCTL.bit.FREE_SOFT = 0;
|
||||
|
||||
// Efface un trip herite d'AVANT ce demarrage. TZFLG.OST est latche dans
|
||||
// le materiel : il survit a un reset logiciel comme a un simple
|
||||
// rechargement de programme, et sans ce clear le PWM resterait coupe
|
||||
// sans raison visible.
|
||||
//
|
||||
// Sans danger : a ce stade pwm_init() a laisse les deux sorties forcees
|
||||
// a l'etat bas (AQCSFRC), donc rien ne demarre. Et si la condition de
|
||||
// defaut est toujours presente, le trip se re-verrouille immediatement
|
||||
// -- on n'efface qu'un etat perime, jamais un defaut reel.
|
||||
EPwm1Regs.TZCLR.bit.DCAEVT1 = 1;
|
||||
EPwm1Regs.TZCLR.bit.OST = 1;
|
||||
EPwm1Regs.TZCLR.bit.INT = 1;
|
||||
EPwm2Regs.TZCLR.bit.DCAEVT1 = 1;
|
||||
EPwm2Regs.TZCLR.bit.OST = 1;
|
||||
EPwm2Regs.TZCLR.bit.INT = 1;
|
||||
|
||||
PieVectTable.EPWM1_TZINT = &epwm1_tzint_isr;
|
||||
PieVectTable.EPWM2_TZINT = &epwm2_tzint_isr;
|
||||
EDIS;
|
||||
|
||||
@ -151,6 +151,30 @@ static int append_field(char *out, int room, const char *tag, float value,
|
||||
return n;
|
||||
}
|
||||
|
||||
void uart_link_restart(void)
|
||||
{
|
||||
// Reset logiciel du SCI : vide les FIFO et efface les etats d'erreur.
|
||||
SciaRegs.SCICTL1.bit.SWRESET = 0;
|
||||
SciaRegs.SCICTL1.bit.SWRESET = 1;
|
||||
|
||||
SciaRegs.SCIFFRX.bit.RXFIFORESET = 0;
|
||||
SciaRegs.SCIFFRX.bit.RXFIFORESET = 1;
|
||||
SciaRegs.SCIFFRX.bit.RXFFOVRCLR = 1;
|
||||
SciaRegs.SCIFFRX.bit.RXFFINTCLR = 1;
|
||||
|
||||
SciaRegs.SCIFFTX.bit.TXFIFOXRESET = 0;
|
||||
SciaRegs.SCIFFTX.bit.TXFIFOXRESET = 1;
|
||||
|
||||
// On jette le contenu de l'anneau en avancant seulement la QUEUE.
|
||||
// s_rx_head appartient a l'ISR : ne pas y toucher evite toute course.
|
||||
s_rx_tail = s_rx_head;
|
||||
s_line_len = 0;
|
||||
|
||||
// Trame d'emission en cours abandonnee : elle date d'avant l'arret.
|
||||
s_tx_len = 0;
|
||||
s_tx_pos = 0;
|
||||
}
|
||||
|
||||
// Champ entier (FAULT). Meme convention de bornage que append_field :
|
||||
// renvoie 0 et n'ecrit rien si le champ ne tient pas.
|
||||
static int append_field_int(char *out, int room, const char *tag, int value)
|
||||
|
||||
@ -6,6 +6,16 @@
|
||||
|
||||
void uart_link_init(void);
|
||||
|
||||
// Remet la liaison a plat sans reconfigurer le baud ni les broches :
|
||||
// reset logiciel du SCI, vidage des FIFO, abandon de la ligne en cours
|
||||
// d'assemblage et de la trame en attente d'emission.
|
||||
//
|
||||
// A appeler apres une interruption prolongee du CPU (EMUSTOP) : pendant la
|
||||
// halte l'ESP32 a continue d'emettre, l'anneau de reception a deborde et
|
||||
// la ligne en cours est tronquee. Repartir propre evite de traiter des
|
||||
// fragments anterieurs a l'arret.
|
||||
void uart_link_restart(void);
|
||||
|
||||
// Construit une trame $T,...*XX et la met en attente (docs/ESP32-UART.md).
|
||||
// NE BLOQUE PAS : rien n'est emis ici, c'est uart_link_service_tx() qui
|
||||
// alimente la ligne ensuite. Renvoie false si la trame precedente n'est pas
|
||||
|
||||
Reference in New Issue
Block a user