Hex-Editor           

von  Thomas Baum                      
Elektronik-Labor  Projekte   AVR   T13-Contest

 

German and English, translated by Jürgen Pintaske:   HEX EDITOR v1.pdf

Inspiriert von der "TPS" und der händigen Programmierung aus dem "Assembler-Kurs" habe ich die Tage mal meinen Hexdump-Code zu einem rudimentären aber nutzbaren Hex-Editor aufgebohrt. Damit lässt sich der kleine AVR in Verbindung mit einem SPI-Nokia-Display ohne sonstige Hardware programmieren. Nicht einfach aber dafür mit vollem Zugriff auf die Hardware.

Verwendete Hardware:
Attiny13a
Nokia 5510 Display

Bedienung:
Editormodus starten:    Port 0 beim Start auf Masse ziehen
Eingabe:        kurz-weiter; lang-bestätigen
Organisation:        Seitenwahl->Zeilenwahl->Wortwahl->Worteingabe->Speichern
Abbruch:        Stromversorgung trennen


Aufbau und Funktion:
Der Programmcode des Editors befindet sich am Ende des Speichers. Das hat den Vorteil, dass man mit dem eigenen Programm auch die Interruptvektortabelle definieren kann. Der Befehlszähler beginnt jedoch immer bei 0. Deshalb ist der erste Befehl ein Sprung in den Programmcode des Editors. Zur Vereinfachung zeigt dieser auf die letzte Adresse im Speicher, die man sich beim Überschreiben des Resetinterrupts besser merken kann, will man nochmal beim Neustart in den Editormodus. :)
Selbst geschriebene Programme können nach dem ersten Wort angefügt werden, oder man kümmert sich selbst in seinem Code um den Aufruf des Editors. Die einzige Beschränkung gegenüber einem blanken Attiny13 ist:
- nur noch 25% freier Programmspeicher verfügbar.

Wichtig: In den Fusebits muss die Selbstprogrammierung aktiviert werden. (Fuse 6AEF statt 6AFF)
Auf eine Änderung des Reset Pins wurde bewusst verzichtet. Damit lässt sich der Controller auch noch per ISP programmieren.



Youtube-Video: http://youtu.be/_kgd4lg4PgM

Download:  avr-hex-editor.zip

;Onboard AVR-Hex-Editor (attiny13) Thomas Baum (th.baum@online.de)
;Display Nokia 5510
;             ____
;            -|  |-VCC (3V)
;    LCD_SDIN-|  |-LCD_SCE
;    LCD_SCLK-|  |-LCD_DC
;    GND     -|__|-INPUT(Button nach Masse)
;
;kurz-weiter; lang-bestätigen
;Seitenwahl->Zeilenwahl->Wortwahl->Worteingabe->Speichern

.device ATtiny13A            ;Fuse 6AEF

.equ    INPUT        = 0        ;Pinbelegung
.equ    LCD_DC        = 1
.equ    LCD_SCE        = 2
.equ    LCD_SDIN    = 3
.equ    LCD_SCLK    = 4

.org 0x0000
flash_start:
        rjmp    flash_end        ;Sprung zum Editor

user_prog:                ;Userspace
;---------------------------------------
;---------------Userspace---------------
;---------------------------------------
;---
;---------------------------------------               
.org 0x0080                ;Startadresse Editorcode
    rjmp    user_prog        ;Lauf in Editor verhindern   
init:   
    ldi    r16, RAMEND        ;Stackinitalisierung
    out    spl, r16   
    sbi    PORTB, INPUT        ;Pullup definieren
    cbi    DDRB, INPUT        ;Definition der Ports
    sbi    DDRB, LCD_DC
    sbi    DDRB, LCD_SCE
    sbi    DDRB, LCD_SDIN
    sbi    DDRB, LCD_SCLK

start_prog:                ;Startmenu
    rcall    delay
    in    r16, PINB        ;Pin0 abfragen
    andi    r16, 0x01
    cpi    r16, 0x00        ;ist Pin0 0?
    brne    start_user_prog        ;wenn nicht starte Userprogramm
    rcall    button            ;Taster absteigende Flake abfangen
    rjmp    reset
start_user_prog:
    rjmp    user_prog        ;Adressierung nur über rjmp möglich   
reset:                    ;Variablen zurücksetzen
    clr    r31
    clr    r30
    clr    r10            ;Seitenvariable
    clr    r02            ;Zeilenvariable
    ldi    r16, 20            ;Offset Zeilennummerierung
    mov    r03, r16        ;word_pos
    clr    r04            ;word_add
    clr    r06            ;Eingabe
    clr    r07            ;Eingabe

main:
    rcall    init_display        ;Displayinitialisierung
    ldi    r16, 0x00        ;Ausgabeposition
    rcall    set_x_position        ;x auf 0
    ldi    r16, 0x00
    rcall    set_y_position        ;y auf 0
    rcall    draw_init
    rcall    out_page        ;Ganze Seite ausgeben
    rcall    draw_finish
    rcall    button            ;Eingabe abfragen
    cpi    r16, 0x02        ;langer Tastendruck?
    breq    line            ;gehe Zeile auswählen
    inc    r10           
    mov    r16, r10
    cpi    r16, 22            ;Ãœberlauf?
    brne    main            ;Nein, dann nächste Seite
    ldi    r31, 0x00        ;Ãœberlaufkorrektur
    ldi    r30, 0x00
    ldi    r16, 0x00
    mov    r10, r16
    rjmp    main

line:                    ;Zeile auswählen
    rcall    init_display
    ldi    r16, 0x00
    rcall    set_x_position
    mov    r16, r02
    rcall    set_y_position
    rcall    draw_init
    ldi    r16, 0xff        ;Zeilenmarkierung laden
    rcall    spi_out            ;Zeilenmarkierung zeichnen
    rcall    draw_finish
    rcall    button            ;Zeile auswählen?
    cpi    r16, 0x02        ;ja, dann Wort wählen
    breq    word
    inc    r02
    rcall    init_display        ;alte Markierung löschen
    ldi    r16, 0x00
    rcall    set_x_position
    mov    r16, r02
    dec    r16
    rcall    set_y_position
    rcall    draw_init
    ldi    r16, 0x00        ;leere Markierung
    rcall    spi_out            ;Zeilenmarkierung zeichnen
    mov    r16, r02
    cpi    r16, 6            ;Zeilenüberlauf
    brne    line           
    ldi    r16, 0x00        ;Werte korrigieren
    mov    r02, r16
    rjmp    line
   
word:                    ;Wort auswählen
    rcall    init_display
    mov    r16, r03   
    rcall    set_x_position
    mov    r16, r02
    rcall    set_y_position
    rcall    draw_init
    ldi    r16, 0xff        ;Wortmarkierung laden
    rcall    spi_out            ;ausgeben
    rcall    draw_finish
    rcall    button            ;Eingabe abfragen
    cpi    r16, 0x02
    breq    edit
    mov    r16, r03
    ldi    r17, 16
    add    r16, r17
    mov    r03, r16
    inc    r04
    rcall    init_display        ;Alte Markierung löschen
    mov    r16, r03
    ldi    r17, 16
    sub    r16, r17
    rcall    set_x_position
    mov    r16, r02
    rcall    set_y_position
    rcall    draw_init
    ldi    r16, 0x00
    rcall    spi_out
    rcall    draw_finish
    mov    r16, r03
    cpi    r16, 84
    brne    word
    ldi    r16, 20            ;Ãœberlaufkorrektur
    mov    r03, r16
    ldi    r16, 0x00
    mov    r04, r16
    rjmp    word

edit:                    ;Worteinagbe
    ldi    r16, 0x00
    mov    r07, r16        ;Ergebnis zurücksetzen
    mov    r06, r16

    rcall    get_nibble        ;1. Zeichen einlesen
    andi    r20, 0x0f
    swap    r20
    mov    r07, r20   
    ldi    r16, 0x04
    add    r03, r16

    rcall    get_nibble        ;2. Zeichen einlesen
    andi    r20, 0x0f
    or    r07, r20
    ldi    r16, 0x04
    add    r03, r16

    rcall    get_nibble        ;3. Zeichen einlesen
    andi    r20, 0x0f
    swap    r20
    mov    r06, r20
    ldi    r16, 0x04
    add    r03, r16

    rcall    get_nibble        ;4. Zeichen einlesen
    andi    r20, 0x0f
    or    r06, r20
    ldi    r16, 0x04
    add    r03, r16

add_page:                ;Seitenadresse ermitteln
    clr    r24
    clr    r25
    mov    r16, r10
add_add_p:   
    cpi    r16, 0x00
    breq    add_line
    adiw    r25:r24, 48        ;48 Byte pro Seite
    dec    r16
    rjmp    add_add_p
       
add_line:                ;Zeilenadresse ermitteln
    mov    r16, r02
add_add:
    cpi    r16, 0x00
    breq    ok1
    adiw    r25:r24, 0x08        ;addiere pro Zeile 8
    dec    r16
    rjmp    add_add
ok1:
    mov    r16, r04   
add_add1:
    cpi    r16, 0x00
    breq    ok2
    adiw    r25:r24, 0x02        ;addiere pro Wort 2
    dec    r16
    rjmp    add_add1
ok2:
    mov    r31, r25        ;lade Pageadresse
    mov    r30, r24
    andi    r30, 0b11100000        ;Wortadresse auf 0

    ldi    r21, 0x00
    mov     r22, r24        ;geänderte Wortadresse ermitteln
    andi    r22, 0x1f       
    lsr    r22            ;wort bit 1, 0 ist byteadresse
page_l:
    cpi    r21, 16            ;für alles 16 Wörter
    breq    delete

    cp    r21, r22
    brne    fill_old
    mov    r00, r07        ;geänderte Werte laden
    mov    r01, r06
    adiw    r31:r30, 0x02        ;z manuell hochzählen
    rjmp    fill_add
fill_old:                ;lade alte Werte   
    lpm    r00, Z+
    lpm    r01, Z+
fill_add:
    mov    r28, r30        ;rette Adresszeiger
    mov    r29, r31
    mov    r30, r21
    lsl    r30            ;Linksshift   
    ldi    r16, 0b00000001
    out    spmcsr,    r16
    spm                ;Wörter in Puffer laden
    mov    r30, r28
    mov    r31, r29
    inc    r21
    rjmp    page_l

delete:                    ;Page löschen und neu schreiben
    mov    r31, r25        ;Pageadresse laden
    mov    r30, r24
    andi    r31, 0b00000011        ;Page maskieren
    andi    r30, 0b11100000
    ldi    r16, 0b00000011        ;Löschbefehl laden
    out    spmcsr,    r16
    spm                ;löschen
    ldi    r16, 0b00000101        ;Schreibbefehl laden       
    out    spmcsr,    r16
    spm                ;schreiben
    rcall    button            ;User bereit für neue Anzeige?
    rjmp reset            ;Editor neu starten

get_nibble:                ;Zeichen einlesen
    clr    r20            ;Ausgabe 0 setzen
s_get_nibble:
    rcall    init_display
    mov    r16, r03   
    rcall    set_x_position
    mov    r16, r02
    rcall    set_y_position
    rcall    draw_init
    mov    r16, r20
    rcall    out_nibble        ;Zeichen ausgeben
    rcall    draw_finish
    rcall    button            ;Eingabe abfragen
    cpi    r16, 0x02        ;lange gedrückt?
    breq    f_get_nibble        ;Eingabe beenden
    inc    r20            ;Eingabezähler++
    ldi    r16, 0x0f
    and    r20, r16       
    rjmp    s_get_nibble
f_get_nibble:
    ret

button:                    ;Taster abfragen
    in    r16, PINB        ;PortB einlesen
    andi    r16, 0x01        ;Port0 separieren
    cpi    r16, 0x00        ;wurde auf Masse gezogen?
    brne    button            ;nein dann weiter abfragen
    rcall    delay            ;ja dann kurz warten
    in    r16, PINB        ;erneut abfragen
    andi    r16, 0x01
    cpi    r16, 0x00        ;noch auf Masse?
    breq    ok            ;ja dann langes Event           
    ldi    r16, 0x01        ;Rückgabewert für "kurz"
    ret
ok:                    ;langes drücken erkannt
    rcall    delay            ;delay
    in    r16, PINB        ;PortB einlesen
    andi    r16, 0x01
    cpi    r16, 0x00        ;noch auf Masse?
    breq    ok            ;ja dann warte
    ldi    r16, 0x02        ;Rückgabewert für "lang"
    ret

out_page:                ;Seite ausgeben (6 Zeilen)
    cpi    r16, 0x00        ;erste Seite?
    breq    load_p
load_add:
    adiw    r31:r30, 48        ;pro Seite 48 Byte weiter
    dec    r16
    brne    load_add
load_p:
    ldi    r19, 0x06
    mov    r26, r30        ;Anfangsadresse laden
    mov    r27, r31   
p_loop:
    rcall    out_line
    adiw    r27:r26, 8        ;Adresse für die nächsten 8 Byte
    dec    r19
    brne    p_loop   
    ret

delay:                    ;Delay mit 2 Schleifen
    ldi    r16, 0x00
    ldi    r17, 0x00
d_wait:
    inc    r16
    cpi    r16, 0x00
    brne    d_wait
    inc    r17   
    cpi    r17, 0xa0
    brne    d_wait
    ret

out_line:                  ;Zeile ausgeben
    mov    r16, r27        ;Adresse laden (H)
    rcall    out_byte      ;Adresse ausgeben
    mov    r16, r26        ;Adresse laden (L)
    rcall    out_byte      ;Adresse ausgeben
    ldi    r16, 0x00       ;":" bauen und ausgeben
    rcall spi_out
    ldi    r16, 0x00
    rcall spi_out
    ldi    r16, 0x24
    rcall spi_out
    ldi    r16, 0x00
    rcall spi_out
    ldi    r18, 0x08        ;Zähler für 8 Byte   
l_loop:
    lpm    r16, Z+            ;Byte laden
    rcall    out_byte        ;Byte ausgeben
    dec    r18
    brne    l_loop   
    ret

out_byte:                ;Byte ausgeben
    mov    r00, r16        ;Zeichen retten
    swap    r16            ;oberstes Nibble
    andi    r16, 15            ;Maskierung
    mov    r28, r30        ;rette Z-Register
    mov    r29, r31        ;rette Z-Register
    rcall    out_nibble        ;Ausgabe
    mov    r16, r00        ;unterstes Nibble   
    andi    r16, 15            ;Maskierung
    rcall    out_nibble        ;Ausgabe
    mov    r30, r28        ;rette Z-Register
    mov    r31, r29        ;rette Z-Register
    ret

draw_init:                ;Datenübertragung vorbereiten
    sbi    PORTB, LCD_DC        ;LCD Kommandomodus setzen
    cbi    PORTB, LCD_SCE        ;LCD aktivieren
    ret

draw_finish:                ;LCD Schreibvorgang abschließen
    sbi    PORTB, LCD_SCE        ;LCD deaktivieren   
    ret

init_display:
    ldi    r16, 0x21        ;Displayinitialisierung
    rcall    lcd_write_cmd
    ldi    r16, 0xD0
    rcall    lcd_write_cmd
    ldi    r16, 0x04
    rcall    lcd_write_cmd
    ldi    r16, 0x13
    rcall    lcd_write_cmd
    ldi    r16, 0x20
    rcall    lcd_write_cmd
    ldi    r16, 0x0C
    rcall    lcd_write_cmd

    ldi    r16, 0x21        ;Kontrasteinstellung
    rcall    lcd_write_cmd
    ldi    r16, 0x80 | 0x3C    ;Kontrastwert laden
    rcall    lcd_write_cmd
    ldi    r16, 0x20
    rcall    lcd_write_cmd

    ret

set_x_position:                ;LCD x-Position setzen
    ori    r16, 0x80        ;Wertnormalisierung
    rcall    lcd_write_cmd        ;Daten schreiben
    ret

set_y_position:                ;LCD y-Position setzen
    ori    r16, 0x40        ;Wertnormalisierung
    rcall    lcd_write_cmd        ;Daten schreiben
    ret

spi_out:                ;Software SPI
    cbi    PORTB, LCD_SCLK        ;Clock auf 0 setzen
    ldi    r17, 0x08        ;Schleifenzähler für 8 Bit
spi_out_byte:                ;Byte ausgeben
    sbrc    r16, 0x07        ;Prüfe ob Bit 7 nicht gesetzt
    sbi    PORTB, LCD_SDIN        ;Datenleitung auf 1 setzen
    sbrs    r16, 0x07        ;Prüfe ob Bit 7 gesetzt
    cbi    PORTB, LCD_SDIN        ;Datenleitung auf 0 setzen
    sbi    PORTB, LCD_SCLK        ;Clock auf 1 setzen
    cbi    PORTB, LCD_SCLK        ;Clock auf 0 setzen
    lsl    r16            ;Shift für nächstes Bit   
    dec    r17            ;Schleifenzähler--
    brne    spi_out_byte        ;Für alle übrigen Bits
    ret

lcd_write_cmd:                ;LCD Kommando schreiben
    cbi    PORTB, LCD_DC        ;LCD Kommandomodus setzen
    rcall    lcd_out            ;Daten schreiben
    ret

lcd_write_data:                 ;LCD Daten schreiben
    sbi    PORTB, LCD_DC        ;LCD Datenmodus setzen
    rcall    lcd_out            ;Daten schreiben
    ret

lcd_out:
    cbi    PORTB, LCD_SCE        ;LCD aktivieren
    rcall    spi_out             ;Daten ausgeben
    sbi    PORTB, LCD_SCE        ;LCD deaktivieren
    ret

out_nibble:                ;Nibble ausgeben   
    add    r16, r16
    add    r16, r16        ;Zeichenadressierung
    ldi    r30, LOW(data*2)    ;Offset laden
    ldi    r31, HIGH(data*2)   
    add    r30, r16        ;Zeichenposition addieren
    ldi    r16, 0x00
    adc    r31, r16
    lpm    r16, Z+         ;Zeichen laden
    rcall spi_out          ;Zeichen ausgeben
    lpm    r16, Z+         ;...
    rcall spi_out
    lpm    r16, Z+
    rcall spi_out
    lpm    r16, Z+
    rcall spi_out   
    ret

data:                    ;Zeichensatz
    .db    0x00, 0x3E, 0x41, 0x3E    ;0
    .db    0x00, 0x04, 0x02, 0x7F    ;1
    .db    0x00, 0x46, 0x71, 0x4E    ;2
    .db    0x00, 0x49, 0x49, 0x36    ;3
    .db    0x00, 0x0C, 0x0A, 0x7F    ;4
    .db    0x00, 0x4F, 0x49, 0x31    ;5
    .db    0x00, 0x3E, 0x49, 0x31    ;6
    .db    0x00, 0x01, 0x79, 0x0F    ;7
    .db    0x00, 0x36, 0x49, 0x36    ;8
    .db    0x00, 0x46, 0x49, 0x3E    ;9
    .db    0x00, 0x7F, 0x09, 0x7F    ;A
    .db    0x00, 0x7F, 0x49, 0x36    ;B
    .db    0x00, 0x3E, 0x41, 0x41    ;C
    .db    0x00, 0x7F, 0x41, 0x3E    ;D
    .db    0x00, 0x7F, 0x49, 0x49    ;E
    .db    0x00, 0x7F, 0x09, 0x09    ;F

flash_end:
    rjmp    init            ;jmp to editor



Elektronik-Labor  Projekte   AVR   T13-Contest