5
\$\begingroup\$

I want to play some short audio clips (they range from 50ms to 100ms) from my atmega168 microcontroller and output it to a cheap speaker.

I was following this tutorial but because I'm using a atmega168 and not a atmega2560, I had to modify my code as well as replaced the sound.

Like in the tutorial, I'm using a 8MHz internal oscillator (I set LFUSE = 0xE2 in the Makefile) and all the PWM settings are the same...which means

  • Using a 16 Timer/Counter1 with 8-bit PWM resolution
  • Set OC1A on compare match (and clear on bottom)
  • No prescaling

As for the sound clip, I sampled it at 15.625 kHz and exported it to a 8-bit PCM wav file. I then get a hex dump via xxd -i which gives me the output c file. I didn't remove the header as I figured I can just skip it by just going to the 44th position of the output array as it is the data position.

The problem I'm having is that while I do get my (distorted and soft) sound, there is this loud beeping noise that is overlapping with my sound and I have no idea what is causing it.

Unfortunately I don't have access to an oscilloscope, so I can't really nail down if it is the way I sampled my sound, or if my sound is way too short or something that's beyond my current understanding.

Main.c

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/power.h>
#include <util/setbaud.h>
#include <avr/interrupt.h>

#include "switch.h"
#define WAV_DATA_POSITION 44 
#define BASE_LEVEL 0xff

unsigned int  count=WAV_DATA_POSITION;;
volatile uint16_t timerOverflowCount;
unsigned char sample_count =2;

// This interrupt service reads new data, 
ISR(TIMER1_OVF_vect){

    sample_count--;

    if(sample_count ==0)
    {
        sample_count = 2;
        if (count < switchResampled_len){
            OCR1A= pgm_read_byte(&switch_raw[count]);
            count++;
        }
    }
    else{
        OCR1A= BASE_LEVEL;
    }
}

void pwm_init()
{

    SREG |=0x80;

    //select Fast PWM with 8-bit resolution,set OC1A on compare match and clear at bottom
    TCCR1A|=(1<<COM1A1)|(1<<COM1A0)|(1<<WGM10);  

    //No prescaling therefore timer frequency = 8 MHz and PWM frequency = 31250 Hz
    TCCR1B|=(1<<WGM12)|(1<<CS10);          

    TCNT1=0x00;

    OCR1A=BASE_LEVEL;
    //Enable the timer interrupt
    TIMSK1 |= ( 1 <<TOIE1); 

    //Clear all interupt flags
    TIFR1|=(1<<ICF1)|(1<<OCF1B)|(1<<OCF1A)|(1<<TOV1); 

    //Enable interrupts
    sei(); 

}


int main(void)
{
    //PB1 is OC1A. Use this for output
    DDRB |= (1 << PB1);
    pwm_init();

    /* insert your hardware initialization here */
    for(;;){

        /* insert your main loop code here */
    }
    return 0;   /* never reached */
}

Switch.h (Sound file)

const unsigned char switch_raw[] PROGMEM = {
  0x52, 0x49, 0x46, 0x46, 0x0d, 0x04, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45,
  0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
  0x09, 0x3d, 0x00, 0x00, 0x09, 0x3d, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00,
  0x64, 0x61, 0x74, 0x61, 0x4b, 0x03, 0x00, 0x00, 0x80, 0x7f, 0x7f, 0x7e,
  0x7f, 0x80, 0x7f, 0x80, 0x7f, 0x7f, 0x7e, 0x7f, 0x80, 0x7f, 0x7e, 0x7e,
  0x7f, 0x80, 0x7f, 0x7f, 0x7e, 0x7d, 0x7f, 0x81, 0x7e, 0x7e, 0x7f, 0x7f,
  0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7d, 0x80, 0x81, 0x7e, 0x7b, 0x7e, 0x7f,
  0x7f, 0x81, 0x7e, 0x7e, 0x7d, 0x80, 0x80, 0x7e, 0x7e, 0x7e, 0x7d, 0x7e,
  0x80, 0x80, 0x7e, 0x7c, 0x7d, 0x7f, 0x81, 0x7f, 0x7e, 0x81, 0x7d, 0x7b,
  0x80, 0x7f, 0x7f, 0x83, 0x7f, 0x7b, 0x7f, 0x80, 0x7f, 0x82, 0x7f, 0x7e,
  0x80, 0x7e, 0x81, 0x7f, 0x7f, 0x81, 0x7d, 0x7e, 0x80, 0x7e, 0x80, 0x7e,
  0x7f, 0x85, 0x80, 0x83, 0x7b, 0x77, 0x83, 0x81, 0x86, 0x7e, 0x7b, 0x7e,
  0x81, 0x83, 0x7f, 0x7f, 0x7f, 0x7f, 0x81, 0x82, 0x7f, 0x7e, 0x7e, 0x7e,
  0x81, 0x83, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x7e, 0x7e, 0x80, 0x83,
  0x7f, 0x7d, 0x7b, 0x80, 0x80, 0x94, 0x79, 0x68, 0x87, 0x75, 0x98, 0x87,
  0x6e, 0x7f, 0x8c, 0x81, 0x54, 0x7c, 0xc4, 0x8a, 0x46, 0x31, 0x72, 0xe2,
  0xc1, 0x8a, 0x41, 0x2f, 0x71, 0xb3, 0xc8, 0x96, 0x51, 0x41, 0x67, 0x9c,
  0xb1, 0x98, 0x74, 0x57, 0x5f, 0x76, 0x99, 0xa8, 0x96, 0x72, 0x5b, 0x64,
  0x7e, 0x97, 0xa1, 0x8f, 0x6f, 0x56, 0x66, 0x89, 0xa0, 0x9d, 0x7e, 0x62,
  0x5c, 0x78, 0x93, 0x9d, 0x90, 0x70, 0x58, 0x68, 0x88, 0x9c, 0x95, 0x7c,
  0x66, 0x64, 0x7c, 0x90, 0x95, 0x82, 0x6f, 0x6b, 0x75, 0x86, 0x8e, 0x88,
  0x79, 0x72, 0x75, 0x80, 0x88, 0x89, 0x81, 0x78, 0x74, 0x78, 0x84, 0x85,
  0x84, 0x7d, 0x7b, 0x7c, 0x7b, 0x80, 0x85, 0x87, 0x81, 0x76, 0x74, 0x7e,
  0x82, 0x89, 0x84, 0x7c, 0x77, 0x78, 0x84, 0x83, 0x84, 0x7c, 0x78, 0x7b,
  0x83, 0x83, 0x80, 0x83, 0x80, 0x7d, 0x7a, 0x7e, 0x83, 0x87, 0x82, 0x79,
  0x78, 0x7e, 0x87, 0x8b, 0x80, 0x76, 0x79, 0x7e, 0x87, 0x87, 0x80, 0x7a,
  0x7a, 0x80, 0x82, 0x84, 0x81, 0x7f, 0x7e, 0x7d, 0x7a, 0x82, 0x88, 0x7f,
  0x7a, 0x79, 0x7f, 0x85, 0x85, 0x7f, 0x7d, 0x7a, 0x7d, 0x83, 0x87, 0x7e,
  0x78, 0x7e, 0x7d, 0x83, 0x7e, 0x81, 0x81, 0x80, 0x7f, 0x81, 0x81, 0x7c,
  0x7e, 0x7a, 0x81, 0x82, 0x81, 0x84, 0x82, 0x79, 0x78, 0x84, 0x86, 0x7d,
  0x7b, 0x7e, 0x7f, 0x7c, 0x7d, 0x7d, 0x82, 0x80, 0x80, 0x7e, 0x7d, 0x85,
  0x84, 0x7b, 0x75, 0x7c, 0x7d, 0x84, 0x88, 0x84, 0x7b, 0x7b, 0x7d, 0x7c,
  0x88, 0x88, 0x7b, 0x78, 0x78, 0x78, 0x88, 0x8d, 0x80, 0x78, 0x7f, 0x86,
  0x86, 0x83, 0x7c, 0x71, 0x76, 0x84, 0x97, 0x93, 0x70, 0x6d, 0x68, 0x78,
  0xc5, 0xb6, 0x5f, 0x37, 0x2a, 0x69, 0xc1, 0xc9, 0xa0, 0x7a, 0x52, 0x50,
  0x71, 0x8a, 0x9d, 0xa0, 0x8b, 0x69, 0x67, 0x68, 0x79, 0x94, 0x96, 0x87,
  0x7b, 0x81, 0x84, 0x7e, 0x7d, 0x7e, 0x75, 0x72, 0x89, 0x8e, 0x88, 0x86,
  0x6d, 0x6d, 0x7a, 0x82, 0x8d, 0x8e, 0x78, 0x76, 0x7f, 0x7d, 0x7e, 0x82,
  0x80, 0x7b, 0x7d, 0x7a, 0x7b, 0x86, 0x89, 0x80, 0x7b, 0x75, 0x77, 0x7f,
  0x83, 0x84, 0x86, 0x81, 0x81, 0x7e, 0x73, 0x7c, 0x83, 0x7f, 0x85, 0x82,
  0x7d, 0x83, 0x7d, 0x7d, 0x80, 0x7b, 0x7c, 0x86, 0x85, 0x7e, 0x7b, 0x74,
  0x7d, 0x86, 0x85, 0x8b, 0x81, 0x76, 0x78, 0x78, 0x7e, 0x89, 0x83, 0x7e,
  0x80, 0x78, 0x7f, 0x84, 0x81, 0x84, 0x7a, 0x77, 0x7f, 0x81, 0x84, 0x81,
  0x78, 0x7a, 0x7e, 0x84, 0x8e, 0x81, 0x72, 0x75, 0x77, 0x83, 0x8b, 0x84,
  0x84, 0x7b, 0x72, 0x7a, 0x83, 0x84, 0x85, 0x7e, 0x78, 0x7e, 0x7d, 0x7f,
  0x82, 0x80, 0x7f, 0x7e, 0x7f, 0x81, 0x7e, 0x7b, 0x80, 0x85, 0x84, 0x82,
  0x7d, 0x7a, 0x7d, 0x7f, 0x84, 0x84, 0x7f, 0x79, 0x7b, 0x82, 0x80, 0x80,
  0x81, 0x80, 0x81, 0x81, 0x7f, 0x7e, 0x79, 0x79, 0x81, 0x86, 0x85, 0x82,
  0x7d, 0x77, 0x7a, 0x7e, 0x86, 0x8b, 0x84, 0x7a, 0x7a, 0x7d, 0x7e, 0x83,
  0x83, 0x80, 0x7e, 0x7c, 0x7c, 0x80, 0x7f, 0x80, 0x7f, 0x7f, 0x80, 0x7e,
  0x7f, 0x80, 0x7f, 0x7c, 0x80, 0x7d, 0x7e, 0x85, 0x81, 0x7c, 0x7d, 0x7b,
  0x7c, 0x81, 0x80, 0x7d, 0x7f, 0x80, 0x7d, 0x82, 0x84, 0x7e, 0x7a, 0x7e,
  0x7e, 0x7f, 0x82, 0x7d, 0x7e, 0x81, 0x7f, 0x7d, 0x7e, 0x81, 0x83, 0x83,
  0x80, 0x79, 0x77, 0x7c, 0x83, 0x87, 0x86, 0x7f, 0x75, 0x79, 0x7e, 0x85,
  0x86, 0x80, 0x7a, 0x78, 0x7c, 0x7f, 0x83, 0x81, 0x82, 0x7d, 0x7a, 0x7f,
  0x81, 0x80, 0x80, 0x7c, 0x7b, 0x80, 0x7e, 0x80, 0x85, 0x84, 0x7d, 0x7b,
  0x7a, 0x7f, 0x83, 0x82, 0x80, 0x7c, 0x7e, 0x7e, 0x80, 0x81, 0x7f, 0x7f,
  0x7e, 0x7f, 0x82, 0x7f, 0x7c, 0x7b, 0x7b, 0x82, 0x84, 0x83, 0x7e, 0x7c,
  0x7b, 0x7e, 0x83, 0x81, 0x7f, 0x7d, 0x7e, 0x7f, 0x83, 0x81, 0x7e, 0x7d,
  0x7b, 0x7d, 0x80, 0x82, 0x81, 0x80, 0x7f, 0x7d, 0x7f, 0x80, 0x7f, 0x80,
  0x81, 0x80, 0x7f, 0x7d, 0x7d, 0x7e, 0x81, 0x82, 0x80, 0x7d, 0x7f, 0x81,
  0x80, 0x7f, 0x7d, 0x7c, 0x7e, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7e, 0x7f,
  0x7d, 0x7e, 0x80, 0x81, 0x80, 0x7f, 0x7e, 0x7c, 0x7f, 0x81, 0x80, 0x80,
  0x80, 0x7f, 0x7e, 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81, 0x80, 0x7d,
  0x7c, 0x7f, 0x80, 0x83, 0x81, 0x7c, 0x7b, 0x7c, 0x7f, 0x82, 0x82, 0x7f,
  0x7d, 0x7c, 0x7e, 0x80, 0x81, 0x81, 0x7f, 0x7f, 0x7d, 0x7d, 0x80, 0x82,
  0x7f, 0x7e, 0x7d, 0x7d, 0x80, 0x83, 0x82, 0x7f, 0x7f, 0x7a, 0x7d, 0x80,
  0x81, 0x81, 0x80, 0x7e, 0x7f, 0x80, 0x7e, 0x7e, 0x7f, 0x80, 0x7f, 0x80,
  0x7f, 0x7e, 0x7d, 0x7f, 0x7e, 0x80, 0x81, 0x81, 0x80, 0x80, 0x7e, 0x7e,
  0x7e, 0x7d, 0x80, 0x7f, 0x80, 0x81, 0x7f, 0x7e, 0x7e, 0x7c, 0x7f, 0x82,
  0x7f, 0x80, 0x7f, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x7f, 0x7e, 0x7f,
  0x7f, 0x7f, 0x80, 0x7e, 0x7f, 0x7f, 0x80, 0x81, 0x7f, 0x7e, 0x7d, 0x7d,
  0x81, 0x82, 0x80, 0x7e, 0x7d, 0x7e, 0x80, 0x81, 0x7f, 0x80, 0x80, 0x7f,
  0x7f, 0x7d, 0x7f, 0x81, 0x80, 0x7f, 0x80, 0x7f, 0x7e, 0x7d, 0x7e, 0x4c,
  0x49, 0x53, 0x54, 0x48, 0x00, 0x00, 0x00, 0x49, 0x4e, 0x46, 0x4f, 0x49,
  0x43, 0x52, 0x44, 0x12, 0x00, 0x00, 0x00, 0x32, 0x30, 0x31, 0x36, 0x2d,
  0x31, 0x30, 0x2d, 0x31, 0x37, 0x20, 0x31, 0x37, 0x3a, 0x32, 0x37, 0x00,
  0x00, 0x49, 0x53, 0x46, 0x54, 0x22, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x76,
  0x66, 0x35, 0x37, 0x2e, 0x37, 0x32, 0x2e, 0x31, 0x30, 0x31, 0x20, 0x28,
  0x6c, 0x69, 0x62, 0x73, 0x6e, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x2d, 0x31,
  0x2e, 0x30, 0x2e, 0x32, 0x34, 0x29, 0x00, 0x69, 0x64, 0x33, 0x20, 0x46,
  0x00, 0x00, 0x00, 0x49, 0x44, 0x33, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x3c, 0x54, 0x58, 0x58, 0x58, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
  0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, 0x4c, 0x61, 0x76,
  0x66, 0x35, 0x37, 0x2e, 0x37, 0x32, 0x2e, 0x31, 0x30, 0x31, 0x54, 0x44,
  0x52, 0x43, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x32, 0x30, 0x31,
  0x36, 0x2d, 0x31, 0x30, 0x2d, 0x31, 0x37, 0x20, 0x31, 0x37, 0x3a, 0x32,
  0x37

};

