#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. | |||||
} | |||||
#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(); | |||||
} | |||||
#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; | |||||
} | |||||
} | |||||
<script type="text/javascript"> | <script type="text/javascript"> | ||||
RED.nodes.registerType('AudioInputADC',{ | |||||
RED.nodes.registerType('AudioInputAnalog',{ | |||||
shortName: "adc", | shortName: "adc", | ||||
inputs:0, | inputs:0, | ||||
outputs:1, | outputs:1, | ||||
icon: "arrow-in.png" | icon: "arrow-in.png" | ||||
}); | }); | ||||
</script> | </script> | ||||
<script type="text/x-red" data-help-name="AudioInputADC"> | |||||
<script type="text/x-red" data-help-name="AudioInputAnalog"> | |||||
<h3>Summary</h3> | <h3>Summary</h3> | ||||
<p>Receive audio using the built-in analog to digital converter.</p> | <p>Receive audio using the built-in analog to digital converter.</p> | ||||
<h3>Audio Connections</h3> | <h3>Audio Connections</h3> | ||||
<tr class=top><th>Port</th><th>Purpose</th></tr> | <tr class=top><th>Port</th><th>Purpose</th></tr> | ||||
<tr class=odd><td align=center>Out 0</td><td>Audio Channel</td></tr> | <tr class=odd><td align=center>Out 0</td><td>Audio Channel</td></tr> | ||||
</table> | </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> | <h3>Functions</h3> | ||||
<p>This object has no functions to call from the Arduino sketch. It | <p>This object has no functions to call from the Arduino sketch. It | ||||
simply streams data from the ADC to its output port.</p> | simply streams data from the ADC to its output port.</p> | ||||
<h3>Hardware</h3> | <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>Signal range is 0 to 1.2V</p> | ||||
<p>Need for DC bias, approx 0.6V</p> | <p>Need for DC bias, approx 0.6V</p> | ||||
<p>TODO: suggested circuity for signal input</p> | <p>TODO: suggested circuity for signal input</p> | ||||
<p>TODO: actual noise measurements with different input circuitry | <p>TODO: actual noise measurements with different input circuitry | ||||
(it's not nearly as quiet as the audio shield)</p> | (it's not nearly as quiet as the audio shield)</p> | ||||
</script> | </script> | ||||
<script type="text/x-red" data-template-name="AudioInputADC"> | |||||
<script type="text/x-red" data-template-name="AudioInputAnalog"> | |||||
<div class="form-row"> | <div class="form-row"> | ||||
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label> | <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> | ||||
<input type="text" id="node-input-name" placeholder="Name"> | <input type="text" id="node-input-name" placeholder="Name"> |
bool AudioInputAnalog::update_responsibility = false; | bool AudioInputAnalog::update_responsibility = false; | ||||
DMAChannel AudioInputAnalog::dma; | 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; | 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 | // Configure the ADC and run at least one software-triggered | ||||
// conversion. This completes the self calibration stuff and | // conversion. This completes the self calibration stuff and | ||||
// leaves the ADC in a state that's mostly ready to use | // leaves the ADC in a state that's mostly ready to use | ||||
analogReadRes(16); | analogReadRes(16); | ||||
analogReference(INTERNAL); // range 0 to 1.2 volts | analogReference(INTERNAL); // range 0 to 1.2 volts | ||||
//analogReference(DEFAULT); // range 0 to 3.3 volts | |||||
analogReadAveraging(8); | analogReadAveraging(8); | ||||
// Actually, do many normal reads, to start with a nice DC level | // Actually, do many normal reads, to start with a nice DC level | ||||
for (i=0; i < 1024; i++) { | for (i=0; i < 1024; i++) { | ||||
} | } | ||||
dc_average = sum >> 10; | 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 | // 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 | // enable the ADC for hardware trigger and DMA | ||||
ADC0_SC2 |= ADC_SC2_ADTRG | ADC_SC2_DMAEN; | ADC0_SC2 |= ADC_SC2_ADTRG | ADC_SC2_DMAEN; | ||||
// set up a DMA channel to store the ADC data | // set up a DMA channel to store the ADC data | ||||
dma.begin(true); | |||||
dma.TCD->SADDR = &ADC0_RA; | dma.TCD->SADDR = &ADC0_RA; | ||||
dma.TCD->SOFF = 0; | dma.TCD->SOFF = 0; | ||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); | ||||
dma.attachInterrupt(isr); | dma.attachInterrupt(isr); | ||||
} | } | ||||
void AudioInputAnalog::isr(void) | void AudioInputAnalog::isr(void) | ||||
{ | { | ||||
uint32_t daddr, offset; | uint32_t daddr, offset; | ||||
uint16_t *dest_left; | uint16_t *dest_left; | ||||
audio_block_t *left; | audio_block_t *left; | ||||
//digitalWriteFast(3, HIGH); | |||||
digitalWriteFast(2, HIGH); | |||||
daddr = (uint32_t)(dma.TCD->DADDR); | daddr = (uint32_t)(dma.TCD->DADDR); | ||||
dma.clearInterrupt(); | dma.clearInterrupt(); | ||||
// need to remove data from the second half | // need to remove data from the second half | ||||
src = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; | src = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; | ||||
end = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES]; | end = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES]; | ||||
if (AudioInputAnalog::update_responsibility) AudioStream::update_all(); | |||||
if (update_responsibility) AudioStream::update_all(); | |||||
} else { | } else { | ||||
// DMA is receiving to the second half of the buffer | // DMA is receiving to the second half of the buffer | ||||
// need to remove data from the first half | // need to remove data from the first half | ||||
src = (uint16_t *)&analog_rx_buffer[0]; | src = (uint16_t *)&analog_rx_buffer[0]; | ||||
end = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; | end = (uint16_t *)&analog_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; | ||||
} | } | ||||
left = AudioInputAnalog::block_left; | |||||
left = block_left; | |||||
if (left != NULL) { | 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) offset = AUDIO_BLOCK_SAMPLES/2; | ||||
//if (offset <= AUDIO_BLOCK_SAMPLES/2) { | //if (offset <= AUDIO_BLOCK_SAMPLES/2) { | ||||
dest_left = (uint16_t *)&(left->data[offset]); | dest_left = (uint16_t *)&(left->data[offset]); | ||||
AudioInputAnalog::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | |||||
block_offset = offset + AUDIO_BLOCK_SAMPLES/2; | |||||
do { | do { | ||||
*dest_left++ = *src++; | *dest_left++ = *src++; | ||||
} while (src < end); | } while (src < end); | ||||
//} | //} | ||||
} | } | ||||
//digitalWriteFast(3, LOW); | |||||
digitalWriteFast(2, LOW); | |||||
} | } | ||||
unsigned int dc, offset; | unsigned int dc, offset; | ||||
int16_t s, *p, *end; | int16_t s, *p, *end; | ||||
//Serial.println("update"); | |||||
// allocate new block (ok if NULL) | // allocate new block (ok if NULL) | ||||
new_left = allocate(); | new_left = allocate(); | ||||
class AudioInputAnalog : public AudioStream | class AudioInputAnalog : public AudioStream | ||||
{ | { | ||||
public: | public: | ||||
AudioInputAnalog(unsigned int pin) : AudioStream(0, NULL) { begin(pin); } | |||||
AudioInputAnalog(); | |||||
virtual void update(void); | virtual void update(void); | ||||
void begin(unsigned int pin); | |||||
friend void dma_ch9_isr(void); | |||||
private: | private: | ||||
static audio_block_t *block_left; | static audio_block_t *block_left; | ||||
static uint16_t block_offset; | static uint16_t block_offset; |
} | } | ||||
// set the programmable delay block to trigger DMA requests | // 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->SADDR = dac_buffer; | ||||
dma.TCD->SOFF = 2; | dma.TCD->SOFF = 2; |
public: | public: | ||||
AudioOutputPWM(void) : AudioStream(1, inputQueueArray) { begin(); } | AudioOutputPWM(void) : AudioStream(1, inputQueueArray) { begin(); } | ||||
virtual void update(void); | virtual void update(void); | ||||
void begin(void); | |||||
private: | private: | ||||
static audio_block_t *block_1st; | static audio_block_t *block_1st; | ||||
static audio_block_t *block_2nd; | static audio_block_t *block_2nd; | ||||
audio_block_t *inputQueueArray[1]; | audio_block_t *inputQueueArray[1]; | ||||
static DMAChannel dma; | static DMAChannel dma; | ||||
static void isr(void); | static void isr(void); | ||||
void begin(void); | |||||
}; | }; | ||||
#endif | #endif |
// to set their sample rate. They must all configure the same | // to set their sample rate. They must all configure the same | ||||
// period to avoid chaos. | // 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 | #if F_BUS == 60000000 | ||||
#define PDB_PERIOD (1360-1) | #define PDB_PERIOD (1360-1) |