PIC Microcontoller Input / Ouput Method

PIC Dumb Terminal PIC16F84 + LCD module + Keyboard + RS232 comm

; File: LCDRS232.ASM
; First time that it works: 3 August of 2000.
; Author: Alejandro Lavarello - R.O. del Uruguay - South America
; Contact: alejol@adinet.com.uy
; Colocado en la PicListLatina el 4/7/00.
; PicListLatina: Lista de correos dedicada a micro PIC y clones (SCENIX) en
; portugues y espanol.
 
; Gracias Norberto por compartir tu codigo! :)

; Part of the code is adapted from the magazine "Electronica y Computadores" edited by CEKIT. 

; Abstract:
; ===========
;           The basic idea is to made a simple character terminal that can be used as
;           man-machine interfce in some equipment.
;          The PIC1F84 is conected to a 4 x 4 matrix keyboard. RA1 and RA0 are connected
;           to a MAX232  in order to convert TTL/RS232 levels.
;          Each keypressed in the 4x4 keyboard is send to a PC runningan terminal emulator
;           like "Hyperterminal".
;          Each key pressed in the PC is displayed in a LCD module atached to the PIC.
;          Neither TMR0 nor interrupts are used. Because it, I think that a OTP PICMICRO
;          may be used in final version in order to make the device more cheap.
;                
; Disadvantages/drawbacks/ future improvements needed:
; ====================================================
;       0) Make the code more easily readable...sorry, I am not an English speaker...
;       1) Txspeed = Rx speed = 1200 bauds (fixed) , 8 bits, no parity, 1 stop bit.
;         No flow control implemented.
;	2) The null character ( 0 ) is not send nor received, it is ignored.
;       3) No display control ( "clear display", "position cursor", etc) are implemented.
;	4) It is possible to connect the pin R/W to ground and liberate RA3 for another use.
;       5) Only one key pressed admited, the firmware rejects simultaneous keys pressed.


; Basic ideas:
; ============
;       1) This code is based in "isocrhonous code", this is, each routine have a fixed execution time. 
;        In case of branches inside a routine, just before each "return" I have added a adjustable
;        software delay in order to mantain the same execution time.  
;       2) The MAIN_LOOP is "perpetual" and executes in 277 microseconds. This allows to sample 
;         3 times each bit at 1200bauds. The value ("1" or "0") of the pin Pin_RX is sampled
;        3 times spaced 2 microseconds, and the pin value is decided by majority.
;       3) The routines RS232 and READ_KEYBOARD using state machines.
;       4) Is not implemented "auto repeat" of the keys. Only one charater is send and
;        READ_KEYBOARD waits for key release.
;       
; Hardware notes:
;=================
;  Display used model:  JM161A  (1 line x 16 characters). Web page:
;              http://www.china-lcd.com/english/eindex.html
;       
;  Each pin of the PORTB has a 100 ohm series resistor (I have seen this in various circuits...)

;  PORTA,4 has a pull-up resistor of 1 kohm.
      
;  PORTB<3..0> are used as outputs, and are conected to DB7..DB4 of the LCD,  and too to the
;   LINE1 to LINE4 of the 4 x 4 keyboard.

;  PORTB<7..4> are inputs conected to the keyboard columns COL4 to COL1. 

;  Contrast adjust of the LCD (input named V5 of the LCD) are made with a 5k multi- potentiometer
;  The best contrast is achieved with about 0,7 V in V5.
;  Low nibble of the LCD _DATA_ bus (DB3 to DB0) are connected to ground.
;
;  The MAX232 has capacitors of 10 microfarads. Pin 11 of MAX232 connected to PORTA, 1
;   and pin 12 to PORTA, 0

; The 4x4 matrix keyb. has 0 to 9 keys, plus: "HELP" key, "2ND" key,
; up arrow, down arrow , "CANCEL" and "ENTER".
; Keyboard is labelled "MADE BY ACT"(the most cheap that I have encountered).


        LIST P=16F84          ; The old and good PIC16F84 

   INCLUDE "P16F84.INC"  ; Standard file of Microchip with equates that match _DATA_sheets.

   __CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC	; Configuration word= 0x11, tis is,
			; Code protection= OFF, Watchdog = Not used, Power On Timer= Used,
			; Oscillator type= XT Crystal 

	; Crystal used: 4 MHz
	; Delays dependent of crystal value.

; ******************************************** 
; **** User RAM  ("file registers")*********** 
; ********************************************


_DATA__REG        equ     0x0C    ; Aux. reg. to write in display.
REG_1mseg       EQU     0x0D    ; Loop counter for delay of aprox 1ms.
REG_Various_ms   equ     0x0E    ; Another loop counter for delay.
SHADOWB         EQU     0x0F    ; Used to "create" the value that will be write in PORTB 

KEY             equ     0x10     ; ASCII code of the key pressed.

CONTROL_TX      equ     0x11 ; Variable that controls progress of TX state-machine.
CONTROL_RX      equ     0x12 ; idem RX
CONTROL_KEYBOARD equ    0x13 ; Idem for the keyboard state machine.

TX_REG          equ     0x14 ; Character to transmit.Destroyed in Tx.
CHAR_RX         equ     0x15 ; Character received.
RX_REG          equ     0x16 ; Temporal storing of received bits.

REG_LOOP        equ     0x17 ; Counter for variable delays.
SHADOWA         equ     0x18 ; Used "to make" the byte that will be write in PORTA.

SCAN_CODE       equ     0x19 ; "Crude" value of keyboard scan (must be converted to ASCII)
AUX             equ     0x1A ; Auxilar variable.
DELAY_KEYBOARD  equ     0x1B ; Help in wait for keyboard debounces.
SCAN_PORTB      equ     0x1C ; Stores which line of PORTB is zero in order to scan keyb.

LINE            equ     0x1D ; Which line is scanned (1, 2, 3, 4)
COUNT_CHARACTERS equ    0x1E    ; How many char. were written to the LCD. Used for
                               ; wraparound.

; *********** End of RAM definition ******


; ******************************** 
; *** Various bit equates    ***** 
; ********************************
E       EQU     0x04    ;Connected to LCD's "Enable" (a kind of "clock")! Pin of PORTA
RS      EQU     0x02    ;   "      "  LCD's "Register Select" pin.        
RW      EQU     0x03    ;   "      "  LCD's pin "Read/Write" .      
Pin_TX  EQU     0x01  ; Output Rs232.( TTL level)
Pin_RX  EQU     0x00  ; Input RS232.( TTL level)

; ********* End of bit equates *****


;	 *************************************
;        ****** MAIN PROGRAM ***********
;	 *************************************
        ORG     0x00    ; Reset vector.
        goto    BEGIN

        ORG     0x04    ; Interrupt vector. Not used.
        nop
;------------------------------------------------------------------
	ORG     0x100
BEGIN     ; In the power-up, begins here. 

        ; Microchip reccomends "not to use TRIS" "not to use OPTION".
        ; It works very fine and saves instructions.

        movlw   b'00000000'     ; Pull-ups enabled, etc.
	OPTION
        movlw   b'00000001'     ; PORTA: All outputs except PORTA, 0
	TRIS	PORTA
        movlw   b'11110000'     ; Port B: high nibble inputs, low outputs.
	TRIS	PORTB
	
        clrf    PORTA   ; I will write-only in the LCD. 
        CLRF    PORTB   ; (The R/W pin may be connected to ground).
        bsf     PORTA, Pin_TX   ; "IDLE STATE" for the RS232 TX line.

	; -----------------------------------------
        ; ** Power ON wait ***
	; -----------------------------------------

        movlw   d'60'           ; Will wait about 60 milliseconds - wait for 
        movwf   REG_Various_ms   ;  the LCD to wake-up -.

