20 KiB
Prompt pour Claude Code — Firmware TMS320F28027 « dual boost »
Version 2 — brochage corrigé, constantes de calibration réelles. À coller dans Claude Code (CCS v21) après avoir ouvert le dépôt
TMS320F28027-dualboost.
1. Contexte
Firmware d'une alimentation haute tension à deux étages boost en cascade, pour préamplis à tubes (charge ~10-15 mA, modulée à fréquence audio).
- MCU : TMS320F28027, LQFP48 (PT), carte sur mesure
- IDE : Code Composer Studio v21 (Theia), compilateur C2000
- Bibliothèque : C2000Ware, headers bitfield (
DSP28x_Project.h,F2802x_Device.h) — pas driverlib - Sonde : Olimex TMS320-XDS100-V3
- Horloge : oscillateur interne INTOSC1 (10 MHz), PLL → SYSCLKOUT = 60 MHz
- Dépôt :
git@git.syoul.fr:alim-High-Volt-boost/TMS320F28027-dualboost.git
Un ESP32-C3 (dépôt ESP32-C3-dualboost, déjà fonctionnel : OLED SSD1322
256x64 + serveur web) gère l'IHM. Protocole dans docs/ESP32-UART.md —
source de vérité, ne rien inventer.
Puissance par étage : MOSFET IPD60R360 (600 V), diode SiC STPSC406 (600 V/4 A), inductance 47-100 µH. Sortie HT commutée par MOSFET N piloté par optocoupleur photovoltaïque VOM1271 (~265-325 µs — coupure de sécurité lente, pas une protection rapide).
2. Brochage définitif (LQFP48 PT)
Entrées analogiques
| Signal | Broche | Fonction MCU | Rôle | Échelle |
|---|---|---|---|---|
V-Batt-adc |
10 | ADCINA0 / VREFHI | VIN | 11,11 V/V (PE 36,7 V) |
I-Batt-adc |
8 | ADCINA1 | IIN | 1,125 A/V (PE 3,71 A) |
I-shunt1-adc |
9 | ADCINA2 / COMP1A / AIO2 | I1 + protection étage 1 | 0,02 Ω × 30 → 1,667 A/V (PE 5,5 A) |
I-HV-adc |
7 | ADCINA3 | IOUT | 16,67 mA/V (PE 55 mA) |
I-shunt2-adc |
5 | ADCINA4 / COMP2A / AIO4 | I2 + protection étage 2 | idem |
V-HV-adc |
14 | ADCINB2 / AIO10 | VOUT | 178,6 V/V (PE 589 V) |
V-inter-adc |
16 | ADCINB4 / AIO12 | V1 | 31,25 V/V (PE 103 V, V1max = 50 V → 1,6 V) |
Temp1 |
17 | ADCINB6 / AIO14 | NTC étage 1 | voir §5 |
Temp2 |
18 | ADCINB7 | NTC étage 2 | voir §5 |
| libre | 13 | ADCINB1 | — | |
| libre | 15 | ADCINB3 | — |
Points d'attention obligatoires :
- Les broches 5, 9, 14, 16, 17 sont des AIO (AIO4, AIO2, AIO10, AIO12,
AIO14) et sont en mode numérique par défaut au reset. Sans écriture
explicite dans
AIOMUX1(valeur du champ = 2), ni l'ADC ni les comparateurs ne voient le signal. À faire en tout début d'init. - Les shunts sont obligatoirement sur les entrées
COMPxA(pins 9 et 5). Contrainte de silicium :COMPxAest câblée en dur sur l'entrée non-inverseuse (+) du comparateur ;COMPxBn'existe que comme alternative au DAC sur l'entrée inverseuse (−). Il n'y a aucun mux permettant de les échanger. Seules les entrées A sont donc utilisées, avecCOMPSOURCE = 0(référence = DAC interne). - Les broches 9 et 5 servent simultanément d'entrée ADC et d'entrée
comparateur — c'est la même broche physique, le signal alimente les deux
chemins en parallèle. Pas de conflit,
I1/I2restent disponibles en télémétrie. VREFHIest partagé avecADCINA0, utilisé pour VIN → référence ADC interne obligatoire (ADCCTL1.bit.ADCREFSEL = 0, pleine échelle 3,3 V). Conséquences documentées : erreur de gain ±60 LSB, tempco −50 ppm/°C. Le signal sur ADCINA0 ne doit jamais dépasser VDDA.V-inter: pleine échelle 103 V pour une tension intermédiaire maximale de 50 V → ~48 % de la plage ADC utilisée, ~2000 codes sur la plage utile. Pas de saturation, diviseur conservé tel quel.
Répartition des tensions
- Étage 1 : 10 V → 50 V max (D ≈ 0,8)
- Étage 2 : 50 V → 400 V max (D ≈ 0,875), consigne programmable 200 / 250 / … / 400 V
Entrées / sorties numériques
| Signal | Broche | Fonction MCU | Sens |
|---|---|---|---|
UART-RX |
48 | GPIO28 / SCIRXDA | Entrée (← ESP32 GPIO7) |
UART-TX |
1 | GPIO29 / SCITXDA | Sortie (→ ESP32 GPIO6) |
Stage1-PWM |
29 | GPIO0 / EPWM1A | Sortie PWM étage 1 (HRPWM possible) |
Stage1-default |
28 | GPIO1 / COMP1OUT | Sortie comparateur 1 |
Stage2-PWM |
37 | GPIO2 / EPWM2A | Sortie PWM étage 2 (HRPWM possible) |
Stage2-default |
38 | GPIO3 / COMP2OUT | Sortie comparateur 2 |
Stage1-EN |
27 | GPIO16 | Sortie — enable logiciel étage 1 |
Stage2-EN |
26 | GPIO17 | Sortie — enable logiciel étage 2 |
HV_EN |
31 | GPIO32 | Sortie — commande sortie HT (VOM1271) |
| LED bleue | 47 | GPIO12 | Sortie |
| LED rouge | 35 | GPIO33 | Sortie |
| JTAG | 20-23 | TDI/TMS/TDO/TCK | Ne pas reconfigurer en GPIO |
Les LEDs sont câblées avec une résistance de 1 kΩ vers le 3,3 V → logique
probablement inversée (LED allumée quand le GPIO est bas) : à vérifier au
premier test et à encapsuler dans led_set() pour ne pas polluer le reste
du code.
Aucune broche TZ externe n'est disponible (GPIO12 = TZ1 → LED, GPIO16/17
= TZ2/TZ3 → Stage-EN). Tous les trips passent par le Digital Compare interne
depuis COMPxOUT. Conséquence assumée : pas de possibilité d'ajouter un trip
externe (arrêt d'urgence, thermostat matériel) sans reprendre le routage.
Logique de sécurité câblée (hors MCU)
Deux portes ET 74HVC1G08 (IC8, IC9), une par étage :
COMPxOUT (Stagex-default, CMPINV=1 : 1=OK, 0=défaut) ──┐
├─ AND ─→ PWMx-EN (driver)
GPIO Stagex-EN (logiciel : 1=autorisé, 0=inhibé) ──┘
Les drivers sont actifs à l'état haut, d'où CMPINV = 1 sur les deux
comparateurs. Le logiciel peut inhiber en plus, jamais outrepasser la
protection matérielle.
3. Horloge — contraintes liées à l'oscillateur interne
Pas de quartz : INTOSC1 (10 MHz nominal). PLL pour 60 MHz :
PLLCR = 12, DIVSEL = /2 → 10 × 12 / 2 = 60 MHz.
Obligatoire : quand la source est l'oscillateur interne, PLLLOCKPRD
doit être écrit avec 10000 minimum (sinon le verrouillage PLL est mal
temporisé).
Dérive à documenter et à gérer : 3,03 à 4,85 kHz/°C, plage 9,6-10,6 MHz sur −40/+125 °C, soit ~2 % sur 40 °C d'échauffement. Impacts :
- UART fixé à 57600 bauds (au lieu de 115200) pour élargir la marge de
tolérance sur le timing des trames. Côté ESP32,
Serial1.begin(57600, ...)doit être mis à jour en conséquence. - Fréquence de découpage réelle à ±2 % →
FREQ1/FREQ2en télémétrie sont indicatifs, à annoter comme tels. - Prévoir un point d'accroche (fonction vide + commentaire) pour ajouter plus tard la compensation d'oscillateur via le capteur de température interne (voir Oscillator Compensation Guide + code C2000Ware).
4. Architecture logicielle demandée
src/
main.c boucle principale, ordonnancement
bsp_clock.c/h InitSysCtrl, INTOSC1 + PLL 60 MHz, PLLLOCKPRD, watchdog
bsp_gpio.c/h mux GPIO, AIOMUX1, LEDs, HV_EN, StageX-EN
safety.c/h comparateurs + DAC, Digital Compare, Trip Zone, EMUSTOP
pwm.c/h init ePWM1/ePWM2, duty, fréquence (HRPWM plus tard)
adc.c/h init ADC (réf interne), SOC déclenché par ePWM, lecture brute
measure.c/h conversion brut -> unités physiques (V, A, °C)
control.c/h régulation — SQUELETTE UNIQUEMENT pour l'instant
uart_link.c/h SCI-A 57600 8N1, trames $T,...*XX et $C,...*XX
calib.h TOUTES les constantes matérielles, rien ailleurs
protocol.h tags, structures Telemetry / CommandState
docs/
ESP32-UART.md protocole (copié depuis le dépôt ESP32)
5. Constantes de calibration (calib.h)
Aucune de ces valeurs ne doit apparaître ailleurs que dans ce header.
Tensions et courants
| Grandeur | Relation | Coefficient |
|---|---|---|
| VIN | 1,8 V @ 20 V | VIN = Vadc × 11,111 |
| IIN | 2,4 V @ 2,7 A | IIN = Vadc × 1,125 |
| V1 (inter) | 3,2 V @ 100 V | V1 = Vadc × 31,25 |
| VOUT | 2,8 V @ 500 V | VOUT = Vadc × 178,571 |
| IOUT | 3,0 V @ 50 mA | IOUT = Vadc × 0,016667 (A) |
| I1, I2 | shunt 0,02 Ω, gain ×30 | I = Vadc / 0,6 = Vadc × 1,6667 |
Conversion brut → volts : Vadc = raw × 3.3f / 4096.0f (référence interne).
Seuil de protection (identique sur les deux étages)
- Seuil : 3 A →
Vadc = 3 × 0,02 × 30 = 1,8 V - Code DAC 10 bits :
round(1.8 / 3.3 × 1023)= 558 - Marges connues : gain DAC −1,5 %, offset DAC 10 mV, offset comparateur ±5 mV, hystérésis 35 mV (≈ 58 mA ramenés au courant) → précision réelle du seuil de l'ordre de ±2 %, largement suffisant face à la marge des composants.
- Temps de réponse comparateur → Trip Zone : 30 ns (asynchrone).
- Définir le seuil en ampères dans
calib.h, et calculer le code DAC par macro — ne jamais écrire 558 en dur.
NTC (B57451V5103J062, boîtier 0805)
Montage : 3,3 V — NTC 10 k — R 10 k — 0 V, mesure au point milieu.
R25 = 10 kΩ, B25/100 = 4000 K ±3 %.
R_ntc = R_fixe × Vadc / (3.3 - Vadc)
T(K) = 1 / ( 1/298.15 + ln(R_ntc / 10000) / 4000 )
Points de repère attendus : 0,75 V à 0 °C · 1,65 V à 25 °C · 2,94 V à 80 °C · 3,09 V à 100 °C. La tension monte avec la température. Sensibilité vers 100 °C : ~7,7 mV/°C, soit ~10 LSB/°C — suffisant pour de la surveillance. La tolérance ±3 % sur B donne ~±2 °C d'erreur vers 100 °C sans calibration individuelle : acceptable pour de la protection thermique, pas pour de la mesure de précision. À documenter.
Résistance fixe confirmée à 10 kΩ (pont 10 k NTC / 10 k).
6. Étapes d'implémentation (un commit par étape)
Étape 1 — Squelette + « hello LED »
- Projet CCS F28027, fichier
.cmdFlash InitSysCtrl()adapté : INTOSC1,PLLLOCKPRD = 10000, PLLCR 12, DIVSEL /2- Watchdog désactivé au début (à réactiver plus tard)
- Clignotement LED pour valider compilation + flash + XDS100v3
Critère : LED à 1 Hz, compilation sans warning, SYSCLKOUT vérifié à 60 MHz (sortie XCLKOUT ou mesure d'un timer).
Étape 2 — Sécurité (AVANT toute génération PWM)
Aucun PWM ne doit sortir tant que les protections ne sont pas armées et testées.
AIOMUX1: basculer AIO2, AIO4, AIO10, AIO12, AIO14 en mode analogique (valeur du champ = 2).- Horloges de module :
PCLKCR3.bit.COMP1ENCLK = 1etCOMP2ENCLK = 1, sinonCOMPSTSne se met jamais à jour. - Comparateurs COMP1 / COMP2 (shunt sur l'entrée A, DAC en référence) :
COMPCTL.bit.COMPDACEN = 1— sans ça le bloc reste éteintCOMPCTL.bit.COMPSOURCE = 0(entrée inverseuse = DAC interne)DACVAL.bit.DACVAL= code du seuil 3 A. Formule TI :V = DACVAL × (VDDA − VSSA) / 1023→ 1023, pas 4096. Calculé par macro depuisSAFETY_ISHUNT_THRESHOLD_A, jamais en dur.COMPCTL.bit.CMPINV = 1(driver actif haut : sortie 1 = OK, 0 = défaut)COMPCTL.bit.SYNCSEL = 0(asynchrone) — c'est ce qui donne les 30 ns de temps de réponse vers le Trip Zone.QUALSELn'a d'effet qu'en mode synchrone, qui ajoute de la latence : n'activer la qualification qu'après avoir observé le bruit réel au scope, jamais par précaution a priori.- Hystérésis d'entrée activée par défaut (~100 kΩ de contre-réaction,
35 mV) : compatible avec la sortie basse impédance de l'ampli.
COMPHYSTCTL(module ADC) permet de la désactiver si besoin. - Sorties routées : GPIO1 (COMP1OUT, mux 3) et GPIO3 (COMP2OUT, mux 3)
- Trip Zone via Digital Compare
DCTRIPSEL.bit.DCAHCOMPSEL= COMP1OUT (code1000b) pour ePWM1, COMP2OUT (1001b) pour ePWM2TZDCSEL.bit.DCAEVT1: configurer l'événement sur DCAH basTZSEL.bit.DCAEVT1 = 1→ one-shot (latché), pas cycle-by-cycleTZCTL.bit.TZA = TZ_FORCE_LO
- TZ6 = EMUSTOP sur ePWM1 et ePWM2 :
Le signal
EPwm1Regs.TZSEL.bit.OSHT6 = 1; EPwm1Regs.TZCTL.bit.TZA = TZ_FORCE_LO;EMUSTOPdu CPU existe bien et est câblé en dur sur TZ6 sur cette famille (TRM SPRUI09A §3.2.7) ; il n'y a simplement pas de macro nomméeEMUSTOPdans les headers, on écrit directementOSHT6. Indispensable : sans ça, un breakpoint posé pendant que le MOSFET conduit laisse l'inductance se charger sans limite. TBCTL.bit.FREE_SOFT = 0sur les deux ePWM, en plus et non à la place du point 5. Attention :FREE_SOFTgèle seulement le compteur de base de temps — l'Action Qualifier a déjà positionné la sortie, qui reste figée dans son dernier état. Si le CPU s'arrête pendant le temps de conduction, la broche reste haute. Seul TZ6 +TZ_FORCE_LOgarantit la mise à l'état bas.EPWMx_TZINT: diagnostic logiciel uniquement (log, LED rouge, notification ESP32). La coupure est déjà faite en matériel.- API :
safety_get_fault_flags(),safety_clear_faults()(écritureTZCLR[OST], jamais appelée automatiquement). - Test :
TZFRC[OSHT]pour forcer un trip logiciel, sans provoquer de vrai court-circuit.
Critère : TZFRC force les deux sorties à 0 et lève le flag ; un
breakpoint coupe le PWM ; le flag ne se réarme pas seul.
Étape 3 — GPIO de commande, état sûr au reset
HV_EN,Stage1-EN,Stage2-ENà 0 avant toute autre configuration- ⚠️ L'état au reset n'est PAS fail-safe : GPIO1 et GPIO3 (COMPxOUT) sont
des broches à fonction PWM, dont les pull-ups ne sont pas activées au
reset → elles flottent. GPIO16/17 (Stage-EN) ne sont pas des broches PWM
→ pull-ups activées → état haut. Résultat : à la mise sous tension, une
entrée de chaque porte ET flotte et l'autre est à 1 → sortie
indéterminée, le driver peut être validé avant que le firmware ne tourne.
- Correctif matériel obligatoire : pull-down 10 kΩ sur les deux entrées de chaque porte ET, et sur les lignes PWM vers les drivers. Le firmware ne peut rien pour la fenêtre pré-boot.
- Correctif logiciel complémentaire : désactiver les pull-ups internes de
GPIO16/17 via
GPAPUDdès l'init.
- LED rouge = défaut latché, LED bleue = fonctionnement nominal
- Encapsuler la polarité des LED dans
led_set()
Étape 4 — ADC
- Référence interne (
ADCREFSEL = 0),Device_cal()appelé - 9 canaux (§2), SOC déclenché par ePWM, jamais en free-run : échantillonner à un instant stable, loin des fronts de commutation
- ISR
ADCINT1courte : stockage des valeurs brutes, rien de plus - Moyenne glissante côté
measure.cpour la télémétrie. La protection rapide ne passe pas par l'ADC.
Étape 5 — Conversion physique (measure.c)
Implémenter les relations du §5. Signature du type
float measure_vin(uint16_t raw), unités documentées dans les prototypes.
Étape 6 — PWM
- ePWM1 → étage 1 (EPWM1A, GPIO0) · ePWM2 → étage 2 (EPWM2A, GPIO2)
- Mode up-count, TBCLK = SYSCLKOUT = 60 MHz
TBPRD = 60e6 / Fpwm - 1(599 à 100 kHz, 299 à 200 kHz)- Fréquences par étage : À CONFIRMER
- Duty de départ = 0 %, puis rampe logicielle montante (soft-start), indépendante de la régulation. Ne jamais démarrer au duty nominal : le condensateur de sortie vide provoquerait un appel de courant destructeur.
Important — un boost à 0 % de duty ne donne pas 0 V en sortie. Le chemin
Vin → L → diode → Coutreste passant en permanence. Avec les deux étages à 0 %, la sortie HT est déjà à ~10 V ; si l'étage 1 régule à 50 V pendant que l'étage 2 est à 0 %, la sortie est à 50 V. Le seul organe qui isole réellement la charge estHV_EN(VOM1271). Conséquence pour le firmware : toute séquence d'arrêt, de mise en sécurité ou de timeout UART doit couperHV_ENen plus d'inhiber les PWM, jamais seulement les PWM.
- API :
pwm_set_duty(stage, float duty_0_1),pwm_set_freq(stage, hz),pwm_enable(stage, bool) - HRPWM : ne pas l'implémenter maintenant, mais structurer
pwm.cpour l'accueillir. Notes à consigner en commentaire :- disponible uniquement sur les sorties EPWMxA → les deux étages sont compatibles tels que routés
- SYSCLKOUT ≥ 50 MHz requis (OK à 60), MEP 150-310 ps, SFO obligatoire
- limitation de 3 cycles SYSCLK sur le rapport cyclique : à 200 kHz, duty min 0,67 % et max 99 % — à confronter aux points de fonctionnement
- à 100 kHz sans HRPWM : 600 pas ≈ 9,2 bits, risque de limit cycling en boucle fermée
Étape 7 — Liaison UART
Conforme à docs/ESP32-UART.md, avec 57600 bauds 8N1 (et non 115200 —
mettre à jour le document et le firmware ESP32).
- SCI-A, GPIO28 (RX) / GPIO29 (TX)
- Émission
$T,...*XXtoutes les 200-500 ms (l'ESP32 déclare la liaison perdue au-delà de 2 s) - Réception
$C,HT=x,PWM1=x,PWM2=x*XXtoutes les 500 ms - Checksum XOR entre
$et*, format%02Xmajuscules (sinon rejet silencieux côté ESP32), terminaison\n, ligne ≤ 160 caractères - Réception par ISR + buffer circulaire, parsing dans la boucle principale
- Trame au checksum invalide : ignorée silencieusement
- Timeout côté TMS320 : sans trame
$Cvalide depuis > 2 s → retour en état sûr (PWM inhibés, HT coupée). La sécurité ne dépend jamais de l'ESP32. - Vérifier le support
%fdu compilateur C28x danssprintf; si coût trop élevé, formater les flottants manuellement (entier + décimales)
Champs : FREQ1 FREQ2 DUTY1 DUTY2 VIN IIN V1 I1 T1 VOUT I2 T2 IOUT
Étape 8 — Régulation : squelette seulement
Aucune régulation active à ce stade. Le PWM reste à sa valeur de départ.
typedef struct {
float setpoint; // consigne (V)
float measured;
float duty; // 0..1
float duty_min, duty_max;
bool enabled;
} control_stage_t;
void control_init(void);
void control_set_setpoint(int stage, float volts);
float control_update(int stage, float measured, float dt); // renvoie duty inchangé
Prévoir l'emplacement d'appel cadencé (dt constant) dans l'ISR ADC ou PWM.
Documenter en commentaire : le boost à fort gain présente un zéro dans le
demi-plan droit qui limite la bande passante atteignable ; la charge
(~10-15 mA, modulée à fréquence audio) impose surtout un bon PSRR en bande
audio ; une structure ADRC ou un PI cascadé sera évalué ensuite.
7. Règles
- Headers bitfield uniquement, pas de driverlib, pas de mélange sur un même périphérique
EALLOW/EDISautour de tous les registres protégés (TZSEL, TZCTL, TZEINT, TZCLR, TZFRC, HRCNFG, GPxMUX, AIOMUX1, INTOSCnTRIM, table des vecteurs PIE…)- Aucune valeur magique : tout dans
calib.h, commenté avec son origine - ISR courtes : pas de
sprintf, pas de flottant lourd, pas d'attente - Ordre d'init strict : GPIO en état sûr → AIOMUX1 → horloge → sécurité → ADC → UART → PWM en dernier
- Commits atomiques, un par étape
8. Interdits
- Activer le PWM avant que l'étape 2 soit testée
- Effacer automatiquement un flag de défaut latché
- Faire dépendre une protection du CPU, de l'ADC ou de l'UART
- Implémenter la régulation en boucle fermée maintenant
- Toucher aux broches JTAG (20-23)
- Inventer le protocole UART
9. Points encore ouverts
- Fréquence de découpage par étage (100 ou 200 kHz)
- Polarité réelle des LED (à confirmer au premier test)
- Valeur d'inductance retenue par étage (47 ou 100 µH) — influe sur le mode CCM/DCM et sur la vitesse de montée d'un courant de défaut
10. Première action attendue
Avant d'écrire du code : lire le dépôt, lister l'existant, poser les questions sur les points du §9, puis proposer un plan et attendre validation avant de commencer l'étape 1.