Jean Aboutboul
Back to Projects

Station de pointage d'antenne satellitaire

Projet MICRO-210 EPFL en assembleur AVR : une station embarquée temps réel sur ATmega128L qui pilote un stepper d'azimut, un servo d'élévation, une télécommande RC5, une base EEPROM, un LCD et un mode SCAN 360°.

microcontrollers avr-assembly embedded-systems epfl robotics uart eeprom state-machines

Résumé

Station de pointage d’antenne satellitaire est un projet de microcontrôleurs réalisé à l’EPFL au printemps 2026 dans le cadre du cours MICRO-210. Le but était de construire, à l’échelle d’un microcontrôleur 8 bits, une station capable de pointer une antenne miniature vers des satellites stockés en mémoire.

Le système tourne sur une carte STK-300 avec un ATmega128L à 4 MHz. Il pilote un moteur pas-à-pas X27.168 pour l’azimut, un servomoteur Futaba S3003 pour l’élévation, un écran LCD HD44780 2×16, une télécommande infrarouge RC5, un buzzer piézoélectrique, une liaison UART à 19 200 bauds et une base satellites persistante en EEPROM.

La contrainte qui rend le projet vraiment intéressant : tout est écrit en assembleur AVR, sans ligne de C. Chaque timer, interruption, registre, flag, bit d’EEPROM et cycle d’attente est manipulé explicitement.

Projet réalisé avec Lars Clausen.

Dépôt GitHub : AboutJean/MCU-station-pointage-de-sattelites

Cours

MICRO-210 · EPFL

Projet MCU2026-G111, groupe 111, section Génie microtechnique.

Matériel

ATmega128L + STK-300

Stepper X27 pour l’azimut, servo S3003 pour l’élévation.

Modes

MANUEL · AUTO · SCAN

Pilotage direct, Go-To depuis l’EEPROM et balayage complet du ciel.

Code

~5 762 lignes AVR

20 fichiers, architecture drivers/libs, FSM et routines temps réel.

Simulation interactive

Station de pointage virtuelle

Une petite maquette web du firmware : télécommande RC5, couches BROWSING/IN_MODE, mode MANUEL, Go-To AUTO, sweep SCAN, LCD, UART et actionneurs.

Mode AUTO Couche BROWSING Action IDLE UART ON

Servo élévation · PB5 / OC1A

EL · PWM 500

Stepper azimut · PORTD[0:3]

AZ 000° · 0 pas partiels

IR + buzzer · PE7 / PE2

Dernière commande INIT

ATmega128L 4 MHz · FSM coopérative

LCD HD44780 · XMEM

MODE: AUTO AZ:000 EL:000

EEPROM / sélection

Slot 0 · ISS

Slots valides 10/32 · Hits SCAN 0

UART terminal · 19 200 bps

System init... OK
FSM ready.
Vivanco UR Z2 · RC5
VOL
MUTE
CH

Astuce : maintiens CH ou VOL pour voir l'accélération RC5. POWER interrompt tout mouvement.

Montage STK-300 avec télécommande RC5, stepper X27, servo Futaba S3003 et LCD 2x16.

Prototype physique : carte STK-300, modules EPFL, télécommande RC5, stepper d’azimut et LCD.

1. Introduction

1.1 Contexte et inspiration

Le réseau ESTRACK de l’ESA utilise des antennes paraboliques réparties sur le globe pour communiquer avec des satellites. Chaque antenne doit pointer vers une cible en orbite avec une précision de l’ordre du dixième de degré, en contrôlant deux axes :

  • l’azimut, rotation horizontale sur 0°–360° ;
  • l’élévation, inclinaison verticale sur 0°–90°.

Notre projet reprend ce concept en version miniature : une station de pointage capable de stocker des satellites, de s’orienter automatiquement vers eux, ou de balayer le ciel pour retrouver ceux qui correspondent à l’élévation courante.

Le choix de l’assembleur n’était pas du masochisme, même si le Git log suggère parfois le contraire. C’était le cœur pédagogique du projet : programmer en AVR force à comprendre exactement ce que fait le processeur à chaque cycle d’horloge. Chaque bit de chaque registre a une signification, et aucune abstraction ne vient masquer le matériel.

1.2 Ce que fait le système

La station sait faire trois choses :

  1. Pointer manuellement : l’utilisateur contrôle l’azimut et l’élévation à la télécommande, comme avec un joystick.
  2. Pointer automatiquement : l’utilisateur choisit un satellite dans une base de 32 slots EEPROM, puis l’antenne s’y déplace seule par le chemin azimutal le plus court.
  3. Scanner le ciel : l’antenne effectue une rotation complète de 360° et détecte les satellites visibles depuis l’élévation actuelle.

1.3 En un coup d’œil

CaractéristiqueDétail
MCUATmega128L à 4 MHz, oscillateur RC interne
CarteSTK-300 EPFL
Axe azimutMoteur pas-à-pas X27.168, 1/3° par pas, 360° continu
Axe élévationServomoteur Futaba S3003, PWM 50 Hz, 0°–90°
InterfaceTélécommande IR Vivanco UR Z2, protocole RC5
AffichageLCD HD44780 2×16, memory-mapped
CommunicationUART 19 200 bauds, miroir et injection de commandes
StockageEEPROM interne, 32 slots × 16 octets
Code~5 762 lignes d’assembleur AVR, 20 fichiers
Langage100% assembleur AVR

2. Architecture matérielle

2.1 Vue d’ensemble du montage

Le système est construit autour de la STK-300. L’ATmega128L reçoit les commandes IR ou UART, met à jour sa machine à états, puis pilote les actionneurs et les retours utilisateur.

Architecture matérielle

STK-300 au centre, périphériques autour

Le montage garde l'ATmega128L comme point de convergence : entrées RC5 et UART d'un côté, actionneurs, LCD et buzzer de l'autre.

Entrées

IR Demod TSOP RC5 sur PE7 / INT7
Terminal PC UART USB 19 200 Bd
événements
STK-300 ATmega128L 4 MHz · assembleur AVR
signaux

Actionneurs et affichage

LCD HD44780 Bus XMEM 0x8000 / 0xC000
Servo S3003 PWM Timer1, 50 Hz
Stepper X27 PORTD[0:3], demi-pas
Buzzer piézo Bit-bang sur PE2

Schéma système

Architecture embarquée du projet