INITIAL_DELAY                 
        call    DELAY_1ms
        decfsz  REG_Various_ms, F
        goto    INITIAL_DELAY

        ; -------End of Power-On wait -------------


        ; ---------------------------------------------
        ; ---------  DISPLAY INITIALIZATION   ---------
	; ---------------------------------------------
     ; We need 5 steps :      

        ; Step 1
        movlw   0x02       ; 4 bits interface initialization.
        call    CONTROL    ; Seems to be redundant wit step 2, 
        call    DELAY_1ms  ; but is really needed.
        call    DELAY_1ms
        ; Step 2
        movlw   b'00101000'     ; Initializing  4 bits and 2 lines.
        call    CONTROL         ; Instruction named:
        call    DELAY_1ms         ; "FUNCTION SET"
	; Paso 3
	movlw	b'00001111'	; Display On, cursor On, blink on
        call    CONTROL         ; Instruction named:
        call    DELAY_1ms         ; "DISPLAY ON/OFF CONTROL"
	; Paso 4
        movlw   b'00000110'     ; Name: "ENTRY MODE SET"
        call    CONTROL         ; Right display shifting 
        call    DELAY_1ms       ; Entire display shifting: disabled.
	; Paso 5
        movlw   b'00000001'     ; Name:
	call	CONTROL		; "CLEAR DISPLAY".
        call    DELAY_1ms       ; Display RAM pointer set to 0.
        call    DELAY_1ms
        call    DELAY_1ms       ; Approx. 1.53 millsec. minimum exec. time.

        movlw   0x80            ; Set display's RAM pointer to 0 
        call    CONTROL         ; (perhaps redundant...)
        call    DELAY_1ms       ;

;--------- Initialization of important variables ------
	clrf	CONTROL_TX
	clrf	CONTROL_RX
        clrf    CONTROL_KEYBOARD
        clrf    COUNT_CHARACTERS

        movlw   ">"  ; After power-up, sends a ">" to the PC signalling "i am alive"
        movwf   KEY 
        movlw   ":"     ; Writes this in the LCD at power on. 
	movwf	CHAR_RX

; *******************************************************

MAIN_LOOP
        call    RS232   ; Routine rx/tx : 39 machine cycles = 39 usec with 4MHz
                        ; crystal.
        call    READ_KEYBOARD     ;  40 cycles
        call    WRITE_TO_DISPLAY  ; 153 cycles
        call    SEND_KEY          ; 14 cycles
                                  ; 246 cycles. Need a total time of 277 usec.
	;-----------------
	movlw	d'08'
        call    Variable_Wait     ; 28 cycles
	;----------------	
        nop     ; 1 cycle

        goto    MAIN_LOOP         ; 2 cycles
                  ; MAIN_LOOP is executed in 246 +28+1+2 = 277 microseconds.

; *******************************************************

DELAY_1ms     ; Delay of about 1 msec.
	clrf	REG_1mseg
DECRE_1	
	decfsz	REG_1mseg, F
	goto	DECRE_1

RET_230                 ; delay of about 234 usec.
	movlw	d'76'
	movwf	REG_1mseg
DECRE_2
	decfsz	REG_1mseg, F
	goto	DECRE_2
     return 

;--------------------------------------------
SEND_KEY    
        movf    CONTROL_TX, F  ; control=0 ? (is the TX machine free to send?) 
	btfsS	STATUS, Z
         goto   No_SEND_KEY1  ; wait for TX state machine ...

        movf    KEY, F        ; KEY to send is 0?
	btfsc	STATUS, Z
         goto   No_SEND_KEY2  ; yes, is zero,no send it.
                              ; nop, is different of zero, send it.
        incf    CONTROL_TX, F 
        movf    KEY, W
	movwf	TX_REG
        clrf    KEY   ; "Eating" the KEY :)
        return          

No_SEND_KEY1

	goto	$+1
	nop
No_SEND_KEY2
	nop
	goto	$+1	
        return  
;--------------------------------------------
WRITE_TO_DISPLAY
      
	movf	CHAR_RX, W
	btfss	STATUS, Z
         goto   Write_Char_Received 
	GOTO	No_Write

Write_Char_Received
        ; COUNT_CHARACTERS incremented with each caracter OR control
        ; bytes sends to the LCD module.

        incf    COUNT_CHARACTERS, F   ; How many characters writed?

	movlw	d'09'
        xorwf   COUNT_CHARACTERS, W ; Is the 9nth. write to the LCD?
	btfsc	STATUS, Z
         goto   SECOND_LINE

	movlw	d'18'
        xorwf   COUNT_CHARACTERS, W  ; Is the 18nth write to the LCD?
	btfsc	STATUS, Z
         goto   FIRST_LINE  ; Wraparound of the LCD display.

Write_LCD 
       
        movf    CHAR_RX, W      
	nop	
        clrf    CHAR_RX ; "Eat" the just-received character.
        goto    _DATA_    ; We will use the RETURN of the _DATA_ routine.
                        
                   
SECOND_LINE
        nop     
	goto	$+1
        goto    $+1         
        movlw   0xC0    ; Point to the first "second line" position.
        goto    CONTROL ; Next char in the virtual "second line"
                        ;  RETURN of the  CONTROL routine used.
FIRST_LINE
        clrf    COUNT_CHARACTERS  
        movlw   0x80 ; Point to the very first character in display's RAM.  
        goto    CONTROL ;

No_Write
      
	movlw	d'46'	  	
        call    Variable_Wait  
	;-------------------
	nop	
        return          
;-----------------------------------------------
; Variable_Wait =  2 (call) + 3*n +2 (return) = 3*n + 4
; n is the  W value.

Variable_Wait
	movwf	REG_LOOP
Repeat
	decfsz	REG_LOOP, F
         goto   Repeat 
	return
;--------------------------------------------------------------------------------
;--------------------------------------------------------------------------------
CONTROL ; Receive a byte in W and writes it to the LCD as control code.

        bcf     PORTA, RS  ;Signals to the LCD that is a control code.
	goto	$+1
        goto    _DATA_2

_DATA_    ; The byte in W is passed as _DATA_ .
        bsf     PORTA, RS ;Signals that char. is a _DATA_ char.
	goto	$+1
        goto    _DATA_2
