Emit a Sound from Built-In Speaker on Butterfly using AVR Native Machine Language.
PREAMBLE:
As a new comer to AVR programming, when I got my first Butterfly, the first program I wanted to write was the simple "Hello World" program. However after days of total confusion trying to figure out how to use the LCD screen from the data sheets. The Butterfly does not have any LEDs I can blink (although I could have connection one easy enough) I decided that my first effort would be a program that made a sound come from the on board speaker.
UPDATE:
The initial program read the PORTB into memory, changed reversed BIT 5 and wrote the result back out to PORTB. I later found the the AVR Machine Language has instructions that allow one to just change the bits on a port. I have update the following program to use this much better method of activating and deactivating the speaker line on Pin #5 of Port B.
NOTE: An explanation of the program follows the listing.
THE PROGRAM:
CODE
;---------------------------------------------------.; ; BEEP.AMS for AVR BUTTERFLY MAKE THAT FAMOUS SOUND .; ; BY:RETRODAN CREATED:01-MAR-06 UPDATED:28-MAR-06 .; ;---------------------------------------------------.;
.INCLUDE "M169DEF.INC";(BUTTERFLY DEFINITIONS)
;----------------------------------------; ; FIRST I'LL DEFINE SOME REGISTER TO USE.; ;----------------------------------------; .DEF A = R16 ;GENERAL PURPOSE ACCUMULATOR .DEF I = R21 ;INDEXES FOR LOOP CONTROL .DEF J = R22
.ORG 0000 RJMP ON_RESET;REBOOT/START-UP VECTOR ;INTO OUR PROGRAM
;-----------------------------------------; ; WE START HERE ON ALL RESEST AND REBOOTS.; ; FIRST WE SETUP A STACK AREA THEN SET .; ; DIRECTION BIT FOR PORT-B FOR OUTPUT .; ;-----------------------------------------; START: ON_RESET: LDI A,LOW(RAMEND) ;SETUP STACK POINTER OUT SPL,A ;SO CALLS TO SUBROUTINES LDI A,HIGH(RAMEND);WORK CORRECTLY OUT SPH,A SBI DDRB,5 ;PORTB PIN 5 FOR OUTPUT
;--------------; ; MAIN ROUTINE.; ;--------------; BEEP: CLR I BLUPE: SBI PORTB,5 ;SPKR ON RCALL PAUSE ;WAIT CBI PORTB,5 ;SPKR OFF RCALL PAUSE ;WAIT DEC I BRNE BLUPE
LOOP: RJMP LOOP;DONE
;---------------.; ; PAUSE ROUTINE .; ;---------------.; PAUSE: NOP DEC J BRNE PAUSE RET
AVR_Admin- 04-12-2006
TITLE: HOW TO BEEP THE BUTTERFLY SPEAKER
BREAKDOWN AND EXPLANATION OF THE PROGRAM:
CODE
.INCLUDE "M169DEF.INC";(BUTTERFLY DEFINITIONS)
The first thing we do is read-in the Atmel file that defines all the constants and parameters for the chip we are writing for. The Butterfly uses an Atmega169 so that's the file we use.
CODE
.DEF A = R16 ;GENERAL PURPOSE ACCUMULATOR .DEF I = R21 ;INDEXES FOR LOOP CONTROL .DEF J = R22
Next we give names to the registers (accumulators) we are using. The A register is for general use and the I & J are counters for loop control.
CODE
.ORG 0000
Here we tell the Assembler(Compiler) that we want our program to start at the beginning of Flash (Program Memory) and that is what the .ORG 0000 directive does.
CODE
RJMP ON_RESET ;REBOOT/START-UP VECTOR
When the AVR chips are first powered-up or reset they go to the first location in program memory and excecute whatever is there. Since there are usually other "interupt" vectors that appear after this line in more complex programs, it is good practice not to start your actual program at this location, but rather insert a line of code that jumps to the first line of your program. That is what we are doing here. There's no real reason for it here. It does not accomplish anything other than sticking to that programming convention.
CODE
ON_RESET: LDI A,LOW(RAMEND) ;SETUP STACK POINTER OUT SPL,A ;SO CALLS TO SUBROUTINES LDI A,HIGH(RAMEND) ;WORK CORRECTLY OUT SPH,A
Before we can call a subroutine, we need a stack to hold our return addresses. To do this we use the constant RAMEND which is defined in the file m169def.inc that we loaded earlier.
RAMEND is set to the last memory address in SRAM. Typically stacks are placed at the top-of-memory and are allowed to grow downwards and that is what we are doing here. We load the low-byte of that address into the low-byte of stack pointer, and then do the same with the high byte.
CODE
SBI DDRB,5 ;PORTB PIN 5 FOR OUTPUT
From looking over the schematics for the Butterfly, it looked like the speaker was attached to PIN #5 on PORT B.
Since AVR ports can be set-up for either input or output, we need to inform the hardware that we are going to use PIN #5 on Port B as an output so we send a logic one to the Data Direction Register for port B (DDRB).
CODE
;--------------; ; MAIN ROUTINE; ;--------------; BEEP: CLR I BLUPE: SBI PORTB,5 ;SPKR ON RCALL PAUSE ;WAIT CBI PORTB,5 ;SPKR OFF RCALL PAUSE ;WAIT DEC I BRNE BLUPE
This is the Main Loop of the program. We set a Counter to Zero and at the end of the loop we subtract one and stop when we roll-over to Zero again, so the loop will exectute 255 times.
Inside the Loop we write a logic one to Pin #5 of Port B, wait, then send a logic Zero, then wait and loop around. This will produce a square wave from our speaker, the frequency is determined by our clock speed (normally 2Mhz for the Butterfly) and also by the amount of time we spend wasting time inside our PAUSE rotuine.
CODE
LOOP: RJMP LOOP;DONE
What do we do after we're done? I could have drove you crazy by having the program loop-back and start over, making your Butterfly beep continuously over-and-over again. Instead I just lock it into an infinite loop that does nothing but loop-back to itself.
CODE
;----------------; ;PAUSE ROUTINE ; ;----------------; PAUSE: NOP DEC J BRNE PAUSE RET
This is the Pause Routine that will determine the frequency of the BEEP that will come from your speaker.
We are decrementing a counter (J) until it reaches Zero. In the mean time we're executing a NOP (No OP) that does nothing 255 times. You can increase or lower the frequency by adding to removing NOPs from inside that loop.
NOTE: (Dirty Programming Trick) Normally you are "supposed-to" always initialize the J counter to Zero on each call but I "cheated" with a "Dirty Trick" to save one program step because I know it will be Zero from the previous call to this very routine and only time I might be in error is on the very first call. If you use such 'Dangerous Tricks" remember that the register (J) cannot be used anywhere else or its "default" value of Zero could get changed.
You can also change the frequency of the "Beep" by initializing the J-Register yourself to something other than Zero each time the routine is called.
Have fun turning your Butterfly into a hand-held R2D2 unit...Beep, Beep, Bloop!
AVR_Admin- 04-12-2006
CONTINUING EDUCATION:
If you would like to learn more about the Butterfly I recommend the Butterfly, STK502 and ATmega169 PDF files available as a free download from the Atmel Site at www.atmel.com.
REQUEST FOR FEED-BACK:
If you find this posting educational and would like to see more like it, please post a response in this thread.
Thank you for your time and consideration.
Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.