Thread: Output music tone from piezo buzzer algorithm, NEED HELP!

  1. #1
    Registered User
    Join Date
    Feb 2008
    Posts
    116

    Output music tone from piezo buzzer algorithm, NEED HELP!

    I am trying to figure out how to write a c program/algorithm that will output music tone (like middle C, C#, D, etc...). I am using an ATMEGA324P chip, and coding in C.

    I am trying not to use the PWM functions, so how would I create the wave form of a specific frequency, and output it continuously unless something disturbs it (controlling it from a DIP switch, so one switch will play one note, and another switch will play another note)...

    Could someone help?

    Thanks

  2. #2
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    In Turbo C, you can control the built in speaker (if you mobo supports one and your mobo or case, has one), in DOS or Windows up to XP, by using a program like this:

    Code:
    #include <dos.h>
    
    int main() {
    
      sound(440);  //this is an A, and in Mhz IIRC.
      delay(1000);  //sound will continue for 1 second
      nosound();    //shuts off the sound
    
      return 0;
    }
    It's been a very long time since I used this function, and never with your kind of interface, so let us know how it goes, and good luck.
    Last edited by Adak; 01-25-2010 at 10:14 AM.

  3. #3
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472
    o man,,,that takes me back, The victory music in my first bash at a game..'roulette' Qbasic had a neat 'PLAY ' function i think it was called, it had predefined note lengths and octave arguments so you could interpret your actual musical notation.

  4. #4
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    One of the first real character based games for me was "Prince of Persia". Fantastic game, and the music you heard coming from the tiny little case speaker, was made extra creepy because of the poor sound quality.

    With external speakers, the sound was much better - but not as deliciously creepy.

  5. #5
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472
    Haha defo, the spiky, scratchy tone was so dry and immediate, really unsettling, some melodic arpeggio stuff sounds strangely neat tho. Prince of persia, we all loved that guys moves haha

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,664
    > I am using an ATMEGA324P chip, and coding in C.
    Without more details about your environment, we can't help (despite all the DOS'ism reminiscing going on).

    How is the speaker attached to your board?
    Are there any libraries which come with the board, or the compiler?

    At the most basic level, you output a '1' to a pin, and the speaker moves one way. Output a '0', and it moves back. Do that at the right frequency, and you hear notes.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Registered User
    Join Date
    Feb 2008
    Posts
    116
    well, I guess I am looking for more of a general answer.

    But here is what I am using. ATMEL ATMEGA324P, with AVR Studio 4 as my environment. And basically, I am going to have a piezo buzzer attached to one of the pins.

    I have an example code, but I'm just trying to figure out what it means.

    Code:
    #include <avr/io.h>
    #include <util/delay.h>
    #include "LCD.h"
    
    #define BUZZER_PORT	PORTA
    #define BUZZER_DDR  DDRA
    #define BUZZER_PIN  0
    
    // Provide the duration and period of buzzer signal in ms
    void BUZZ(float duration, float period)
    {
    	long int i,cycles;
    	float half_period;	// Initialize variables
    
    	cycles=duration/period;	// Compute the number of cycles to loop toggling the pin
    	half_period = period/2;	// Compute a half cycle period
    
    	BUZZER_DDR = (1 << BUZZER_PIN) | BUZZER_DDR;	// Set the port for the buzzer output
    
    	for (i=0;i<cycles;i++)	// Toggle the speaker the appropriate number of cycles
    	{
    		_delay_ms(half_period);							// Wait a half cycle to toggle port pin
    		BUZZER_PORT = (1 << BUZZER_PIN) | BUZZER_PORT;	// Set the port pin
    		_delay_ms(half_period);							// Wait a half cycle to clear the port pin
    		BUZZER_PORT = ~(1 << BUZZER_PIN) & BUZZER_PORT;	// Clear the port pin
    	}
    
    	return;		// Return to the main program
    }
    
    void Alert(void)
    {
    	int i;
    
    	for (i=0; i<4; i=i+1)
    	{
    		BUZZ(75,0.5);					// Output a waveform of 75ms at 2KHz (period of 2KHz is 1/2000=0.5 ms)
    		PORTA = 0b00000010 ^ PORTA;		// Toggle the LED pin by XOR (whenever a bit is XORed with 1, it is toggled)
    		_delay_ms(75);					// Wait for 75 ms
    	}
    	return;
    }
    
    int main(void)
    {
    	LCD_INIT();
    
    	DDRA = DDRA | 0b00000010;
    	PORTA = PORTA & 0b00000000;
    
    	while(1)
    	{
    		LCD_COMMAND(LCD_CLEAR_HOME);	// Clear LCD screen, send cursor to start
    		_delay_ms(500);					// Wait for 500 ms
    		LCD_STRING("Junior Design");	// Write "Juior Design" to LCD
    		_delay_ms(500);					// Wait for 500 ms
    
    		LCD_ADDR(0x40);					// Send cursor to address 0x40 (second row)
    		LCD_STRING("ATMEL Lecture");	// Write "ATMEL Lecture" to LCD
    		_delay_ms(500);					// Wait for 500 ms
    		Alert();						// Go to subroutine Alert()
    	}	
    }

    I guess the way they are using it is using the function BUZZ to create a square wave form, with half the period as a high, and half the period as a low, to a whatever pin the buzzer is connected to.

    Can someone explain what this is meant for?

    Code:
    BUZZER_DDR = (1 << BUZZER_PIN) | BUZZER_DDR;

    The thing that I am trying to figure out is, that I am using a dip switch to control what music note I want, so how would I make the loop for the sound pulse to run infinitely until another switch is set, or that switch is turned off. The code example shows it as running for a specific amount of time. I suppose would you use a while statement?

    Thanks, appreciate all the help.

  8. #8
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,664
    Each pin can be input or output.

    The Data Direction Register (DDR) tells each corresponding pin whether it's in or out.
    That line of code is configuring a pin to be output.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  9. #9
    Registered User
    Join Date
    Feb 2008
    Posts
    116
    but what does the (1 << BUZZER_PIN) do? that's what I don't understand

  10. #10
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,664
    Well BUZZER_PIN has a value from 0 to 7

    So it's just 1<<0 (in this case), which is 1.

    << shifts a value by a number of bits.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  11. #11
    Registered User
    Join Date
    Feb 2008
    Posts
    116
    Is there another way to write it to have the same function? I'm just sort of having a hard time visualizing it as that.

    Can it be like:

    Code:
    _delay_ms(half_period);
    BUZZER_PORT = 0x01;
    _delay_ms(half_period);
    BUZZER_PORT = 0x00;
    Is there a real advantage or disadvantage between one or the other?

    Thanks

  12. #12
    Registered User
    Join Date
    Feb 2008
    Posts
    116
    Ok, here is my code.

    There is 1 warning and it says, statement with no effect...It is for the for loop statement near the end in the main section. Do you know what the problem is?

    Code:
    *	Project assignment: Hardware problem
     *	Read in an 8 bit port value (DIP switch ) and output a musical note for each corresponding bit ie B0 -> middle C,
     *	B1 -> C#, B2 -> D, etc...If more than one bit is set, chose the lowest note frequency for output and keep the 
     *	synthesizer monophonic.
     *
     */
    
    #include <avr/io.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <util/delay.h>
    #include "LCD.h"
    
    #define BUZZER_PORT	PORTA
    #define BUZZER_DDR  DDRA
    #define BUZZER_PIN  0
    
    
    int switch_input;				//global variable
    
    
    void BUZZ(float period)
    {
    	int new_input;
    	float half_period;			
    	
    	new_input = PIND;							//variable to test old switch_input variable
    	half_period = period/2;						// Compute a half cycle period
    	
    	BUZZER_DDR = (1 << BUZZER_PIN) | BUZZER_DDR;		// Set the port for the buzzer output
    
    	while(new_input == switch_input)
    	{
    		_delay_ms(half_period);				// Wait a half cycle to toggle port pin
    		BUZZER_PORT = (1 << BUZZER_PIN) | BUZZER_PORT;	// Set the port pin as a 1
    		_delay_ms(half_period);				// Wait a half cycle to clear the port pin
    		BUZZER_PORT = ~(1 << BUZZER_PIN) & BUZZER_PORT;	// Clear the port pin to 0
    	
    		new_input = PIND;
    	}
    
    	return;
    }
    
    
    int main(void)
    {	
    	DDRD = 0x00;				//PORTD is all input for DIP switch
    	DDRA = 0x01;				//PORTA pin 0 is an output for buzzer	
    	PORTA = 0x00;				//Set the buzzer pin initially to 0
    
    	int i,count,bit_check;
    	float C,C2,D,D2,E,F,F2,G;	
    
    	C = 261.63;			//initialize vairables to specifc frequency (in HZ)
    	C2 = 277.18;
    	D = 293.66;
    	D2 = 311.13;
    	E = 329.63;
    	F = 349.23;
    	F2 = 369.99;
    	G = 392.00;
    
    
    	while(1)
    	{
    		switch_input = PIND;		//Reads in the DIP switch values and stores in switch_input
    
    LOOP:	switch(switch_input)		//determines which DIP switch is turned ON to play a 
    		{							//specific note
    			case 0x0:
    				BUZZ((1/C)*100);	//Branches to function BUZZ with period of C ((1/261.63)*100 = 3.822 ms)
    				break;
    
    			case 0x1:
    				BUZZ((1/C2)*100);
    				break;
    
    			case 0x2:
    				BUZZ((1/D)*100);
    				break;
    
    			case 0x3:
    				BUZZ((1/D2)*100);
    				break;
    
    			case 0x4:
    				BUZZ((1/E)*100);
    				break;
    
    			case 0x5:
    				BUZZ((1/F)*100);
    				break;
    
    			case 0x6:
    				BUZZ((1/F2)*100);
    				break;
    
    			case 0x7:
    				BUZZ((1/G)*100);
    				break;
    	
    			default:						//Default case is for if more than one switch is on
    										//Check each bit, starting from the lowest bit, to determine
    				count = 0;				//which is the lowest switch on.
    				for(i=0x01;i<=0x80;i<<1)		//starts at bit 0 stop after bit 7, i shifts left 1 bit
    				{
    					bit_check = (switch_input & i);		//Masks all bits except for bit i in PIND	
    
    					if(bit_check == i)			//checks if the bit is a 1 or a 0
    						break;	
    					else
    						count = count + 1;		//count used to determine what bit is lowest
    				}
    				
    				switch_input = pow(2,count);			//2^count gives the lowest switch
    
    				goto LOOP;
    		}
    	}	
    }
    Thanks

  13. #13
    Registered User ssharish2005's Avatar
    Join Date
    Sep 2005
    Location
    Cambridge, UK
    Posts
    1,732
    Quote Originally Posted by dcwang3 View Post
    Is there another way to write it to have the same function? I'm just sort of having a hard time visualizing it as that.

    Can it be like:

    Code:
     
    _delay_ms(half_period);
    BUZZER_PORT = 0x01;
    _delay_ms(half_period);
    BUZZER_PORT = 0x00;
    Is there a real advantage or disadvantage between one or the other?

    Thanks
    You could always have function written to write to the BUZZER_PORT. It would very dangerous to do what your doing above. Cos the BUZZER_PORT is pointing to a 8 bit regsiter and you will have to toggle just one bit, leaving the remaining bit untouched cos, it might be used for other purpose. Unless you know what the other pins have been connected and configured to, donot touch them.

    You could have function as follow to write to the PORTA pin as follow:

    Code:
    void turn_on( PIN )
    {
        BUZZER_PORT = (1 << PIN ) | BUZZER_PORT; 
    }
     
    void turn_off( PIN )
    {
        BUZZER_PORT = ~(1 << PIN ) & BUZZER_PORT; 
    }
    ssharish
    Life is like riding a bicycle. To keep your balance you must keep moving - Einstein

  14. #14
    Registered User
    Join Date
    Feb 2008
    Posts
    116
    oh so basically the original code changes only the bits you are interested in either using an OR operator to turn something on, and an AND operator to turn something off.

    Also, when they have the BUZZER_PIN = 0 in a macro, why don't they automatically use the code like this:

    Code:
    _delay_ms(half_period);
    BUZZER_PORT = 1 | BUZZER_PORT;
    _delay_ms(half_period);
    BUZZER_PORT = 0 & BUZZER_PORT;

    instead of using the (1<<BUZZER_PIN) which is always automatically 0?

    Maybe it is good programming techniques?

  15. #15
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,664
    > for(i=0x01;i<=0x80;i<<1)
    Perhaps you meant to change i with the left shift.

    for(i=0x01;i<=0x80;i <<= 1)

    > switch_input = pow(2,count);
    Except your cases are 0 to 7, not powers of 2.
    So perhaps just count?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Having trouble making a function do what I want.
    By Shamino in forum C++ Programming
    Replies: 9
    Last Post: 12-07-2007, 11:20 AM
  2. Base converter libary
    By cdonlan in forum C++ Programming
    Replies: 22
    Last Post: 05-15-2005, 01:11 AM
  3. Need help fixing bugs in data parsing program
    By daluu in forum C Programming
    Replies: 8
    Last Post: 03-27-2003, 06:02 PM
  4. Control different DA output value!
    By Hunterhunter in forum A Brief History of Cprogramming.com
    Replies: 1
    Last Post: 03-13-2003, 12:11 PM
  5. Simple File Creation Algorithm
    By muffin in forum C Programming
    Replies: 13
    Last Post: 08-24-2001, 03:28 PM