_DATA_2
        bsf     PORTA, E   ; Rises E (LCD's clock)
        goto    $+1        ; 4 bit Interface , the _DATA_ is send
                            ; in two nibbles.
        movwf     _DATA__REG  ; Save char in aux. register.  
        swapf   _DATA__REG, W     ; Write high nibble.
	movwf	PORTB		
	goto	$+1
	goto	$+1
                            ;Falling edge in E.
        bcf     PORTA, E    ; Nibble latched here.
	goto	$+1
	goto	$+1
        bsf     PORTA, E    ; Rises E for next time.
                            
	;------------------
	movlw	d'15'
        call    Variable_Wait
        ;------------------  
        movf    _DATA__REG, W  ; Go for the low nibble...
	movwf	PORTB
	goto	$+1
        goto    $+1      ; Second pulse in E...
        bcf     PORTA, E ; Nibble latched...
        goto    $+1        
	goto	$+1
        bsf     PORTA, E     
	;------------------
	movlw	d'15'		
        call    Variable_Wait
        ;----------------- 
	return

;-------------- End of CONTROL/_DATA_ routine. 
;-------------- 
;-----------------------------------
	ORG 0x005
RS232
; TX
        clrf    PCLATH  ; Take care with PCLATH in computed gotos! 
	movf	CONTROL_TX, W
        addwf   PCL, F        ; TX state machine.
        goto    OUT_TX        ; 0
        goto    SEND_STARTbit    ; 1
        goto    WAIT_TX        ; 2
        goto    WAIT_TX        ; 3
        goto    SEND_BIT      ; 4     bit 0
        goto    WAIT_TX        ; 5
        goto    WAIT_TX        ; 6
        goto    SEND_BIT      ; 7     bit 1
        goto    WAIT_TX        ; 8
        goto    WAIT_TX        ; 9
        goto    SEND_BIT      ; 10    bit 2
        goto    WAIT_TX        ; 11
        goto    WAIT_TX        ; 12
        goto    SEND_BIT      ; 13    bit 3
        goto    WAIT_TX        ; 14
        goto    WAIT_TX        ; 15
        goto    SEND_BIT      ; 16    bit 4
        goto    WAIT_TX        ; 17
        goto    WAIT_TX        ; 18
        goto    SEND_BIT      ; 19    bit 5
        goto    WAIT_TX        ; 20
        goto    WAIT_TX        ; 21
        goto    SEND_BIT      ; 22    bit 6
        goto    WAIT_TX        ; 23
        goto    WAIT_TX        ; 24
        goto    SEND_BIT      ; 25    bit 7
        goto    WAIT_TX        ; 25
        goto    WAIT_TX        ; 27
        goto    SEND_BIT      ; 28    STOP BIT
        goto    WAIT_TX        ; 29
        goto    OUT_TX        ; 30

SEND_STARTbit
	goto	$+1
	goto 	$+1
	goto	$+1
	bcf	PORTA, Pin_TX
	goto	$+1
	incf	CONTROL_TX, F
	goto	RX ; ciclo 17
SEND_BIT
        movf    PORTA, W   ; First I "make" the bit in SHADOWA...
	movwf	SHADOWA
	bcf	SHADOWA, Pin_TX
	btfsc	TX_REG, 0
	bsf	SHADOWA, Pin_TX
	movf	SHADOWA, W
        movwf   PORTA  ;... and then writes the bit in PORTA, Pin_TX

        bsf     STATUS, C ; Shift the TXreg for the next time... 
	rrf	TX_REG, F	
	incf	CONTROL_TX, F 	
        goto    RX 
WAIT_TX
	goto	$+1
	goto	$+1
	goto	$+1
	goto	$+1
	nop	
	incf	CONTROL_TX, F
        goto    RX  
OUT_TX
	goto	$+1
	goto	$+1
	goto	$+1
	goto	$+1
	nop
	clrf	CONTROL_TX
        goto    RX

RX
        clrf    SHADOWA    ; Here Pin_RX is sampled 3 times.
	btfsc	PORTA, Pin_RX
	incf	SHADOWA, F
	btfsc	PORTA, Pin_RX
	incf	SHADOWA, F
	btfsc	PORTA, Pin_RX
        incf    SHADOWA, F     ; If Pin_RX was "1" the majority of the time,
                           ; then SADOWA,1 is "1" too.

        movf    CONTROL_RX, W  ; RX state machine.
	addwf	PCL, F
        goto    LOOK_FOR_STARTbit       ; 0
        goto    LOOK_FOR_STARTbit       ; 1
        goto    WAIT_RX        ; 2 
        goto    WAIT_RX        ; 3 
        goto    RECEIVE_A_BIT      ; 4     bit 0
        goto    WAIT_RX        ; 5
        goto    WAIT_RX        ; 6 
        goto    RECEIVE_A_BIT      ; 7     bit 1
        goto    WAIT_RX        ; 8
        goto    WAIT_RX        ; 9 
        goto    RECEIVE_A_BIT      ; 10    bit 2
        goto    WAIT_RX        ; 11
        goto    WAIT_RX        ; 12 
        goto    RECEIVE_A_BIT      ; 13    bit 3
        goto    WAIT_RX        ; 14
        goto    WAIT_RX        ; 15 
        goto    RECEIVE_A_BIT      ; 16    bit 4
        goto    WAIT_RX        ; 17
        goto    WAIT_RX        ; 18 
        goto    RECEIVE_A_BIT      ; 19    bit 5
        goto    WAIT_RX        ; 20
        goto    WAIT_RX        ; 21 
        goto    RECEIVE_A_BIT      ; 22    bit 6
        goto    WAIT_RX        ; 23
        goto    WAIT_RX        ; 24 
        goto    RECEIVE_A_BIT      ; 25    bit 7
        goto    WAIT_RX        ; 26
        goto    WAIT_RX        ; 27
        goto    CONFIRM_STOP   ; 28    STOP?

LOOK_FOR_STARTbit
	incf	CONTROL_RX, F
        btfsc   SHADOWA, 1      ; Is Pin_RX promedially equal to 0?
         clrf   CONTROL_RX  ; nope, look the next time.
        goto    $+1         ; yes, goto the next step in state machine.
	goto	$+1
        goto    END_RS232   
WAIT_RX
	incf	CONTROL_RX, F
	goto	$+1
	goto	$+1
	goto	$+1
        goto    END_RS232

RECEIVE_A_BIT
	incf	CONTROL_RX, F
	rrf	SHADOWA, F
	rrf	SHADOWA, F
	rrf	RX_REG, F
	goto	$+1
	nop
        goto    END_RS232

CONFIRM_STOP    ; Is stop bit present? If not, clear all and start again.
	clrf	CONTROL_RX	
	btfsc	SHADOWA, 1
         goto   Rx_Ok      ; yes, stop bit is present.
	goto	$+1
	goto	$+1
        goto    END_RS232  ; ciclo 37
Rx_Ok
        nop
	movf	RX_REG, W
        movwf   CHAR_RX    ; Stores byte received for future use.
        goto    END_RS232  

END_RS232
        RETURN

;---- End of RS232 routine --------------
;-----------------------------------------

 	ORG 0x200

READ_KEYBOARD
	bCf	PCLATH,0
        bSf     PCLATH,1  ; Take care of the annoying PCLATH...

_CONVERT_KEY      EQU     d'13'
_WAIT_FOR_RELEASE EQU     d'14'
_WARNING_TIME     EQU     d'15'

        movf    CONTROL_KEYBOARD, W
        addwf   PCL, F              ; Keyboard state machine.
        goto    KEY_PRESSED?     ; 0
        goto    LOOK_OTHER_LINE  ; 1
        goto    STILL            ; 2
        goto    WE_HAVE_KEY?     ; 3
        goto    LOOK_OTHER_LINE  ; 4
        goto    STILL            ; 5
        goto    WE_HAVE_KEY?     ; 6
        goto    LOOK_OTHER_LINE  ; 7
        goto    STILL            ; 8
        goto    WE_HAVE_KEY?     ; 9
        goto    LOOK_OTHER_LINE  ; 10
        goto    STILL            ; 11
        goto    WE_HAVE_KEY?     ; 12
        goto    CONVERT_KEY      ; 13
        goto    WAIT_FOR_RELEASE ; 14
        goto    WARNING_TIME     ; 15 
;------------------------------------
KEY_PRESSED?
	clrf	PORTB
	goto	$+1
	goto	$+1
	movf	PORTB, W
	xorlw	b'11110000'
        btfsc   STATUS, Z       ; All "ones"?
         goto   No_Pressed     ; yes, neither key pressed. 

        movlw   0x01    ; A "zero" exist! Next step, scan lines.
        movwf   CONTROL_KEYBOARD 
        
        clrf    LINE   ; Prepare to scan keyboard...
	movlw	b'11101111'
        movwf   SCAN_PORTB    

        ;--------------------
        movlw   d'03'   
        call    Variable_Wait
	goto	$+1
	return

No_Pressed
        clrf    CONTROL_KEYBOARD   
	;------------
        movlw   d'04' 
        call    Variable_Wait
	goto	$+1
	return
;----------------------------------------
LOOK_OTHER_LINE
        incf    LINE, F        ; Next line to scan...

        bsf     STATUS, C
        rrf     SCAN_PORTB, F  ; Put only this line to zero.
        movf    SCAN_PORTB, W
	movwf	PORTB
        goto    $+1
        goto    $+1     

        movf    PORTB, W        ; Catch keys...
        movwf   SCAN_CODE       ; Store for next comparison.

        incf    CONTROL_KEYBOARD, F ; Next steps: make a pause, then compare SCAN_CODE.
        movlw   d'18'   ; pause of about 5 millisecond (18 passes of the MAIN_LOOP)
        movwf   DELAY_KEYBOARD

	;-----------
        movlw   d'03'   
        call    Variable_Wait
	goto	$+1
	return
;-----------------------------------------
STILL
        decfsz  DELAY_KEYBOARD, F  ; Is the 18th decrement ?
          GOTO   Still_Waiting
        incf    CONTROL_KEYBOARD, F ; Next step: look what happens
                                    ; after this wait.
	;-------------
Still_Waiting
        movlw   d'07'   
        call    Variable_Wait 
	nop       
	return
;-----------------------------------------
WE_HAVE_KEY?                               
        movf    SCAN_PORTB, W  
        movwf   PORTB                                                                 
        goto    $+1                                                          
        goto    $+1                                                           
        movf    PORTB, W        
        xorwf   SCAN_CODE, W 
        btfss   STATUS, Z       
         goto   Not_Coincident1

        ; --- Try if only one key is pressed.
        clrf    AUX             
        btfss   SCAN_CODE, 7  ; 
	 incf	AUX, F
        btfss   SCAN_CODE, 6  ; 
	 incf	AUX, F
        btfss   SCAN_CODE, 5  ; 
	 incf	AUX, F
        btfss   SCAN_CODE, 4  ; 
         incf   AUX, F          ;
                                ; Each zero in SCAN_CODE, AUX was incremented.
        movlw   0x01            ;
        xorwf   AUX, W          ;  AUX = 1 ?
        btfss   STATUS, Z       ;  KEY acceptable?
         goto   Not_Coincident2
        movlw   _CONVERT_KEY ; 24
        movwf   CONTROL_KEYBOARD ;  KEY is good!!! Convert it to ASCII.
       

	return

Not_Coincident1    ; Reads not match. Go for the next line.
        incf    CONTROL_KEYBOARD, F       
        btfsc   LINE, 2           ; The 4th line was scanned?
          clrf   CONTROL_KEYBOARD ; If yes, we no have luck, no valid key...
                                  ; If not, go for next line. 
	;-------------------
	movlw	d'03'
        call    Variable_Wait
	goto	$+1
	return

Not_Coincident2    ; Trying next LINE.
        incf    CONTROL_KEYBOARD, F       
        btfsc   LINE, 2 ; Is the 4th line?
         clrf   CONTROL_KEYBOARD ; if yes, no good keys. Restart. 
                                 ; if not, go for next line.
       
	goto	$+1
	nop
        ; This is the most long code segment in READ_KEYBOARD.
	return
;------------------------------------------------

CONVERT_KEY ; Here we makes the assignment of ASCII values.

        btfss   SCAN_CODE, 4
	movlw	0x00
        btfss   SCAN_CODE, 5
	movlw	0x01
        btfss   SCAN_CODE, 6
	movlw	0x02
        btfss   SCAN_CODE, 7
	movlw	0x03

        bcf     STATUS, C       ; Multiply LINE by 4
        rlf     LINE, F
        rlf     LINE, F
        addwf   LINE, W

        call    ASCII_TABLE  
        MOVWF   KEY   ; KEY OBTAINED!!!

        MOVLW   _WAIT_FOR_RELEASE ; Wait for the person to loose the key.
        MOVWF   CONTROL_KEYBOARD

	;--------------
        MOVLW   d'01'  
        CALL    Variable_Wait
	RETURN
;-------------------------------------------------

WAIT_FOR_RELEASE
	clrf	PORTB
	goto	$+1
	goto	$+1
	movlw	0xF0
	xorwf	PORTB, W
	btfss	STATUS, Z
         goto   Still_Pressed

        movlw	d'18'
        movwf   DELAY_KEYBOARD ;Wait for 18 MAIN_LOOP passes. 

        movlw   _WARNING_TIME
        movwf   CONTROL_KEYBOARD

	;--------------
        movlw   d'04'  
        call    Variable_Wait
	return

Still_Pressed
	;--------------
        movlw   d'05' 
        call    Variable_Wait
	return

;-------------------------------------------------
WARNING_TIME
        decfsz  DELAY_KEYBOARD, F
         goto   Wait_only_a_few
        clrf    CONTROL_KEYBOARD 
                                ; Ready to initiate the keyboard scan again.
Wait_only_a_few 
	;-------------
        movlw   d'07' 
        call    Variable_Wait
	nop
	return
;----------------------------------------------

; -----------------  ASCII table: assigns scan-codes with arbitrary  ------------
; ------------------  ASCII's characters. -----------------------------------
; The 4x4 matrix keyb. has 0 to 9 keys, plus: "HELP" key, "2ND" key,
; up arrow, down arrow , "CANCEL" and "ENTER".

	ORG	0x300
ASCII_TABLE
	bsf	PCLATH, 0
	bsf	PCLATH, 1
	addwf	PCL, F
LIN0                    ; Error, the count begins with line 1.
        RETLW   "."     ; Error characters.
	RETLW	";"
	RETLW	"-"
	RETLW	"*"
LIN1                    ; First valid line.
 	retlw	"1"	
	retlw	"2"	
	retlw	"3"	
        retlw   "u"     ; Up arrow.
LIN2
 	retlw	"4"	
	retlw	"5"	
	retlw	"6"	
        retlw   "d"     ; Down arrow.
LIN3
 	retlw	"7"	
	retlw	"8"	
	retlw	"9"	
        retlw   "N"     ; KEY labelled as "2ND"
LIN4			
        retlw   "C"     ; KEY labelled "CANCEL"
	retlw	"0"	
        retlw   "H"     ; KEY "HELP"
        retlw   "E"     ; KEY "ENTER"
 	END

;*********************************************
;  ********* FIN :-) *****************************
;    ***********************************************

;and now in Spanish:
;Archivo: LCDRS232.ASM
; Primera vez que funcionó: 3 de agosto de 2000.
; Autor: Alejandro Lavarello - R.O. del Uruguay
; Contacto: alejol at adinet.com.uy
; Colocado en la PicListLatina el 4/7/00.
; ¡Gracias Norberto por compartir tu código! :)
; Parte del codigo está adaptado de la revista "Electrónica y Computadores"
; editada por CEKIT.

; Para qué sirve esto:
; ====================
;                      El PIC16F84 esta conectado a un teclado 4x4
;                      y a un conversor TTL / RS232 (el clásico MAX232).
;                      Cada tecla apretada se envía a un PC que corre 
;                      un emulador de terminal (p. ej., "Hyperterminal") 
;                      y cada caracter recibido se muestra en el módulo LCD.
;                      La idea es hacer una interfaz para equipos que la requieran...
;                      Como no se usa el  TMR0 ni interrupciones, creo que se puede
;                       adaptar para un PIC OTP y asi hacerla mas barata.
; Desventajas:
; ============
;              1) Velocidad de transmision = vel. de recepcion = 1200 baudios (fija) 
;                  8 bits sin paridad, 1 stop-bit. No hay control de flujo.
;              2) No se puede enviar ni recibir el caracter nulo ( 0 ).
;              3) No se prevee ningun control del display (como borrarlo o posicionar el cursor)
;              4) La comunicacion PIC-display es unidireccional, se podria haber
;                  conectado el pin R/W a tierra y asi liberar RA3 para algun uso.
;              5) Rechaza dos teclas juntas apretadas.
; Futuras mejoras:
; ===============
;                0) Hacer mas legible el codigo (disculpen...)
;                1) Manejar un modulo LCD de 16 caracteres por 2 líneas.
;                2) Permitir que los bytes recibidos que tienen el MSB a "1"
;                  (o sea, estan fuera del ASCII) provoquen ciertas acciones, como posicionar
;                 el cursor, escribir una eñe, etc.
; Ideas basicas:
; ============
;               1) Usé "código isócrono", o sea, tuve que contar cuantos ciclos de máquina
;               demoraba cada rutina y además asegurarme que, en caso de saltos, siempre se
;               ejecutaran en el mismo tiempo... por eso agrego  algunos "nops" 
;               o "call DEMORA" y cosas asi.
;
;               2) El BUCLE_PRINCIPAL es "perpetuo" y se ejecuta en 277 microsegundos.
;               Esto permite muestrear 3 veces cada bit si se usa transmisión/recepción RS232
;               a 1200 baudios. Se decide el estado (0 o 1) del  Pin_RX por mayoría.
;  
;               3)La lectura del teclado no permite "auto-repeat" de las teclas: hasta que
;                el tipo no suelta la tecla, no se envía otra tecla.
; Notas de hardware:
; ==================
;                   Se usó como display un modulo LCD modelo JM161A. (1 linea x 16 caracteres)
;                   Página del fabricante:
;                     http://www.china-lcd.com/english/eindex.html
;                   RB0 a RB3 se usan tanto para escanear las lineas del teclado como
;                    para enviar datos (4 bits) al LCD.
;                   Los primeros 8 caracteres del display estan en la "linea 1" y los
;                     otros 8 en la "linea 2" (virtuales, diriamos...)
;                   Los 8 pines del PORTB tienen en serie Rs de 100 Ohm para protegerlos 
;                     de la estatica.
;                   El MAX232 tiene su pin 11 conectado a RA1 y su pin 12 a RA0.
;                     RA4 tiene una R de pull-up de 1k.
;                   El ajuste de contraste del LCD ( V5 ) se hace con un preset multivuelta 
;                    de 5k. Se consiguio buen contraste con 0,5V en el pin V5 del modulo.
;                   El nibble bajo ( DB0 a DB3 ) de los datos del LCD se conecto a tierra.
;  Ah! Me parece que el registro "BANDERAS" al final no lo usé para nada...

        LIST P=16F84          ; Uso el viejo y querido PIC16F84 

   INCLUDE "P16F84.INC"  ; Archivo estandar de Microchip con la definicion
                         ; de los registros y los bits con el mismo nombre
                         ; de las hojas de datos, p. ej.: Z es el bit ZERO del registro
                         ; STATUS, etc.

   __CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC    ; Palabra de configuracion= 0x11, o sea,
                        ; Proteccion de codigo = OFF, Watchdog = No usado, Power On Timer= Usado,
                        ; Tipo de oscilador= Cristal XT

        ; Cristal del micro: 4 MHz
        ; Si se usa otro, ajustar retardos.
