ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Edit: I've fixed a number of coding errors and the uncontrolled looping has gone (why???), but still nothing is displayed. I have fixed initialising to 8-bit data. I'm still looking for a hardware include file.
Hi,
I'm having a go with assembler on the ATmega328p, but am having a few problems. I am not new to coding assembler, so it's the details of the ATmega328p and possibly avr-as or avr-ld that may be the problem.
I have not been able to get the hardware #defines in avr/iom328p.h included using avr/io.h since the files are for C. If I try to include it directly I get an error. I have my own #defines coded from the Register Summary in the ATmega328p's specsheet. They may not be correct.
I use M4 for preprocessing. The generated TestProg.s code is used below.
The following code works. The LED blinks slowly. However the 3 characters that should be displayed at the end of dLCD_INIT are not displayed.
If I uncomment the 3 lines following 'rcall dLCD_INIT' the program fails with very rapid blinking, and after pressing the reset button on the UNO the rapid blinking restarts immediately even though there should be a 3 second delay. Power cycle is needed.
Code:
.data
.org 0x
DATA_LCD: .byte 0
STATE_LCD: .byte 0
Msg: .byte 12
.ascii "test message"
.byte 0xFF
.text
.ORG 0x0000
rjmp main ; Reset Handler
dLCD_blink:
sbi PORTB,5
ldi r16, 1
rcall Delay_x1s
cbi PORTB,5
ldi r16, 1
rcall Delay_x1s
ret
main:
ldi r16, lo8(RAMEND) ; set up the stack
out SPL, r16 ;
ldi r16, hi8(RAMEND) ;
out SPH, r16 ;
; Arduino: 2-5 (PORTD)
ldi r16, 0b00111100 ; set PortD2 - D5 (dLCD data) as output
out DDRD, r16 ; write value in r16 to Data Direction Register D
; Arduino: 10-12 (PORTB)
ldi r16, 0b00111100 ; set PortB2-B4 (dLCD control), and B5 (LED) as output
out DDRB, r16 ; write value in r16 to Data Direction Register B
ldi r16, 0x00
out PORTB, r16 ; initialise low
out PORTD, r16 ;
clr r15 ; used for adding 8's to 16's
clr r14
inc r14 ; used to mark pixels in ArrayWork
ldi r16, '0
mov r4, r16
cli ; no interrupts
ldi r16, 3
rcall Delay_x1s ; 3 second delay before starting
rcall dLCD_INIT
; ldi r16, 'Z
; sts DATA_LCD, r16
; rcall dLCD_PUTCH
1: nop
rcall dLCD_blink
rjmp 1b
;
; Some delay functions - Timing depends on 16MHz clock
;
Null: ret ; worth 7 cycles
Delay_x1us: ; number of us is in r16 0 gives 256us
push r16
dec r16
Dx1u_loop:
rjmp . ; 16 instruction cycles 3 to get here and 4 to return
rjmp .
rjmp .
rcall Null
dec r16
brne Dx1u_loop
rjmp .
nop
rjmp .
pop r16
ret
Delay_x100us: ; number of us / 100 is in r16
push r16
nop
Dx100u_loop:
ldi r16, 99
nop
rcall Delay_x1us
pop r16
dec r16
push r16
rcall Null
brne Dx100u_loop
nop
pop r16
ret
Delay_x10ms: ; number of ms / 10 is in r16
push r16
nop
Dx10m_loop:
ldi r16, 99
nop
rcall Delay_x100us
rcall Delay_x1us
pop r16
dec r16
push r16
rcall Null
brne Dx10m_loop
nop
pop r16
ret
Delay_x1s: ; number of seconds is in r16
push r16
Dx1s_loop:
ldi r16, 100
rcall Delay_x10ms
pop r16
dec r16
push r16
brne Dx1s_loop
pop r16
ret
;
; Functions to send data & commands to an HD44780 based LCD display
; Two bytes must be reserved in the calling code:
; DATA_LCD The data byte to send
; STATE_LCD The state of cursor blink, cursor on/off, and display on/off
; Commands should not be sent directly. Use the included subroutines.
;
;
; 2us delay
;
dLCD_C4: ; worth 8 instruction cycles
nop
ret
dLCD_Delay_2us:
nop
rcall dLCD_C4
rcall dLCD_C4
rcall dLCD_C4
ret
;
; Send the byte in r16 to the LCD, assumes RS already set
;
dLCD_SEND_HALF:
cbi PORTB,PORTB3 ; low = write
cbi PORTB,PORTB1
sbrc r16,7 ; Load nybble
sbi PORTB,PORTB1
cbi PORTB,PORTB0
sbrc r16,6
sbi PORTB,PORTB0
cbi PORTD,PORTD7
sbrc r16,5
sbi PORTD,PORTD7
cbi PORTD,PORTD6
sbrc r16,4
sbi PORTD,PORTD6
rcall dLCD_Delay_2us ; let things settle
sbi PORTB,PORTB2 ; data is processed on falling edge
rcall dLCD_Delay_2us
rcall dLCD_Delay_2us
cbi PORTB,PORTB2
push r16
ldi r16, 0x28
rcall Delay_x1us
pop r16
ret
dLCD_SEND:
rcall dLCD_SEND_HALF
swap r16 ; swap nybbles
rcall dLCD_SEND_HALF
push r16
ldi r16, 0x0A
rcall Delay_x1us ; extra bit between bytes
pop r16
ret
; Initialize the LCD
dLCD_INIT:
push r16
cbi PORTB,PORTB4
cbi PORTB,PORTB3
cbi PORTB,PORTB2 ; active low
ldi r16, 0x01
rcall Delay_x10ms ; Init delay - 10ms
ldi r16, 0b00110000 ; first 8bit reset 0 0 1 1
rcall dLCD_SEND_HALF
ldi r16, 0x30 ; 4.8ms
rcall Delay_x100us
ldi r16, 0b00110000 ; second 8bit reset 0 0 1 1
rcall dLCD_SEND_HALF
ldi r16, 0x30
rcall Delay_x100us
ldi r16, 0b00110000 ; third 8bit reset 0 0 1 1
rcall dLCD_SEND_HALF
ldi r16, 0x02
rcall Delay_x100us
; ldi r16, 0b00100000 ; 4-bit mode ?? 0 0 1 0
; rcall dLCD_SEND_HALF
ldi r16, 0x11
rcall Delay_x1us
ldi r16, 0b00111100 ; 0 0 0 1
rcall dLCD_SEND
ldi r16, 0b00001000 ; 0 0 0 0
rcall dLCD_SEND
ldi r16, 0b00000001 ; 0 0 0 0
rcall dLCD_SEND
; ldi r16, 0b00000110 ; entry mode 0 0 0 1
; rcall dLCD_SEND_HALF
ldi r16, 0x17
rcall Delay_x100us ; 2.1ms
; ldi r16, 0b00001111 ; 0 0 0 0
; rcall dLCD_SEND
sts STATE_LCD, r15 ; initialise saved state
sbi PORTB,PORTB4
ldi r16, 'A
rcall dLCD_SEND
ldi r16, 'B
rcall dLCD_SEND
ldi r16, 'C
rcall dLCD_SEND
pop r16
ret
; Send the byte in r16 as an instruction byte to the LCD
dLCD_SENDINS:
cbi PORTB,PORTB4 ; low = instruction
rcall dLCD_SEND ; Send the instruction byte
ret
; Send the byte in LCD_DATA as a character to the LCD
dLCD_PUTCH:
sbi PORTB,PORTB4 ; high = character
push r16
lds r16, DATA_LCD
rcall dLCD_SEND ; Send the character
pop r16
ret
; Display Clear
dLCD_CLEAR:
push r16
ldi r16, 0b00000001 ; Clear the LCD display
rcall dLCD_SENDINS
ldi r16, 0x14
rcall Delay_x100us
pop r16
ret
; Display On/Off - If r16,0 is 0 then display off else display on
dLCD_DISPLAY_ONOFF:
push r16
bst r16, 0 ; save the bit of interest
lds r16, STATE_LCD ; fetch saved command state
bld r16, 2 ; Update flags
rjmp 1f
; Cursor On/Off - If r16,0 = 0 then cursor off else cursor on
dLCD_CURSOR_ONOFF:
push r16
bst r16, 0 ; save the bit of interest
lds r16, STATE_LCD ; fetch saved command state
bld r16, 1 ; Update flags
rjmp 1f
; Blinking Cursor On/Off - If r16,0 = 0 then blinking cursor on else off
dLCD_CURSBLINK_ONOFF:
push r16
bst r16, 0 ; save the bit of interest
lds r16, STATE_LCD ; fetch saved command state
bld r16, 0 ; Update flags
1: sts STATE_LCD, r16 ; save control byte
ori r16, 0x08 ; Set command bit
rcall dLCD_SENDINS ; Send the instruction
pop r16
ret
; Move cursor - r16 = offset
dLCD_MOVECURS:
ori r16, 0b10000000 ; Set Write Data Address command bit
rcall dLCD_SENDINS ; Send the instruction
ret
; Move cursor to Line 1
dLCDdLINE1:
ldi r16, 0x00 ; Move cursor to 0
rcall dLCD_MOVECURS
ret
; Move cursor to Line 2
dLCDdLINE2:
ldi r16, 0x14 ; Move cursor to 20
rcall dLCD_MOVECURS
ret
; Move cursor to Line 3
dLCDdLINE3:
ldi r16, 0x28 ; Move cursor to 40
rcall dLCD_MOVECURS
ret
; Move cursor to Line 4
dLCDdLINE4:
ldi r16, 0x3C ; Move cursor to 60
rcall dLCD_MOVECURS
ret
You can dispense with defines altogether, as each of them is simply a pointer to a hex number listed in the datasheet. Print the few relevant pages and you're sucking diesel.
I always found assemblers that supported defines extremely intolerant of the least syntax irregularity, so I would check that
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.