@@ -1,41 +0,0 @@ | |||
#include <Audio.h> | |||
#include <Wire.h> | |||
#include <SPI.h> | |||
#include <SD.h> | |||
// Create the Audio components. These should be created in the | |||
// order data flows, inputs/sources -> processing -> outputs | |||
// | |||
AudioInputAnalog analogPinInput(A2); // analog A2 (pin 16) | |||
AudioOutputI2S audioOutput; // audio shield: headphones & line-out | |||
AudioOutputPWM pwmOutput; // audio output with PWM on pins 3 & 4 | |||
// Create Audio connections between the components | |||
// | |||
AudioConnection c1(analogPinInput, 0, audioOutput, 0); | |||
AudioConnection c2(analogPinInput, 0, audioOutput, 1); | |||
AudioConnection c3(analogPinInput, 0, pwmOutput, 0); | |||
// Create an object to control the audio shield. | |||
// | |||
AudioControlSGTL5000 audioShield; | |||
void setup() { | |||
// Audio connections require memory to work. For more | |||
// detailed information, see the MemoryAndCpuUsage example | |||
AudioMemory(12); | |||
// Enable the audio shield and set the output volume. | |||
audioShield.enable(); | |||
audioShield.volume(0.6); | |||
} | |||
void loop() { | |||
// Do nothing here. The Audio flows automatically | |||
// When AudioInputAnalog is running, analogRead() must NOT be used. | |||
} | |||
@@ -0,0 +1,41 @@ | |||
#include <Audio.h> | |||
#include <Wire.h> | |||
#include <SPI.h> | |||
#include <SD.h> | |||
#include "utility/pdb.h" | |||
// GUItool: begin automatically generated code | |||
AudioInputAnalog adc1; //xy=161,80 | |||
AudioOutputAnalog dac1; //xy=329,47 | |||
AudioOutputPWM pwm1; //xy=331,125 | |||
AudioConnection patchCord1(adc1, dac1); | |||
AudioConnection patchCord2(adc1, pwm1); | |||
// GUItool: end automatically generated code | |||
void setup() { | |||
// Audio connections require memory to work. For more | |||
// detailed information, see the MemoryAndCpuUsage example | |||
AudioMemory(12); | |||
} | |||
void loop() { | |||
// Do nothing here. The Audio flows automatically | |||
// When AudioInputAnalog is running, analogRead() must NOT be used. | |||
delay(200); | |||
Serial.print("PDB: SC="); | |||
Serial.print(PDB0_SC, HEX); | |||
Serial.print(", CONFIG="); | |||
Serial.print(PDB_CONFIG, HEX); | |||
Serial.print(", MOD="); | |||
Serial.print(PDB0_MOD); | |||
Serial.print(", IDLY="); | |||
Serial.print(PDB0_IDLY); | |||
Serial.print(", CH0C1="); | |||
Serial.print(PDB0_CH0C1); | |||
Serial.print(", DMA: "); | |||
Serial.print(dma_channel_allocated_mask, HEX); | |||
Serial.println(); | |||
} | |||
@@ -1,95 +0,0 @@ | |||
#include <Audio.h> | |||
#include <Bounce.h> | |||
#include <Wire.h> | |||
#include <SPI.h> | |||
#include <SD.h> | |||
const int myInput = AUDIO_INPUT_LINEIN; | |||
//const int myInput = AUDIO_INPUT_MIC; | |||
// Create the Audio components. These should be created in the | |||
// order data flows, inputs/sources -> processing -> outputs | |||
// | |||
//AudioInputAnalog analogPinInput(16); // analog A2 (pin 16) | |||
AudioInputI2S audioInput; // audio shield: mic or line-in | |||
AudioSynthWaveform toneLow(AudioWaveformSine); | |||
AudioSynthWaveform toneHigh(AudioWaveformTriangle); | |||
AudioMixer4 mixerLeft; | |||
AudioMixer4 mixerRight; | |||
AudioOutputI2S audioOutput; // audio shield: headphones & line-out | |||
AudioOutputPWM pwmOutput; // audio output with PWM on pins 3 & 4 | |||
// Create Audio connections between the components | |||
// | |||
AudioConnection c1(audioInput, 0, mixerLeft, 0); | |||
AudioConnection c2(audioInput, 1, mixerRight, 0); | |||
AudioConnection c3(toneHigh, 0, mixerLeft, 1); | |||
AudioConnection c4(toneHigh, 0, mixerRight, 1); | |||
AudioConnection c5(toneLow, 0, mixerLeft, 2); | |||
AudioConnection c6(toneLow, 0, mixerRight, 2); | |||
AudioConnection c7(mixerLeft, 0, audioOutput, 0); | |||
AudioConnection c8(mixerRight, 0, audioOutput, 1); | |||
AudioConnection c9(mixerLeft, 0, pwmOutput, 0); | |||
// Create an object to control the audio shield. | |||
// | |||
AudioControlSGTL5000 audioShield; | |||
// Bounce objects to read two pushbuttons (pins 0 and 1) | |||
// | |||
Bounce button0 = Bounce(0, 12); | |||
Bounce button1 = Bounce(1, 12); // 12 ms debounce time | |||
void setup() { | |||
pinMode(0, INPUT_PULLUP); | |||
pinMode(1, INPUT_PULLUP); | |||
// Audio connections require memory to work. For more | |||
// detailed information, see the MemoryAndCpuUsage example | |||
AudioMemory(12); | |||
// Enable the audio shield and set the output volume. | |||
audioShield.enable(); | |||
audioShield.inputSelect(myInput); | |||
audioShield.volume(0.60); | |||
} | |||
elapsedMillis volmsec=0; | |||
void loop() { | |||
// Check each button | |||
button0.update(); | |||
button1.update(); | |||
// fallingEdge = high (not pressed - voltage from pullup resistor) | |||
// to low (pressed - button connects pin to ground) | |||
if (button0.fallingEdge()) { | |||
toneLow.frequency(256); | |||
toneLow.amplitude(0.4); | |||
} | |||
if (button1.fallingEdge()) { | |||
toneHigh.frequency(980); | |||
toneHigh.amplitude(0.25); | |||
} | |||
// risingEdge = low (pressed - button connects pin to ground) | |||
// to high (not pressed - voltage from pullup resistor) | |||
if (button0.risingEdge()) { | |||
toneLow.amplitude(0); | |||
} | |||
if (button1.risingEdge()) { | |||
toneHigh.amplitude(0); | |||
} | |||
// every 50 ms, adjust the volume | |||
if (volmsec > 50) { | |||
float vol = analogRead(15); | |||
vol = vol / 10.24; | |||
audioShield.volume(vol); | |||
volmsec = 0; | |||
} | |||
} | |||
@@ -112,7 +112,7 @@ | |||
<script type="text/javascript"> | |||
RED.nodes.registerType('AudioInputADC',{ | |||
RED.nodes.registerType('AudioInputAnalog',{ | |||
shortName: "adc", | |||
inputs:0, | |||
outputs:1, | |||
@@ -121,7 +121,7 @@ | |||
icon: "arrow-in.png" | |||
}); | |||
</script> | |||
<script type="text/x-red" data-help-name="AudioInputADC"> | |||
<script type="text/x-red" data-help-name="AudioInputAnalog"> | |||
<h3>Summary</h3> | |||
<p>Receive audio using the built-in analog to digital converter.</p> | |||
<h3>Audio Connections</h3> | |||
@@ -129,18 +129,12 @@ | |||
<tr class=top><th>Port</th><th>Purpose</th></tr> | |||
<tr class=odd><td align=center>Out 0</td><td>Audio Channel</td></tr> | |||
</table> | |||
<h3>Parameters</h3> | |||
<table class=doc align=center cellpadding=3> | |||
<tr class=top><th>Name</th><th>Type</th><th>Function</th></tr> | |||
<tr class=odd><td align=center>Pin</td><td>Integer</td><td>Analog Pin To Use</td></tr> | |||
</table> | |||
<p>The pin number should be specified as "A0" to "A20"</p> | |||
<p align=center><img src="adcpins2.jpg"></p> | |||
<p align=center><img src="adcpins1.jpg"></p> | |||
<h3>Functions</h3> | |||
<p>This object has no functions to call from the Arduino sketch. It | |||
simply streams data from the ADC to its output port.</p> | |||
<h3>Hardware</h3> | |||
<p>Pin A2 is used for audio input.</p> | |||
<p align=center><img src="adcpins2.jpg"></p> | |||
<p>Signal range is 0 to 1.2V</p> | |||
<p>Need for DC bias, approx 0.6V</p> | |||
<p>TODO: suggested circuity for signal input</p> | |||
@@ -151,7 +145,7 @@ | |||
<p>TODO: actual noise measurements with different input circuitry | |||
(it's not nearly as quiet as the audio shield)</p> | |||
</script> | |||
<script type="text/x-red" data-template-name="AudioInputADC"> | |||
<script type="text/x-red" data-template-name="AudioInputAnalog"> | |||
<div class="form-row"> | |||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> | |||
<input type="text" id="node-input-name" placeholder="Name"> |
@@ -33,34 +33,17 @@ uint16_t AudioInputAnalog::block_offset = 0; | |||
bool AudioInputAnalog::update_responsibility = false; | |||
DMAChannel AudioInputAnalog::dma; | |||
// #define PDB_CONFIG (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT) | |||
// #define PDB_PERIOD 1087 // 48e6 / 44100 | |||
void AudioInputAnalog::begin(unsigned int pin) | |||
AudioInputAnalog::AudioInputAnalog() : AudioStream(0, NULL) | |||
{ | |||
unsigned int pin = A2; | |||
uint32_t i, sum=0; | |||
// pin specified in user sketches should be A0 to A13 | |||
// numbers can be used, but the recommended usage is | |||
// with the named constants A0 to A13 | |||
// constants A0-A9 are actually 14 to 23 | |||
// constants A10-A13 are actually 34 to 37 | |||
if (pin > 23 && !(pin >= 34 && pin <= 37)) return; | |||
dma.begin(true); // Allocate the DMA channel first | |||
//pinMode(2, OUTPUT); | |||
//pinMode(3, OUTPUT); | |||
//digitalWriteFast(3, HIGH); | |||
//delayMicroseconds(500); | |||
//digitalWriteFast(3, LOW); | |||
// Configure the ADC and run at least one software-triggered | |||
// conversion. This completes the self calibration stuff and | |||
// leaves the ADC in a state that's mostly ready to use | |||
analogReadRes(16); | |||
analogReference(INTERNAL); // range 0 to 1.2 volts | |||
//analogReference(DEFAULT); // range 0 to 3.3 volts | |||
analogReadAveraging(8); | |||
// Actually, do many normal reads, to start with a nice DC level | |||
for (i=0; i < 1024; i++) { | |||
@@ -68,22 +51,25 @@ void AudioInputAnalog::begin(unsigned int pin) | |||
} | |||
dc_average = sum >> 10; | |||
// testing only, enable adc interrupt | |||
//ADC0_SC1A |= ADC_SC1_AIEN; | |||
//while ((ADC0_SC1A & ADC_SC1_COCO) == 0) ; // wait | |||
//NVIC_ENABLE_IRQ(IRQ_ADC0); | |||
// set the programmable delay block to trigger the ADC at 44.1 kHz | |||
SIM_SCGC6 |= SIM_SCGC6_PDB; | |||
PDB0_MOD = PDB_PERIOD; | |||
PDB0_SC = PDB_CONFIG | PDB_SC_LDOK; | |||
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG; | |||
PDB0_CH0C1 = 0x0101; | |||
if (!(SIM_SCGC6 & SIM_SCGC6_PDB) | |||
|| (PDB0_SC & PDB_CONFIG) != PDB_CONFIG | |||
|| PDB0_MOD != PDB_PERIOD | |||
|| PDB0_IDLY != 1 | |||
|| PDB0_CH0C1 != 0x0101) { | |||
SIM_SCGC6 |= SIM_SCGC6_PDB; | |||
PDB0_IDLY = 1; | |||
PDB0_MOD = PDB_PERIOD; | |||
PDB0_SC = PDB_CONFIG | PDB_SC_LDOK; | |||
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG; | |||
PDB0_CH0C1 = 0x0101; | |||
} | |||
// enable the ADC for hardware trigger and DMA | |||
ADC0_SC2 |= ADC_SC2_ADTRG | ADC_SC2_DMAEN; | |||
// set up a DMA channel to store the ADC data | |||
dma.begin(true); | |||
dma.TCD->SADDR = &ADC0_RA; | |||
dma.TCD->SOFF = 0; | |||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | |||
@@ -101,6 +87,7 @@ void AudioInputAnalog::begin(unsigned int pin) | |||
dma.attachInterrupt(isr); | |||
} | |||
void AudioInputAnalog::isr(void) | |||
{ | |||
uint32_t daddr, offset; | |||
@@ -108,7 +95,7 @@ void AudioInputAnalog::isr(void) | |||
uint16_t *dest_left; | |||
audio_block_t *left; | |||
//digitalWriteFast(3, HIGH); | |||
digitalWriteFast(2, HIGH); | |||
daddr = (uint32_t)(dma.TCD->DADDR); | |||
dma.clearInterrupt(); | |||
@@ -117,26 +104,26 @@ void AudioInputAnalog::isr(void) | |||
// need to remove data from the second half | |||
src = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; | |||
end = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES]; | |||
if (AudioInputAnalog::update_responsibility) AudioStream::update_all(); | |||
if (update_responsibility) AudioStream::update_all(); | |||
} else { | |||
// DMA is receiving to the second half of the buffer | |||
// need to remove data from the first half | |||
src = (uint16_t *)&analog_rx_buffer[0]; | |||
end = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; | |||
} | |||
left = AudioInputAnalog::block_left; | |||
left = block_left; | |||
if (left != NULL) { | |||
offset = AudioInputAnalog::block_offset; | |||
offset = block_offset; | |||
if (offset > AUDIO_BLOCK_SAMPLES/2) offset = AUDIO_BLOCK_SAMPLES/2; | |||
//if (offset <= AUDIO_BLOCK_SAMPLES/2) { | |||
dest_left = (uint16_t *)&(left->data[offset]); | |||
AudioInputAnalog::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | |||
block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | |||
do { | |||
*dest_left++ = *src++; | |||
} while (src < end); | |||
//} | |||
} | |||
//digitalWriteFast(3, LOW); | |||
digitalWriteFast(2, LOW); | |||
} | |||
@@ -157,6 +144,8 @@ void AudioInputAnalog::update(void) | |||
unsigned int dc, offset; | |||
int16_t s, *p, *end; | |||
//Serial.println("update"); | |||
// allocate new block (ok if NULL) | |||
new_left = allocate(); | |||
@@ -33,9 +33,9 @@ | |||
class AudioInputAnalog : public AudioStream | |||
{ | |||
public: | |||
AudioInputAnalog(unsigned int pin) : AudioStream(0, NULL) { begin(pin); } | |||
AudioInputAnalog(); | |||
virtual void update(void); | |||
void begin(unsigned int pin); | |||
friend void dma_ch9_isr(void); | |||
private: | |||
static audio_block_t *block_left; | |||
static uint16_t block_offset; |
@@ -48,11 +48,18 @@ void AudioOutputAnalog::begin(void) | |||
} | |||
// set the programmable delay block to trigger DMA requests | |||
SIM_SCGC6 |= SIM_SCGC6_PDB; | |||
PDB0_IDLY = 1; | |||
PDB0_MOD = PDB_PERIOD; | |||
PDB0_SC = PDB_CONFIG | PDB_SC_LDOK; | |||
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG | PDB_SC_PDBIE | PDB_SC_DMAEN; | |||
if (!(SIM_SCGC6 & SIM_SCGC6_PDB) | |||
|| (PDB0_SC & PDB_CONFIG) != PDB_CONFIG | |||
|| PDB0_MOD != PDB_PERIOD | |||
|| PDB0_IDLY != 1 | |||
|| PDB0_CH0C1 != 0x0101) { | |||
SIM_SCGC6 |= SIM_SCGC6_PDB; | |||
PDB0_IDLY = 1; | |||
PDB0_MOD = PDB_PERIOD; | |||
PDB0_SC = PDB_CONFIG | PDB_SC_LDOK; | |||
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG; | |||
PDB0_CH0C1 = 0x0101; | |||
} | |||
dma.TCD->SADDR = dac_buffer; | |||
dma.TCD->SOFF = 2; |
@@ -35,7 +35,6 @@ class AudioOutputPWM : public AudioStream | |||
public: | |||
AudioOutputPWM(void) : AudioStream(1, inputQueueArray) { begin(); } | |||
virtual void update(void); | |||
void begin(void); | |||
private: | |||
static audio_block_t *block_1st; | |||
static audio_block_t *block_2nd; | |||
@@ -45,6 +44,7 @@ private: | |||
audio_block_t *inputQueueArray[1]; | |||
static DMAChannel dma; | |||
static void isr(void); | |||
void begin(void); | |||
}; | |||
#endif |
@@ -33,7 +33,7 @@ | |||
// to set their sample rate. They must all configure the same | |||
// period to avoid chaos. | |||
#define PDB_CONFIG (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT) | |||
#define PDB_CONFIG (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT | PDB_SC_PDBIE | PDB_SC_DMAEN) | |||
#if F_BUS == 60000000 | |||
#define PDB_PERIOD (1360-1) |