Sound Program

Explanation

This code listing is a small program that will play Smoke on the Water on an Atmega168 with a 16MHz oscillator. It uses timer 0 and timer 1 to create delays and the square wave which is responsible of the sound.
The song tones are as follows:

0 1 2 3 4 5 6 7 Tone
E | F | G | G | Bar 1
8 9 10 11 12 13 14 15 Tone E = 329.6 Hz
E | F | G# | G | Bar 2 F = 349.24 Hz
16 17 18 19 20 21 22 23 Tone G = 392.00 Hz
E | F | G | G | Bar 3 G# = 415.32 Hz
24 25 26 27 28 29 30 31 Tone
F | E | E | E | Bar 4

Smoke on the Water tones and frequencies.

(1)
f_{\rm clk_io} = 16 Mhz
(2)
OCR1A = 512
(3)
f_{\rm interrupt} = 31250 Hz
(4)
T_{\rm e} = {31250 Hz\over 329.6 Hz} = 94.81Iterations
(5)
E_{\rm tck} = {94.81 \over 2} = 47.4 => 47 ticks.

We need tck = 47 in order to create a square signal with a frequency of 329.6 Hz.
There is an error after rounding. The final tone frequency is 31250 * 47 * 2 = 332.45 Hz

#define SETUPNIB    PORTB |= (1<<7) | (1<<6) | (1<<5) | (1<<4);
#define RESETUPNIB    PORTB |= (0<<7) | (0<<6) | (0<<5) | (0<<4);
#define RESETLOWNIB    PORTB |= (0<<3) | (0<<2) | (0<<1) | (0<<0);
#define INVERTPORTD     PORTD = ~PORTD;
 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <avr/pgmspace.h>
 
uint8_t count;
uint8_t tone;
uint8_t timeout;
int ticks;
 
void delay_ms(void){
    int i;
    for(i=880u; i != 0; i--);
}
 
void delay(uint8_t tms){
    uint8_t    i;
    for(i = tms; i != 0; i--){
        delay_ms();
    }
}
 
ISR(TIMER1_COMPA_vect){
    static uint8_t tck = 0;
    cli();
    //the following code works as a multiplexer
    if (tone == 0 | tone == 8 | tone == 16 | tone > 26){            
                    // to choose Hz singal
        if(tck == 47){
            tck = 0;    // reset count to create a 277 Hz signal
            INVERTPORTD;    // create a squeare signal
        }
        else{
            tck++;        // increment the count
        }
    }        
    else if(tone == 2 | tone == 10 | tone == 18 | tone == 24){        
                    // to choose  Hz
        if(tck == 45){
            tck = 0;
            INVERTPORTD;
        }
        else{
            tck++;
        }
    } 
    else if(tone == 4 | tone == 5| tone == 6 | tone == 14 | tone == 20 | tone == 22){
                    // Hz
        if(tck == 39){
            tck = 0;
            INVERTPORTD;
        }
        else{
            tck++;
        }
    }
    else if(tone == 12){        //  Hz
        if(tck == 38){
            tck = 0;
            INVERTPORTD;
        }
        else{
            tck++;
        }
    }
    else if(tone % 2 != 0 && tone != 5){    //odd = silence
        PORTD = 0x00;//Silence
    }
    else{
        //In case for error reset tone
        tone = 0;    // make sure we have it within its limits
        tck = 0;    // avoid overflow if error
    }
    sei();
}
 
ISR(TIMER0_OVF_vect){
    cli();
    count--;
    if (ticks == 11){
        ticks = 0;
        if (count == 0x00){
            RESETLOWNIB;
            count = 0x0f;
        } 
        else{
            PORTB = (PORTB & 0xf0) | (count & 0x0f);
        }
        tone++;
        tone = tone % 32;    // tone will go from 0 to 3
    }
    else{
        ticks++;
    }
    sei();
}
 
void iniconf(void){
    cli();            // Disable interrupts
 
    /* Program timer0    */
    TCNT0 = 0x00;        // Reset timer count
    TCCR0A |= 0x00;        // Normal functioning, clk_io/1024
    TCCR0B |= 0x05;
    TIMSK0 |= _BV(TOIE0);    // Enable interrupts when overflow
 
    /* Program timer1    */
    TCNT1H = 0x00;        // Reset timer count
    TCNT1L = 0x00;
    OCR1AH = 0x01;        // Set output compare value
    OCR1AL = 0xFF;
    TCCR1A |= 0x00;        // Normal functioning, clk_io
    TCCR1B |= 0x09;
    TIMSK1 |= _BV(OCIE1A);    // Enable interrupts when overflow
 
    sei();            // Enable interrupts
 
    /* Program the ports    */
    DDRB = 0xff;        // PortB as output port
    DDRC = 0x01;        // PortC0 for wave output
    DDRD = 0xff;
}
 
int main(void){
    /* Initialize the system    */
    iniconf();
 
    /* Initialize goblal variables    */
    count = 0;
    ticks = 0;
    tone = 0;
 
    while(1){
        delay(50);
        PORTB = (PORTB & 0x0f) | 0xf0; //SETUPNIB;
        delay(50);
        PORTB = (PORTB & 0x0f); //RESETUPNIB;
    }
}
page_revision: 13, last_edited: 1209614305|%e %b %Y, %H:%M %Z (%O ago)
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License