Une lecture compacte du système : commandes RC5 ou UART, FSM à deux couches, drivers bas niveau, affichage, persistance EEPROM et actionneurs pour l'azimut et l'élévation.

ATmega128L @ 4 MHz 3 modes: MANUEL / AUTO / SCAN INT7 + Timer2 + Timer1 PWM

Entrées utilisateur

  • télécommande IR RC5 sur PE7
  • terminal PC sur UART0
  • touches chiffres, GUIDE, AV, POWER

Cœur FSM

  • couche BROWSING / IN_MODE
  • dispatch par priorité
  • handlers AUTO, MANUEL, SCAN

Actionneurs

  • stepper X27 sur PORTD[0:3]
  • servo S3003 sur PB5 / OC1A
  • buzzer piézo sur PE2

Temps et interruptions

  • INT7 capture une trame RC5
  • Timer2 décrémente les overlays à 1 ms
  • Timer1 génère le PWM servo en hardware

Affichage et retour

  • LCD HD44780 2x16 via XMEM
  • overlays temporises SAVED / DELETED
  • miroir VT100 sur UART

Mémoire et données

  • 32 slots EEPROM de 16 octets
  • cache SRAM pour le balayage SCAN
  • curseurs, cibles et position courante

2.2 L’ATmega128L, le cerveau

L’ATmega128L est un microcontrôleur AVR 8 bits à architecture Harvard, avec mémoire programme et mémoire données séparées.

RessourceCapacitéUtilisation dans le projet
Flash128 KoCode, tables, chaînes, environ 8 Ko utilisés
SRAM4 KoVariables FSM, buffers, cache SCAN
EEPROM4 KoBase de données satellites, 512 octets utilisés
Timer116 bitsPWM servo 50 Hz, Fast PWM mode 14
Timer28 bitsTick système 1 ms, mode CTC
INT7Interruption externeCapture télécommande IR sur front descendant
UART0SérieMiroir LCD et injection de commandes
Bus XMEMExterneLCD memory-mapped
GPIO53 brochesStepper, buzzer, servo, IR

Le processeur tourne à 4 MHz avec son oscillateur RC interne. Cette fréquence peut varier d’environ ±3% selon la température. C’est encore compatible avec le décodage RC5, mais seulement parce que la constante T1 a été calibrée à 1870 µs plutôt qu’aux 1778 µs théoriques.

2.3 Le moteur pas-à-pas X27, l’azimut

Le X27.168 est un micro-moteur pas-à-pas bipolaire utilisé dans des tableaux de bord automobiles. Il possède un réducteur interne de 1:180 :

  • 6 patterns électriques = 1 tour moteur = 2° à la sortie ;
  • 3 patterns = 1° ;
  • 1080 patterns = 360°.
IndexPD3PD2PD1PD0Valeur hexBobines actives
001010x05C1+ et C2+
100010x01C1+
210110x0BC1+ et C2-
310100x0AC2-
411100x0EC1- et C2-
501000x04C2+
motor_sequence_table:
    .db 0x05, 0x01, 0x0B, 0x0A, 0x0E, 0x04

Le X27 n’a pas besoin de pont en H externe dans ce montage. Ses bobines font environ 160 Ω : à 5 V, cela donne environ 31 mA par bobine, ce qui reste dans les capacités des GPIO de l’ATmega128. Le projet attaque donc les bobines directement depuis PORTD[0:3].

Le curseur motor_step_idx avance ou recule dans la table. Un délai de 3 ms entre deux pas laisse le temps à la mécanique de se stabiliser.

2.4 Le servomoteur S3003, l’élévation

Le Futaba S3003 est commandé par un signal PWM à 50 Hz. La position est proportionnelle à la largeur de l’impulsion :

Signal PWM

Un créneau de 20 ms pour commander l'élévation

Timer1 produit le signal en matériel. Le firmware ne fait que modifier OCR1A, donc la période reste stable et sans jitter logiciel.

20 ms · 50 Hz
PWM servo : impulsions de 1.0 ms à 1.8 ms dans une période de 20 ms 1.0 ms → 0° 1.8 ms → 90° période complète
Conversion firmware OCR1A = 500 + angle × 400 / 90

Le Timer1 est configuré en Fast PWM mode 14, avec ICR1 comme TOP :

ParamètreValeurSignification
Prescaler/81 tick = 2 µs à 4 MHz
ICR110 000Période de 20 ms, soit 50 Hz
OCR1A min500Pulse 1.0 ms, donc 0°
OCR1A max900Pulse 1.8 ms, donc 90°
Résolution400 ticks~0.225° par tick
Pas utilisateur10 ticks~2.25° par pression VOL
servo_init:
    sbi    DDRB, 5
    OUTI   TCCR1A, (1<<COM1A1)|(1<<WGM11)
    OUTI   TCCR1B, (1<<WGM13)|(1<<WGM12)|(1<<CS11)
    LDI2   a1,a0, 10000
    sts    ICR1H, a1
    sts    ICR1L, a0
    LDI2   a1,a0, 500
    sts    OCR1AH, a1
    sts    OCR1AL, a0
    STS2   target_ticks, a1,a0
    ret

Le double buffering matériel de OCR1A évite les glitchs : une nouvelle valeur n’est appliquée qu’au prochain passage par TOP.

2.5 L’écran LCD HD44780

Le LCD est interfacé via le bus mémoire externe XMEM de l’ATmega128. Le contrôleur LCD est mappé dans l’espace d’adressage :

XMEM LCD

Le LCD vu comme deux adresses mémoire

Le bus mémoire externe génère les signaux ALE, /WR et /RD. Le code applicatif écrit donc dans le LCD comme dans une zone mémoire.

0x8000 LCD Instruction Register

Commandes HD44780 : clear, cursor home, mode d'entrée, configuration.

0xC000 LCD Data Register

Caractères ASCII écrits sur la ligne courante du LCD 2×16.

Écrire un caractère devient une seule instruction assembleur :

ldi   a0, 'A'
sts   0xC000, a0

Le bus XMEM utilise PORTA pour les données, PORTC pour l’adresse haute et PORTG pour /WR, /RD et ALE. Le bit SRE active le bus externe, avec un wait state SRW10=1 pour respecter le timing du HD44780. Le busy flag doit être vérifié avant chaque écriture.

2.6 La télécommande infrarouge RC5

Le protocole RC5 de Philips encode chaque trame sur 14 bits Manchester :

Trame RC5

14 bits Manchester, capturés sans périphérique dédié

Le démodulateur TSOP réveille INT7. L'ISR attend ensuite le centre de chaque bit et échantillonne PE7.

S11 bit S21 bit Toggle1 bit Adresse5 bits Commande6 bits
0 logique transition HIGH → LOW au milieu du bit
1 logique transition LOW → HIGH au milieu du bit
Timing T1 ≈ 1870 µs, calibré pour l'oscillateur 4 MHz

Le décodeur fonctionne par échantillonnage bit-bang :

  1. INT7 se déclenche sur un front descendant ;
  2. le firmware attend T1/4 pour se centrer sur le premier bit ;
  3. il lit PE7, pousse le bit dans un registre résultat ;
  4. il attend T1 et répète 14 fois.
rc5_capture_from_edge:
    WAIT_US (T1/4 - 2)
    ldi     b2, 14
_rc5_loop:
    P2C     PINE, 7
    ROL2    b1, b0
    WAIT_US (T1 - 4)
    DJNZ    b2, _rc5_loop
    com     b0
    ret

Le toggle bit distingue une nouvelle pression d’une touche maintenue. S’il change, c’est un nouvel appui ; s’il reste identique, c’est une répétition. Ce mécanisme alimente held_count pour accélérer le mode MANUEL.

2.7 Le buzzer piézoélectrique

Le buzzer est piloté par bit-banging sur PE2. Le son standard est un petit « doot-doot » descendant de 1500 Hz vers 1000 Hz, sur environ 60 ms.

chirp:
    ldi   _w, 45
_n1:
    sbi   BUZZER_PORT, BUZZER_PIN
    WAIT_US 333
    cbi   BUZZER_PORT, BUZZER_PIN
    WAIT_US 333
    dec   _w
    brne  _n1

    ldi   _w, 30
_n2:
    sbi   BUZZER_PORT, BUZZER_PIN
    WAIT_US 500
    cbi   BUZZER_PORT, BUZZER_PIN
    WAIT_US 500
    dec   _w
    brne  _n2
    ret

2.8 L’interface UART

L’UART0 fonctionne à 19 200 bauds, 8-N-1. Il sert à deux choses :

  • miroir LCD : chaque rendu LCD est répliqué sur terminal avec des séquences ANSI/VT100 ;
  • injection de commandes : les touches du clavier PC sont converties en commandes RC5 équivalentes.
ESC[H
---- MCU2026-G111 ----
MODE: AUTO   LAYER: IN_MODE
AZ: 0771 steps  EL: 0650 us
ACTION: MOVING
--------LCD--------
L1: Sat: ISS
L2: AZ:257 EL:024
--------------------

Le calcul de baudrate est favorable : UBRR = 4000000 / (16 × 19200) - 1 = 12.02. L’arrondi à 12 donne une erreur d’environ 0.16%, très acceptable. À 115 200 bauds, l’erreur aurait été autour de 8.5%, donc inutilisable.

2.9 Carte des broches

PortAllocation
PORTABus données LCD, XMEM AD0-AD7
PORTBPB5 = OC1A vers le servo S3003
PORTCAdresse haute LCD, XMEM A8-A15
PORTDPD0 = Coil1+, PD1 = Coil1-, PD2 = Coil2+, PD3 = Coil2- vers le stepper X27
PORTEPE0 = TXD0, PE1 = RXD0, PE2 = buzzer, PE7 = IR TSOP / INT7
PORTFLibre, ADC et extension future
PORTGPG0 = /WR, PG1 = /RD, PG2 = ALE pour le contrôle XMEM

3. Architecture logicielle

3.1 Philosophie de conception

Le code est séparé en deux couches :

Architecture logicielle

Deux couches, une frontière volontaire

libs/ Logique applicative

FSM, modes, affichage, overlays et handlers partagés.

Ne manipulent presque pas les registres hardware.
primitives
drivers/ Accès matériel

LCD, servo, stepper, RC5, UART, EEPROM, timers et math bas niveau.

Exposent des fonctions réutilisables au reste du firmware.

Les drivers ne connaissent pas la FSM. Ils exposent des primitives comme step_motor_forward, servo_nudge_up ou load_satellite. Les libs orchestrent ces primitives et maintiennent la logique applicative.

3.2 La machine à états

Machine à états

Deux couches pour garder le firmware lisible

La couche BROWSING choisit le mode actif. La couche IN_MODE délègue ensuite les touches au handler du mode courant. POWER reste prioritaire partout.

Couche 1 BROWSING

CH+/CH- font défiler AUTO, MANUEL et SCAN.

AV entrer / sortir
Couche 2 IN_MODE

Les commandes sont interprétées par le mode sélectionné.

AUTO

  1. Browse sat
  2. Moving AZ
  3. Moving EL
  4. Arrived

DEL ouvre une branche de confirmation avant suppression.

MANUEL

  1. Jogging
  2. Confirm save
  3. SAVED overlay

Le compteur held_count transforme un maintien RC5 en accélération.

SCAN

  1. Idle
  2. Sweeping 360°
  3. No results
  4. Filtered browse

Les hits sont stockés dans un bitmap de 32 bits puis réutilisés par AUTO.

Verrou de mouvement Quand flag_is_moving = 1, la navigation est bloquée pour éviter une position SRAM désynchronisée de la position physique.

La FSM est volontairement divisée en deux couches. BROWSING choisit le mode, IN_MODE exécute le mode. Le bouton AV bascule entre les deux. POWER force le retour vers BROWSING depuis n’importe quel état et agit comme arrêt d’urgence.

Le verrou can_transition empêche de changer de mode pendant un mouvement automatique. Sans cette protection, un Go-To interrompu au mauvais moment pourrait désynchroniser la position SRAM et la position physique du stepper.

3.3 La boucle principale

Le firmware suit un pattern coopératif non-préemptif :

PrioritéTest dans la boucleAction
1Overlay expiré ?Restaurer lcd_l2_buf et redessiner le LCD
2Donnée UART ?Convertir l’ASCII en commande RC5 synthétique
3ir_event_flag ?Dispatcher la commande utilisateur
4flag_is_moving ?Appeler le handler actif avec cmd = 0xFF
5Rien à faireReboucler immédiatement

Points subtils :

  • l’UART est pollé avant le flag IR ;
  • quand flag_is_moving = 1, les handlers avancent le mouvement par petits pas, sans OS ni scheduler ;
  • les handlers remplissent lcd_l2_buf, puis draw_lcd fait le rendu final ;
  • les commandes sont dispatchées avec priorité : POWER, puis overlays, MUTE, AV, puis handler de mode.

3.4 Organisation du code

code_source/
├── main.asm                    Point d'entrée, ISR, dispatch FSM
├── Projet_MC.asmproj           Projet Atmel Studio, ATmega128

├── libs/
│   ├── constants.inc           Constantes .equ
│   ├── definitions.asm         Alias registres, constantes HW
│   ├── macros.asm              1840 lignes de macros AVR
│   ├── pinout.inc              Allocation des broches
│   ├── shared_handlers.asm     Colle FSM
│   ├── overlay.asm             Messages transitoires temporisés
│   ├── display.asm             Rendu LCD
│   ├── mode_auto.asm           Mode AUTO
│   ├── mode_manuel.asm         Mode MANUEL
│   └── mode_scan.asm           Mode SCAN

├── drivers/
│   ├── rc5_logic.asm           Décodage IR RC5
│   ├── lcd.asm                 Driver HD44780 via XMEM
│   ├── logique_pointage.asm    PWM servo et math pointage
│   ├── 0ec_motor_sequence.asm  Séquence stepper X27
│   ├── eeprom_layout.asm       Base EEPROM
│   ├── timer_manager.asm       Timer2 CTC 1 ms
│   ├── uart_control.asm        Miroir UART et injection
│   ├── buzzer.asm              Bips
│   └── math_revised.asm        Arithmétique 16 bits

├── libraries_cours/
│   └── uart_et_strings/
│       └── uart.asm            UART0 fourni par le cours

└── Debug/
    ├── Projet_MC.hex           Firmware Intel HEX
    └── Projet_MC.eep           Image EEPROM initiale

L’ordre des .include dans main.asm est critique, car le code assembleur est linéarisé :

.include "libs/definitions.asm"
.include "libs/macros.asm"
.include "libs/constants.inc"
.include "libs/pinout.inc"
.include "libs/shared_handlers.asm"
.include "libs/overlay.asm"
.include "libs/display.asm"
.include "libs/mode_auto.asm"
.include "libs/mode_manuel.asm"
.include "libs/mode_scan.asm"
.include "drivers/rc5_logic.asm"
.include "drivers/logique_pointage.asm"
; ...
.include "drivers/lcd.asm"

lcd.asm est placé en dernier car certaines routines l’appellent avec call plutôt que rcall. Sur AVR, rcall est limité à ±2K mots ; call n’a pas cette limite, mais coûte 2 cycles de plus.

3.5 Conventions de registres

Registre(s)AliasRôle
r0charCible implicite de LPM
r1_sregSauvegarde SREG dans les ISR
r2_uSauvegarde de u dans les ISR
r3uScratch pour WAIT_US / WAIT_MS
r4-r5e0, e1Temporaires PRINTF
r6-r7Libres
r8-r11c0..c3Résultat de mul22 / div22
r12-r15d0..d3Reste de div22
r16wRegistre de travail principal
r17_wRegistre de travail secondaire / ISR
r18-r21a0..a3Opérande A, 32 bits
r22-r25b0..b3Opérande B, 32 bits
r26-r27XPointeur X
r28-r29YPointeur Y
r30-r31ZPointeur Z et accès LPM

Les registres r0-r15 ne supportent pas ldi, cpi ou andi. Les macros du cours contournent cette asymétrie avec des pseudo-instructions comme _LDI, _CPI et _ANDI.

3.6 La bibliothèque de macros

macros.asm compte environ 1840 lignes. Il fournit :

CatégorieExemplesUsage
PointeursLDIX, LDIY, LDIZCharger une adresse SRAM ou Flash
I/OOUTI, OUTEIÉcrire une valeur immédiate dans un port
AttenteWAIT_US, WAIT_MSDélais calibrés à 4 MHz
BranchementJK, JNK, JSKSauts conditionnels étendus
Multi-octetsLDI2, STS2, ADD2, CP2Arithmétique 16/32 bits
BitsMOVB, JB0, JB1, P2CManipulation de bits
PilePUSH2, POP2Sauvegarde de paires

4. Les trois modes

4.1 Mode MANUEL

Le mode MANUEL fait de la télécommande un joystick :

ToucheEffet
CH+Azimut positif
CH-Azimut négatif
VOL+Élévation positive
VOL-Élévation négative
GUIDEArmer puis confirmer la sauvegarde

L’accélération progressive dépend de held_count :

MaintienPas par répétitionVitesse approximative
moins de 1 s~10°/s
1 à 3 s~50°/s
plus de 3 s10°~100°/s
pick_step_size:
    lds   w, held_count
    cpi   w, HOLD_THRESHOLD_FAST
    brsh  _fast
    cpi   w, HOLD_THRESHOLD_MED
    brsh  _med
_slow:
    ldi   a0, HOLD_STEP_SLOW
    ret
_med:
    ldi   a0, HOLD_STEP_MED
    ret
_fast:
    ldi   a0, HOLD_STEP_FAST
    ret

La sauvegarde se fait en deux pressions : le premier GUIDE affiche SAVE:PRESS GUIDE, le second écrit la position courante dans le premier slot EEPROM libre. L’overlay SAVED reste affiché pendant environ 500 ms.

4.2 Mode AUTO

Le mode AUTO est un navigateur de base satellites. Il affiche le nom de la cible et ses coordonnées :

Sat: ISS AZ:257 EL:024
Rendu logique du LCD 2×16 en mode AUTO.

CH+ et CH- passent au satellite valide suivant ou précédent. Les touches 0-9 donnent un accès direct aux premiers slots. Le curseur saute les slots vides, identifiés par l’absence du magic byte 0x5A. Un compteur de sécurité limité à 32 essais évite une boucle infinie si la base est vide.

Le Go-To déclenché par GUIDE se déroule en deux phases :

  1. calcul du plus court chemin d’azimut ;
  2. mouvement du stepper jusqu’à la cible ;
  3. ajustement de l’élévation par servo ;
  4. chirp et overlay ARRIVED.

La suppression utilise aussi deux pressions :

  1. premier DEL : DEL? PUSH DEL ;
  2. deuxième DEL : magic byte mis à 0x00, overlay DELETED ;
  3. toute autre touche annule la suppression.

4.3 Mode SCAN

Le mode SCAN effectue un balayage de 360°. Il compare chaque degré d’azimut à la base satellites et détecte les cibles compatibles avec l’élévation courante.

Mode SCAN

Du bouton GUIDE au navigateur filtré

1 IDLE

Attente avec SCAN: PUSH GUIDE.

2 Préparation

Cache EEPROM vers SRAM, puis snapshot de l'élévation actuelle.

3 SWEEPING

Rotation 360° et test des 32 slots à chaque degré.

4 Bitmap résultats

Chaque satellite détecté positionne un bit dans scan_results.

5a NO_RESULTS

Aucune cible compatible avec la bande d'élévation.

5b RESULTS_BROWSE

Navigation AUTO filtrée sur les satellites détectés.

Un satellite est détecté si :

  • son azimut correspond au degré courant ;
  • son élévation est dans la bande élévation_courante ± 10°.

La tolérance d’élévation simule le lobe d’ouverture d’une vraie antenne.

Le mode charge d’abord les données EEPROM dans un cache SRAM :

Buffer SRAMTailleRôle
scan_cache_az64 octets32 azimuts stockés sur 2 octets
scan_cache_el32 octets32 élévations stockées sur 1 octet
scan_cache_valid4 octetsBitmap de validité des 32 slots

Sans cache, un tour complet demanderait environ 360 × 32 accès EEPROM. Avec le cache, il reste seulement 32 lectures initiales puis des comparaisons SRAM.

Les résultats sont stockés dans un bitmap de 32 bits :

OctetSlots représentés
scan_results[0]slots 0 à 7
scan_results[1]slots 8 à 15
scan_results[2]slots 16 à 23
scan_results[3]slots 24 à 31

L’accès au bit utilise une LUT en Flash :

bitmask_lut:
    .db 1, 2, 4, 8, 16, 32, 64, 128

Après le sweep, si au moins un bit est positionné, slot_filter_mode = 1 et le navigateur AUTO ne montre plus que les satellites détectés.

5. Mini-lab interactif

Le composant ci-dessous rejoue les calculs essentiels du firmware : chemin azimutal le plus court, conversion en pas partiels du X27, largeur PWM du servo et fenêtre de détection du SCAN.

Mini labo

Mini simulation du pointage

Ce petit labo reprend deux idées du projet : le Go To choisit toujours le chemin azimutal le plus court modulo 360 degrés, puis ajuste l'élévation via le PWM du servo.

Delta azimutal -92°
Pas partiels X27 276
PWM servo cible 749 ticks
Fenêtre SCAN 14°-34°
1

Rotation la plus courte

Diminuer l'azimut de 92°, soit 276 pas partiels.

2

Élévation par PWM

Appliquer 56° sur le servo, soit environ 1.50 ms.

tap: 1° / répétition maintien ~1.1 s: 5° / répétition maintien ~3.4 s: 10° / répétition

6. Deep-dive technique

6.1 Décodage RC5

Le décodage RC5 est le morceau le plus sensible au timing. Le TSOP fournit un signal digital, mais pas d’horloge. Le firmware échantillonne donc au centre de chaque bit Manchester.

ÉtapeTimingRôle
1Front descendant sur PE7Déclenche INT7
2Attente T1/4Se placer au centre du premier bit
3Lecture de PE7Échantillonner S1, puis S2, Toggle, Adresse, Commande
4Attente T1Passer au centre du bit suivant
514 répétitionsRecomposer la trame RC5 complète

L’ISR EXT_INT7 est bloquante pendant environ 26 ms. Pendant ce temps, aucune autre interruption n’est servie et la boucle principale est gelée. C’est un compromis assumé : capturer la trame sans jitter est prioritaire, et les overlays de 500 ms tolèrent cette erreur temporelle.

6.2 PWM servo

Le Timer1 16 bits est le bon choix pour le servo :

OptionRésolutionFréquenceProblème
Timer0 Fast PWM~31 µs1.9 kHzTrop rapide pour un servo
Timer0 Phase Correct~31 µs961 HzEncore trop rapide
Timer1 Fast PWM 142 µs50 HzAdapté

La relation est :

fPWM=fCPUprescaler×(TOP+1)=40000008×1000150 Hzf_{PWM} = \frac{f_{CPU}}{\text{prescaler} \times (\text{TOP}+1)} = \frac{4\,000\,000}{8 \times 10\,001} \approx 50\ \text{Hz}

Un bug matériel a marqué le projet : la première version utilisait PB4/OC0 avec Timer0. La fréquence disponible faisait vibrer le servo au lieu de le positionner. Le déplacement du jumper M4 vers PB5/OC1A pour utiliser Timer1 a résolu le problème.

6.3 Stepper et demi-pas

Le X27 est un moteur bipolaire à deux bobines. Le pilotage alterne entre états à deux bobines et états à une bobine :

PositionC1+C1-C2+C2-Champ magnétique
0ONoffONoffNord-Est
1ONoffoffoffEst
2ONoffoffONSud-Est
3offoffoffONSud
4offONoffONSud-Ouest
5offONoffoffOuest

Avec le réducteur 1:180, il faut 6 × 180 = 1080 pas partiels pour un tour complet, soit 1/3° par pas.

6.4 EEPROM persistante

Chaque slot satellite occupe 16 octets :

OffsetContenuTailleExemple ISS
0Magic byte1 octet0x5A
1-10Nom ASCII padded10 octets"ISS "
11-12Azimut little-endian2 octets0x0101 = 257°
13Élévation1 octet0x18 = 24°
14-15Padding2 octets0x0000

L’écriture EEPROM impose une séquence critique :

EEPROM_write_byte:
    sbic   EECR, EEWE
    rjmp   EEPROM_write_byte
    out    EEARH, r17
    out    EEARL, r16
    out    EEDR, r18
    in     r19, SREG
    cli
    sbi    EECR, EEMWE
    sbi    EECR, EEWE
    out    SREG, r19
    ret

Le cli est indispensable. Si une interruption se produit entre EEMWE et EEWE, le délai de 4 cycles est dépassé et l’écriture peut échouer silencieusement.

6.5 LCD memory-mapped

Grâce à XMEM, une commande LCD ressemble à une écriture mémoire normale :

ldi   w, 0x01
sts   LCD_IR, w

Le matériel génère ensuite les signaux : données sur PORTA, adresse haute sur PORTC, pulse ALE, puis pulse /WR.

6.6 Overlays temporisés

Les overlays affichent temporairement SAVED, DELETED, UART ON, ARRIVED ou d’autres messages sur la ligne 2 du LCD.

Overlays

Messages temporaires sans bloquer la boucle principale

  1. Handler

    Appelle overlay_show(500) après une action utilisateur.

  2. Timer2 ISR

    Décrémente overlay_ms_left toutes les millisecondes.

  3. Flag dirty

    Quand le compteur atteint zéro, l'ISR pose overlay_dirty = 1.

  4. Main loop

    Reconstruit lcd_l2_buf et redessine le LCD.

overlay_ms_left est sur 16 bits ; les écritures depuis le contexte principal sont donc protégées par cli/sei.

6.7 UART comme debug en production

Le miroir UART est un outil de debug embarqué. Quand il est actif, chaque affichage LCD envoie aussi l’état interne sur terminal :

  • couche FSM ;
  • mode courant ;
  • position azimut et élévation ;
  • statut de mouvement ;
  • contenu logique des deux lignes LCD.

L’injection de commandes forge un événement IR synthétique : l’UART écrit dans last_cmd, inverse last_tog, positionne le flag, puis la boucle principale traite la commande exactement comme si elle venait de la télécommande.

7. Algorithmes clés

7.1 Chemin le plus court en azimut

L’azimut vit sur un cercle. Aller de 350° à 10° doit prendre +20°, pas -340°.

shortest_path_az:
    sub    r18, r16
    sbc    r19, r17

    cpi    r18, low(181)
    ldi    w, high(181)
    cpc    r19, w
    brlt   _check_neg
    subi   r18, low(360)
    sbci   r19, high(360)

_check_neg:
    cpi    r18, low(-180)
    ldi    w, high(-180)
    cpc    r19, w
    brge   _done
    subi   r18, low(-360)
    sbci   r19, high(-360)

_done:
    mov    r16, r18
    mov    r17, r19
    ret

7.2 Arithmétique 16 bits en assembleur

L’ATmega128 est 8 bits. Les nombres 16 bits demandent donc des paires de registres et une propagation manuelle des retenues.

Multiplication 16×16 bits :

mul22:
    CLR2   c3, c2
    LDI2   c1, c0, b1, b0
    ldi    w, 16
_mul_loop:
    LSR2   c1, c0
    brcc   _mul_no_add
    ADD2   c3, c2, a1, a0
_mul_no_add:
    ROR4   c3,c2,c1,c0
    DJNZ   w, _mul_loop
    ret

Division 16÷16 bits non-restoring :

div22:
    MOV2   c1,c0, a1,a0
    CLR2   d1, d0
    ldi    w, 16
_div_loop:
    ROL4   d1,d0,c1,c0
    SUB2   d1,d0, b1,b0
    brcc   _div_ok
    ADD2   d1,d0, b1,b0
_div_ok:
    ROL2   c1, c0
    DJNZ   w, _div_loop
    COM2   c1, c0
    ret

7.3 Accélération hold-to-accelerate

La télécommande envoie environ 9 trames par seconde quand une touche est maintenue. Le toggle bit reste alors constant :

SituationToggle bitheld_count
Touche maintenue1, 1, 1, 1, ...0, 1, 2, 3, ...
Touche relâchée puis ré-appuyée1, 1, 1 puis 0, 0, 0reset à 0, puis recommence

Les seuils 10 et 30 répétitions correspondent à environ 1 s et 3 s.

7.4 Bitmap de détection

Pour tester ou positionner le bit du slot N :

QuantitéCalcul
Octet ciblebyte_offset = N / 8
Bit dans l’octetbit_position = N % 8
Masquemask = bitmask_lut[bit_position]

La LUT {1, 2, 4, 8, 16, 32, 64, 128} remplace une boucle de shift. On dépense 8 octets de Flash pour rendre l’accès O(1).

8. Guide utilisateur

8.1 Mise en route

Matériel requis :

  • carte STK-300 avec ATmega128L ;
  • module M1 pour le stepper ;
  • module M2 pour IR et buzzer ;
  • module M4 pour le servo ;
  • télécommande Vivanco UR Z2 compatible RC5 ;
  • câble USB pour l’alimentation et éventuellement l’UART.

Procédure :

  1. brancher l’alimentation de la STK-300 ;
  2. attendre MODE: AUTO / AZ:000 EL:000 sur le LCD ;
  3. laisser le servo se positionner à 0° ;
  4. aligner manuellement le stepper si nécessaire ;
  5. activer le miroir UART avec MUTE si besoin.

8.2 Commandes télécommande

ToucheBROWSINGMANUELAUTOSCAN
CH+ / CH-Changer de modeAzimut ±Satellite suivant/précédentInactif
VOL+ / VOL-InactifÉlévation ±2.25°InactifInactif
AVEntrer dans le modeSortirSortirSortir
GUIDEInactifSauvegarderLancer Go-ToLancer sweep
DELInactifInactifSupprimerInactif
0-9InactifInactifAccès direct slotInactif
MUTEToggle UARTToggle UARTToggle UARTToggle UART
POWERArrêt urgenceArrêt urgenceArrêt urgenceArrêt urgence

8.3 Commandes UART

Terminal série : 19 200 bauds, 8-N-1.

Touche clavierCommande RC5Fonction
W / wVOL_UPÉlévation +
S / sVOL_DNÉlévation -
A / aCH_UPAzimut + ou mode suivant
D / dCH_DNAzimut - ou mode précédent
EspaceGUIDEAction principale
X / xDELSupprimer satellite
M / mAVEntrer ou sortir du mode
P / pPOWERArrêt d’urgence
0-9CMD_0..9Accès direct slot
L / lspécial UARTDump liste satellites
U / uMUTEToggle miroir UART

8.4 Signaux sonores

SonSignification
1 chirpConfirmation simple
1 chirp par satelliteDétection pendant le SCAN
2 chirpsArrêt d’urgence POWER
1 chirp + overlayTransit terminé

9. Journal de développement

9.1 Timeline

Le projet a été développé en un sprint d’environ 48 heures, du 24 au 26 mai 2026.

24 mai 2026 — Jour 1 : fondations

12:40  Lars    Initial commit
18:19  Jean    Ajout documents + bibliothèques cours + helpers
19:07  Jean    Intégration helpers + réorganisation des dépendances
21:19  Jean    Mode save + contrôle EEPROM + feux rouges v3
21:56  Jean    Fix bug mode save
22:31  Jean    Mode AUTO v1
22:57  Jean    Fix scroll + mouvement en arrière-plan + deg→pas
23:36  Jean    Fix bug mode auto

25 mai 2026 — Jour 2 : la nuit la plus longue

00:11  Jean    Fix bug mode auto
01:06  Jean    New fix mode auto
01:27  Jean    Magic byte implementation
01:42  Jean    Empty slot try again
02:24  Lars    empty fix + save_not_working
02:42  Jean    fix save cette fois (baruch hashem si ça marche)
02:50  Jean    fix 2/3 bugs en plus pour save
03:00  Jean    Élévation en degrés + EL max 1800
03:21  Jean    Fonction delete
03:38  Jean    Fix affichage LCD suppression
16:16  Lars    SCAN MODE IMPLÉMENTÉ
17:45  Jean    UART test 1
18:05  Jean    Fix UART bugs
18:25  Jean    Fix registre r16 pour UART + affichage LCD dans UART
18:38  Jean    Fix UART bug encore
18:52  Jean    Retour arrière UART + correction affichages satellites
19:00  Jean    Reset UART first try
19:11  Jean    Nettoyage : arrêt suivi dossier Debug
19:13  Jean    Revert "add UART test 1"
19:14  Jean    Ajout .gitignore pour Debug
19:31  Jean    fix uart STPPPPPP ça marche
19:55  Jean    Fix affichage UART
20:02  Jean    Fix affichage UART
20:16  Jean    Ajout touches pour UART
20:31  Jean    Toggle pour UART
20:38  Jean    Fix toggle UART
21:54  Lars    Buzzer fix
23:41  Lars    Refactor + timer displays

26 mai 2026 — Jour 3 : finition

02:30  Lars    IR interrupts
04:28  Lars    final_v1
14:01  Lars    init github
14:06  Lars    change names

9.2 Les galères mémorables

La saga du Save a été le bug le plus tenace. Entre 01h et 03h du matin, plusieurs commits se sont enchaînés autour du format EEPROM. Le vrai problème : un décalage d’un octet dans le slot, avec 5 espaces de padding au lieu de 4, décalait les champs azimut et élévation.

La saga de l’UART a demandé environ 15 commits en 3 heures, dont un revert complet. Le bug principal venait de r16 alias w, modifié par des macros utilisées dans le driver UART, ce qui corrompait la commande RC5 forgée.

9.3 Statistiques

MétriqueValeur
Commits totaux40
Durée~48 heures
Commits par Jean31, soit 78%
Commits par Lars9, soit 22%
Lignes de code~5 762 lignes .asm / .inc
Fichiers source20
Nuit blancheAu moins 1
Commit le plus désespéréfix save cette fois (baruch hashem si ça marche)

10. Concepts du cours appliqués

ConceptApplication
Timer CTCTimer2 avec OCR2=124, tick 1 ms pour les overlays
Timer Fast PWMTimer1 mode 14, PWM 50 Hz pour le servo
PrescalerTimer1 /8, Timer2 /32
Double buffering OCROCR1A bufferisé pour éviter les glitchs servo
Interruption externeINT7 sur front descendant pour RC5
ISR.org INT7addr et .org OC2addr dans main.asm
Sauvegarde contextePush/pop de SREG, r16, r17 et registres touchés
Section critiqueEEPROM et accès 16 bits à overlay_ms_left
Architecture HarvardFlash, SRAM et EEPROM séparées
LPMLecture tables stepper, chaînes LCD, LUT bitmask
XMEMLCD HD44780 à 0x8000 / 0xC000
UART asynchrone19 200 bauds, polling RXC0
ManchesterDécodage RC5 par échantillonnage bit-bang
Adressage indirectX/Y/Z pour buffers SRAM, EEPROM, Flash
Machine à étatsFSM à deux couches
Arithmétique multi-octetsmul22, div22, comparaisons 16 bits

11. Compilation et déploiement

Environnement :

  • IDE : Atmel Studio ou Microchip Studio ;
  • cible : ATmega128 ;
  • configuration : Debug.

Pour recompiler :

  1. ouvrir code_source/Projet_MC.asmproj ;
  2. vérifier Project → Properties → Device = ATmega128 ;
  3. lancer Build → Build Solution ou F7.

Fichiers de sortie :

FichierDescriptionUsage
Debug/Projet_MC.hexFirmware principal Intel HEXFlash via ISP/JTAG
Debug/Projet_MC.eepImage EEPROM initialeProgramme les 10 satellites
Debug/Projet_MC.lssListing assembleurDebug et vérification
Debug/Projet_MC.mapCarte mémoireVérifier les adresses

Flash via avrdude :

avrdude -p m128 -c stk500 -U flash:w:Projet_MC.hex:i
avrdude -p m128 -c stk500 -U eeprom:w:Projet_MC.eep:i

12. Rétrospective

12.1 Ce qui a bien fonctionné

  • la séparation drivers/libs a permis de travailler en parallèle ;
  • le framework de macros du cours a rendu l’assembleur soutenable ;
  • le miroir UART a été indispensable pour diagnostiquer l’état interne ;
  • la FSM à deux couches a bien absorbé l’ajout du mode SCAN.

12.2 Ce qu’on referait différemment

  • ajouter un référencement automatique du stepper au démarrage ;
  • remplacer l’ISR RC5 bloquante par un décodage non bloquant avec Timer Capture ;
  • construire des tests unitaires via simulateur AVR pour les algorithmes math et la logique FSM.

12.3 Limites du système

  • pas de tracking orbital en temps réel ;
  • pas de communication radio avec les satellites ;
  • oscillateur RC à ±3%, ce qui limite la marge de décodage RC5 ;
  • pas de sauvegarde de la position azimut entre deux redémarrages.

13. Annexes techniques

13.1 Carte mémoire SRAM

AdresseVariableTailleDescription
0x0100last_addr1BAdresse RC5 dernière trame
0x0101last_cmd1BCommande RC5 dernière trame
0x0102last_tog1BToggle bit RC5
0x0103ir_event_flag1BFlag ISR → main
0x0104mode1B0=AUTO, 1=MANUEL, 2=SCAN
0x0105sub_state1BSous-état du mode actif
0x0106fsm_layer1B0=BROWSING, 1=IN_MODE
0x0107flag_is_moving1BTransit moteur en cours
0x0108lcd_l2_buf16BBuffer ligne 2 LCD
0x0118current_az_steps2BPosition azimut 0..1079
0x011Aheld_count1BCompteur répétitions RC5
0x011Btarget_ticks2BConsigne OCR1A servo
0x011Dauto_substate1BSous-état AUTO
0x011Etarget_sat_id1BIndex curseur satellite
0x011Ftarget_az_auto2BAZ cible
0x0121target_el_auto1BEL cible
0x0122current_sat_name11BNom satellite
0x012Dslot_filter_mode1B0=normal, 1=filtre SCAN
0x012Escan_results4BBitmap 32 bits
0x0132scan_cache_az64BCache AZ
0x0172scan_cache_el32BCache EL
0x0192scan_cache_valid4BBitmap validité cache
0x0196scan_steps_done2BProgression sweep
0x0198scan_el_at_start1BSnapshot EL
0x0199motor_step_idx1BCurseur séquence stepper
0x019Auart_enabled1BMiroir UART actif
0x019Boverlay_ms_left2BCompteur overlay
0x019Doverlay_dirty1BFlag expiration overlay

13.2 Satellites pré-programmés

SlotSatelliteAzimutÉlévationType
0ISS257°24°Station spatiale
1Hubble142°56°Télescope spatial
2Tiangong89°72°Station spatiale
3Starlink310°45°Constellation LEO
4Envisat15°81°Observation Terre
5NOAA-19198°33°Météorologie
6Iridium275°65°Communication
7Terra45°12°Observation Terre
8Aqua120°88°Observation Terre
9Suomi NPP82°41°Météorologie

13.3 EEPROM initiale Intel HEX

:100000005A495353202020202020200101180000AD
:100010005A487562626C65202020208E00380000F3
:100020005A5469616E676F6E67202059004800002B
:100030005A537461726C696E6B202036012D000016
:100040005A456E766973617420202020200F005100B0
:100050005A4E4F41412D31392020C600210000D2
:100060005A49726964697570202013014100003B
:100070005A5465727261202020202D000C000055
:100080005A41717561202020202078005800002E
:100090005A53756F6D69204E505052002900002C
:00000001FF

13.4 Constantes principales

T1                  = 1870
TOGGLE_MASK         = 0x08

MODE_AUTO           = 0
MODE_MANUEL         = 1
MODE_SCAN           = 2
MODE_COUNT          = 3
LAYER_BROWSING      = 0
LAYER_IN_MODE       = 1

ST_AUTO_BROWSING    = 0
ST_AUTO_MOVING_AZ   = 1
ST_AUTO_MOVING_EL   = 2
ST_AUTO_ARRIVED     = 3
ST_AUTO_CONFIRM_DEL = 4

ST_SCAN_IDLE          = 0
ST_SCAN_SWEEPING      = 1
ST_SCAN_NO_RESULTS    = 2
ST_SCAN_RESULTS_BROWSE = 3

CMD_0 .. CMD_9      = 0x00..0x09
CMD_DEL             = 0x0A
CMD_POWER           = 0x0C
CMD_MUTE            = 0x0D
CMD_VOL_UP          = 0x10
CMD_VOL_DN          = 0x11
CMD_CH_UP           = 0x20
CMD_CH_DN           = 0x21
CMD_GUIDE           = 0x22
CMD_AV              = 0x38

TIMER_TOP           = 10000
TICKS_MIN           = 500
TICKS_MAX           = 900
TICKS_STEP          = 10

AZ_MAX_STEPS        = 1080
AZ_STEP_PARTIAL     = 3
STEPPER_DELAY_MS    = 3

HOLD_THRESHOLD_MED  = 10
HOLD_THRESHOLD_FAST = 30
HOLD_STEP_SLOW      = 1
HOLD_STEP_MED       = 5
HOLD_STEP_FAST      = 10

SAT_MAGIC           = 0x5A
SAT_SLOT_SIZE       = 16
SAT_MAX_SLOTS       = 32
SAT_NAME_LEN        = 10

13.5 Références techniques

  1. ATmega128/128L Datasheet, Atmel/Microchip, document 2467.
  2. TSOP1838, Vishay, récepteur IR démodulateur 38 kHz.
  3. HD44780U, Hitachi, contrôleur LCD dot-matrix.
  4. Futaba S3003, spécification servo analogique standard.
  5. Switec X27.168, micro-stepper bipolaire pour instrumentation.
  6. Vivanco UR Z2, télécommande universelle RC5.
  7. RC5 Protocol, Philips, protocole infrarouge bi-phase.

14. Assets à ajouter plus tard

Oui, j’inclurais une vidéo. C’est même l’asset le plus utile pour ce projet : un système embarqué avec moteurs, LCD et télécommande se comprend beaucoup plus vite en mouvement qu’en photo. Idéalement, une vidéo courte de 20 à 40 secondes suffit.

À ajouter si possible :

  • une vidéo de démonstration montrant successivement MANUEL, AUTO, puis SCAN ;
  • une capture du terminal UART avec le miroir VT100 actif ;
  • une photo ou un schéma de câblage annoté avec PB5, PE7, PE2, PD0:3, PE0/1 et les modules M1/M2/M4 ;
  • une photo rapprochée du servo et de la tête d’antenne pour visualiser l’axe d’élévation ;
  • une capture nette du LCD au démarrage, en MODE: AUTO ;
  • une capture pendant MANUEL MOVING ;
  • une capture pendant SCANNING AZ:xxx ou N SATS DETECTED ;
  • un chronogramme RC5 propre, exporté depuis une simulation ou redessiné ;
  • un schéma plus propre de l’organigramme de la boucle principale ;
  • éventuellement un court GIF du mini-balai SCAN si la vidéo complète est trop lourde pour la page.

Rapport original

Le PDF ci-dessous conserve la version académique complète du projet, avec les annexes et le code source remis.

Embedded PDF

Station de pointage d'antenne satellitaire — rapport original

Open full PDF

Rapport complet du projet MICRO-210 EPFL, incluant la documentation technique, les annexes, les tableaux périphériques et le code assembleur.