Skip to content

Instantly share code, notes, and snippets.

@BobBurns
Created November 18, 2014 00:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BobBurns/6cf90f8c76851e4e8440 to your computer and use it in GitHub Desktop.
Save BobBurns/6cf90f8c76851e4e8440 to your computer and use it in GitHub Desktop.
AVR assembly program to toggle a speaker from USART serial connection. Based on serialOrgan.c from E.Williams AVR Programming p.100
;Serial Organ program p.100
;compile with gavrasm organAssembly.asm
;flash with avrdude -c avrisp -p m168 -P /dev/tty.usbmodem1411 -b 19200 -U flash:w:organAssembly.hex;
;USART configure: 2x 1Mhz clock speed, 9600 baud UBBR = 12
.equ UBBRvalue = 12
.def temp = r16
.def zL = r30
.def zH = r31 ;not defined
.def byte_tx = r18
.def inB = r20
.def wordL = r24
.def wordH = r25
.def delayL = r26
.def delayH = r27
.device atmega168
.cseg
;--inits---
in temp,DDRD
ori temp,(1 << PD6)
out DDRD,temp
ldi temp,low(RAMEND)
out SPL,temp
ldi temp,high(RAMEND)
out SPH,temp
rcall initUSART
lp:
rcall receive
brcc lp ;waits until input
rcall transmit
clc
mov inB,byte_tx
;--- play notes ---
.def checkN = r19
.def count = r17
ldi count,1
ldi zH,high(keyTable * 2)
ldi zL,low(keyTable * 2) ;*2 because LSbit of Z is used as High/Low flag
lp2: lpm checkN,Z+
cp checkN,inB
breq play
mov byte_tx,checkN
rcall transmit
inc count
cpi count,19
brsh lp
rjmp lp2
play:
.undef checkN
.def temp2 = r19
;.undef count
ldi byte_tx,'X'
rcall transmit
ldi zL,low(noteTable * 2)
ldi zH,high(noteTable * 2)
lp3: lpm temp,Z+ ;low byte first
lpm temp2,Z+
dec count
brne lp3 ;get right index
push temp
push temp2
ldi temp,20 ;note duration
push temp
rcall pNote
rjmp lp
;
;--- subroutines ---
initUSART:
ldi temp,low(UBBRvalue)
sts UBRR0L,temp
ldi temp,high(UBBRvalue)
sts UBRR0H,temp
lds temp,UCSR0A
ori temp,(1 << U2X0)
sts UCSR0A,temp
ldi temp,(1 << TXEN0) | (1 << RXEN0) ;enable transmit and receive
sts UCSR0B,temp
ldi temp,(1 << UCSZ01) | (1 << UCSZ00) ;8 data bits, 1 stop bit
sts UCSR0C,temp
ret
receive: ; needs byte_tx defined by caller
clc
lds temp,UCSR0A
sbis temp,RXC0 ;is byte in rx buffer?
ret ;not yet
lds byte_tx,UDR0;
sec ;to indicate a byte was received
ret
transmit:
lds temp,UCSR0A
sbis temp,UDRE0 ;wait for Tx buffer to be empty
rjmp transmit ;not ready
sts UDR0,byte_tx;
ret
pNote:
.def duration = r19
.def eoReg = r17
pop zH
pop zL
pop duration
pop delayH ;high byte first
pop delayL ;switched order
outer: mov wordL,delayL
mov wordH,delayH
dlp: sbiw wordH:wordL,1
brne dlp
in temp,PORTD
ldi eoReg,(1 << PD6)
eor temp,eoReg
out PORTD,temp
dec duration
brne outer
.undef duration
.undef eoReg
icall ;indirect call points to address in Z
noteTable:
.dw 1043, 984, 929, 877, 827, 781, 737, 696, 657, 620, 585, 552, 521, 492, 464, 438, 414, 390
keyTable:
.db "awsedftgyhjikolp;'"
@BobBurns
Copy link
Author

This program uses the stack to call a function and indirect addressing with the Z register. The pitches aren't exactly right but its working. I need to dial in the delay loop to reflect a microsecond.

The hard part of writing this was figuring out the LPM instruction uses the least significant bit in Z as a flag to tell which byte of the word address to use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment