8051 LCD 'Hello World' - замена DB на переменную

Я использую проприетарную плату 8051 для изучения программирования на ассемблере. В настоящее время я работаю над программой «Hello World» для LCD. Вот код.

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

Все работает нормально. Однако у меня возникли проблемы с выводом переменной на ЖК-дисплей. Замена #mesg1 шестнадцатеричным значением (для простоты ascii) просто выводит на экран зашифрованные символы. То же самое можно сказать и о вызове подпрограммы, которая каждый раз просто увеличивает значение, поэтому я не уверен, в каком формате должны быть данные, когда они перемещаются в dptr.

Что-нибудь глупое, что я пропустил?

Спасибо!


person Joe of Loath    schedule 10.01.2013    source источник


Ответы (2)


dptr содержит address текста для отображения. Итак, если вы замените #mesg1 чем-то вроде

mov dptr, #045h

вы выводите (случайное) содержимое из памяти по адресу 0x45, что объясняет зашифрованные символы, которые вы видите.

Чтобы вывести какое-то десятичное значение, вам нужно сначала преобразовать его в строку ascii, а затем вы можете использовать существующую процедуру wr_string, чтобы распечатать ее. См. http://www.electro-tech-online.com/microcontrollers/14371-hex-decimal-then-ascii.html для примера кода (i,j,k содержат результирующую строку, которую вам все еще нужно завершить нулем для вашей процедуры wr_string).


В следующем коде показана аналогичная процедура. Обратите внимание, что wr_string необходимо изменить для чтения данных из XDATA, а не из памяти кода (movx a, @dptr вместо 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
person Andreas Fester    schedule 10.01.2013
comment
Спасибо, первый бит теперь имеет смысл. Однако я изо всех сил пытаюсь понять, в каком регистре должны быть данные, чтобы wr_string мог их вывести. Акк? - person Joe of Loath; 10.01.2013
comment
Я добавил пример кода (раньше у меня не было среды 8051, и я не хотел публиковать непроверенный код :-)) Код предполагает, что ваша плата содержит некоторую внешнюю память. (Десятичные) данные должны быть в формате r7 для преобразования. wr_string по-прежнему читает из @dptr, но его нужно изменить для чтения из XDATA (не обязательно, если xdata идентичен code ram, но это зависит от вашей платы). Кстати: я использовал эту IDE, которая включает симулятор 8051: mcu8051ide.sourceforge.net, на всякий случай до сих пор ищут... - person Andreas Fester; 11.01.2013
comment
Это полезно, спасибо! В настоящее время у меня нет доступа к плате, чтобы протестировать ее, но я думаю, что могу понять, что происходит сейчас. - person Joe of Loath; 11.01.2013

В вашем коде dptr содержит адрес вашей памяти кода, в которой есть строка, которую вы хотите вывести. Итак, если вы измените #mesg1 на какое-то шестнадцатеричное значение, например:

mov dptr, #mesg1

LCD попытается записать значение ascii, которое находится на этом шестнадцатеричном адресе. И вы не знаете, что в нем содержится. Чтобы вывести переменные на ЖК-дисплей (так же, как значения регистров), вы должны попытаться:

1 - Вы не можете хранить переменные данные в памяти программы с инструкцией DB. Это не работает. Вы должны записать значение переменной во внутреннюю или внешнюю память данных. Например, вы не можете сделать это:

   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          

В приведенном выше примере ЖК-дисплей будет выводить только MY STRING, независимо от значения R1.

2 - Вам нужно преобразовать значение вашей переменной (которая будет двоичной, десятичной или шестнадцатеричной) в ASCII. Просто для 0 до 9, это очень простая задача. Просто добавьте к вашей переменной шестнадцатеричное значение 30H. Так, например:

    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

Таким образом, вариант состоит в том, чтобы разбить вашу переменную на отдельные десятичные числа, а затем преобразовать в ascii каждое из них. код здесь делает это, и сохраняет каждое значение ascii для переменных i, j, k.

Раньше я использовал две функции: одну для чтения строк из памяти кода, которая ранее была сохранена инструкцией БД, а другую для чтения значений переменных из внутренней памяти:

    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'

Приведенный выше код должен вывести на ЖК-дисплей строку HELLO WORLD, за которой следует значение R1.

person José Augustinho    schedule 28.10.2015