; ******************************************** 
; **** RAM de usuario ("registros")*********** 
; ********************************************

PUNTERO_CHAR    EQU     0x0C    ; Puntero al caracter a enviar
REG_PAUSA       EQU     0x0D    ; Usado para pausa entre mensajes
REG_DATO        equ     0x0E    ; Mantiene el caracter a enviar
REG_1mseg       EQU     0x0F    ; Usado para retardo de 1 mseg aprox.
REG_Varios_ms   equ     0x10    ; Usado para dar tiempo al display a iniciarse.
SHADOWB         EQU     0x11    ; Uso esto para "armar" el dato a escribir en PORTB
BANDERAS        EQU     0x12     ; Agrupa bits que indican condiciones.
PROX_LINEA      equ     0x13
TECLA           equ     0x14
REG_ESCANEO     equ     0x15
ESTADO          equ     0x16
CONTROL_TX      equ     0x17 ; controla maquina de estados de Tx.
CONTROL_RX      equ     0x18 ; idem RX
CONTROL_TECLADO equ     0x19 ; Idem para manejar la lectura del teclado.
TX_REG          equ     0x1A ; Caracter a enviar.
CHAR_RX         equ     0x1B ; Caracter recibido.
REG_LOOP        equ     0x1C
SHADOWA         equ     0x1D
RX_REG          equ     0x1E ; registro auxiliar para armar el caracter que se esta recibiendo.
CODIGO_SCAN     equ     0x1F ; Valor "en bruto" de la lectura del teclado.
AUX             equ     0x20
RETARDO_TECLADO equ     0x21 ; ayuda a no hacer nada, esperando por "debounces" y cosas asi.
LINEA           equ     0x22
BARRE_PORTB     equ     0x23 ; determina cual linea pongo a cero.
CANT_CARACTERES equ     0x24    ; Indica cuantos caracteres se escribieron al LCD.
; *********** Fin de la definicion de la RAM ******                          
; ************************************* 
; *** Definicion de bits diversos ***** 
; *************************************
E       EQU     0x04    ;Conectado a pin "Enable" (especie de clock) ! Pines del Puerto A
RS      EQU     0x02    ;   "      a pin "Register Select"           ! para control del
RW      EQU     0x03    ;   "      a pin "Read/Write"                ! modulo.
Pin_TX  EQU     0x01  ; El pin RA1 es el de salida Rs232.(niveles TTL)
Pin_RX  EQU     0x00  ; RA0 es entrada RS232.(niveles TTL)
Nuevo_Char equ   0x03 ; Este bit se pone a "1" cuando se recibio un byte nuevo.
; ********* Fin de la definicion de bits *****    
; Uso los pines RB0 a RB3 para enviar datos al modulo. (DB4 a DB7)
;        *************************************
;        ****** PROGRAMA PRINCIPAL ***********
;        *************************************
        ORG     0x00    ; Vector de reset .
        goto    INICIO

        ORG     0x04    ; Vector de interrupcion. No usado.
        nop
;------------------------------------------------------------------
        ORG     0x100
INICIO     ; Al prender el micro viene aca primero. 

        ; Microchip recomienda no usar TRIS, pero la verdad es que mantienen esta
        ; instruccion en toda la linea 16XX. Funciona ok.
	
        movlw   b'00000000'     ; Habilito los pull-ups en PORTB
        OPTION
        movlw   b'00000001'     ; PORTA: Todos salidas salvo RA0.
        TRIS    PORTA
        movlw   b'11110000'     ; Port B: nibble bajo salidas
        TRIS    PORTB
	
        clrf    PORTA   ; Solo escribire al LCD. Mantengo siempre RW en bajo 
        CLRF    PORTB   ; (se podria haber conectado a tierra directamente).
        bsf     PORTA, Pin_TX   ; Pongo linea RS232 en estado de reposo.
        ; -----------------------------------------
        ; ** Espera de Power ON
        ; -----------------------------------------

        movlw   d'60'           ; Esperare unos 60 miliseg antes de hacer nada (espero 	
        movwf   REG_Varios_ms   ; por el lento LCD).  

RETA_INI                	
        call    RETARDO
        decfsz  REG_Varios_ms, F
        goto    RETA_INI
        ; -------Fin de la espera de Power-On -------------
        ; ---------------------------------------------
        ;--------- INICIALIZACION DEL DISPLAY ---------
        ; ---------------------------------------------
     ; Necesito 5 pasos para inicializarlo.      
BEGIN
        ; Paso 1
        movlw   0x02    ; Inicializo a 4 bits.
        call    CONTROL
        call    RETARDO
        call    RETARDO
        ; Paso 2
        movlw   b'00101000'     ; Inicializo a 4 bits y 2 lineas.
        call    CONTROL         ; Nombre de la instruccion:
        call    RETARDO         ; "FUNCTION SET"
        ; Paso 3
        movlw   b'00001111'     ; Display On, cursor On, blink on
        call    CONTROL         ; Nombre de la instruccion:
        call    RETARDO         ; "DISPLAY ON/OFF CONTROL"
        ; Paso 4
        movlw   b'00000110'     ; Nombre: "ENTRY MODE SET"
        call    CONTROL         ; Direccion de movimiento: hacia la derecha
        call    RETARDO         ; Desplazamiento de todo el display: no habilitado.
        ; Paso 5
        movlw   b'00000001'     ; Nombre de la instruccion:
        call    CONTROL         ; "CLEAR DISPLAY". Llena la pantalla de espacios.
        call    RETARDO         ; Coloca el puntero de la RAM interna del modulo ("DDRAM")
        call    RETARDO         ; apuntando a la posicion 0 (equivale a borrar display y
                                ; despues escribir el byte de control 0x80). 
        call    RETARDO         ; 
                                ; Le toma al menos 1,53 mseg hacer esto.
        movlw   0x80            ; Apunto a la primera posicion de la DRAM del modulo.
        call    CONTROL         ; (por las dudas)
        call    RETARDO	
;--------- Inicializacion de las variables clave del bucle principal ----
        clrf    CONTROL_TX
        clrf    CONTROL_RX
        clrf    CONTROL_TECLADO
        clrf    CANT_CARACTERES
        movlw   ">"  ; Enviara esto para avisarle al PC que revivió :)
        movwf   TECLA	
        movlw   ":"     ; Escibirá esto en la pantallita LCD al encenderlo.
        movwf   CHAR_RX
;----------------------------------------------------------------------
BUCLE_PRINCIPAL
        call    RS232   ; Rutina rx/tx : 39 ciclos	
        call    LEER_TECLADO ; Duracion: 2 + 6 + 30 + 2 = 40 ciclos
        call    Escribir_Tecla; Duracion: 2 + 151 = 153 ciclos
        call    Enviar_Tecla    ; 2 (call) + 12 = 14 ciclos
                ; 246 ciclos
        ;-----------------
        movlw   d'08'
        call    DEMORA   ; 28 ciclos
        ;----------------	
        nop     ; 1 ciclo
        goto    BUCLE_PRINCIPAL  ; 2 ciclos
; *******************************************************
; *******************************************************
RETARDO     ; Retardo de algo mas de 1 mseg. 
        clrf    REG_1mseg
DECRE_1	
        decfsz  REG_1mseg, F
        goto    DECRE_1

RET_230                 ; Retardo de 234 usec.
        movlw   d'76'
        movwf   REG_1mseg
DECRE_2
        decfsz  REG_1mseg, F
        goto    DECRE_2
     return 
;--------------------------------------------
Enviar_Tecla    ; Demora 12 ciclos aca.
        movf    CONTROL_TX, F  ; control=0 ? (o sea, esta libre para tx?)	
        btfsS   STATUS, Z
         goto   No_Enviar_Tecla1

        movf    TECLA, F        ; Tecla vale 0?
        btfsc   STATUS, Z
         goto   No_Enviar_Tecla2

        incf    CONTROL_TX, F
        movf    TECLA, W
        movwf   TX_REG
        clrf    TECLA   ; Consumir la tecla :)
        return          ; 12 ciclos
No_Enviar_Tecla1

        ; 4 ciclos
        goto    $+1
        nop
No_Enviar_Tecla2
        nop
        goto    $+1	
        return   ; 12 ciclos
;--------------------------------------------
Escribir_Tecla
        ; Dura: 151 ciclos
        movf    CHAR_RX, W
        btfss   STATUS, Z
         goto   Escribir_char_rx	
        GOTO    No_Escribir

Escribir_char_rx
        incf    CANT_CARACTERES, F      ; ciclo 5 Me fijo cuantos caracteres escribi
        movlw   d'09'
        xorwf   CANT_CARACTERES, W ; ¿sera el noveno caracter?
        btfsc   STATUS, Z
         goto   SEGUNDA_LINEA
        movlw   d'18'
        xorwf   CANT_CARACTERES, W  ; ¿sera el decimooctavo?
        btfsc   STATUS, Z
         goto   PRIMERA_LINEA  ; hacer "wraparound" o como se pronuncie :)

Escribe_LCD	
        ; ciclo 13
        movf    CHAR_RX, W      ; ciclo 14
        nop	
        clrf    CHAR_RX ; ciclo 16 "Consumo" el caracter recien recibido.
        goto    DATO    ;16 + 2(goto) + 133(ejecucion incluido return)  = 151 ciclos
                        ; aca termina la escritura del caracter, se usa el return
                        ; de la rutina DATO
SEGUNDA_LINEA
        nop     ; ciclo 11
        goto    $+1
        goto    $+1           ; ciclo 15
        movlw   0xC0  ; ciclo 16
        goto    CONTROL ; El prox. caracter va en la "2a linea" (virtual)
                        ; Uso el RETURN de la rutina CONTROL.
PRIMERA_LINEA
        clrf    CANT_CARACTERES  ; ciclo 15
        movlw   0x80   ; ciclo 16
        goto    CONTROL ; El prox caracter va en la "1a. linea"
No_Escribir
        ; 5 ciclos
        ;-----------------
        movlw   d'46'     	
        call    DEMORA    ; 5 + 1 + 142 = 148
        ;-------------------
        nop	
        return          ; 151 ciclos
;-----------------------------------------------
; Demora =  2 (call) + 3*n +2 (return) = 3*n + 4
; n es el valor que previamente se cargo en W .
DEMORA
        movwf   REG_LOOP
Repite
        decfsz  REG_LOOP, F
         goto   Repite	
        return
;--------------------------------------------------------------------------------
;--------------------------------------------------------------------------------
CONTROL ; Recibe un byte en W y lo manda al LCD como caracter de control.
        ; Tiempo de esta subrutina incluido el return: 133 ciclos
        bcf     PORTA, RS       ; Indica que enviara un caracter de control (RS=0)
        goto    $+1
        goto    DATO2            
DATO    ; Recibe byte en W y lo manda como un caracter a mostrar.
        ; Tiempo de esta subrutina incluido el return: 133 ciclos
        bsf     PORTA, RS       ; Indica al modulo que enviara un DATO (RS=1)
        goto    $+1
        goto    DATO2
DATO2
        bsf     PORTA, E        ; ciclo 6 Sube E para que el LCD pueda despues capturar el dato.
        goto    $+1             ; Utiliza la interface a 4 bits
                                ; por eso tiene que partir el dato y
                                ; enviar los dos nibbles.
        movwf     REG_DATO     ; Conserva el dato en este registro auxiliar. 	
        swapf   REG_DATO, W     ; Escribo nibble alto.
        movwf   PORTB   	
        goto    $+1
        goto    $+1
                                ; Aca doy el pulso en E. El LCD recien captura los datos en 
        bcf     PORTA, E        ; el flanco de bajada de su entrada E.
        goto    $+1
        goto    $+1
        bsf     PORTA, E        ; Vuelvo a subir E para el proximo pulso...
                                ; ciclo 21
        ;------------------
        movlw   d'15'
        call    DEMORA
        ;------------------  ciclo 70
        movf    REG_DATO, W     ; Escribo nibble bajo.
        movwf   PORTB
        goto    $+1
        goto    $+1                             ; Segundo pulso en E
        bcf     PORTA, E        ; Lo captura el modulo...
        goto    $+1             ; Fin del segundo pulso
        goto    $+1
        bsf     PORTA, E        ; ciclo 82
        ;------------------
        movlw   d'15'   	
        call    DEMORA
        ;----------------- ciclo 131
        return                  ; ciclo 133 Ya escribio el byte entero en el LCD
