Search This Blog

Sunday, February 7, 2010

\/\/00+!!! LCD is Working!

Snowy here in Pennsyltucky.  In between digging out the cars, driveway, mailbox, etc., I managed to get my 16f88 to write to the Hitachi LCD.  I soldered a 16 pin female header to the LCD, mangled a 16 pin male header to fit the breadboard, and got the thing printing.  On the hardware end ( not my strong suit ), I got the stupid contrast set.  Pin 3 on the LCD lets you set the contrast.  Had to do some Googling to find the circuit.  I played around with the values and ended up with 2 1K resistors, one on vdd and one on vss to a 10K pot.  This gave me a nice, crisp display.  The LCD I got displays white text on black.  Definitely need the backlight to see it.  I was able to wire the contrast adjustment and the backlight all onto the same power as the pic and LCD.
    Code bugs were aplenty on this one.  Had cabin fever kids "helping" me every step of the way.  Here is the basic code.  When I figure out how to generate circuit diagrams, I'll post those, too.  This code just reads a table and prints out "Hi Wiley and Maura" on the LCD.  I'm already adding code to allow instructions as well as data.  The screen allows 2 16 char lines to print in small font.  A lot of the routines ( notably delays ) are not used.  This is dev code, folks.  Here it is:
    ;******************************************************************************
