PIC12F675Keyer_1352Depuis plusieurs semaines je parcours la documentation technique des petits processeurs 12F6xx de MICROCHIP. Petits, mais richement dotés à en juger par l’étendue de la documentation qui les accompagne. Poussé par le désir de ne pas en rester à la connaissance purement théorique du sujet et aussi par l’envie de « manger » un peu de code, j’ai cherché un projet qui pourrait me permettre de concrétiser mes acquis. Mon choix s’est porté rapidement sur un manipulateur électronique, petite machine utile à la station. Générer les DIT et les DAH c’est bien mais ce n’est pas tout, moyennant un peu de programmation, quelques fonctions fort utiles peuvent être implémentées. Il est agréable de pouvoir envoyer automatiquement les messages de base CQ CQ CQ DE F8EOZ…  et aussi CQ DX et un autre pour les CONTEST. Trois messages enregistrés d’une manière simple au choix de l’utilisateur devraient suffire. Pas besoin de dizaines de mémoires. Il est intéressant d’avoir une écoute locale que l’on peut activer/désactiver ce qui permet d’utiliser le manipulateur pour l’entraînement ou d’utiliser le sidetone de son TRX. Changer la vitesse facilement pour s’adapter à celle de son correspondant est nécessaire. Choisir le mode iambique A ou B pourquoi pas. Enfin une dernière possibilité utile pour régler son antenne, générer une tonalité continue ou à pulsations très rapides.  Toutes ces fonction accessibles rapidement et directement par boutons-poussoirs, pas de menus et sous-menus sélectionnés par paddle.

1. Description du projet

1.1. Logiciel

MPLAB IDE v8.92. J’utilise avec satisfaction cet environnement de développement MICROCHIP. A peu de frais, avec mon programmateur maison, il me permet de faire tous les tests de simulation et de débogage.

MPASMWIN v5.51. A priori, je pensais utiliser le langage C. D’une part, j’aurais quand même dû y broder un peu de code assembleur, d’autre part, la taille limitée de la mémoire 1 ou 2K, ont donné la préférence à l’assembleur. Le programme ne comprend pas de calculs compliqués et il n’y a qu’une bonne trentaine d’instructions assembleur à assimiler. Elles sont les mêmes que celles que j’ai utilisées pour le fréquencemètre.

1.2. Matériel

MCU. Les processeurs de la série 12F6 sont dotés de 6 ports mais pas des mêmes possibilités. Je tiens à garder le port GP3 en fonction MCLR (reset) ce qui me laisse 5 ports. Le postulat émis en introduction d’avoir accès aux fonctions directement par boutons-poussoirs m’oriente vers un petit clavier connecté à un port ADC. Je dispose du 12F675 et du 12F683. Je pense que 1K de mémoire devrait suffire à tenir le code du programme et les 128 bytes de l’EEPROM suffisent pour les messages. J’opte finalement pour le 12F675.

Clavier. Les touches de fonctions sont regroupées sur un petit clavier 3×3 réalisé ex nihilo. L’assignation des fonctions est libre et indépendante de la place physique des touches. Cela permet à chacun de choisir la disposition qui lui convient. (personnalisation). L’assignation est faite par des définitions qui font le lien entre le numéro de touche et la fonction.

Ecoute locale. Elle est réalisée par un petit buzzer connecté à un transistor servant de buffer.

Commande du TX. Elle est réalisé par un transistor.

La figure 1 ci-dessous montre le schéma du circuit.

Figure 1 - Schéma du circuit

Figure 1 – Schéma du circuit

2. Analyse fonctionnelle

2.1. Expression des besoins

En introduction, l’expression fonctionnelle du besoin a permis de définir les  fonctions que la machine devra réaliser. Je les rappelle:

  • Générer le code Morse en mode Iambique ou Droit,
  • Envoyer 3 messages au choix,
  • Incrémenter, décrémenter la vitesse,
  • Choisir le mode iambique A ou B,
  • Choisir l’utilisation de l’écoute locale, BUZZER ON/OFF,
  • Générer pour le réglage d’antenne un signal, TUNE Pulse ou Carrier.
2.2. Schéma fonctionnel

Le schéma figure 2 ci-dessous montre le diagramme fonctionnel du manipulateur.

Figure 2 - Diagramme fonctionnel du manipulateur électronique

Figure 2 – Diagramme fonctionnel du manipulateur électronique

Chacun des blocs mis en évidence fera l’objet d’une description des fonctions principales et des données qu’elles manipulent dans la suite de l’article.

2.2.1. Organes de sortie

Buzzer via un buffer.
Fonction : écoute locale sur option, message de service.

Commutateur Tx: commutateur à transistor.
Fonction : émission en code Morse via le Tx, des messages préenregistrés en clair, émission de la manipulation.

2.2.2. Organes d’entrée

Clef Morse: Iambique (choix du mode A ou B), Droite détectée au setup si le DAH est à la masse.
Fonction : manipulation du message, le DIT sert à avorter (abort) un message préenregistré en cours d’émission.

Clavier matriciel 3×3: organe de commande personnalisable, accessible pendant les temps morts c’est à dire quand la clef Morse reste inactive plus d’une seconde (paramètre).
Fonction: un bouton, une fonction, soit 9 fonctions.

Reset  bouton de reset.
Fonction : setup.

2.2.3. Flux de données

Message de service: c’est un message local court destiné à informer l’utilisateur que la commande a été comprise. Il s’agit en général d’un caractère émis en code Morse à la vitesse courante. Il est destiné uniquement au BUZZER même quand celui-ci est OFF. Il est stocké en RAM et personnalisable.

Message préenregistré: c’est un message destiné au TX et au BUZZER s’il est ON. Il est de longueur variable (0 à n caractères),  sa limite physique est celle de l’espace disponible dans l’EEPROM compte-tenu de la place prise par les autres messages. Il est muni de 2 propriétés: un identificateur symbolique qui correspond simplement au numéro du bouton qui le commande, un facteur de répétition personnalisable (0 à ∞). Il peut être interrompu par un DIT de la clef Morse. Il est rédigé en clair et transformé en code Morse par la machine. Il est diffusé à la vitesse courante de la machine.

Message manipulé: c’est le message généré par la clef Iambique ou Droite destiné au TX et au BUZZER s’il est ON. Avec la clef Iambique, quand les 2 paddles sont pincés, la répétition automatique des DIT et DAH est commandée en mode A ou en mode B.

2.2.4. Organes internes

Les organes internes de l’automate réalisent les fonctions techniques. Leur conception autonome facilite leur développement et leur mise au point. Ils peuvent ainsi être réutilisés.

KEY INTERFACE: traduit les appuis de la CLEF en DIT et DAH. Le type de clef Iambique ou Droite est déterminé automatiquement au Setup (DAH à la masse = clef  Droite). Le mode Iambique A ou B est sélectionnable par une touche du clavier fonctionnant en bascule. L’interface commande l’accès au clavier blocage/déblocage de manière asynchrone par l’interruption TIMER1.

MORSE PLAYER ou PLAYER: joue DIT,  DAH et espace entre les mots (GAP) à la demande de la CLEF ou de l’ENCODER. Autrement-dit, il commande les ports de sortie BUZZER et TX au niveau haut et bas à la vitesse courante.
Remarque: le signal PULSE n’est qu’une série de DIT qui se prolonge indéfiniment jusqu’à interruption, CARRIER est un signal continu qui se prolonge indéfiniment jusqu’à interruption.
L’ interrupteur BUZF autorise ou non la sortie BUZZER, l’interrupteur TXF autorise ou non la sortie TX.
Paramètre : unité de temps = durée d’un point à la vitesse courante.

MORSE TIMER: donne l’unité de temps en ms (le point) en fonction de la vitesse (nombre de mots/mn). L’unité de temps peut être incrémentée ou décrémentée par touches successives du clavier. La valeur par défaut est fixée à 12 mots/mn. La dernière valeur utilisée est conservée en EEPROM et reprise ensuite à chaque SETUP.

KEYPAD INTERFACE: gère le clavier matriciel 3×3. L’interface utilise l’ADC du MCU.
Paramètres: description du clavier (assignation des touches aux fonctions), table des seuils de tension analogique, numérique (intervalle numérique réservé à chaque touche).

ENCODER: traduit un caractère, lettre, chiffre, ponctuation, procédure, en code Morse à destination du PLAYER.
Trois entrées possibles : un message préenregistré de l’EEPROM, un message de service stocké en RAM, un message provenant du déroulement du programme.
Le message peut être interrompu par un appui et un relâchement de la clef DIT.
Paramètre: règles de traduction du caractère.

3. Méthode de développement

L’option MPLAB SIM du Debugger de MPLAB IDE permet de vérifier efficacement le bon fonctionnement du programme. Mais le test réel révèle toujours des défauts à corriger. La difficulté est de n’avoir aucun moyen de visualiser, ni de tracer le programme. Quoiqu’ en y réfléchissant, nous avons 2 moyens: nos oreilles et l’EEPROM. L’envoi vers le BUZZER de signaux Morse aux endroits à étudier permet de suivre le programme. L’enregistrement en EEPROM de données partielles permet aussi d’obtenir des indications. Il suffit de replacer le MCU sur le programmateur. PICPgm va relire le contenu de l’EEPROM et l’afficher. Ces traces seront naturellement provisoires (utilisation de define) et disparaissent dans la version Release. Cette observation conditionne la chronologie de développement. J’ai d’abord commencé par produire les sons, ensuite les messages et le reste a suivi.

4. Le clavier matriciel 3×3 touches

4.1. Conception

Je me suis fondé sur le document « Two-wire, four-by-four-key keyboard  interface saves power »   de « Stefano Salvatori, University of Rome, and Gabriele Di Nucci, EngSistemi, Rome, Italy » paru dans la rubrique designideas de EDN Network.

Figure 3 - Schéma du clavier matriciel 3x3

Figure 3 – Schéma du clavier matriciel 3×3

Le clavier figure 3, ci-dessus, est une matrice 3×3 dans la quelle il est possible de calculer la tension Vout lorsque l’un quelconque des boutons est pressé en appliquant l’équation:
V(x,y) = Vref . Rb/( xRa + yRb + Rb) (1).
L’équation (1) peut s’écrire sous forme d’un rapport de résistances:
V(x,y)/Vref = Rb/( xRa + yRb + Rb) (2).
Posons Rb=Ra/p, l’équation (2) peut s’écrire:
V(x,y)/Vref =  (Ra/p)/( xRa + y(Ra/p) + (Ra/p))
ou en multipliant par (p/Ra)/(p/Ra)
on obtient le rapport diviseur de tension quel que soit la touche pressée:
r(x,y) = V(x,y)/Vref = 1 / ( 1 + x.p + y)  (3)
dans laquelle p = Ra/Rb représente le rapport entre les résistances de lignes et de colonnes.
On observe facilement que le ratio Rb/Ra<3 donne des doublons et ne convient pas. En prenant une résolution ADC N=10 bits et en choisissant une tolérance T=0,01 pour les résistances Ra et Rb, il est possible de distinguer avec une bonne marge de sécurité 9 valeurs de tension.
J’ai choisi Ra=11K 1% et Rb=33K 1% simplement parce que je les avais en stock. D’autres combinaisons de rapport 1/3 sont possibles : Ra=1,3K et Rb=3,9K ou Ra=1,2K et Rb=3,6K par exemple. Ces dernières valeurs étant, par ailleurs, meilleures que celles que j’ai utilisées car je me suis rendu compte que la documentation MICROCHIP conseille de ne pas dépasser 10K en entrée de l’ADC. En effet, la résistance intervient dans le temps d’acquisition.
Il est maintenant très facile de calculer les valeurs numériques de chaque bouton dans l’intervalle [0, 1023]
AD RESULT = 1023. r(x,y) (4).
On constate un resserrement des valeurs les plus basses mais encore très acceptable. Enfin, cette expression montre que le résultat dépend essentiellement de la qualité des résistances. Dans la réalité, j’ai fixé une petite fenêtre d’examen autour de cette valeur qui laisse une marge d’erreur.
Ces valeurs sont reportées sur la figure ci-dessus.  Notre schéma maintenant complet, le clavier peut être programmé et fabriqué.

4.2. Mise en oeuvre de l’ADC

Le clavier est connecté à l’entrée AN0. Cette entrée est lue cycliquement. Le résultat est placé dans un registre ADRES de 10 bits donnant une valeur numérique de 0 à 1023 qui correspond au niveau de tension de la touche pressée. Le clavier n’est qu’un diviseur de la tension Vdd, tension de référence.
Sa mise en oeuvre passe par l’initialisation de quelques registres. Les chapitres 6 et 7 de la documentation technique du 12F675 décrivent le Comparateur et l’ADC.

Le registre ADCON0:
Tout est dit dans le diagramme 7-1 figure 4, ci-dessous, extrait de la documentation.

Figure 4 - A/D BLOCK DIAGRAM (MICROCHIP)

Figure 4 – A/D BLOCK DIAGRAM (MICROCHIP)

Le registre ADCON0 met ce circuit en état de fonctionnement. Voici sa définition et son initialisation dans le programme:

;----------------------------------------------------------------------
; ADC Config
#define ADC_CON b'10000001'
#define ADC_AN0 b'00000000'
#define ADC_AN1 b'00000100'
#define ADC_AN2 b'00001000'
#define ADC_AN3 b'00001100'
;         		  1-------  ADFM: 1=right justified result
;         		  -0------  VCFG: 0=Vdd is voltage reference
;         		  --xx----  not implemented
;         		  ----00--  00=select channel 00 (AN0=GP0)
;				      01 	= Channel 01 (AN1=GP1)
;				      10	= Channel 02 (AN2=GP2)
;					  11 	= Channel 03 (AN3=GP4)
;         		  ------0-  GO/NOT DONE Status bit: 0=A/D conversion not started
;         		  -------1  ADON: Conversion Status bit 1=A/D converter module is operating

#define ADC_AN_KPAD ADC_AN0 ; assign Keypad to Channel 00

	; set up A/D converter
	banksel ADCON0
    movlw   ADC_CON | ADC_AN_KPAD
    movwf   ADCON0

Le registre ANSEL:
Sa fonction est de désigner les ports qui sont affectés au convertisseur et de fixer la fréquence de conversion du cycle A/D.  Choisir la fréquence ad hoc, ni trop, ni trop peu. TAD = A/D clock period. Le minimum TAD requis est de 1.6us, paragraphe 7-1-4 de la documentation technique,  à 4MHz avec Fosc=8 le minimum TAD = 4MHz/8 = 2us. Les valeurs Fosc inférieures ne conviennent pas puisque TAD<1,6us. La table 7-1 , ci-dessous, extraite de la documentation, résume cela.

Figure 5 - TAD vs. DEVICE OPERATING MIFREQUENCIES (MICROCHIP)

Figure 5 – TAD vs. DEVICE OPERATING FREQUENCIES (MICROCHIP)

Note importante: placer un port en entrée analogique enlève automatiquement les résistances internes de pull-ups. Il faut donc utiliser des résistances externes.

En résumé, voici son initialisation dans le programme:


	; set up A/D converter
    banksel ANSEL
    movlw   b'00010001'
    ;         x-------  not implemented
    ;         -001----  ADCS A/D Conversion Clock: 001=Focs/8 Conversion Clock
    ;         -101----  ADCS A/D Conversion Clock: 001=Focs/16 Conversion Clock
    ;         ----0---  ANS3:ANS0: Analog Select bits 0=digital I/O, GP4, Tx output
    ;         -----0--  ANS3:ANS0: Analog Select bits 0=digital I/O, GP2, DIT paddle input
    ;         ------0-  ANS3:ANS0: Analog Select bits 0=digital I/O, GP1, DAH paddle input
    ;         -------1  ANS3:ANS0: Analog Select bits 1=analog  I/O, GP0, analog input
    movwf   ANSEL

Echantillonage – Calcul du temps d’acquisition minimum:
Un cycle d’échantillonnage comprend 2 phases:

  • le temps d’acquisition ou TACQ qui commence après que le canal a été choisi ou qu’un cycle s’achève et que le condensateur interne commence sa charge,
  • le temps de conversion qui commence à fin du TACQ et quand le GO bit=1, qui s’achève après 11 TAD.
Figure 6: ACQUISITION TIME  (MICROCHIP)

Figure 6: ACQUISITION TIME (MICROCHIP)

La résistance d’entrée intervient dans le calcul du temps de charge TC du condensateur interne et par conséquent du TACQ minimum. Avec une résistance de 10K en entrée le TACQ = 19.2us. Comme j’ai utilisé une résistance un peu plus élevée de 33K je calcule
TC = 120 pF(1K+7K+33K)ln0.0004885 = 37.5us
ce qui donne pour TACQ = 40us environ. En y ajoutant 11 * 2us nous obtenons environ 62us. Ce temps est largement inférieur à mon temps de réaction et d’appui sur les touches du clavier… L’équation encadrée, ci-dessus, extraite de la documentation, résume cela.

Le registre CMCON:
Puisque le module comparateur n’est pas utilisé, on choisit l’option « Comparator off ». Voici son initialisation dans le programme:


	; set up comparator
    banksel CMCON
    movlw   b'00000111'
    ;         x-------  not implemented
    ;         -0------  COUT: Comparator Output bit: 0 = VIN+ < VIN- When CINV = 0
    ;         --x-----  not implemented
    ;         ---0----  CINV: Comparator Output Inversion bit 0=non-inverted output
    ;         ----0---  CIS: Comparator Input Switch bit When CM2:CM0 = 110 or 101
    ;         -----111  CM2:CM0: Comparator Mode bits 111=comparator off
    movwf   CMCON

4.3. Programmation

Description du clavier:
La programmation du clavier figure 7, ci-dessous, est réalisée en se fixant 2 objectifs: rendre le module KEYPAD INTERFACE réutilisable pour un autre projet, rendre le clavier personnalisable.

Figure 7 - Agencement du clavier

Figure 7 – Agencement du clavier

Les touches sont numérotées de 0 à 8 en fonction de leur position x,y, les fonctions liées à ces touches sont assignées ensuite. Pour personnaliser le clavier il suffit de changer l’assignation. J’ai choisi de placer les touches d’envoi des messages sur la première colonne, il serait possible de les assigner à une autre colonne ou une ligne, par exemple. La programmation n’en serait nullement affectée. Voici sa description dans le programme:

; Definition
KPAD_00	EQU 0
KPAD_10	EQU 1
KPAD_20	EQU 2
KPAD_01	EQU 3
KPAD_11	EQU 4
KPAD_21	EQU 5
KPAD_02	EQU 6
KPAD_12	EQU 7
KPAD_22	EQU 8
KPAD_FF	EQU 0xFF ; no key
; Assign key 3x3 push button
; This allows one to change keypad layout without change algorithm
; My Layout:
;    (y2)
;    (y1)
;    (y0)
;  (x2)	   (x1)	   (x0)
KPAD_SEND0		EQU KPAD_22
KPAD_SEND1		EQU KPAD_21
KPAD_SEND2	 	EQU KPAD_20
KPAD_SPEED_UP	EQU KPAD_12
KPAD_SLOW_DOWN 	EQU KPAD_11
KPAD_BUZ	 	EQU KPAD_10
KPAD_TUNEC		EQU KPAD_02
KPAD_TUNEP		EQU KPAD_01
KPAD_MOD		EQU KPAD_00

Définition des seuils de tension:
A chaque touche correspond un niveau de tension et un seul. Mais comme rien n’est parfait, il faut attribuer à chaque touche une fenêtre de visée. J’ai défini 9 intervalles [min, max[ disjoints (pas de chevauchement). Pour faciliter la mise au point, les limites min et max sont des paramètres. Voici la description dans le programme:

; Fitted after real analyze

#define VTOLPLUS 10 ; tolerance
#define VTOLMINUS 1 ; tolerance
; Row 0 (3069=1023*3)
#define KPAD_00_MAX (1023+VTOLPLUS)		    ;key 0,0 (maximum maximorum)
#define KPAD_00_MIN (1023-VTOLMINUS)		;key 0,0
#define KPAD_10_MAX ((3069/4)+VTOLPLUS)		;key 1,0
#define KPAD_10_MIN ((3069/4)-VTOLMINUS)	;key 1,0
#define KPAD_20_MAX ((3069/5)+VTOLPLUS)		;key 2,0
#define KPAD_20_MIN ((3069/5)-VTOLMINUS)	;key 2,0
; Row 1
#define KPAD_01_MAX ((1023/2)+VTOLPLUS)		;key 0,1
#define KPAD_01_MIN ((1023/2)-VTOLMINUS)	;key 0,1
#define KPAD_11_MAX ((3069/7)+VTOLPLUS)		;key 1,1
#define KPAD_11_MIN ((3069/7)-VTOLMINUS)	;key 1,1
#define KPAD_21_MAX ((3069/8)+VTOLPLUS)		;key 2,1
#define KPAD_21_MIN ((3069/8)-VTOLMINUS)	;key 2,1
; Row 2
#define KPAD_02_MAX ((1023/3)+VTOLPLUS)		;key 0,2
#define KPAD_02_MIN ((1023/3)-VTOLMINUS)	;key 0,2
#define KPAD_12_MAX ((3069/10)+VTOLPLUS)	;key 1,2
#define KPAD_12_MIN ((3069/10)-VTOLMINUS)	;key 1,2
#define KPAD_22_MAX ((3069/11)+VTOLPLUS)	;key 2,2
#define KPAD_22_MIN ((3069/11)-VTOLMINUS)	;key 2,2
; NO KEY
#define KPAD_FF_MAX VTOLPLUS				; no key
#define KPAD_FF_MIN 0						; no key

Remarque: les calculs dans les définitions peuvent réserver des surprises, c'est pourquoi j'ai simplifié la formule en multipliant 1023 par le facteur 3.

Une façon simple de gérer une liste de données, moyennant quelques précautions dans l'implantation mémoire, est d'implémenter une "Lookup Table". Voici sa description dans le programme:

ADC_Vth_TABLE
	movlw 	HIGH(ADC_Vth_KEYPAD); PCLATH = HIGH bit address
	movwf 	PCLATH 				;
	movf	ADCVthPointer,W		; for PCL
	addwf	PCL ,F ; Jump to character pointed to in W register
ADC_Vth_KEYPAD ; maximum voltage threshold for keypad: analog to digital convert

	; real digit value saved into EEPROM for analyze (use __ADRES_TRON to get result)
	; 	(2)   (1)   (0)
	; 8---- 7---- 6---- (2)
	; 01 18 01 34 01 58
	; 5---- 4---- 3---- (1)
	; 01 82 01 B8 02 01
	; 2---- 1---- 0---- (0)
	; 02 69 03 01 03 FF

	; -------xy: digit maximum voltage threshold 	: digit minimum voltage threshold
	DT	KPAD_FF,(HIGH KPAD_FF_MAX),LOW (KPAD_FF_MAX),(HIGH KPAD_FF_MIN),LOW (KPAD_FF_MIN)
	DT	KPAD_22,(HIGH KPAD_22_MAX),LOW (KPAD_22_MAX),(HIGH KPAD_22_MIN),LOW (KPAD_22_MIN)
	DT	KPAD_12,(HIGH KPAD_12_MAX),LOW (KPAD_12_MAX),(HIGH KPAD_12_MIN),LOW (KPAD_12_MIN)
	DT	KPAD_02,(HIGH KPAD_02_MAX),LOW (KPAD_02_MAX),(HIGH KPAD_02_MIN),LOW (KPAD_02_MIN)
	DT	KPAD_21,(HIGH KPAD_21_MAX),LOW (KPAD_21_MAX),(HIGH KPAD_21_MIN),LOW (KPAD_21_MIN)
	DT	KPAD_11,(HIGH KPAD_11_MAX),LOW (KPAD_11_MAX),(HIGH KPAD_11_MIN),LOW (KPAD_11_MIN)
	DT	KPAD_01,(HIGH KPAD_01_MAX),LOW (KPAD_01_MAX),(HIGH KPAD_01_MIN),LOW (KPAD_01_MIN)
	DT	KPAD_20,(HIGH KPAD_20_MAX),LOW (KPAD_20_MAX),(HIGH KPAD_20_MIN),LOW (KPAD_20_MIN)
	DT	KPAD_10,(HIGH KPAD_10_MAX),LOW (KPAD_10_MAX),(HIGH KPAD_10_MIN),LOW (KPAD_10_MIN)
	DT	KPAD_00,(HIGH KPAD_00_MAX),LOW (KPAD_00_MAX),(HIGH KPAD_00_MIN),LOW (KPAD_00_MIN) 	; 5V
	; don't remove line below
	retlw 0xFF	; high value = end of table
	IF ((HIGH ($)) != (HIGH (ADC_Vth_KEYPAD)))
    	ERROR "ADC_Vth_KEYPAD CROSSES PAGE BOUNDARY!"
	ENDIF;

Algorithme Keystroke:
Keystroke, figure 8 ci-dessous, est l'algorithme principal de l'interface KEYPAD. Il lance le cycle ADC, récupère son résultat, sélectionne la touche frappée et lance la fonction liée à la touche.

Figure 8 - Algorithme Keystroke

Figure 8 – Algorithme Keystroke

Le code présenté ci-dessous est volontairement abrégé. Il ne montre pas le détail des fonctions liées à chaque touche ni la routine de comparaison CMP_A_B. On se reportera au programme source disponible en téléchargement.

;-------------------------------------
; KPAD_Keystroke process
; Scan ADC KPAD channel until find key
; Execute command associated with key
KPAD_Keystroke

KPAD_GetADRES
	movlw	ADC_CON | ADC_AN_KPAD	; select channel Keypad
	call	KPAD_GetADCvalue
	BANK1					; store ADC result into compare register A
    movf	ADRESL,W       	; 8 bits low
	BANK0
	movwf	CmpAL
	movf	ADRESH,W        ; 2 bits High
	movwf	CmpAH

	; point to first ADC table item
	movlw	ADC_Vth_KEYPAD - ADC_Vth_TABLE - 1 -3 -3 	; W = value relative address - 3
	movwf	ADCVthPointer		; Hold pevious line address

KPAD_Lookup	; read one table item
	incf	ADCVthPointer		; skip min value of previous table item
	incf	ADCVthPointer
	incf	ADCVthPointer		; point to current table item
	call	ADC_Vth_TABLE		; lookup voltage scale to find key pressed
	movwf   KeyPadXYtemp       	; hold KeyPadXY byte

	incf	ADCVthPointer,f		; Point to next byte
	call	ADC_Vth_TABLE
 	movwf   CmpBH       		; hold MAX value high byte into compare register B

	incf	ADCVthPointer,f		; Point to next byte
	call	ADC_Vth_TABLE
   	movwf   CmpBL	       		; hold MAX value low byte into compare register B

 	call	CMP_A_B				; compare ADC : MAX value
	btfss	UCON1,A_LT_B		; is ADC value < MAX table value ? 	goto	KPAD_Lookup			; no: NEXT item------>

	; value is < MAX: then look for MIN value 	incf	ADCVthPointer,f		; Point to next byte 	call	ADC_Vth_TABLE  	movwf   CmpBH       		; hold MIN value high byte into compare register B 	incf	ADCVthPointer,f		; Point to next byte 	call	ADC_Vth_TABLE    	movwf   CmpBL	       		; hold MIN value low byte into compare register B  	call	CMP_A_B				; compare ADC : MIN value 	btfsc	UCON1,A_EQ_B		; is ADC value = MIN table value 	goto	KPAD_GetKey_Found	; yes: Key found------>
	btfss	UCON1,A_GT_B		; is ADC value > MIN table value
	goto	KPAD_GetADRES		; no: value out of voltage span, read again------>

KPAD_GetKey_Found
 	movf	KeyPadXY,W			; Hold previous key
	movwf	KeyPadXYPre
 	movf	KeyPadXYtemp,W		; Hold new key
	movwf	KeyPadXY

;-------------------------------
; only for analyze and benchmark
KPAD_Report
	ifdef __ADRES_TRON
 		; Report ADRES value into EEPROM for analyse
		addwf	KeyPadXY,W		; pointer = 2*KeyPadXY
		btfsc	STATUS,C		; carry set if key 0xFF
		goto	KPAD_Report_End	; >>>no report
		sublw	LOW(EE_ADRESL0); pointer to LSB
		BANK1
		movwf	EEADR			; into EEADR
		movf	ADRESL,W		; ADC value LSB into EEDATA
		movwf	EEDATA
		call	WriteEEPROM		; write one byte to EEPROM
		BANK1
		decf	EEADR,f			; pointer to MSB
		BANK0
		movf	ADRESH,W		; ADC value MSB into EEDATA
		BANK1
		movwf	EEDATA
		call	WriteEEPROM		; write one byte to EEPROM
		BANK0
	endif
KPAD_Report_End
;--------------

	; switch case KeyPadXY
	;
	; case: NO KEY
	movfw	KeyPadXY
	xorlw	KPAD_FF		; Check if no key
	btfsc	STATUS, Z		; Z=0: no next key
	goto	KPAD_Keystroke_End	; Z=1: yes, end key ====>
	;
	; case: KEY = PREVIOUS KEY
	movfw	KeyPadXY
	xorwf	KeyPadXYPre,W	; Check if equal
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_End	; end key ====>
	;
	; case: SEND0
	movfw	KeyPadXY
	xorlw	KPAD_SEND0	; Check if key Send0
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_Sendn	; yes, ====>
	;
	; case: SEND1
	movfw	KeyPadXY
	xorlw	KPAD_SEND1	; Check if key Send1
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_Sendn	; yes, ====>
	;
	; case: SEND2
	movfw	KeyPadXY
	xorlw	KPAD_SEND2	; Check if key Send2
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_Sendn	; yes, ====>
	;
	; case: BUZZER
	movfw	KeyPadXY
	xorlw	KPAD_BUZ		; Check if key Buz
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_Buz	; yes, ====>
	;
	; case: TUNEP
	movfw	KeyPadXY
	xorlw	KPAD_TUNEP	; Check if key Tune PULSE
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_TunP	; yes, ====>
	;
	; case: TUNEC
	movfw	KeyPadXY
	xorlw	KPAD_TUNEC	; Check if key Tune CARRIER
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_TunC	; yes, ====>
	;
	; case: MODEAB
	movfw	KeyPadXY
	xorlw	KPAD_MOD		; Check if key Mod
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_Mod	; yes, ====>
	;
	; case: SPEED UP
	movfw	KeyPadXY
	xorlw	KPAD_SPEED_UP	; Check if key Speed up
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_SpeedUp	; yes, ====>
	;
	; case: SLOW_DOWN
	movfw	KeyPadXY
	xorlw	KPAD_SLOW_DOWN; Check if key Slow down
	btfsc	STATUS, Z		; Z=0: no
	goto	KPAD_Keystroke_SlowDown	; yes, ====>
	;
KPAD_Keystroke_End ; End of keypad
	return	;>>>>>>>>>>>>>>>>END OF KEYPAD

; Send message #0,#1,#2 to Tx
;----------------------------
KPAD_Keystroke_Sendn
	...
	return	;>>>>>>>>>>>>>>>>END OF KEYPAD

; Side Tone Buzzer ON/OFF
; Only for TX message
; Not for service message
;------------------------
KPAD_Keystroke_Buz
	...
	return	;>>>>>>>>>>>>>>>>END OF KEYPAD

; Tune mode constant carrier
;---------------------------
KPAD_Keystroke_TunC
	...
	return	;>>>>>>>>>>>>>>>>END OF KEYPAD

; Tune mode pulse 50% duty cycle dits
;------------------------------------
KPAD_Keystroke_TunP
	...
	return	;>>>>>>>>>>>>>>>>END OF KEYPAD

; Toggle mode A or B
;-------------------
KPAD_Keystroke_Mod
	...
	return	;>>>>>>>>>>>>>>>>END OF KEYPAD

; Speed up
;---------
KPAD_Keystroke_SpeedUp
	...
	return	;>>>>>>>>>>>>>>>>END OF KEYPAD

; Slow down
;----------
KPAD_Keystroke_SlowDown
	...
	return	;>>>>>>>>>>>>>>>>END OF KEYPAD

;----------------------------------------------------------------------
; KPAD_GetADCvalue
; Convert analog voltage level to 10 bits number
; Input: WREG=AD config with channel number to read
; Output: 10 bit ADC value is read from the pin and returned justified right.
; Please refer data sheet Page 41 (Section 7.1.4) for the AD Conversion
; clock explanation.  The 10-BIT ADC in the PIC12F675 are request
; minimum Tad with 1.6uS, and the conversion time need 11 Tad, so the
; mininum conversion time is 1.6uS * 11Tad = 17.6uS.

KPAD_GetADCvalue
	movwf	ADCON0			; AD config channel ANx
KPAD_GetADCvalue_Go
	movlw	15 		; wait for Acquisition time
	call	Delay	; 15*5us=75us
	; A/D conversion cycle in progress
    bsf		ADCON0,GO_NOT_DONE	; Init GO_NOT_DONE
    btfsc	ADCON0,GO_NOT_DONE	;
    return

Réglages:
Pour m’assurer de la qualité de l’intervalle de mesure [min, max[ les résultats sont stockés provisoirement en EEPROM pour être visualisés a posteriori avec PICPgm. La section de code optionnelle encadrée par les étiquettes KPAD_Report et KPAD_Report_End disparaît en version Release. De même, le précieux espace réservé en EEPROM est récupéré en mettant en commentaire la définition     __ADRES_TRON . J'ai pu ainsi simplement vérifier que la théorie rejoignait la pratique.

Download  Télécharger le programme source, le fichier .hex et tous les schémas.

5. Encodeur morse – Gestion des messages

5.1. Messages préenregistrés

La quasi totalité de l'EEPROM est réservée à l'enregistrement des messages. Un indicatif et 3 messages peuvent être enregistrés. l'espace est distribué de manière dynamique. Chaque message de longueur variable s'insère dans la place restant disponible comme le montre l'extrait du programme ci-dessous.

;--------------
; MESSAGE BLOCK
;--------------
; Your call sign here
EE_CALLSIGN	; don't remove this label
;
	DE "F8EOZ" ; your call sign here CAPS LOCK ONLY
	DE _EOM ; end of call sign don't remove

;----------------------
; Prerecorded message#:
; A..Z CAPS LOCK ONLY
; @ alias for CALLSIGN
EE_MESSAGE ; don't remove this label
;
; message#0:
; CQ CQ CQ DE F8EOZ F8EOZ...
	DE _HOM,KPAD_SEND0,0x03 ; Head of message directive: message number (button assign), beacon time (0xFF=infinity)
	DE "CQ CQ CQ DE @ @ K "
	DE _EOM ; end of message#0
;
; message#1:
; CQ DX CQ DX CQ DX DE F8EOZ F8EOZ...
	DE _HOM,KPAD_SEND1,0x03 ; Head of message directive: message number (button assign), beacon time (0xFF=infinity)
	DE "CQ DX CQ DX CQ DX DE @ @ K "
	DE _EOM ; end of message#1
;
; message#2:
; CQ CQ F8EOZ F8EOZ TEST ...
	DE _HOM,KPAD_SEND2,0x03 ; Head of message directive: message number (button assign), beacon time (0xFF=infinity)
	DE "CQ CQ @ @ TEST "
	DE _EOM ; end of message#2
;----------------------------------------
;----------------------------------------
;----------------------------------------

L'indicatif: il est traité comme un texte pour être inséré dans les messages au moyen d'un alias. Il doit être placé après l'étiquette EE_CALLSIGN. La directive de fin de message _EOM le termine.

Les messages sont placés à la suite de l'indicatif après l'étiquette EE_MESSAGE.

Le message: se compose de 3 parties : l'entête, le texte du message, la fin.

L'entête:  comprend les données suivantes:

  • directive _HOM  (head of message),
  • le nom symbolique du bouton qui l'active,
  • le facteur de répétition (beacon time) [0, hFF] où FF = infini sachant qu’un message en cours peut toujours être interrompu par un DIT.

Le texte: c’est une chaîne de caractères ASCII et de codes procédure (prosign).
Caractère ASCII: [h20 (espace), h5A (Z)], portion de table comprenant les signes de ponctuation, les chiffres, les lettres majuscules. Les minuscules et tous les autres caractères ne sont pas utilisés.
Le caractère @ est utilisé comme alias de l’indicatif. L’encodeur le reconnaît et effectue son expansion.
Le caractère Espace est utilisé comme espace entre les mots (Word Gap).
Les codes de procédure sont situés dans l’intervalle [h80,hFF]. Les codes _AR,_BT,_SK peuvent être insérés simplement en fractionnant le texte. Exemple:

...	DE "TEXTE",_BT,"SUITE..."

La fin du message: directive _EOM (End of Message)

5.2. Messages de service

Le principe reste le même que les messages préenregistrés à la différence que les messages sont stockés dans une table de la RAM. Chaque message est placé derrière l’étiquette qui correspond à l’événement déclencheur.
Personnalisation : dans ma version, le message est constitué d’un seul caractère mais il pourrait être aussi un texte. Pour rendre muet le message il suffit de le remplacer par un espace. L’alias @ ne peut être utilisé. Voici la description de la table:

MSG_TABLE
	movlw 	HIGH(MSG_DATA) ; PCLATH = HIGH bit address
	movwf 	PCLATH 			;
	movf	TabPointer,W	; for PCL
	addwf	PCL ,F 			; Jump to character pointed to in W register
SERVICE_MESSAGE
;
; SERVICE MESSAGE
;
IAMBIC_MSG		; iambic key
	DT "I",_EOM
STRAIGHT_MSG	; straight key
	DT "S",_EOM
SPEED_UP_MSG	; signal for speed change up
	DT "U",_EOM
SLOW_DOWN_MSG	; signal for speed change down
	DT "D",_EOM
MOD_A_MSG		; signal for mode A change
	DT "A",_EOM
MOD_B_MSG		; signal for mode B change
	DT "B",_EOM
BUZ_MSG			; signal for sidetone buzzer on/off
	DT _AR,_EOM
ERROR_HM_NOT_FOUND	; signal for error header message not found
	DT _ERR,_EOM
;
	IF ((HIGH ($)) != (HIGH (SERVICE_MESSAGE)))
    	ERROR "MSG_TABLE CROSSES PAGE BOUNDARY!"
	ENDIF;

5.3. L’encodeur

Le diagramme figure 9 ci-dessous, montre le processus d’encodage.

Figure 9 - Diagramme du processus d'encodage

Figure 9 – Diagramme du processus d’encodage

Registres:

  • BitFrame: 8 bits comprenant le code Morse et une clé.
  • ControlTape: bande pilote de 8 bits qui commande de gauche à droite l’émission du code Morse (1= émission, 0=fin de code).
  • MorseCode: 8 bits de gauche à droite qui représente le code Morse (1 = DAH, 0=DIT).

ControlTape et MorseCode sont en correspondance biunivoque : ControlTape[b8]<->MorseCode[b8],…, ControlTape[b0]<->MorseCode[b0]

Entrée: un caractère ASCII ou un prosign.

Processus:

1) Transcodage:

  • Phase 1 : au moyen de la table RAM ENC_BIT_FRAME_TABLE, le caractère ASCII [h20,h5A] est transformé en BitFrame, le prosign passe ce premier filtre sans changement.
  • Phase 2 : Le prosign est transformé en Bit Frame. Les prosigns spéciaux passent ce second filtre sans changement.

2) Séparation:

Le BitFrame au moyen de sa clé, est divisé en 2 parties de 8 bits, MorseCode et ControlTape.

3) Encodage:

Enfin, l’encodage du signal morse est réalisé en faisant glisser vers la gauche la bande pilote (rotation gauche avec bit C) qui commande l’émission du signe Morse qui lui est relié.

Download  Télécharger le programme source, le fichier .hex et tous les schémas.

6. Le manipulateur électronique

La boucle principale du programme figure 10 ci-dessous, inclut l’interface de la clé Morse. Au démarrage ou au Reset, le Setup restaure  les dernières options de la machine à partir de l’EEPROM :

  • le registre UCON (User Control Register)
  • la vitesse de manipulation en mots/mn.

La détection du type de clé, Iambique ou Droite, est réalisée au Setup en examinant le port DAH. Voici la description du registre UCON.


;----- UCON Bits -----------------------------------------------------
IAMBIC_KEY 	EQU 0x00	; StraightKey=0 Iambic= 1
BUZE	 	EQU 0x01	; = 1 if side tone buzzer enable
TXF 		EQU 0x02	; Tx flag = 1 if message sending to Tx
MSGF		EQU 0x03	; msg flag = 1 if send message running
KPADF		EQU 0x04	; = 1 if keypad unlocked
MOD_B		EQU 0x05	; = 1 if keyer mode B, 0 mode A
BUZF		EQU 0x06	; buzzer flag = 1 if message sending to buzzer

;----- DEFAULT Options --------------------------------------------------
; Iambic Key + Buzzer on
#define UCON_DEFAULT (1<<IAMBIC_KEY) | (1<<BUZE) | (1<<BUZF)

L’accès au clavier est commandée de manière asynchrone par l’interruption TIMER1 (délai d’une seconde fixé par les paramètres TMR1_INIT  et TMR1_OVF_INIT) qui, elle même, est commandée par l’appui sur la clé du maniulateur.

Figure 10 - Boucle principale

Figure 10 – Boucle principale

Download  Télécharger le programme source, le fichier .hex et tous les schémas.

7. Réalisation

7.1. Clavier 3×3
Figure 11 - Clavier face avant

Figure 11 – Clavier face avant

Le circuit est réalisé  sur une plaque PCB FR4 pastillée étamée à trous métallisés double face de 5x7x0,16 cm. Le MCU et les composants qui l’entourent sont placés sur une seconde plaque identique, placée en dessous et qui s’enfiche à la première plaque. Il y a beaucoup de place. Les boutons poussoirs 6x6x9mm ont été achetés sur Ebay chez Electronics-Salon  qui propose des composants d’excellente qualité. L’utilisation d’une plaque double face est très pratique dans ce genre de circuit qui comprend surtout du fil! L’implantation des composants suit exactement le schéma théorique.

Vérification: avant de le connecter au MCU, s’assurer de son fonctionnement sous tension (problème de soudure). Pour ce faire, le relier seul à l’alimentation 5V, connecter le voltmètre à sa sortie, appuyer successivement sur chaque bouton et noter la tension.

Download  Télécharger le programme source, le fichier .hex et tous les schémas.

7.2. Carte principale
7.2.1. Prototype
Figure 12 - Prototype Carte principale

Figure 12 – Prototype Carte principale

Le prototype a été réalisé à l’aide du logiciel fritzing. C’est la première fois que je l’utilise. Sa prise en main est facile. Fritzing offre une protoboard dont les dimensions sont réglables. Je n’ai pas trouvé dans sa bibliothèque le 12F675. Qu’à cela ne tienne! l’éditeur de composants permet facilement de créer le sien à partir d’un modèle générique. Trois fichiers SVG sont nécessaires: breadbord.svg (platine d’essai), schematic.svg (schéma), pcb.svg (circuit imprimé). Si vous êtes curieux où si vous souhaitez peaufiner un peu, ces fichiers sont manipulables sous Inkscape. Attention cependant, au préalable, installer les polices de caractères OCR-A et Droid Sans qui sont utilisées par fritzing.

7.2.2. Carte câblée

Les 2 photos ci-dessous, montrent la carte principale cablée. Une diode de protection 1N4001 a été ajoutée en entrée du régulateur 5V. Le clavier s’embroche par dessus. L’ensemble est maintenu par des entretoises coupées dans de simples chevilles en plastique et 4 vis de 3mm provenant de la quincaillerie la plus proche.

Pour conclure, la mise en oeuvre du 12F675 est aisée mais n’oubliez pas: RTFM!

keyer12F675_1370

keyer12F675_1364

Download  Télécharger le programme source, le fichier .hex et tous les schémas et fichiers Inkscape et fritzing.

 

Références

Two-wire, four-by-four-key keyboard  interface saves power by Stefano Salvatori, University of Rome, and Gabriele Di Nucci, EngSistemi, Rome, Italy

Index des articles de la catégorie microcontroleur