Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] ASM: LCD to PIC 4-bit writing to screen problem

Status
Not open for further replies.

picmonkey122

Newbie level 4
Joined
Jul 10, 2014
Messages
6
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
43
Hi everyone,

I have a problem with writing to a hitachi hd44780 16x2 display using the PIC16F1459. I have written several programs in C for this particular arrangement so I know from this that the wiring, LCD and PIC are all working correctly. I decided to switch over to writing in assembly because I wanted more control over the PIC. I am not a beginner with assembly would say I have a moderate understanding and have searched around a lot of forums and websites to better understand writing assembly for PIC's. However I cant seem to get the LCD to print out the correct characters. The code attached below initialises the LCD correctly - 16 boxes over 2 lines but when I want to write to it I can only get the LCD to print out strange characters (see attached photo).


IMG_20140710_181808.jpg

Code:
;***************************************
; date: Tue 8th July                    * 
; version: 1.0                             *
; filename: LCD                          *
; PIC: p16f1459                         *
; clock frequency: 4MHz            *
; cty: 1 us                                 *
;**************************************

;=================
; Config Settings

        list    P=16f1459
        include "p16f1459.inc"

; CONFIG1
; __config 0x3FE4
 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_ON & _FCMEN_ON
; CONFIG2
; __config 0x1FFF
 __CONFIG _CONFIG2, _WRT_OFF & _CPUDIV_CLKDIV6 & _USBLSCLK_48MHz & _PLLMULT_3x & _PLLEN_ENABLED & _STVREN_ON & _BORV_LO & _LPBOR_OFF & _LVP_OFF




;======================
; Declarations

#define     LCD_PORT    LATC
#define     LCD_RS      LATC,4
#define     LCD_E       LATC,5

; RW is grounded
; D4 ----> RC0
; D5 ----> RC1
; D6 ----> RC2
; D7 ----> RC3

;=====================
; Variable Definitions

        cblock  H'20'
        count
        delay_1
        delay_2
        delay_3
        tempLCD
        endc

;==============
; Reset Vector

        org   0x0000
        goto  Start

Init
;====================
; Disable comparators

        bcf     CM1CON0,C1ON    ; clear C1ON
        bcf     CM2CON0,C2ON    ; clear C2ON

;========================
;Set Oscillator frequency

        banksel OSCCON
        movlw   B'11110111'     ; 4MHz internal oscillator ; cty = 1us
        movwf   OSCCON

        banksel TRISC           ; select LCD bus
        movlw   B'00000000'     ; all pins output
        movwf   TRISC

        banksel LATC
        clrf    LATC
        banksel ANSELC
        clrf    ANSELC

        ; initialisation delay - 1 sec
        movlw   D'100'
        call    delay_long

        return

;============================================
; Delay subroutine which lasts for 100 * cty

delay_long
        movwf   delay_3
delay_long_loop2
        movlw   D'13'
        movwf   delay_2
        clrf    delay_1
delay_long_loop1
        decfsz  delay_1,f
        goto    delay_long_loop1
        decfsz  delay_2,f
        goto    delay_long_loop1
        decfsz  delay_3,f
        goto    delay_long_loop2

        return

;=========================
; Delay subroutine 10 * cty

delay_short
        movwf   delay_2
delay_short_loop2
        movlw   D'249'
        movwf   delay_1
delay_short_loop1
        nop
        decfsz  delay_1,f
        goto    delay_short_loop1
        decfsz  delay_2,f
        goto    delay_short_loop2

        return

;==================
;LCD Initialisation

LCD_Init
        banksel LATC
        bcf     LCD_RS          ; commands so RS bit low

        movlw   D'16'
        call    delay_short
        banksel LATC
        movlw   0x03
        movwf   LCD_PORT
        call    Nibble
        movlw   D'6'
        call    delay_short
        banksel LATC
        movlw   0x03
        movwf   LCD_PORT
        call    Nibble
        movlw   D'1'
        call    delay_short
        banksel LATC
        movlw   0x03
        movwf   LCD_PORT
        call    Nibble
        movlw   D'5'
        call    delay_short

        banksel LATC
        movlw   0x20            ; set 4 bit mode
        movwf   LCD_PORT
        call    Nibble
        movlw   0x28            ; 2 lines, 5 x 7 font
        call    LCD_Cmd
        movlw   0x08            ; display switch: D = 0, C = 0, B = 0
        call    LCD_Cmd
        movlw   0x0F            ; display switch: D = 1, C = 1, B = 1
        call    LCD_Cmd
        call    LCD_Clear       ; clear display
        movlw   0x06            ; input set: I/D = 1, S = 0
        call    LCD_Cmd


        return

