InitFlash manquante : tout le firmware tournait 4x trop lentement

DEFAUT PRINCIPAL -- etats d'attente de la flash jamais configures

  Le firmware s'execute depuis la flash (_FLASH), mais InitFlash() n'etait
  jamais appelee. Au reset FBANKWAIT vaut son MAXIMUM (0x0F0F, soit
  RANDWAIT = PAGEWAIT = 15) : chaque acces flash coutait 16 cycles au lieu
  des 3 necessaires a 60 MHz.

  Ironie : le memcpy des ramfuncs en tete de main() existe PRECISEMENT pour
  copier InitFlash() en RAM, puisqu'elle doit s'executer hors flash. La
  recopie se faisait depuis le premier jour sans que la fonction ne soit
  jamais appelee -- la section ramfuncs ne contenait que 4 mots, elle en
  fait 0x21 maintenant.

  Mesure au scope sur l'ISR ADC :
    avant : 22,3 kHz, ISR 30 us, 88 % de charge CPU
    apres : 66,85 kHz, ISR 4,9 us, 33 % de charge CPU
  66,85 kHz est le maximum theorique (200 kHz / SOCAPRD=3), atteint sans
  aucun declenchement manque. Recoupe au compteur : 67,5 kHz.

Optimisation compilateur

  -O2 ajoute : le projet compilait sans aucune option d'optimisation.
  Contribution mesuree : ISR de 41 a 30 us, mais SANS changer la cadence --
  ce n'etait pas le facteur limitant. Conserve, le gain reste reel.

Discipline ISR (ni multiplication ni division en interruption)

  status_led_tick() : quatre modulos (%200, %40, %100, %100) remplaces par
    un compteur qui reboucle sur comparaison.
  cpu_timer0_isr()  : "tick % 30" remplace par un compteur dedie.
  control_tick()    : le tableau stages[2] etait initialise sur la pile a
    chaque appel, des dizaines de milliers de fois par seconde -> static const.
  pwm_set_duty_counts() : une division flottante y mettait a jour la
    consigne, sur un F28027 sans FPU. Supprimee ; pwm_get_duty() relit
    CMPA, la telemetrie reste juste.

control.c/h -- balayage de caracterisation (BOUCLE OUVERTE)

  Ce module ne regule rien : il balaie le duty de 1 % a 50 % puis revient,
  par pas de 1 LSB de CMPA a chaque conversion ADC. Appel cadence depuis
  l'ISR ADC, conformement a PROMPT §6 etape 8.

  API en counts ajoutee a pwm.c : le pas minimal est 1 LSB de CMPA et
  DEPEND DE L'ETAGE (1/300 = 0,333 % a 200 kHz, 1/600 = 0,167 % a 100 kHz).
  Une consigne flottante ne permet pas d'exprimer "le plus petit pas".

  Resultat mesure a 66,7 kHz :
    etage 1 : 1->50 % en 2,2 ms, cycle 4,4 ms, 22 %/ms
    etage 2 : 1->50 % en 4,4 ms, cycle 8,9 ms, 11 %/ms
  L'etage 2 est deux fois plus lent PARCE QUE sa resolution est deux fois
  plus fine, a cadence de mise a jour identique.

  La telemetrie transporte desormais le duty reel, relu depuis CMPA.

L'instrumentation de mesure a ete RETIREE : GPIO32 est rendue a HV_EN, qui
en a de nouveau l'usage exclusif (verifie sur cible, broche stable a 0).

Note : ce defaut de wait states etait present depuis le debut du projet.
Toutes les mesures de timing anterieures etaient faussees d'un facteur 4.
Les frequences PWM et les protections sont materielles et restent valides.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 21:28:34 +02:00
parent b1bb0fa21a
commit 69801df648
10 changed files with 333 additions and 48 deletions

View File

@ -50,8 +50,14 @@ void status_led_set_state(led_state_t state)
}
}
// Appelee en ISR : aucune multiplication ni division. Le compteur de phase
// reboucle par COMPARAISON, jamais par modulo -- sur C28x un "%" par une
// valeur qui n'est pas une puissance de deux est une division logicielle,
// donc plusieurs dizaines de cycles.
void status_led_tick(void)
{
uint16_t period;
uint16_t on_time;
bool blue = false;
bool red = false;
@ -60,25 +66,55 @@ void status_led_tick(void)
s_startup_hold--;
}
// Periode et duree du niveau actif du motif courant.
switch (s_state)
{
case LED_STATE_NOMINAL:
period = NOMINAL_PERIOD;
on_time = NOMINAL_ON;
break;
case LED_STATE_LINK_LOST:
period = LINK_LOST_PERIOD;
on_time = LINK_LOST_ON;
break;
case LED_STATE_EMUSTOP:
period = EMUSTOP_PERIOD;
on_time = EMUSTOP_HALF;
break;
case LED_STATE_OVERTEMP:
period = OVERTEMP_PERIOD;
on_time = OVERTEMP_ON;
break;
default: // STARTUP et OVERCURRENT : niveau fixe, pas de motif
period = 1U;
on_time = 1U;
break;
}
s_phase++;
if (s_phase >= period)
{
s_phase = 0U;
}
switch (s_state)
{
case LED_STATE_NOMINAL:
blue = ((s_phase % NOMINAL_PERIOD) < NOMINAL_ON);
break;
case LED_STATE_LINK_LOST:
blue = ((s_phase % LINK_LOST_PERIOD) < LINK_LOST_ON);
blue = (s_phase < on_time);
break;
case LED_STATE_EMUSTOP:
blue = ((s_phase % EMUSTOP_PERIOD) < EMUSTOP_HALF);
blue = (s_phase < on_time);
red = !blue;
break;
case LED_STATE_OVERTEMP:
red = ((s_phase % OVERTEMP_PERIOD) < OVERTEMP_ON);
red = (s_phase < on_time);
break;
case LED_STATE_OVERCURRENT: