L’afficheur est commandé par le HD44780 LCD controller. Pour le commander nous disposons des bornes suivantes:
RS = 0 instruction input, 1 data input,
R/W = 0 write to LCD, 1 read from LCD,
E = enable signal,
DB0 à DB7 = data bus line 0 (LSB) to line 7 (MSB).
L’afficheur comprend 2 lignes de 16 caractères:
- ligne 0 de 0×00 à 0x0F,
- ligne 1 de 0×40 à 0x4F.

L’afficheur peut être utilisé en mode 8 bits (8-bit interface) ou en mode 4 bits (8-bit interface). Dans ce dernier cas, seules les lignes DB4 à DB7 sont utilisées. Le quartet (nibble) de poids fort et écrit puis le quartet de poids faible. C’est cette dernière méthode qui est utilisée ici.

J’ai construit le module d’affichage pour qu’il soit entièrement réutilisable par une autre application. Il comprend un ensemble de routines utilisées au travers de macros. Il comprend les éléments suivants:

Définitions:

#define DECIMALPOINT    '.'            ; decimal symbol : dot, comma, …
#define Number_notLeadingZero     LCDOption,0x07    ; 1 = not leading zero
; NUMBER_FORMAT code
#define    END_FORMAT       0xFF
#define    DOT_FORMAT       0x2E
#define    NINE_FORMAT    0x39
#define    ZERO_FORMAT    0x5A

Variables:

    LCD_TEMP:1    ; LCD subroutines internal use
    LCDOption:1   ; internal register don't use
    LCDIndex:1    ; general purpose index

Macros:
IR_WRITE    commande d’écriture d’instruction

BF_READ    commande de lecture du busy flag

DR_WRITE    commande d’écriture de data

DR_READ    commande de lecture de data

LCDDisplay_NumberF  <MSB_nombre>,  <adresseFormat> afficher un nombre avec format

LCDDisplay_Textl  <adresseTexte> afficher un texte

CURSOR_POS    placer le curseur à la position indiquée dans le registre W

CURSOR_POS l   <position> placer le curseur à la position indiquée en paramètre

LCDSet_Display_Control  contrôle de l’afficheur: curseur ou non, clignotement

SET_NOT_LEADING_ZERO afficher les zéros

Tables:
TABLE_TEXT textes à afficher.
NUMBER_FORMAT format des nombres à afficher.
Attention à l’implantation de ces tables qui modifient PCL, registre de 8 bits, qui est l’adresse basse du compteur ordinal. Ces 2 tables de moins de 256 octets ont été placées pour cela à partir de l’adresse 0×01.

Description de TABLE_TEXT
La première instruction de la table modifie PCL.
Chaque texte commence par une étiquette, se termine par le retour de zéro binaire sous forme:

ETIQUETTE_TEXTE
   DT   "TEXTE"
   RETLW 0

Ci-dessous TABLE_TEXT de notre programme.

;*****************************************************************************
; Text to display
; Relative character address is in W
;*****************************************************************************
TABLE_TEXT
    addwf    PCL ,F ; Jump to character pointed in W register
MOD_0
    DT   "NO IF"  ; Note the zero termination.
    retlw    0
MOD_1
    DT   "LO+IF"  ; Note the zero termination.
    retlw    0
MOD_2
    DT   "LO-IF"  ; Note the zero termination.
    retlw    0
MOD_3
    DT   "IF-LO"  ; Note the zero termination.
    retlw    0
MODE_LABEL
    DT    "MODE:"
    retlw    0
IF_LABEL
    DT    "IF:"
    retlw    0
UNDERFLOW_ERROR
    DT    "Underflow Error"
    retlw    0
MHz_UNIT
    DT    "MHz"
    retlw    0
TABLE_TEXT_END
    retlw    0
;
    IF ( (TABLE_TEXT & 0x0FF) >= (TABLE_TEXT_END & 0x0FF) )
        MESSG   "==============Warning - User Defined: Table 'TABLE_TEXT' crosses page boundary in computed jump=============="
    ENDIF

Afficher un texte
L’affichage d’un texte se résume à une seule ligne qui utilise la macro LCDDisplay_Textl. Par exemple pour afficher l’unité de la fréquence:
LCDDisplay_Textl    MHz_UNIT

Cas particulier de la traduction d’un code en texte
Ce cas se pose ici pour l’option MODE du setup qui propose 4 valeurs de 0 à 3. On affiche le code et sa traduction litérale. Ce problème se règle simplement en définissant une table intermédiaire ici appelée MOD_x (placé ici sous TABLE_TEXT) qui retourne l’adresse du texte à afficher dans le registre W. Le texte est affiché directement par la routine (et non la macro) LCDDisplayText de la façon suivante:

    movf    MODindex, w      ; get address from TABLE_TEXT
    call    MOD_x            ; return address MODE option text from TABLE_TEXT in W
    call    LCDDisplayText   ; display text

Table intermédiaire MOD_x:

MOD_x    ; MODindex is in W. Return TABLE_TEXT address into W register
    addwf    PCL ,F ; Jump to character pointed to in W register
    retlw    MOD_0 - TABLE_TEXT - 1
    retlw    MOD_1 - TABLE_TEXT - 1
    retlw    MOD_2 - TABLE_TEXT - 1
    retlw    MOD_3 - TABLE_TEXT - 1

Description de NUMBER_FORMAT
La première instruction de la table modifie PCL.
Chaque format commence par une étiquette, se termine par le retour de oxFF sous forme:

ETIQUETTE_FORMAT
   DT   "FORMAT"
   RETLW 0xFF

Description du format
Le format sert à supprimer les zéros non significatifs, à indiquer la position du point décimal, à insérer des espaces pour séparer les milliers.
Z = supprime un zéro non significatif (leading zero). Dès qu’un chiffre différent de zéro est atteint ce code devient inactif.
. = point décimal, insère le caractère défini par DECIMALPOINT et inactive Z.
9 = insère le chiffre quel que soit sa valeur et inactive Z.
espace (valeur par défaut) = insère un espace.
L’affichage du nombre commence par la gauche (MSB) et s’arrête au dernier 9 s’il existe ou sinon au dernier Z. Le nombre doit être sous forme Décimal Codé Binaire étendu ( pas de nombre en ASCII).

Ci-dessous NUMBER_FORMAT de notre programme.

;*****************************************************************************
; Number format
; Relative format code address is in W
;*****************************************************************************
NUMBER_FORMAT
    addwf    PCL ,F ; Jump to character pointed in W register
FORMAT_MHz
    DT   "Z Z99.999 999"  ; Note the FF termination.
    retlw    0xFF
FORMAT_IF
    DT   "99.999"  ; Note the FF termination.
    retlw    0xFF
FORMAT_MOD
    DT   "9"  ; Note the FF termination.
    retlw    0xFF
NUMBER_FORMAT_END
    retlw    0xFF
;
    IF ( (NUMBER_FORMAT & 0x0FF) >= (NUMBER_FORMAT_END & 0x0FF) )
        MESSG   "==============Warning - User Defined: Table 'NUMBER_FORMAT' crosses page boundary in computed jump=============="
    ENDIF

Ci-dessous la routine d’affichage d’un nombre.

;*****************************************************************************
; Display a number at cursor position using a table NUMBER_FORMAT
; Format sample: ZZZ Z9 or 99 or 9.999 or 9 999 999 ....
; Start address character must be in FSR
; Start address table NUMBER_FORMAT must be in W
; Number_notLeadingZero = 0 => Leading zeros are not displayed
; Number_notLeadingZero = 1 => zero is not Leading zero
;_______________________________________________________
; ASCII value    x30 x31 x32 x33 x34 x35 x36 x37 x38 x39
; number           0   1   2   3   4   5   6   7   8   9
;_______________________________________________________
;*****************************************************************************
LCDDisplayNumberF
    movwf    LCDIndex                ; Holds format address in table NUMBER_FORMAT
    clrf     LCDOption
LCDDisplayNumberFLoop
    ; is leading Zero ?
    movfw    LCDIndex
    call     NUMBER_FORMAT
    xorlw    0x5A                    ; Check if "Z" format
    btfsc    STATUS, Z
    goto     Z_format                ; yes, ====>
    ; is nine ?
    movfw    LCDIndex
    call     NUMBER_FORMAT
    xorlw    0x39                    ; Check if "9" format
    btfsc    STATUS, Z
    goto     nine_format             ; yes, ====>
    ; is dot ?
    movfw    LCDIndex
    call     NUMBER_FORMAT
    xorlw    0x2E                    ; Check if "." format
    btfsc    STATUS, Z
    goto     dot_format              ; yes, ====>
    ; is end ?
    movfw    LCDIndex
    call     NUMBER_FORMAT
    xorlw    0xFF                    ; Check if at end of format
    btfsc    STATUS, Z
    goto     LCDDisplayNumberFEnd    ; yes, end =====================>

space_format    ; default
    btfss    Number_notLeadingZero   ; is a number 1 to 9 already displayed ?
    goto     nextIndexFormat         ; no, no space to insert
    movlw    ' '                     ; yes, insert a space
    call     LCDputChar4             ; Display character
    goto     nextIndexFormat
dot_format
    SET_NOT_LEADING_ZERO             ; to avoid future Z format
    movlw    DECIMALPOINT            ; insert decimal point
    call     LCDputChar4             ; Display character
    goto     nextIndexFormat
nine_format
    SET_NOT_LEADING_ZERO             ; to avoid future Z format
    goto     displayASCII
Z_format
    btfsc    Number_notLeadingZero   ; is a number 1 to 9 already displayed ?
    goto     displayASCII            ; yes, display digit

    movf     INDF, f                 ; INDF -> INDF, set STATUS bit Z
    btfsc    STATUS, Z
    goto     nextDigit               ; yes digit = 0, leading zero not displayed
    SET_NOT_LEADING_ZERO             ; no, display digit and avoid future Z format
displayASCII
    movf     INDF,W                  ; Digit -> W
    iorlw    030h                    ; ASCII value mask
    call     LCDputChar4             ; Display character
nextDigit
    incf     FSR, f
    nextIndexFormat
    incf     LCDIndex,f              ; Point to next character
    goto     LCDDisplayNumberFLoop
LCDDisplayNumberFEnd

return

Afficher un nombre
L’affichage d’un nombre se résume à une seule ligne qui utilise la macro LCDDisplay_NumberF, par exemple pour afficher la fréquence mesurée:
LCDDisplay_NumberF BCD9, FORMAT_MHz

Télécharger le fichier Kicad du schèma .
Télécharger les fichiers source et hexa du fréquencemètre .

Liens

Fréquencemètre à microcontrôleur PIC
Fréquencemètre à microcontrôleur PIC – Description
Fréquencemètre à microcontrôleur PIC – Structure du programme
Fréquencemètre à microcontrôleur PIC – Mesure
Fréquencemètre à microcontrôleur PIC – Commande de l’afficheur LCD
Fréquencemètre à microcontrôleur PIC – Réalisation