;                                                                             *
;      Filename:  helloLcd.asm                                                *                           
;          Date:  2010.01.31                                                  *
;  File Version:  1.0.0                                                       *
;                                                                             *
;        Author:  Tom Hunt                                                    *
;                                                                             *
;******************************************************************************
;  NOTES: Get the parallel LCD working.  Read a string of characters and       *
;         print them on the LCD.                                              *
;          Uses RA0-4, RB7, RA6-7 for the data lines ( RA5 is input only and   *
;          I want to use the built in AUSART on RB2&5.                          *
;                                                                             *
;******************************************************************************
;  CHANGE LOG:                                                                *
;                                                                             *
;                                                                             *
;******************************************************************************

   list      R=DEC, p=16f88           ; list directive to define processor
   #include         ; processor specific variable definitions
    ; _INTRC_IO  _HS_OSC
        ;Program Configuration Register 1
        __CONFIG    _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_IO

        ;Program Configuration Register 2
        __CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF

; '__CONFIG' directive is used to embed configuration data within .asm file.
; The lables following the directive are located in the respective .inc file.
; See respective data sheet for additional information on configuration word.

;******************************************************************************
;  VARIABLE DEFINITIONS                                                       *
;******************************************************************************
#define    Cntrl    PORTB        ; Control Register
#define    RS        0            ; Register select on lcd 1=Data  0=Instruction
#define RW        1            ; Read/Write select         1=Read  0=Write
#define E        3            ; 1= starts data read/write operation

    CBLOCK 0x20
Flags
Count
Byte        ; holds the byte to output
i            ; basic index counter for loop
tIndex        ; table index temp storage
    ENDC

    CBLOCK    0x70     ; interupt context-saving vars here see datasheet chp 15
w_temp
status_temp
pclath_temp
    ENDC

#define    isInstruction    0
#define isLineTwo        1

;******************************************************************************
;  MACRO DEFINITIONS                                                          *
;******************************************************************************
bank0    MACRO
    bcf    STATUS, RP1
    bcf    STATUS, RP0
    ENDM

bank1    MACRO
    bcf    STATUS, RP1
    bsf    STATUS, RP0
    ENDM

;******************************************************************************
;  RESET VECTOR                                                               *
;******************************************************************************
    ORG     0x000             ; processor reset vector
    nop
    nop
    nop
    goto    main              ; go to beginning of program

;******************************************************************************
;  INTERRUPT VECTOR                                                           *
;******************************************************************************
    ORG     0x004             ; interrupt vector location
    MOVWF  w_temp                 ;Copy W to TEMP register
    SWAPF  STATUS, W              ;Swap status to be saved into W
    CLRF   STATUS                 ;bank 0, regardless of current bank, Clears IRP,RP1,RP0
    MOVWF  status_temp            ;Save status to bank zero STATUS_TEMP register
    MOVF   PCLATH, W              ;Only required if using page 1
    MOVWF  pclath_temp            ;Save PCLATH into W
    CLRF   PCLATH                 ;Page zero, regardless of current page
    ;
    ;(ISR)                ;(Insert user code here)
    ;
    MOVF   pclath_temp, W         ;Restore PCLATH
    MOVWF  PCLATH                 ;Move W into PCLATH
    SWAPF  status_temp, W         ;Swap STATUS_TEMP register into W
                                  ;(sets bank to original state)
    MOVWF  STATUS                 ;Move W into STATUS register
    SWAPF  w_temp, F              ;Swap W_TEMP
    SWAPF  w_temp, W              ;Swap W_TEMP into W

    retfie                    ; return from interrupt

;******************************************************************************
;  MAINLINE CODE                                                              *
;******************************************************************************

init
    bank0
    clrf    INTCON                ; For now, clear intcon, disables all interrupts.  We'll change this and
                                ; use the GIE and PEIE peripheral interupt to get writes from USART
    clrf    ADCON0                ;  all digital
    bank1
    movlw    0x00
    movwf    ANSEL                ; go all digital
    movlw    b'01100010'            ; bits for OSCCON internal clock at 4MHz 0110 0000
    movwf    OSCCON^0x080   
    clrf    PIE1^0x080            ; clear peripheral interrupts for now.
    clrf    PIE2^0x080               
    bank0
    return

initLcd
    call    dlay5ms                ; wait 15ms+ to start
    call    dlay5ms
    call    dlay5ms
    call    dlay5ms
    ; This sequence loads values into the D0-7, RS, RW pins and strobes E
    bcf        Cntrl,RS
    bcf        Cntrl,RW
    bsf        PORTB,7
    movlw    b'00010000'            ; value of data 'byte' is 00110000
    movwf    PORTA
    bsf        Cntrl,E
    bcf        Cntrl,E
   
    call    dlay5ms                ; a wait period is needed. For this, we need 5ms
    bsf        Cntrl,E
    bcf        Cntrl,E                    ; strobe lcd again with same function set
    call    dlay150us            ; a wait period of +100us is needed
    bsf        Cntrl,E
    bcf        Cntrl,E                    ; strobe lcd 3rd time with same function set
    call    dlay5ms

    movlw    b'00111000'            ; 0x38 function set for 2 lines, small font
    movwf    PORTA
    bsf        PORTB,7
    bsf        Cntrl,E
    bcf        Cntrl,E
    call    dlay5ms

    ; display off
    movlw    b'00001000'            ; 0x08
    movwf    PORTA
    bcf        PORTB,7
    bsf        Cntrl,E
    bcf        Cntrl,E
    call    dlay5ms

    ; clear display 
    movlw    b'00000001'            ; 0x01
    movwf    PORTA
    bcf        PORTB,7
    bsf        Cntrl,E
    bcf        Cntrl,E
    call    dlay5ms

    ; entry set incr data and no shift
    movlw    b'00000110'           
    movwf    PORTA
    bcf        PORTB,7
    bsf        Cntrl,E
    bcf        Cntrl,E
    call    checkLcdBusy        ; now we can poll the busy flag   

    ; turn on display
    movlw    b'00001110'            ; 0xE - display and cursor on, blink off
    movwf    PORTA
    bcf        PORTB,7
    bsf        Cntrl,E
    bcf        Cntrl,E
    call    checkLcdBusy
    return

initInput
    bank1
    bsf        TRISB^0x080,7
    movlw    0xFF        ; set PORTA to Input
    movwf    TRISA ^ 0x080
    bank0
    clrf    PORTA        ; clear PORTA for clean read
    return   

initOutput
    bcf        Cntrl,RS
    bcf        Cntrl,RW
    bcf        Cntrl,E
    bcf        PORTB,7
    bcf        PORTB,4
    clrf    PORTA            ; clear all output pins
    movlw    b'01100100'
    bank1
    movwf    TRISB ^0x080    ; enable B,7 & B0-1,3 for output B4 for debug
    movlw    b'00100000'        ; set PORTA to Output except input only A,5
    movwf    TRISA ^ 0x080
    bank0
    return

dlay5ms
    movlw    b'00000100'            ; set up tmr0 with prescaler 16 ( for 5ms delay )
    bank1
    movwf    OPTION_REG^0x080
    bank0
    movlw    0x64                ; value 100 for tmr0. with prescaler 16 gives 5ms
    movwf    TMR0
    movf    TMR0, W
    btfss    STATUS, Z            ; wait for 5ms timeout
        goto    $ - 2
    return

dlay2ms
    movlw    b'00000010'            ; set up tmr0 with prescaler 4 ( for 2ms delay )
    bank1
    movwf    OPTION_REG^0x080
    bank0
    movlw    0x006                ; value 6 for tmr0. with prescaler 16 gives 5ms
    movwf    TMR0
    movf    TMR0, W
    btfss    STATUS, Z            ; wait for 2ms timeout
        goto    $ - 2
    return

dlay150us
    movlw    b'00000000'            ; set prescaler to 1:1
    bank1
    movwf    OPTION_REG^0x080
    bank0
    movlw    0xB5                ; value of 181 for tmr0 with prescaler 0 gives 150us
    movwf    TMR0
    movf    TMR0, W
    btfss    STATUS, Z
        goto    $ - 2
    return

dlay50us
    movlw    b'00000000'            ; set prescaler to 1:1
    bank1
    movwf    OPTION_REG^0x080
    bank0
    movlw    0xE7                ; value of 231 for tmr0 with prescaler 0 gives 50us
    movwf    TMR0
    movf    TMR0, W
    btfss    STATUS, Z
        goto    $ - 2
    return

; change ddram address and cursor position to line1 or 2
; based on the isLineTwo flag in Flags
changeLine
    call    checkLcdBusy
    bcf        Cntrl,RW                    ; write operation
    bcf        Cntrl,RS                    ; instruction
    bcf        PORTB,7
    clrf    PORTA
    btfsc    Flags,isLineTwo
        bsf    PORTA,7
    btfsc    Flags,isLineTwo
        bsf    PORTA,6
    bsf        Cntrl,E
    bcf        Cntrl,E
    return

processByte                        ; for now, this is only a write operation
    call    checkLcdBusy
    bcf        Cntrl,RW                    ; write operation
    bsf        Cntrl,RS                    ; data
    movf    Byte,W
    movwf    PORTA
    bcf        PORTB,7
    btfsc    Byte,5
        bsf    PORTB,7
    bsf        Cntrl,E
    bcf        Cntrl,E
    return

checkLcdBusy
    call    initInput
    bsf        Cntrl,RW                    ; set for read ( input = 1 )
    bcf        Cntrl,RS
    bsf        Cntrl,E
    movf    PORTA,W
    bcf        Cntrl,E
    btfsc    PORTA,7                    ; test if D7 is busy
        goto    $ - 4
    bcf        Cntrl,RW
    call    initOutput
    return

debugging                    ; call to blink an led on B4 where needed for debug
    bsf        PORTB,4
    movlw    d'100'            ; c. half second on
    movwf    Count
    call    dlay5ms
    decfsz    Count,F
        goto    $ - 2
    bcf        PORTB,4
    movlw    d'100'            ; c. half second off
    movwf    Count
    call    dlay5ms
    decfsz    Count,F
        goto    $ - 2
    return

main   
    call    init
    call    initOutput
    call    initLcd

    movlw    0
    movwf    i

sayHi   
    call    table
    iorlw    0
    btfsc    STATUS, Z        ; if end of table, end
        goto done
    movwf    Byte
    call    processByte
    incf    i, F
    movf    i, W            ; increment index
    goto    sayHi

table
    movwf    tIndex            ; save table index
    movlw    HIGH tableEntries    ; get the current high page
    movwf    PCLATH            ; put this in PCLATH to move us to the right page
    movf    tIndex, W        ; move the index asked for into w
    addlw    LOW tableEntries ; compute the offset for the block
    btfsc    STATUS, C        ;
        incf    PCLATH, F    ; if in next page, increment PCLATH
    movwf    PCL                ; put the correct addr in PCL
tableEntries
    dt        "Hi Wiley & Maura\0"

done
    goto    $


END                          ; directive 'end of program'
 

    Now that this is working, the next step is to add instructions.  If the code receives 0x80, it knows that the next byte will be an instruction.  It will clear the RS to allow an instruction write rather than a data write.
    Once that works, I'll start adding code to set up the USART to receive data from the PC.  I'll need to configure the pic, wire in a Maxim 3232 facsimile, and write some Python code to send the data.  I'm going for a software protocol, here, limiting wires to 2.  I'm thinking that the pic will initialize itself and wait for a handshake.  The Python code will send a request to send code ( maybe 0x82 ? ).  The pic will acknowledge ( maybe 0x83 ? ).  Then the Python code will start sending bytes.  For the first round, I'll just send text, then change to line 2, then more text, then a finished code ( maybe 0x84 ? ).  or just not send any more.  A timeout could work.  I'm not sure yet.
    All this is in preparation to build bootloading code.  All this is in preparation to build a bootloader for the 16f88.  I want the PC to send a request to send repeatedly up to a timeout.  The pic will be fired up, initialize, read the line and see the request to send.  It will acknowledge and go into wait to receive mode.  The PC will send code and receive an echo of the sent byte.  I'll want some error handling and resend code, too.  Ahhh, so much to do, so much time to do it.  That's the beauty of a hobby.
    For now, I'm just dancing about seeing "Hello World" on my LCD.  It doesn't seem to take much to make me happy.  "Couldn't you just...."  It's amazing how much it takes to satisfy those simple words....

No comments:

Post a Comment