Go Down

Topic: Problem: Custom PWM frequency interferes with SoftSerial (Read 662 times) previous topic - next topic

Apr 05, 2013, 04:46 pm Last Edit: Apr 05, 2013, 05:00 pm by ph77 Reason: 1
Hi! I'm working on a synthesizer that receives MIDI input and generates four waveforms. The waveforms are generated in a timer proc running at 8khz. The waveforms are mixed together and outputted using PWM. The default PWM frequency is too low, so PWM frequency is set to a higher value.

The problem is that I'm losing MIDI data randomly. I think that the timer setup or custom PWM frequency is causing some timing problems with the SoftwareSerial library. If I disable the timer and PWM, MIDI input works without problems.

I believe the culprit is the initialization code in setup() which I don't fully understand. I've copied it from another sketch that uses PWM for audio output.

EDIT: it is also possible that the timer interrupts the serial receiving function just when it is receiving bits and the timing will be off...

Code: [Select]


#include <SoftwareSerial.h>

SoftwareSerial MIDI(10, 11);

ISR(TIMER0_COMPA_vect) // called at 8 KHz
{
 // ...audio sample is generated here...

 // output using PWM
 OCR2B = sample;
}

void setup()
{
 // SETUP PWM & TIMER
 // SOMETHING HERE IS MESSING UP WITH MIDI

 asm("cli");
 CLKPR = 0x80;
 CLKPR = 0x80;
 
 DDRC = 0x12;
 DDRD = 0xff;
 
 TCCR0A = 0x02;
 TCCR0B = 0x02;  // clkIO/8, so 1/8 MHz
 OCR0A = 125;    // 8 KHz
 
 TCCR2A=0b10100011;
 TCCR2B=0b00000001;
 
 TIMSK0 = 0x02;
 
 asm("sei");

 MIDI.begin(31250);

 // for debugging...
 //Serial.begin(9600);
}

byte command;

void readMIDI() {
 while(MIDI.available() > 0) {
   if(MIDI.peek() & 128) {
     command = MIDI.read();
   }
   
   if((command & 0xf0) == MIDI_NOTE_ON) {
     byte channel = command & 0xf;
     byte note = MIDI.read();
     byte velocity = MIDI.read();

     //... play/stop waveforms here...

     // debug...
     //Serial.print("NOTE_ON: channel ");
     //Serial.print(channel);
     //Serial.print(" note ");
     //Serial.print(note);
     //Serial.print(" velocity ");
     //Serial.println(velocity);
   } else {
     // ignore other midi commads
     MIDI.read();
   }
 }
}

void loop() {
   readMIDI();
}


Any ideas?

g_u_e_s_t

timer0 is used by arduino for a lot of different things.  i would suggest switching your pwm to timer1 or timer2.

SoftwareSerial library doesn't seem to be using timers at all. It's using the Pin Change Interrupt on the RX pin so this is my guess what is happening:

1) Timer0 causes an interrupt, the CPU jumps to the audio interrupt handler
2) Incoming serial data from MIDI causes Pin Change Interrupt but since there is an interrupt already being processed, the Pin Change Interrupt gets queued
3) The audio interrupt returns
4) The pending Pin Change Interrupt is now triggered and SoftwareSerial starts reading data from the RX pin but it is already too late and incorrect data is received.

Does this sound plausible?

Hmm, maybe it would work if I could enable nested interrupts within the audio interrupt routine...

Huzzah! It works!

Just added a "sei" at the start of the interrupt handler and the problem disappeared.

Go Up