Audio project- changing pin number in register

Hi everyone, this is my first forum post!

I have successfully reproduced this project that I found online: The Yeah! Woo! machine

(Im only using one Atmega not two like in the project)

My next objective for my project is to add another speaker that produces a tone, using the tone() function to play some simple melody over the top of the sample. One of the caveats of this function is that it breaks PWM on pin 11 and 3.

The existing project uses pin 11 as its main output. Here is the main code body that this topic is concerned with: PCM/PCM.c at master · damellis/PCM · GitHub

void startPlayback(unsigned char const *data, int length)
{
  sounddata_data = data;
  sounddata_length = length;

  pinMode(speakerPin, OUTPUT);
  
  // Set up Timer 2 to do pulse width modulation on the speaker
  // pin.
  
  // Use internal clock (datasheet p.160)
  ASSR &= ~(_BV(EXCLK) | _BV(AS2));
  
  // Set fast PWM mode  (p.157)
  TCCR2A |= _BV(WGM21) | _BV(WGM20);
  TCCR2B &= ~_BV(WGM22);
  
  // Do non-inverting PWM on pin OC2A (p.155)
  // On the Arduino this is pin 11.
  TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
  TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
  
  // No prescaler (p.158)
  TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
  
  // Set initial pulse width to the first sample.
  OCR2A = pgm_read_byte(&sounddata_data[0]);
  
  
  // Set up Timer 1 to send a sample every interrupt.
  
  cli();
  
  // Set CTC mode (Clear Timer on Compare Match) (p.133)
  // Have to set OCR1A *after*, otherwise it gets reset to 0!
  TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
  TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
  
  // No prescaler (p.134)
  TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
  
  // Set the compare register (OCR1A).
  // OCR1A is a 16-bit register, so we have to do this with
  // interrupts disabled to be safe.
  OCR1A = F_CPU / SAMPLE_RATE;    // 16e6 / 8000 = 2000
  
  // Enable interrupt when TCNT1 == OCR1A (p.136)
  TIMSK1 |= _BV(OCIE1A);
  
  lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
  sample = 0;
  sei();
}

void stopPlayback()
{
  // Disable playback per-sample interrupt.
  TIMSK1 &= ~_BV(OCIE1A);
  
  // Disable the per-sample timer completely.
  TCCR1B &= ~_BV(CS10);
  
  // Disable the PWM timer.
  TCCR2B &= ~_BV(CS10);
  
  digitalWrite(speakerPin, LOW);
}

What I would like to do is change pin 11 in the register to any other PWM pin except from pin 3, which is also broken by the tone function.

image

In the notes you can see that pin 0C2A is pin 11. I would like to change this to a different pin so when i use the tone function it doesnt break the sample playback.

Im not sure if this is even possible, as to be honest I was only introduced to the concept of registers 2 days ago. So apologies if this is in the wrong place.

Cheers!

This diagram (Fig-1) may help you to understand that the PWM signal pins are permanently connected with the specified TC Modules, which has been also mentioned in post #2 @Delta_G .


Figure-1:

thanks for your reply! I understand that 0C2A cannot be changed from pin 11, I would like to change it to a different pin but I dont know what the other pins names are in the register. like if 0C2A = pin 11 what would e.g pin 5 be?

Thats awesome thank you! Would I be able to change 0C2A to 0C1A and the function of the code wouldnt change?

image

in this code is 0C2A = COM2A1? So if I wanted to change 0C2A to 0C1A then would I change COM2A1 to COM1A1?

Let us try to understand the meanings of the symbolic names COM1A0, COM1A1, and OC1A with the help of an example: (The meanings of other symbolic names will be similar.)

Assume that we wish to generate the following PWM signal (Fig-1, say: 20 kHz) at DPin-9 using TCNT1/TC1 (Timer/Counter 1).


Figure-1:

1. The symbolic name of the PWM signal is OC1A (Output Compare Pin Channel-A of TC1) which is always available on DPin-9. There is now way that this PWM signal can be routed to any other DPin.

2. The signal is non-inverting which means that DPin-9 is intially at HIGH state. This is determined by the COM1A1 bit (Compare Output Mode Bit-1 for Channel-A of TC1) and COM1A0 bit of TCCR1A Register (Fig-2).
TCCR1Ax
Figure-2:

According to data sheets, COM1A1 = 1 (HIGH), COM1A0 = 0 (LOW) make the PWM signal non-inverting. The codes are:

TCCR1A = 0x00;
biSet(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A0);

3. This is a single slope Fast PWM signal of Mode-14 (see data sheets). The Mode-14 is selected by the WGM13, WGM12, WGM11, and WGM10 (Waveform Generation Bit-0 of TC1) bits of TCCR1 (Fig-2)/TCCR1B (Fig-3) Registers.
TCCR1Bx
Figure-3:

The codes are:

TCCR1B = 0x00;
TCCR1B |= bit(WGM13) | bit(WGM1;   //WGM13 = 1, WGM12 = 1
TCCR1A |= bit(WGM11);  //WGM11 = 1, WGM10 = 0   //1110B = 14

4. The frequency of the PWM signal is given below (for derivation, see data sheets); where, the frequency is detrmined by 16-bit ICR1 (Input Capture Register of TC1) Register and the duty cycle is determined by OCR1A (Output Compare Register of TC1 for Channel-A).

f = clkSYS/(N x (1 + ICR1))
where:
f = 20 000 Hz
clkSYS = 16 000 000 Hz
N = TC1 Clock Prescaler/Divider (1, 8, 64, 256, 1024)
ICR1 = 799 with N = 1

5. For 50% duty cycle, take --
OCR1A = 400 //half of (1 + ICR1)

6. Now, start TC1/TCN1 with the help of CS12 (Clock Selection Bit-2 for TC1) = 0, CS11 = 0, and CS10 = 1 (001 = 1 = N) of TCCRB register.
TCCR1B |= bit(CS10);

7. Complete sketch.

#define OC1A 9

void setup()
{
  Serial.begin(9600);
  pinMode(OC1A, OUTPUT); //Ch-A
  //-------------------------------
  TCCR1A = 0x00;   //reset
  TCCR1B = 0x00;   //TC1 reset and OFF
  //fOC1A/B = clckSys/(N*(1+ICR1); Mode-14 FPWM; OCR1A controls duty cycle
  // 20000 Hz = 16000000/(256*(1+ICR1) N=1, (8,64,256,1024)==> ICR1 = 799
  TCCR1A |= bit(WGM11); //Mode-14 Fast PWM
  TCCR1B |= bit(WGM13) | bit(WGM12);    //Mode-14 Fast PWM
  bitSet(TCCR1A,COM1A1);
  bitClear(TCCR1A, COM1A0);  //Non-invert: HIGH-LOW
  ICR1 = 799;   // TOP for 20000 Hz frequnecy
  OCR1A = 62;  //= 50% duty cycle
  TCNT1 = 0;
  TCCR1B |= bit(CS10);//TC1 statrt with N = 1;
}

void loop(){}

Hey, please see the link I attached at the beginning ( PCM/PCM.c at master · damellis/PCM · GitHub)

The image i posted can be seen here on line 110. Thanks :slight_smile:

Hi can you not see it? I tried to attach it...

the code is on line 110. Apologies I may have pasted it incorrectly. It did say 'this link has already been posted, are you sure you want to post it again'.

( PCM/PCM.c at master · damellis/PCM · GitHub)

did it work this time?

Basically im trying to recreate the yeah woo! machine that i also linked above (will try to paste the link again)

If that doesnt work Its at the very top 'Yeah! Woo! machine'

It plays back a sample of encoded audio at 8khz using an array. Ive managed to get it to reverse the playback and change the rate of playback. Now id like to add another speaker which plays a tone.

The first part of the code is the logic that plays the sample. The part that this topic is concerned with is the startPlayback function that is defined from line 101 to line 153. As you know id just like to change the pin that startPlayback function is using, as the tone function breaks pins 11 and 3.

Hope that clears everything up, if not im happy to give more info :slight_smile:

Hi im really sorry, ill paste in the code.

void startPlayback(unsigned char const *data, int length)
{
  sounddata_data = data;
  sounddata_length = length;

  pinMode(speakerPin, OUTPUT);
  
  // Set up Timer 2 to do pulse width modulation on the speaker
  // pin.
  
  // Use internal clock (datasheet p.160)
  ASSR &= ~(_BV(EXCLK) | _BV(AS2));
  
  // Set fast PWM mode  (p.157)
  TCCR2A |= _BV(WGM21) | _BV(WGM20);
  TCCR2B &= ~_BV(WGM22);
  
  // Do non-inverting PWM on pin OC2A (p.155)
  // On the Arduino this is pin 11.
  TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
  TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
  
  // No prescaler (p.158)
  TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
  
  // Set initial pulse width to the first sample.
  OCR2A = pgm_read_byte(&sounddata_data[0]);
  
  
  // Set up Timer 1 to send a sample every interrupt.
  
  cli();
  
  // Set CTC mode (Clear Timer on Compare Match) (p.133)
  // Have to set OCR1A *after*, otherwise it gets reset to 0!
  TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
  TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
  
  // No prescaler (p.134)
  TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
  
  // Set the compare register (OCR1A).
  // OCR1A is a 16-bit register, so we have to do this with
  // interrupts disabled to be safe.
  OCR1A = F_CPU / SAMPLE_RATE;    // 16e6 / 8000 = 2000
  
  // Enable interrupt when TCNT1 == OCR1A (p.136)
  TIMSK1 |= _BV(OCIE1A);
  
  lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
  sample = 0;
  sei();
}

void stopPlayback()
{
  // Disable playback per-sample interrupt.
  TIMSK1 &= ~_BV(OCIE1A);
  
  // Disable the per-sample timer completely.
  TCCR1B &= ~_BV(CS10);
  
  // Disable the PWM timer.
  TCCR2B &= ~_BV(CS10);
  
  digitalWrite(speakerPin, LOW);
}```

(i thought it would be annoying to put the code in, didnt realise it had a built in formatter)