Hi.
I write to you because right now I'm working on an audio generating program by PWM (the same that is located on the playground).
Now, i'm interested in getting the data from a serial communication with XBEE, so I can have more than 13000 samples that the EEPROM can store.
The problem here is that when I get the characters by the Serial comm, the audio is broken.
If somebody has done something alike or knows how to fix this, please help me.
Thanks in advice
This is the code:
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#define SAMPLE_RATE 16000
int ledPin = 13;
int speakerPin = 11;
volatile uint16_t sample;
byte incomingByte;
// This is called at 8000 Hz to load the next sample.
ISR(TIMER1_COMPA_vect) {
OCR2A = Serial.read();
}
void startPlayback()
{
pinMode(speakerPin, OUTPUT);
Serial.println("---Iniciando playback---");
// 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 = Serial.read();
// 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);
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);
}
void setup()
{
pinMode(ledPin, OUTPUT);
//startPlayback();
//Iniciar el serial a 115200 bps
Serial.begin(115200);
pinMode(speakerPin, OUTPUT);
}
void loop()
{
//Checar si estan llegando datos
if(Serial.available()>0){
startPlayback();
delay(3000);
}
}
Where is the serial data coming from? Are you sure that it's sending data fast enough?
It is coming from the XBEE, and it has a 115200 bps rate, so I think they're coming fast enough.
What does "the audio is broken" mean?
Audio broken means that you can hear only noise.
And the data yes, it's being sent as char, do you know how to convert a char array to a number? Because if I send the "123" number, the arduino gets the numbers separately, so it gets "1", "2","3".
Yes, I have one XBEE sending the serial data to another XBEE connected to the arduino.
Like this:
COMPUTER---XBEE---XBEE---ARDUINO
Its just I'm a newbie on all this of arduino and xbee. I've never worked with AVR microcontrollers (only Microchip), and I'm having my doubts regarding to PWM management.
I'll try to make the sender send the whole number, so arduino gets it right.
Another doubt, in the Playground PWM audio generation example, do you know if the numbers that are generated in the .h file are taken as integers, bytes or chars by the arduino, so it can generate the audio?
Hello.
I have partially solved my problem about receiving the data and sending it to the PWM outputs.
Now, the problem is that the sound is getting too slow.
The code is the following one:
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#define SAMPLE_RATE 8000
int a = 0;
int ledPin = 13;
int speakerPin = 11;
char inData[3];
char numero[3]; // Allocate some space for the string
char inChar; // Where to store the character read
byte index = 0; // Index into array; where to store the character
long arraySize = 999999;
byte num;
// This is called at 8000 Hz to load the next sample.
ISR(TIMER1_COMPA_vect) {
while(Serial.available() > 0) // Don't read unless you know there is data
{
digitalWrite(ledPin,HIGH);
if(index < arraySize) // One less than the size of the array
{
inChar = Serial.read(); // Read a character
//Receive chars until a comma is get
if (inChar!=','){
inData[index] = inChar; // Store it
index++; // Increment where to write next
inData[index] = '\0'; // Null terminate the string
}
//If the char is a comma, send the char array to another array
if(inChar==','){
for(int i = 0; i<3;i++){
numero[i]='\0';//Erase every char of "numero" to write again on it
if(inData[i]!='\0')
numero[i] = inData[i];
}
//Transform it to an integer
char * thisChar = numero;
a = atoi(thisChar);
//Transform it to a byte
num = a;
index = 0; // Set index on position 0
}
}
OCR2A = num;//Send the byte by OCR2A
}
}
void startPlayback()
{
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 = num;
// 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);
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);
}
void setup()
{
pinMode(ledPin, OUTPUT);
//startPlayback();
//Iniciar el serial a 115200 bps
Serial.begin(115200);
pinMode(speakerPin, OUTPUT);
startPlayback();
}
void loop()
{
while (true){
}
}
An ISR is supposed to be very quick. Reading serial data in an ISR is not quick. I suspect that you are missing a lot of interrupts because they are happening while your ISR is running. During an ISR, interrupts are disabled, by default. You are not re-enabling them.
I've tried everything to do this work, but it seems that with serial data reading it won't be able to work at a such high speed as I want it.
Is it better for me to buy a DAC and change the project? or what else should I do? Please help me, I'm still a newbie here... thanks
I have found the problem.
As you said, it was interference. If I shut off all the cellphones around and disconnect the WiFi, there's no distortion on the sound.
How can I change the XBEE channel or frequency?
Thanks