Created
November 18, 2014 00:55
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;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;'" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.