const long switchResampled_len = 1045;
\$\endgroup\$
  • 1
    \$\begingroup\$ That is likely the fundamental PWM rate, wherein you are modifying the DUTY CYCLE but not the repetition rate. \$\endgroup\$ – analogsystemsrf Dec 21 '18 at 2:38
  • 3
    \$\begingroup\$ 1045 samples at 15.625kHz is only 67mSec worth of data. How are you hearing anything in only 67mSec? \$\endgroup\$ – brhans Dec 21 '18 at 2:46
  • \$\begingroup\$ By the way, instead of using PWM, there are cheap and accessible microcontrollers with true analog outputs (e.g. 12-bit resolution). \$\endgroup\$ – Nayuki Dec 21 '18 at 7:20
  • \$\begingroup\$ @analogsystemsrf What do you mean by "repetition rate"? \$\endgroup\$ – cheunste Dec 23 '18 at 0:57
  • \$\begingroup\$ @brhans The original file is 67 ms long...or at least according to audacity. These are sound effect files, so I wasn't joking when I said they're short sound clips. \$\endgroup\$ – cheunste Dec 23 '18 at 0:58
6
\$\begingroup\$

A 4MHz PWM generator (8MHz/2) At 8 bits has a 15.625kHz fundamental. If you are not filtering this out with at least a 7kHz high-order low-pass you will hear it loud and clear.

You need to bring this fundamental to at least above 20kHz or so to get it out of your hearing range, it will still annoy your dog though.

\$\endgroup\$
  • \$\begingroup\$ Or maybe he could use a notch filter. \$\endgroup\$ – Harry Svensson Dec 21 '18 at 4:06
  • \$\begingroup\$ Not sure this is the problem here, probably something more "fundamental" if you excuse the pun. As brhans mentions in a comment, he only plays 67 ms of audio. Where's the beep? You wouldn't even have time to react other than "Yeah, something made a burst of noise". Besides, the PWM frequency is 32 kHz according to the description. \$\endgroup\$ – pipe Dec 21 '18 at 10:32
  • \$\begingroup\$ @HarrySvensson what would be the point in letting through signals with a higher frequency as the fundamental. PWM needs low pass filtering, no matter how. You can even simply use a speaker with a bad high frequency response as a low pass. \$\endgroup\$ – wiebel Dec 21 '18 at 11:30
  • 1
    \$\begingroup\$ The fast PWM in the ATmega168 runs at the main clock frequency, there's no extra division involved. \$\endgroup\$ – pipe Dec 21 '18 at 15:27
  • 1
    \$\begingroup\$ @EdgarBrown There's no need to guess, the datasheet is public. \$\endgroup\$ – pipe Dec 21 '18 at 20:36
4
\$\begingroup\$

The code you copied has a bug, which probably caused the issues he also writes about.

sample_count--;

if(sample_count ==0)
{
    sample_count = 2;
    if (count < switchResampled_len){
        OCR1A= pgm_read_byte(&switch_raw[count]);
        count++;
    }
}
else{
    OCR1A= BASE_LEVEL;
}

Every other tick you set OCR1A=BASE_LEVEL and the next tick you set it to your sample level, but the comments in the example code hints that the else statement is misplaced. Try this:

sample_count--;

if(sample_count ==0)
{
    sample_count = 2;
    if (count < switchResampled_len){
        OCR1A= pgm_read_byte(&switch_raw[count]);
        count++;
    }
    else{
        OCR1A= BASE_LEVEL;
    }
}

(It was difficult not to reformat this to use consistent indents and bracket placement!)

\$\endgroup\$
  • \$\begingroup\$ I fixed the code, but that didn't seem to help unfortunately. \$\endgroup\$ – cheunste Dec 22 '18 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.