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
; _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