;============
; Send nibble

Nibble
        banksel LATC
        bsf     LCD_E           ; high
        movlw   D'1'
        call    delay_short
        banksel LATC
        bcf     LCD_E           ; low
        movlw   D'2'
        call    delay_short

        return

;============
; LCD Command

LCD_Cmd
        movwf   tempLCD
        swapf   tempLCD         ; higher nibble to W
        movfw   tempLCD
        andlw   0xF0
        banksel LATC
        bcf     LCD_RS          ; RS set low due to instruction write
        banksel LATC
        movwf   LCD_PORT
        call    Nibble
        movlw   D'2'
        call    delay_short

        swapf   tempLCD         ; lower nibble to W
        movfw   tempLCD
        andlw   0xF0
        banksel  LATC
        bcf     LCD_RS
        banksel LATC
        movwf   LCD_PORT
        banksel  LATC
        bcf     LCD_RS
        call    Nibble
        movlw   D'10'
        call    delay_short

        return

;==========
; LCD Write

LCD_Write
        movwf   tempLCD
        swapf   tempLCD         ; higher nibble to W
        movfw   tempLCD
        andlw   0xF0
        banksel LATC
        bsf     LCD_RS          ; RS set high due to data write
        banksel LATC
        movwf  LCD_PORT
        call   Nibble
        movlw   D'2'
        call    delay_short

        swapf   tempLCD         ; lower nibble to W
        movfw   tempLCD
        andlw   0xF0
        banksel LATC
        bsf     LCD_RS
        banksel LATC
        movwf   LCD_PORT
        call    Nibble
        movlw   D'10'
        call    delay_short

        return

;===========
; LCD line 1

LCD_Line1
        movlw   0x80
        call    LCD_Cmd

        return

;===========
; LCD line 2

LCD_Line2
        movlw   0xC0
        call    LCD_Cmd

        return

;==============
; Clear Command

LCD_Clear
        movwf   0x01
        call    LCD_Cmd

        return

;=============
; Program main
Start
        call    Init
        call    LCD_Init

Main
        movlw   0x50        ;P
        call    LCD_Write
        movlw   0x49        ;I
        call    LCD_Write
        movlw   0x43        ;C
        call    LCD_Write
        goto    $           ; loop forever

        end

I have a few ideas what might be wrong the obvious one is the LCD_Write subroutine also I'm wondering whether the PIC is correctly initialised. I would be grateful if you could look over the code.

Thanks.
 

Is there LATx in PIC16F devices ? I guess LATx are available only in PIC18F devices.
 

Hi.

Yes on the PIC16F1454/5/9 datasheet there are 3 LATx registers a,b and c in bank 2 of the memory map. Just to check I changed LATC to PORTC in my code and tried that but the same problem still occurs.
 

Looking at your code, In the write and command routines You do the first swapf to move the high nibble to the low nibble and then you incorrectly mask it out you should use 0xFH. The second swapf instruction is not needed and you make the same mistake again with the mask value. There maybe other errors in the code and your LCD initialization seems to be OK.
 
Last edited:

The above two part tutorial is very good and is what I used when I first used LCD.s, I still refer to it whenever I need to do any LCD programming.
 

Hi.

Thanks for your replies I will look through them in detail tomorrow and try out your suggestions. Will post my progress aswell.

Thanks.
 

Hi everyone.

Thank you for all your suggestions, links and help I have had success and now have a fully functioning LCD display using assembly code!

IMG_20140712_140346.jpg

Code:
;*************************************
; written by: pic122                 *
; date: Tue 8th July                 *
; version: 1.0                       *
; date: Sat 12th July                *
; version: 2.0 - working             *
; filename: LCD_Using_Delays         *
; PIC: p16f1459                      *
; clock frequency: 4MHz              *
; cty: 1 us                          *
;*************************************