;-------------- Fin de la rutina que envia datos de control 
;-------------- o bien caracteres al modulo.
;-----------------------------------
        ORG 0x005
RS232
; TX
        clrf    PCLATH  ; ¡Atenti al PCLATH al hacer saltos calculados!	
        movf    CONTROL_TX, W
        addwf   PCL, F	
        goto    SALIR_TX        ; 0
        goto    ENVIAR_START    ; 1
        goto    PAUSA_TX        ; 2
        goto    PAUSA_TX        ; 3
        goto    ENVIAR_BIT      ; 4     bit 0
        goto    PAUSA_TX        ; 5
        goto    PAUSA_TX        ; 6
        goto    ENVIAR_BIT      ; 7     bit 1
        goto    PAUSA_TX        ; 8
        goto    PAUSA_TX        ; 9
        goto    ENVIAR_BIT      ; 10    bit 2
        goto    PAUSA_TX        ; 11
        goto    PAUSA_TX        ; 12
        goto    ENVIAR_BIT      ; 13    bit 3
        goto    PAUSA_TX        ; 14
        goto    PAUSA_TX        ; 15
        goto    ENVIAR_BIT      ; 16    bit 4
        goto    PAUSA_TX        ; 17
        goto    PAUSA_TX        ; 18
        goto    ENVIAR_BIT      ; 19    bit 5
        goto    PAUSA_TX        ; 20
        goto    PAUSA_TX        ; 21
        goto    ENVIAR_BIT      ; 22    bit 6
        goto    PAUSA_TX        ; 23
        goto    PAUSA_TX        ; 24
        goto    ENVIAR_BIT      ; 25    bit 7
        goto    PAUSA_TX        ; 25
        goto    PAUSA_TX        ; 27
        goto    ENVIAR_BIT      ; 28    STOP BIT
        goto    PAUSA_TX        ; 29
        goto    SALIR_TX        ; 30

ENVIAR_START
        goto    $+1
        goto    $+1
        goto    $+1
        bcf     PORTA, Pin_TX
        goto    $+1
        incf    CONTROL_TX, F
        goto    RX ; ciclo 17
ENVIAR_BIT
        movf    PORTA, W   ; Primero armo el byte en SHADOWA..
        movwf   SHADOWA
        bcf     SHADOWA, Pin_TX
        btfsc   TX_REG, 0
        bsf     SHADOWA, Pin_TX
        movf    SHADOWA, W
        movwf   PORTA  ;...y recien aca mando el bit.
        bsf     STATUS, C ; Desplazo el byte a la derecha..
        rrf     TX_REG, F	
        incf    CONTROL_TX, F 	
        goto    RX ; ciclo17
PAUSA_TX
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        nop	
        incf    CONTROL_TX, F
        goto    RX   ; ciclo 17
SALIR_TX
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        nop
        clrf    CONTROL_TX
        goto    RX   ; ciclo 17
RX
        clrf    SHADOWA         ; Muestreo 3 veces el estado del Pin_RX...
        btfsc   PORTA, Pin_RX
        incf    SHADOWA, F
        btfsc   PORTA, Pin_RX
        incf    SHADOWA, F
        btfsc   PORTA, Pin_RX
        incf    SHADOWA, F      ; ...para despues decidir por mayoria (el bit1 
                                ; de SHADOWA será "1" solo si dos o mas muestras valieron uno)
        movf    CONTROL_RX, W
        addwf   PCL, F
        goto    VER_START       ; 0
        goto    VER_START       ; 1
        goto    PAUSA_RX        ; 2	
        goto    PAUSA_RX        ; 3	
        goto    RECIBE_BIT      ; 4     bit 0
        goto    PAUSA_RX        ; 5
        goto    PAUSA_RX        ; 6	
        goto    RECIBE_BIT      ; 7     bit 1
        goto    PAUSA_RX        ; 8
        goto    PAUSA_RX        ; 9	
        goto    RECIBE_BIT      ; 10    bit 2
        goto    PAUSA_RX        ; 11
        goto    PAUSA_RX        ; 12	
        goto    RECIBE_BIT      ; 13    bit 3
        goto    PAUSA_RX        ; 14
        goto    PAUSA_RX        ; 15	
        goto    RECIBE_BIT      ; 16    bit 4
        goto    PAUSA_RX        ; 17
        goto    PAUSA_RX        ; 18	
        goto    RECIBE_BIT      ; 19    bit 5
        goto    PAUSA_RX        ; 20
        goto    PAUSA_RX        ; 21	
        goto    RECIBE_BIT      ; 22    bit 6
        goto    PAUSA_RX        ; 23
        goto    PAUSA_RX        ; 24	
        goto    RECIBE_BIT      ; 25    bit 7
        goto    PAUSA_RX        ; 26
        goto    PAUSA_RX        ; 27
        goto    CONFIRMA_STOP   ; 28    STOP?

VER_START
        incf    CONTROL_RX, F
        btfsc   SHADOWA, 1      ; ¿vale 0?
         clrf   CONTROL_RX	
        goto    $+1
        goto    $+1
        goto    SALIR_RS   ; ciclo 37
PAUSA_RX
        incf    CONTROL_RX, F
        goto    $+1
        goto    $+1
        goto    $+1
        goto    SALIR_RS   ; ciclo 37
RECIBE_BIT
        incf    CONTROL_RX, F
        rrf     SHADOWA, F
        rrf     SHADOWA, F
        rrf     RX_REG, F
        goto    $+1
        nop
        goto    SALIR_RS  ; ciclo 37
CONFIRMA_STOP
        clrf    CONTROL_RX	
        btfsc   SHADOWA, 1
         goto   Rx_Ok	
        goto    $+1
        goto    $+1
        goto    SALIR_RS  ; ciclo 37
Rx_Ok
        bsf     BANDERAS, Nuevo_Char
        movf    RX_REG, W
        movwf   CHAR_RX
        goto    SALIR_RS  ; ciclo 37
SALIR_RS
        RETURN  ; ciclo 39

;-----------------------------------------
        ORG 0x200

LEER_TECLADO
        bCf     PCLATH,0
        bSf     PCLATH,1  ; Acomodo PCLATH para esta pagina...

_CONVERTIR_TECLA        EQU     d'13'
_ESPERA_QUE_SUELTE      EQU     d'14'
_TIEMPO_DE_GUARDA       EQU     d'15'

        movf    CONTROL_TECLADO, W
        addwf   PCL, F
        goto    TECLA_APRETADA? ; 0
        goto    VER_OTRA_LINEA  ; 1
        goto    ESPERAR         ; 2
        goto    HAY_TECLA?      ; 3
        goto    VER_OTRA_LINEA  ; 4
        goto    ESPERAR         ; 5
        goto    HAY_TECLA?      ; 6
        goto    VER_OTRA_LINEA  ; 7
        goto    ESPERAR         ; 8
        goto    HAY_TECLA?      ; 9
        goto    VER_OTRA_LINEA  ; 10
        goto    ESPERAR         ; 11
        goto    HAY_TECLA?      ; 12
        goto    CONVERTIR_TECLA ; 13
        goto    ESPERA_QUE_SUELTE ; 14
        goto    TIEMPO_DE_GUARDA  ; 15	
