Stereoljud med Arduino (4 / 7 steg)
Steg 4: Stereoljudutgång med 8 bitars DAC och 44,1 kHz samplingsfrekvens
I den här koden jag skickar en sinus våg ut DACA och en såg våg ut DACB på samma gång och med en hastighet av 44,1 kHz. Detta är stereoljud, två separata ljudkanaler. För att få detta att fungera, jag kombinerade element sinus och såg mono kod från det sista steget och används WR och DACA/DACB stiften för att växla mellan de två DAC utgångarna.
Jag har ställt in ett avbrott som i det sista steget, men denna gång jag ställa upp med en hastighet av 2 * 44,1 = 88.2 kHz. Sedan varje gång avbrottet avrättades, jag alternerade mellan sänder något till DACA och DACB, så var och en fick ett prov under varje andra avbrott. Detta gör samplingsfrekvens på båda utgångarna 44,1 kHz. Innehållet i rutinen avbrott kopieras nedan:
digitalWrite (WR, hög), //hold utgångar-så nya DAC data inte få skickade innan vi är redo
om (kanal) {
PORTD = sine [index]; //send sine till digital stift 0-7
digitalWrite (outputSelector, låg); //select DACA
index ++, //increment indexvärde av en
om (index == 100) {//reset index om det når 100
index = 0;
}
}
annat {
PORTD = såg; //send såg till digital stift 0-7
digitalWrite (outputSelector, hög); //select DACB
såg ++, //increment såg värde av en
om (såg == 255) {//reset såg om den når 256 (håller produktionen inom 0-255 alltid)
såg = 0;
}
}
digitalWrite (WR, låg); //enable utgång igen
kanalen ^ = 1; //toggle kanal
När avbrotten startar Arduino uppsättningar WR pin-hög, detta tillfälligt innehåller DAC utgångarna på deras nuvarande spänningar och gör att vi kan skicka data till DAC utan att ändra den aktuella markerade DAC-sidan. Variabeln "kanal" växlar mellan värdena 0 och 1 varje tid avbrottet utför, omväxlande sinus och såg utdata. När "kanal" = 1, en från array "sine" har värdet DAC via PORTD. Nästa rad innehåller outputSelector PIN-koden (DACA/DACB stift) låg, som orsakar DACA väljas. Sedan ligger WR lågt, orsakar det nya sine värdet att mata via DACA. I nästa avbrott rutinen orsakar en liknande serie händelser såg värde med utgång från DACB.
< pre > //stereo ljud ut, samplingsfrekvens < = 44,1 kHz
av Amanda Ghassaei
Nov 2012
/*
* Detta program är fri programvara; Du kan vidaredistribuera det och/eller ändra
* det enligt villkoren i GNU General Public License som offentliggjorts av
* den Free Software Foundation; antingen version 3 av licensen, eller
* (på ditt alternativ) någon senare version.
*/
kontroll stift på TLC7528
#define outputSelector 8
#define CS 9
#define WR 10
byte såg = 0; //value såg våg
100 värden av sinus, centrerad på 127, amplitud på 127
byte sine [] = {127, 134, 142, 150, 158, 166, 173, 181, 188, 195, 201, 207, 213, 219, 224, 229, 234, 238, 241, 245, 247, 250, 251, 252, 253, 254, 253, 252, 251, 250, 247, 245, 241, 238, 234, 229, 224, 219, 213, 207, 201, 195, 188, 181, 173, 166, 158, 150, 142, 134, 127, 119, 111, 103, 95, 87, 80, 72, 65 58, 52, 46, 40, 34, 29, 24, 19, 15, 12, 8, 6, 3, 2, 1, 0, 0, 0, 1, 2, 3, 6, 8, 12, 15, 19, 24, 29, 34, 40, 46, 52, 58, 65, 72, 80, 87, 95, 103, 111, 119,};
byte index = 0; //index att retreive värden fom array "sine"
booleska kanal = 0. //state 0 skickar till en kanal, 1 skickar till andra kanalen
void setup() {
för (byte jag = 0; jag < 8; i ++) {
pinMode (i, matas); //set digital stift 0-7 som resultat
}
pinMode(outputSelector,OUTPUT);
pinMode(CS,OUTPUT);
pinMode(WR,OUTPUT);
(CLI); //stop avbrott
ställa in timer1 avbrott på ~88.2kHz (2 * 44,1 kHz)
TCCR1A = 0; / / Ställ in hela TCCR1A register till 0
TCCR1B = 0; / / samma för TCCR1B
TCNT1 = 0; //initialize värde till 0
Set jämför match registrera för 1hz steg
OCR1A = 180, / / = (16 * 10 ^ 6) / (88200 * 1) - 1 (måste vara < 65536)
Aktivera CTC läge
TCCR1B | = (1 << WGM12);
Ange CS10 bit för 1 prescaler
TCCR1B | = (1 << CS10);
Aktivera timern jämför avbrott
TIMSK1 | = (1 << OCIE1A);
SEI (); //enable avbrott
lågt inställd CS pin
digitalWrite(CS,LOW);
}
ISR(TIMER1_COMPA_vect) {//timer1 avbrott ~88.2kHz att skicka ljuddata (faktiskt på 88.398 kHz)
digitalWrite (WR, hög), //hold utgångar-så nya DAC data inte få skickade innan vi är redo
om (kanal) {
PORTD = sine [index]; //send sine till digital stift 0-7
digitalWrite (outputSelector, låg); //select DACA
index ++, //increment indexvärde av en
om (index == 100) {//reset index om det når 100
index = 0;
}
}
annat {
PORTD = såg; //send såg till digital stift 0-7
digitalWrite (outputSelector, hög); //select DACB
såg ++, //increment såg värde av en
om (såg == 255) {//reset såg om den når 256 (håller produktionen inom 0-255 alltid)
såg = 0;
}
}
digitalWrite (WR, låg); //enable utgång igen
kanalen ^ = 1; //toggle kanal
}
void loop() {
göra andra saker här
}
Som i det sista steget, min samplingsfrekvensen var inte exakt 88.2 kHz, det var faktiskt 88.398 kHz (något bättre än 88.2), så jag använder det numret i följande beräkningar:
varaktigheten av varje prov = 2 * 1/samplingsfrekvens
varaktigheten av varje prov = 2 * 1/88398 Hz = 22.6us
som i det sista steget är perioden av sinus och såg följande:
såg period = 22.6us * 256 = 5.8ms
sine period = 22.6us * 100 = 2.3ms
men om du titta på figurerna 2 och 3 du kommer att se att hela provet och utdata vågorna är mycket längre. Detta beror på att koden i rutinen avbrott är ineffektivt och det tar längre tid än 22.6us att verkställa. Att fixa detta jag hade att ersätta Arduino bibliotek kommando "digitalWrite" med mycket effektivare direkt pin manipulation kommandon i koden nedan. Du kan läsa mer om hur de fungerar här, du kan också läsa kommentarerna har jag lagt i koden nedan. Figurerna 4 och 5 visar resultaten från denna optimerad kod, kan du se att de period och -exempelprogram varaktigheterna är vad vi förväntar oss från beräkningarna.
< pre > //stereo ljud ut med 44,1 kHz samplingsfrekvens
av Amanda Ghassaei
Nov 2012
/*
* Detta program är fri programvara; Du kan vidaredistribuera det och/eller ändra
* det enligt villkoren i GNU General Public License som offentliggjorts av
* den Free Software Foundation; antingen version 3 av licensen, eller
* (på ditt alternativ) någon senare version.
*/
kontroll stift på TLC7528
#define outputSelector 8
#define CS 9
#define WR 10
byte såg = 0; //value såg våg
100 värden av sinus, centrerad på 127, amplitud på 127
byte sine [] = {127, 134, 142, 150, 158, 166, 173, 181, 188, 195, 201, 207, 213, 219, 224, 229, 234, 238, 241, 245, 247, 250, 251, 252, 253, 254, 253, 252, 251, 250, 247, 245, 241, 238, 234, 229, 224, 219, 213, 207, 201, 195, 188, 181, 173, 166, 158, 150, 142, 134, 127, 119, 111, 103, 95, 87, 80, 72, 65 58, 52, 46, 40, 34, 29, 24, 19, 15, 12, 8, 6, 3, 2, 1, 0, 0, 0, 1, 2, 3, 6, 8, 12, 15, 19, 24, 29, 34, 40, 46, 52, 58, 65, 72, 80, 87, 95, 103, 111, 119,};
byte index = 0; //index att retreive värden fom array "sine"
booleska kanal = 0. //state 0 skickar till en kanal, 1 skickar till andra kanalen
void setup() {
för (byte jag = 0; jag < 8; i ++) {
pinMode (i, matas); //set digital stift 0-7 som resultat
}
pinMode(outputSelector,OUTPUT);
pinMode(CS,OUTPUT);
pinMode(WR,OUTPUT);
(CLI); //stop avbrott
ställa in timer1 avbrott på ~88.2kHz (2 * 44,1 kHz)
TCCR1A = 0; / / Ställ in hela TCCR1A register till 0
TCCR1B = 0; / / samma för TCCR1B
TCNT1 = 0; //initialize värde till 0
Set jämför match registrera för 1hz steg
OCR1A = 180, / / = (16 * 10 ^ 6) / (88200 * 1) - 1 (måste vara < 65536)
Aktivera CTC läge
TCCR1B | = (1 << WGM12);
Ange CS10 bit för 1 prescaler
TCCR1B | = (1 << CS10);
Aktivera timern jämför avbrott
TIMSK1 | = (1 << OCIE1A);
SEI (); //enable avbrott
lågt inställd CS pin
digitalWrite(CS,LOW);
}
ISR(TIMER1_COMPA_vect) {//timer1 avbrott ~88.2kHz att skicka ljuddata (faktiskt på 88.398 kHz)
PORTB | = B00000100; //digitalWrite (WR, hög); //hold utgångar - så nya DAC data få inte skickas förrän vi är redo
om (kanal) {
PORTD = sine [index]; //send sine till digital stift 0-7
PORTB & = B11111110; //digitalWrite (outputSelector, låg); //select DACA
index ++, //increment indexvärde av en
om (index == 100) {//reset index om det når 100
index = 0;
}
}
annat {
PORTD = såg; //send såg till digital stift 0-7
PORTB | = B00000001; //digitalWrite (outputSelector, hög); //select DACB
såg ++, //increment såg värde av en
om (såg == 255) {//reset såg om den når 256 (håller produktionen inom 0-255 alltid)
såg = 0;
}
}
PORTB & = B11111011; //digitalWrite (WR, låg); //enable utgång igen
kanalen ^ = 1; //toggle kanal
}
void loop() {
göra andra saker här
}
Jag kommer också att notera här, som sedan CS pin hålls låg för varaktigheten av denna kod (inställning det höga vilja göra oduglig skriva nya data till antingen utgång), du kan frigöra en extra Arduino stift genom permanent fästa CS till marken och ta bort instanser av CS i Arduino koden.