;=================
; Config Settings

        list    P=16f1459
        include "p16f1459.inc"

; CONFIG1
; __config 0x3FE4
 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_ON & _FCMEN_ON
; CONFIG2
; __config 0x1FFF
 __CONFIG _CONFIG2, _WRT_OFF & _CPUDIV_CLKDIV6 & _USBLSCLK_48MHz & _PLLMULT_3x & _PLLEN_ENABLED & _STVREN_ON & _BORV_LO & _LPBOR_OFF & _LVP_OFF




;======================
; Declarations

#define     LCD_PORT    LATC
#define     LCD_RS      LATC,4
#define     LCD_E       LATC,5


; RW is grounded
; D4 ----> RC0
; D5 ----> RC1
; D6 ----> RC2
; D7 ----> RC3

;=====================
; Variable Definitions

        cblock  H'20'
        delay_1
        delay_2
        delay_3
        tempLCD
        endc

;==============
; Reset Vector

        org   0x0000
        goto  Start

Init
;====================
; Disable comparators

        bcf     CM1CON0,C1ON    ; clear C1ON
        bcf     CM2CON0,C2ON    ; clear C2ON

;========================
;Set Oscillator frequency

        banksel OSCCON
        movlw   B'00110111'     ; 4MHz internal oscillator ; cty = 1us
        movwf   OSCCON

;=====================
; Set up port and pins

        banksel TRISC           ; select TRISC bus
        movlw   B'00000000'     ; all pins output
        movwf   TRISC

        banksel LATC            ; select LATC and clear
        clrf    LATC

        banksel ANSELC          ; set all pins to digital
        clrf    ANSELC

        return

;============================================
; Delay subroutine which lasts for 100 * cty

delay_long
        movwf   delay_3
delay_long_loop2
        movlw   D'13'
        movwf   delay_2
        clrf    delay_1
delay_long_loop1
        decfsz  delay_1,f
        goto    delay_long_loop1
        decfsz  delay_2,f
        goto    delay_long_loop1
        decfsz  delay_3,f
        goto    delay_long_loop2

        return

;============================
; Delay subroutine 1000 * cty

delay_short
        movwf   delay_2
delay_short_loop2
        movlw   D'249'
        movwf   delay_1
delay_short_loop1
        nop
        decfsz  delay_1,f
        goto    delay_short_loop1
        decfsz  delay_2,f
        goto    delay_short_loop2

        return

;==================
;LCD Initialisation

LCD_Init
        movlw   D'5'
        call    delay_long

        banksel LATC
        bcf     LCD_RS          ; commands so RS bit set low
        movlw   D'16'           ; must be more than 15ms
        call    delay_short
        banksel LATC
        movlw   0x03            ; send first high nibble
        movwf   LCD_PORT
        call    Nibble
        movlw   D'6'            ; must be more than 4.1ms
        call    delay_short
        banksel LATC
        movlw   0x03            ; send second high nibble
        movwf   LCD_PORT
        call    Nibble
        movlw   D'1'            ; must be more than 100us
        call    delay_short
        banksel LATC
        movlw   0x03            ; send third high nibble
        movwf   LCD_PORT
        call    Nibble
        movlw   D'5'            ; must be more than 5ms
        call    delay_short

        banksel LATC
        movlw   0x02            ; set 4 bit mode
        movwf   LCD_PORT
        call    Nibble
        movlw   0x28            ; function set: 4 bit, 2 lines, 5 x 7 font
        call    LCD_Cmd
        movlw   0x08            ; display switch: D = 0, C = 0, B = 0
        call    LCD_Cmd
        call    LCD_Clear
        movlw   0x0C            ; display switch: D = 1, C = 0, B = 0
        call    LCD_Cmd

        movlw   0x06            ; input set: I/D = 1, S = 0
        call    LCD_Cmd

        banksel LATC            ; make sure LATC port is clear
        clrf    LATC

        movlw   D'50'           ; initialisation delay
        call    delay_long

        return

;============
; LCD Command

;****************************
;                           *
; RS   ___________________  *
;                           *
; R/W  ___________________  *
;              __           *
; E    _______/  \________  *
;         _____________     *
; D4/7 __/   Command   \__  *
;                           *
;****************************

LCD_Cmd
        movwf   tempLCD           ; move contents of w into tempLCD
        swapf   tempLCD,w         ; swap contents of tempLCD put result in w
        andlw   0xf               ; select high nibble
        banksel LATC
        movwf   LCD_PORT
        banksel LATC
        bcf     LCD_RS            ; RS set low due to instruction write
        call    Nibble
        movlw   D'1'
        call    delay_short

        movfw   tempLCD           ; move contents of tempLCD into w
        andlw   0xf               ; select low nibble
        banksel LATC
        movwf   LCD_PORT
        banksel LATC
        bcf     LCD_RS            ; RS set low due to instruction write
        call    Nibble
        movlw   D'1'
        call    delay_short

        return

;==========
; LCD Write

;****************************
;         ________________  *
; RS   __/                  *
;                           *
; R/W  ___________________  *
;              __           *
; E    _______/  \________  *
;         _____________     *
; D4/7 __/    Data     \__  *
;                           *
;****************************

LCD_Write
        movwf   tempLCD           ; move contents of w into tempLCD
        swapf   tempLCD,w         ; swap contents of tempLCD put result in w
        andlw   0xf               ; select high nibble
        banksel LATC
        movwf   LCD_PORT
        banksel LATC
        bsf     LCD_RS            ; RS set high due to instruction write
        call    Nibble
        movlw   D'1'
        call    delay_short

        movfw   tempLCD           ; move contents of tempLCD into w
        andlw   0xf               ; select low nibble
        banksel LATC
        movwf   LCD_PORT
        banksel LATC
        bsf     LCD_RS            ; RS set high due to instruction write
        call    Nibble
        movlw   D'5'              ; vary this delay to control writing speed
        call    delay_long
        return

;===========================
; Send nibble - toggle E pin

Nibble
        banksel LATC
        bsf     LCD_E             ; E set high
        movlw   D'1'
        call    delay_short
        banksel LATC
        bcf     LCD_E             ; E set low
        movlw   D'2'
        call    delay_short

        return

;===========
; LCD line 1

LCD_Line1
        movlw   0x80
        call    LCD_Cmd

        return

;===========
; LCD line 2

LCD_Line2
        movlw   0xC0
        call    LCD_Cmd

        return

;==============
; Clear Command

LCD_Clear
        movlw   0x01
        call    LCD_Cmd

        return

;============
; Write Space

LCD_Space
        movlw   0x20
        call    LCD_Write

        return

;=============
; Program main

Start
        call    Init
        call    LCD_Init

Main
        call    LCD_Clear
        call    LCD_Line1

Top_Line
        movlw   'H'
        call    LCD_Write
        movlw   'e'
        call    LCD_Write
        movlw   'l'
        call    LCD_Write
        movlw   'l'
        call    LCD_Write
        movlw   'o'
        call    LCD_Write
        call    LCD_Space
        movlw   'W'
        call    LCD_Write
        movlw   'o'
        call    LCD_Write
        movlw   'r'
        call    LCD_Write
        movlw   'l'
        call    LCD_Write
        movlw   'd'
        call    LCD_Write
        movlw   D'255'
        call    delay_long

Bottom_Line
        call    LCD_Line2
        movlw   'I'
        call    LCD_Write
        movlw   't'
        call    LCD_Write
        movlw   0X27
        call    LCD_Write
        movlw   's'
        call    LCD_Write
        call    LCD_Space
        movlw   'p'
        call    LCD_Write
        movlw   'i'
        call    LCD_Write
        movlw   'c'
        call    LCD_Write
        movlw   '1'
        call    LCD_Write
        movlw   '2'
        call    LCD_Write
        movlw   '2'
        call    LCD_Write
        movlw   D'255'
        call    delay_long

        goto    $                  ; loop forever

        end


I also have successfully written another bit of code which uses the busy flag rather than delays and am going to incorporate a lookup table.

Regards.
 

Hi,

Well done, its always good to see your lcd come alive.

Not sure what you mean by adding a lookup table, to the lcd routine ?

You can advance your existing routine by introducing 32 user registers to hold the data to be displayed, see the attached sample coded.
With this its easy to call predefined messages and update the registers /display , eg when reading a temperature etc.

Looking into the CGRAM is interesting where you can create, store and call your own custom characters/icons
 

Attachments

  • 877alcd.zip
    3.2 KB · Views: 57
Another improvement to your code is to enable the 4 unused lines of your port to be used. At the moment the 4 lines are written to so you cannot use them for anything else. To do this read the data in from the port, set the bottom 4 bits to zero, (mask with andwf 0xf0) and OR with the data you want to send to the LCD.
 

Not sure what you mean by adding a lookup table

Thank you for the example that is what I meant but I didnt make it clear. I dont want all of the data Im sending to the LCD in main.

- - - Updated - - -

Another improvement to your code is to enable the 4 unused lines of your port to be used.

I dont fully understand what you mean by this because I have the RS and E lines also on the port.
 

Thank you for the example that is what I meant but I didnt make it clear. I dont want all of the data Im sending to the LCD in main.

- - - Updated - - -



I dont fully understand what you mean by this because I have the RS and E lines also on the port.

Hi,

You can also send data out by 'Strings', a little more complicated but you can see the idea in this bit of code which is from 18F assembler, you might be able to get it to work on the 16F .

This Table Pointer method can also be used to send basic text and bitmap images to larger glcds eg 128x64 bit

Think PMJ was thinking you were using a different port for the control lines.
However to have access to the remaining 2 unused pins is possible but can be a pain, so would avoid unless you need them desperately; often easier just to more to a large chip.


Code:
;******************************************************************************

; 	Example 2    Sending out two rows of data held in 'Strings' 

; 	here two strings are placed directly in the code, 


string_example

	    movlw	0x80				; add address for top line
		call    lcd_c
        call    PutString       
        dw      "String Top",0      ; string must be terminated with a 0 byte
	    movlw	0xC0				; add address for bottom line
		call    lcd_c
        call    PutString       
        dw      "String Bottom...",0   ; string must be terminated with a 0 byte

		call	delay1sec			; finished display, little pause
		call	delay1sec
		call	delay1sec
		call	delay1sec
		call	delay1sec

  
		goto  	string_example3


; the PutString sub-routine by Mike McClaren, K8LH
; https://www.electro-tech-online.com/microcontrollers/94132-new-18f-memory-look-up-table-questions.html
	
;  PutString - print in-line string via Stack and TBLPTR
;
;  string must be terminated with a 0 byte and does not need
;  to be word aligned



PutString
        movff   TOSL,TBLPTRL    	; copy return address into TBLPTR
        movff   TOSH,TBLPTRH    
        clrf    TBLPTRU         	; assume PIC with < 64-KB
PutNext
        tblrd   *+              	; get in-line string character
        movf    TABLAT,W        	; last character (0)?
        bz      PutExit         	; yes, exit, else
        call   	lcd_d        		; print character
        bra     PutNext         	; and do another
PutExit
        btfsc   TBLPTRL,0       	; odd address?
        tblrd   *+              	; yes, make it even (fix PC)
        movf    TBLPTRH,W       	; setup new return address
        movwf   TOSH            	
        movf    TBLPTRL,W       
        movwf   TOSL            
        return                  	; to 1st instruction after the string table



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

; 	Example 3    Sending out a Line of String data from anywhere in your code using a Macro
;   Macros PutStr1 and 2 placed at the top of your main code, must come before they are calld


string_example3  nop

PutStr1	macro   string1
	    movlw	0x80				; add address for Top line
		call    lcd_c
        call    PutString       
        dw      string1,0        	; null terminated in-line string table
        endm   

PutStr2	macro   string2
	    movlw	0xC0				; add address for top line
		call    lcd_c
        call    PutString       
        dw      string2,0        	; null terminated in-line string table
        endm   



; anywhere in you main code you can output a row  data


		nop		

        PutStr1  "Line1 Again....."         

		call	delay1sec
		call	delay1sec	
       
        PutStr2  "Line2 later....."      

		call	delay1sec
		call	delay1sec
		call	delay1sec
		call	delay1sec
 

I dont fully understand what you mean by this because I have the RS and E lines also on the port

Whenever you send data to the LCD you also reset the 4 other (high) bits of the port to a logic zero. You have the RS and E on these bits, so that when you change the LCD data, these lines are set to zero. This does not matter in this case, but if you wanted to use the other two bits it may do so.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top