Pages: [1]   Go Down
Author Topic: Problem: Custom PWM frequency interferes with SoftSerial  (Read 596 times)
0 Members and 1 Guest are viewing this topic.
Finland
Offline Offline
Jr. Member
**
Karma: 1
Posts: 93
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

#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?
« Last Edit: April 05, 2013, 10:00:14 am by ph77 » Logged

Offline Offline
Jr. Member
**
Karma: 2
Posts: 97
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Finland
Offline Offline
Jr. Member
**
Karma: 1
Posts: 93
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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...
Logged

Finland
Offline Offline
Jr. Member
**
Karma: 1
Posts: 93
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Huzzah! It works!

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

Pages: [1]   Go Up
Jump to: