问题
I'm using a proprietary 8051 board to learn assembly programming. I'm currently working on an LCD 'Hello World' program. Here's the code.
lcd_cmd equ 0800h ;Write COMMAND reg address 0800h
lcd_st equ 0801h ;Read STATUS reg address 0801h
lcd_wr equ 0802h ;Write DATA reg address 0802h
lcd_rd equ 0803h ;Read DATA reg address 0803h
ORG 08100h
hello:
mov P2, #(lcd_cmd SHR 8) ;load P2 with high address
mov R0, #(lcd_cmd AND 255) ;load R0 with command reg addr
mov R7, #03h ;set LCD position, line=1, char=3
mov dptr, #mesg1 ;point to mesg1
acall wr_string ;write mesg1 to LCD
mov R7, #41h ;set LCD position, line= 2, char=1
mov dptr, #mesg2 ;point to mesg2
acall wr_string ;write mesg2 to LCD
stop: ajmp stop ;soft halt
wr_string:
acall lcd_busy ;wait until LCD not busy
mov a, R7 ;get LCD position
orl a, #080h ;msb set for LCD RAM address
movx @R0, a ;write lcd_cmd to set line & char
nxt_char:
acall lcd_busy ;wait until LCD not busy
clr a
movc a, @a+dptr
inc dptr ;point to next byte in string
jz str_end ;if 0 then end of string
mov R1, #(lcd_wr AND 255) ;Load R1 with wr_data address
movx @R1, a ;Write char to LCD
sjmp nxt_char ;get next char in string
str_end: ret
lcd_busy:
mov R1, #(lcd_st AND 255) ;Load R1 with status address
movx a, @R1 ;read LCD status
jb acc.7, lcd_busy ;keep checking until busy bit clear
ret
mesg1: db "Hello ",0
mesg2: db "World ",0
END
Everything works fine. However, I'm having trouble outputting a variable to the LCD. Replacing #mesg1 with a hex value (ascii to keep things simple) simply brings up scrambled characters on the screen. So does calling a subroutine which simply increments a value every time, so I'm unsure what format the data should be when it's moved into the dptr.
Anything stupid I've missed?
Thanks!
回答1:
dptr
contains the address
of the text to display. So, if you replace #mesg1
with something like
mov dptr, #045h
you are outputting the (random) contents from memory at address 0x45, which explains the scrambled characters you see.
In order to output some decimal value, you need to convert it to an ascii string first, then you can use your existing wr_string
routine to get it printed. See http://www.electro-tech-online.com/microcontrollers/14371-hex-decimal-then-ascii.html for a code sample (i,j,k contains the result string which you still need to zero-terminate for your wr_string routine).
The following code shows a similar routine.
Note that wr_string
needs to be modified to read the data from XDATA, not from code memory (movx a, @dptr
instead of clr a
/ movc a, @a+dptr
):
ORG 08100h
hello:
mov r7, #42 ; value to convert
mov dptr, #buffer ; destination buffer
acall str2ascii ; convert value
mov P2, #(lcd_cmd SHR 8) ; load P2 with high address
mov R0, #(lcd_cmd AND 255) ; load R0 with command reg addr
mov R7, #03h ; set LCD position, line=1, char=3
mov dptr, #buffer ; point to buffer
acall wr_string ; write buffer to LCD
...
str2ascii:
; Converts a one byte decimal value into its ASCII string representation.
; Result is prepended with leading zeroes.
; 0 becomes "000"
; 42 becomes "042"
; 255 becomes "255"
;
; @param r7 Input value to convert (1 byte, 0 .. 255)
; @param dptr Destination buffer, at 4 bytes (3 digits plus \0)
;
mov a, r7
mov b, #100
div ab ; leftmost digit in a
add a,#30h ; convert to ASCII
movx @dptr, a
inc dptr
mov a,b ; get reminder
mov b,#10
div ab ; middle digit in a, rightmost digit in b
add a,#30h ; convert to ASCII
movx @dptr, a
inc dptr
mov a,b
add a,#30h ; convert to ASCII
movx @dptr,a
inc dptr
mov a,#0
movx @dptr, a ; terminate string
ret
xseg
buffer: ds 17 ; one LCD line plus terminating \0
end
回答2:
On your code, dptr contains the address of your code memory which has the string you want to output. So, if you change #mesg1 to some hex value, like this:
mov dptr, #mesg1
LCD will try to write the ascii value which is on this hex address. And you don't know what it contains. In order to output variables on LCD (just like registers values), you should be attempted to:
1 - You can't store variable data on program memory with DB instruction. It doesn't work. You should write your variable value on internal or external data memory. For example, you can't do this:
MOV dptr, #mesg1 ;point to mesg1 ACALL wr_string ;write mesg1 to LCD ;LOTS OF INSTRUCTIONS... mesg1: DB 'MY STRING' DB R1 ;In case that R1 is your variable
In above example, LCD will only output MY STRING, independently the value of R1
2 - You need to convert the value of your variable (which will be on binary, decimal or hexadecimal) to ASCII. Just for 0 to 9, this is a very easy task. Just add to your variable the hexadecimal value 30H. So, for example:
mov R1, #9H ;in case that R1 is your variable with some number MOV A, R1 ADD A, #30H MOV R1, A ;R1 will store #39H, which is the ascii value for number 9
Thus, an option is to split your variable into individual decimal numbers, and then convert to ascii every one. The code here do this, and stores each ascii value on variables i, j, k.
What I used to, is to use two functions: one for read strings from code memory, which was previously stored by DB instruction, and another to read variables values from internal memory:
lcd_port EQU P1 ;The port that I send data to LCD data EQU #41H ;Some random register in internal data memory MOV DPTR, #my_string ACALL lcd_string ;This function will write on LCD some string MOV A, R1 ;In case that R1 is my variable MOV data, A ACALL lcd_dta ;Writes R1 value on LCD lcd_string: MOV A, #0x00 MOVC A, @A+DPTR JZ end_lcd_string MOV DATA, A CALL lcd_data INC DPTR JMP lcd_string end_lcd_string: RET lcd_data: CALL lcd_busy ;verify if the LCD is busy, just like your function SETB LCD_RS ;Set bit on LCD RS pin SETB LCD_E ;Set bit on LCD Enable pin MOV lcd_port ;Move your data to LCD CLR LCD_E ;Turn LCD Enable pin to 0 RET my_string: DB 'HELLO WORLD'
The above code should output on LCD the string HELLO WORLD, followed by R1 value.
来源:https://stackoverflow.com/questions/14261374/8051-lcd-hello-world-replacing-db-with-variable