;------------------------------------
TECLA_APRETADA?
        clrf    PORTB
        goto    $+1
        goto    $+1
        movf    PORTB, W
        xorlw   b'11110000'
        btfsc   STATUS, Z       ; Son todos unos?
         goto   Sin_Apretar     ; sip, ninguna apretada.	
        movlw   0x01    ; Lo proximo que hace es empezar a escanear las lineas.
        movwf   CONTROL_TECLADO	
        ;-------------------
        clrf    LINEA   ; Se prepara para barrer el teclado
        movlw   b'11101111'
        movwf   BARRE_PORTB     ; ciclo 14
        ;-------------------
        movlw   d'03'   ; Agrega 1 + 13 + 2 = 16 ciclos
        call    DEMORA
        goto    $+1
        return

Sin_Apretar
        clrf    CONTROL_TECLADO   ; ciclo 11
        ;------------
        movlw   d'04' ; Agrega 1 + 16 + 2
        call    DEMORA
        goto    $+1
        return
;----------------------------------------
VER_OTRA_LINEA
        incf    LINEA, F        ; Escaneare la linea numero....
        bsf     STATUS, C       ; Seleccionar la linea
        rrf     BARRE_PORTB, F  ; 
        movf    BARRE_PORTB, W
        movwf   PORTB
        goto    $+1     ; Restaurar
        goto    $+1     ; Restaurar
        ;movlw  b'01010101'     ; Remover
        ;movwf  TECLA           ; Remover
        ;goto   $+1             ; Remover
        movf    PORTB, W        ; Capturo tecla
        movwf   CODIGO_SCAN
        incf    CONTROL_TECLADO, F      ; Proximo paso: esperar para luego ver si coinciden.
        movlw   d'18'   ; Hare una espera de 1 mseg aprox.
        movwf   RETARDO_TECLADO   ; ciclo 14
        ;-----------
        movlw   d'03'   ; Agrega 1 + 13 + 2 = 16 ciclos
        call    DEMORA
        goto    $+1
        return
;-----------------------------------------
ESPERAR
        decfsz  RETARDO_TECLADO, F
         GOTO   Seguir_Esperando
        incf    CONTROL_TECLADO, F ;ciclo 3
                                   ; Ver que paso despues de
                                   ; la espera
        ;-------------
Seguir_Esperando
        movlw   d'07'   ; Introduce 1 + 25 + 1 = 27 ciclos de demora.
        call    DEMORA	
        nop       
        return
;-----------------------------------------
HAY_TECLA?                              	
        movf    BARRE_PORTB, W  ; 1
        movwf   PORTB           ; 2                                                      
        goto    $+1             ; 4                                             	
        goto    $+1             ; 6                                             	
        movf    PORTB, W        ; 7
        xorwf   CODIGO_SCAN, W  ; 8
        btfss   STATUS, Z       ; 10
         goto   No_Coincide1
        ; --- Pruebo si hay solo una tecla apretada
        clrf    AUX             ; 11
        btfss   CODIGO_SCAN, 7  ; 13
         incf   AUX, F
        btfss   CODIGO_SCAN, 6  ; 15
         incf   AUX, F
        btfss   CODIGO_SCAN, 5  ; 17
         incf   AUX, F
        btfss   CODIGO_SCAN, 4  ; 18
         incf   AUX, F          ; 19
        movlw   0x01            ; 20
        xorwf   AUX, W          ; 21	
        btfss   STATUS, Z       ; 23 Tecla aceptable?
         goto   No_Coincide2
        movlw   _CONVERTIR_TECLA ; 24
        movwf   CONTROL_TECLADO ; 25 Tecla buena!!! Transformarla a ASCII.
        ; Es el segmento de codigo mas largo, lleva 25 ciclos.
        return
No_Coincide1    ; Pruebo la proxima linea.
        incf    CONTROL_TECLADO, F      	
        btfsc   LINEA, 2 ; ¿Ya reviso la cuarta linea?
         clrf   CONTROL_TECLADO ; ciclo 14
                                ; Empezar de nuevo, no detecto teclas buenas.   	
        ;-------------------
        movlw   d'03'
        call    DEMORA  ; agrega 1 + 13 + 2 = 16 ciclos
        goto    $+1
        return
No_Coincide2    ; Pruebo la proxima linea.
        incf    CONTROL_TECLADO, F      	
        btfsc   LINEA, 2 ; ¿Ya reviso la cuarta linea?
         clrf   CONTROL_TECLADO ; Empezar de nuevo, no detecto teclas buenas.	

        ; Este es el segmento mas largo de la parte del teclado: 30 ciclos
        goto    $+1
        nop
        return
;------------------------------------------------
CONVERTIR_TECLA ; Aca veo que valor ASCII le asigno al codigo de scan.	
        btfss   CODIGO_SCAN, 4
        movlw   0x00
        btfss   CODIGO_SCAN, 5
        movlw   0x01
        btfss   CODIGO_SCAN, 6
        movlw   0x02
        btfss   CODIGO_SCAN, 7
        movlw   0x03
        bcf     STATUS, C       ; Multiplico LINEA por 4
        rlf     LINEA, F
        rlf     LINEA, F
        addwf   LINEA, W	
        call    TABLA_ASCII  ; ciclo 14
        MOVWF   TECLA   ; OBTUVO LA TECLA!! ciclo 20
        MOVLW   _ESPERA_QUE_SUELTE ; Esperar que suelte la tecla.
        MOVWF   CONTROL_TECLADO ; ciclo 22
        ;--------------
        MOVLW   d'01'  ; Demora: 1 + 7 = 8 ciclos
        CALL    DEMORA
        RETURN
;-------------------------------------------------
ESPERA_QUE_SUELTE
        clrf    PORTB
        goto    $+1
        goto    $+1
        movlw   0xF0
        xorwf   PORTB, W
        btfss   STATUS, Z
         goto   Sigue_Apretada
        movlw   d'18'
        movwf   RETARDO_TECLADO ; Espero 1 mseg despues que la solto.	
        movlw   _TIEMPO_DE_GUARDA
        movwf   CONTROL_TECLADO  ; ciclo 13
        ;--------------
        movlw   d'04'   ; Demora 1 + 16 = 17
        call    DEMORA
        return
Sigue_Apretada
        ;--------------
        movlw   d'05'  ; Demora 1 + 19 = 20
        call    DEMORA
        return
;-------------------------------------------------
TIEMPO_DE_GUARDA
        decfsz  RETARDO_TECLADO, F
         goto   Espera_otro_poco
        clrf    CONTROL_TECLADO ; ciclo 3
                                ; Pronto para recibir otra tecla.
Espera_otro_poco	
        ;-------------
        movlw   d'07'   ; Demora 1 + 25 + 1 = 27
        call    DEMORA
        nop
        return
;----------------------------------------------

; ----------------- Tabla ASCII ------------
        ORG     0x300
TABLA_ASCII
        bsf     PCLATH, 0
        bsf     PCLATH, 1
        addwf   PCL, F
LIN0                    ; Si llego aca hay error
        RETLW   "."     ; (cuento las lineas a partir de la 1 )
        RETLW   ";"
        RETLW   "-"
        RETLW   "*"
LIN1                    ; Primera linea valida
        retlw   "1"	
        retlw   "2"	
        retlw   "3"	
        retlw   "u"     ; Flecha para arriba
LIN2
        retlw   "4"	
        retlw   "5"	
        retlw   "6"	
        retlw   "d"     ; Flecha para abajo
LIN3
        retlw   "7"	
        retlw   "8"	
        retlw   "9"	
        retlw   "N"     ; Tecla marcada en el teclado como "2ND"
LIN4            	
        retlw   "C"     ; Tecla marcada "CANCEL"
        retlw   "0"	
        retlw   "H"     ; Tecla "HELP"
        retlw   "E"     ; Tecla "ENTER"
        END

;*********************************************
;  ********* FIN ********************************
;    ***********************************************


Interested:

Questions:

See also:

Code:

Comments: