dimanche 4 janvier 2026

tutoriel Charlieplexing

Le Charlieplexing est une technique qui permet de contrôler un grand nombre de DELs sur à petit nombre d'entrée/sortie binaires d'un MCU. Pour utiliser cette technique il faut que chaque E/S utilisée soit configurable selon 3 états.

  1. En mode entrée haute impédance. C'est à dire sans résistance pull up.
  2. En mode sortie haute. C'est à dire à Vdd.
  3. En mode sortie basse. C'est à dire à Vss
Cette technique est rendue possible par le fait que les Diode Électro-Luminescentes comme leur nom l'indique ne conduisent que dans un seul sens. En connectant 2 DELs tête bèche entre 2 E/S d'un MCU on peut allumer l'une ou l'autre ou les éteintre. Si Une des broche est configurée en mode sortie haute et l'autre en mode sortie basse la DEL dont l'anode est sur la sortie haute va allumée. Il suffit d'inverser la polarité des 2 broches pour allumer l'autre DEL. Pour les éteindre il suffit de configurer les 2 E/S en mode entrée haute impédance.

  • Dans ce circuit si PB3 et PB6 sont en mode entrée haute impédance les 2 DELs sont éteintes.
  • Si PB6 est en mode sortie haute et PB3 en mode sortie basse alors D26 est allumée.
  • Si PB6 est en mode sortie basse et PB3 en mode sortie haute alors D25 est allumée.
Pour que cette technique fasse du sens dans son utilisation il faut avoir plus de DELs à contrôler que le MCU dispose d'E/S. Le nombre de DELs multiplexables par cette technique est de N*(N-1)N est le nombre d'E/S disponibles. Dans le circuit de démonstration qui suis on dispose de 4 E/S donc on peut contrôler 12 DELs.

Notez que les broches 7 et 8 du MCU ne sont pas utilisées pour le multiplexeur. La raison en est qu'elles ne sont pas configurable en mode sortie push-pull. Ce sont des broche open drain conçues pour les bus I2C.

À un instant donné une seule DEL peut-être allumée mais en faisant fonctionner le multiplexeur rapidement avec la persistance de la vision on peut créer l'illusion qu'elles sont toutes allumées en même temps.

Circuit de démonstration

interconnection des DELs

Dans la table suivante C est pour cathode et A pour anode.

PA0 PA2 PB5 PB6
D1CD1A
D2AD2C
D3AD3C
D4AD4C
D5AD5C
D6AD6C
D7CD7A
D8CD8A
D9CD9A
D10CD10A
D11CD11A
D12AD12C

vidéo de démonstration

Circuit version 2

J'ai fait un montage permanent avec des DELs bleus montées en cercle. Le Firware contient un engin d'animation avec un jeu de commandes simple pour créer différentes animations.

Ce circuit utilise 3 switches boutons à contact temporaire. 2 servent pour sélectioner l'animation. Le 3ième est pour faciliter la programmation du MCU.

Description du firmware

Les fichiers du projet sont disponible ici. C'est écris en assembleur pour SDCC

La minuterie TIMER 4 est configurée pour générer une interruption à intervalle d'une milliseconde. Le fonctionnement du multiplexeur est géré par la routine d'interruption suivante. Ce code est dans le fichier hardware_init.asm

  ;------------------------------
; TIMER 4 is used to maintain 
; timers and ticks 
; interrupt interval is 1 msec 
;--------------------------------
Timer4UpdateHandler:
	clr TIM4_SR ; reset interrupt flag 
; multiplexer control 	
	call leds_off 
	ld a,mx_step 
	inc a
	cp a,#12
	jrmi 1$ 
	clr a
4$:
; check if pause is active 	
	btjf flags,#F_PAUSE,1$
	dec pause_timer
	jrne 1$ 
; end of pause
	bres flags, #F_PAUSE 
1$: 
; turn on LED if bit set 
; shift the corresponding bit in carry 
    ld mx_step, a 
	ldw x, led_set
	tnz a 
	jreq 3$ 
2$:	srlw x 
	dec a 
	jrne 2$
3$: srLw x ; now bit is in carry flag 
	jrnc 9$ ; bit is reset 
; bit set turn on LED 	
	ld a,mx_step 
	call led_on 
9$: callr read_buttons
	iret 
    

La variable mx_step contient un entier dans l'intervalle {0..11} qui indique quelle DEL doit-être contrôlée à cette étape. cette valeur est incrémentée à chaque interruption de sorte que la boucle de 12 DELs est parcourue en 12 interruptions soit 12msec. Ce qui donne une fréquence du multiplexeur de 83 cycles/seconde. C'est suffisant pour éviter l'effet de scintillement.

Les bits 0..11 de la variable led_set contiennent le patron de configuration allumée/éteint des 12 DELs. Si le bit est à 0 la DEL reste éteinte et elle est allumée si le bit est à 1

L'interruption gère aussi la lecture des boutons tactiles ainsi que le délais de la routine pause qui contrôle la vitesse de l'animation.

Notez que le fonctionnement du multiplexeur est indépendant de l'animation. On pourrait mettre un patron dans la variable led_set et à partir de là faire tourner le programme dans une bouche infinie. Le résultat serait une configuration de DELs fixe. Puisque le logiciel d'animation est un programme client du multiplexeur il est dans un fichier différent, animation-demo.asm

Une animation est une liste de mots de 16 bits. Chaque mot est structuré de la façon suivante:

bits description
0..11ensemble de bits représentant chaque DEL, bit 0 pour la DEL 1 à bit 11 pour la DEL 12
12..15commande

Liste des commandes

commande valeur description
CPY0copie les bits 0..11 dans la variable led_set.
INC1copie les bits 0..11 dans la variable led_set et incrémente la variable anim_step.
DEC2copie les bits 0..11 dans la variable led_set et décrémente la variable anim_step.
RST3Boucle l'animation à partir du début de la liste.
SPD4Initialise la variable anim_delay avec les bits 0..7 et incrément la variable anim_step. Ce délais détermine le temps en multiple de 12 msec entre chaque étape de l'animation.
RND7Initialise la variable led_set avec une valeur aléatoire.

Le bit 15 de chaque commande est appellée INV et a pour effet lorsqu'il est à 1 d'inverser les bits 0..11 avant de les copier dans anim_step.

Code de la routine d'animation

;------------------------------------
; animation client for the charlieplexr 
;-------------------------------------
animation:
;initialize with first animation 
    ldw x,#swing 
    ldw anim_table,x
    ld a,(1,x)
    ld anim_delay,a
1$:
; check for button down 
    btjf flags,#F_BTN1,2$
    call next_anim 
    jra 3$  
2$: btjf flags,#F_BTN2,4$
    call prev_anim 
3$: ; wait button release 
    ld a,#3 
    and a,flags 
    jrne 3$
4$:     
    clrw x 
    ld a,anim_step 
    ld xl,a 
    sllw x 
    addw x,anim_table 
    ldw x,(x)
    ld a,xh 
    swap a 
    and a,#0x7 ; command  
    cp a,#CPY 
    jreq 5$ ; copy_set
    cp a,#INC 
    jreq 6$ ; inc_step
    cp a,#DEC 
    jreq 7$ ;dec_step 
    cp a,#RST
    jreq 8$ ;reset_step
    cp a,#SPD 
    jreq 9$ ;set_speed
    cp a,#RND 
    jreq 10$ ;random_set 
    jra 12$ ; bad command ignored 
5$: ; copy_set
    tnzw x 
    jrpl 52$
    cplw x 
52$:
    ld a,xh 
    and a,#0xF
    ld xh,a 
    ldw led_set,x 
    jra 12$
6$: ;inc_step
    inc anim_step 
    jra 5$ 
7$: ;dec_step
    dec anim_step 
    jra 5$ 
8$: ;reset_step
    clr anim_step 
    jra 1$
9$: ;set_speed
    ld a,xl 
    ld anim_delay,a 
    inc anim_step 
    jra 1$ 
10$: ;random_set
    call lfsr
    ldw led_set,x 
12$: ; animation delay 
    ld a,anim_delay 
    call pause 
    jra 1$
    ret 

La variable anim_step est lue et sert à indexer la table de l'animation dont l'adresse est conservée dans la variable anim_table. Les bits 12..15 sont extrait et un sélecteur de commande est utilisé pour sélectionner le code à exécuter pour cette commande. Pour les commandes 0..3 ainsi que 7 la routine pause est appellée pour générer un délais avant l'exécution de la prochaine commande. Ce délais est consrvé dans la variable anim_delay et contrôle la vitesse d'exécution de l'animation.

La liste des animations disponible est conservée dans la table anim_list et l'animation désirée est sélectionnée en utilisant les boutons btn1 et btn2. Au démarrage c'est la première animation de la liste qui est active. btn1 descend dans la liste et btn2 remonte. L'indice de l'animation active est conservé dans la variable anim_select.