diff --git a/hardware/arduino/sam/cores/arduino/wiring_analog.c b/hardware/arduino/sam/cores/arduino/wiring_analog.c index a9e3cee5c..644921499 100644 --- a/hardware/arduino/sam/cores/arduino/wiring_analog.c +++ b/hardware/arduino/sam/cores/arduino/wiring_analog.c @@ -175,21 +175,6 @@ uint32_t analogRead(uint32_t ulPin) return ulValue; } -static void TC_SetRA(Tc *tc, uint32_t chan, uint32_t v) -{ - tc->TC_CHANNEL[chan].TC_RA = v; -} - -static void TC_SetRB(Tc *tc, uint32_t chan, uint32_t v) -{ - tc->TC_CHANNEL[chan].TC_RB = v; -} - -static void TC_SetRC(Tc *tc, uint32_t chan, uint32_t v) -{ - tc->TC_CHANNEL[chan].TC_RC = v; -} - static void TC_SetCMR_ChannelA(Tc *tc, uint32_t chan, uint32_t v) { tc->TC_CHANNEL[chan].TC_CMR = (tc->TC_CHANNEL[chan].TC_CMR & 0xFFF0FFFF) | v; diff --git a/hardware/arduino/sam/libraries/Audio/Audio.cpp b/hardware/arduino/sam/libraries/Audio/Audio.cpp new file mode 100644 index 000000000..bd8d99186 --- /dev/null +++ b/hardware/arduino/sam/libraries/Audio/Audio.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2012 by Cristian Maglie + * Audio library for Arduino Due. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#include "Audio.h" + +class LockDAC { +public: + LockDAC(DACClass *_dac) : dac(_dac) + { dac->disableInterrupts(); }; + ~LockDAC() { dac->enableInterrupts(); }; + DACClass *dac; +}; + +void AudioClass::begin(uint32_t sampleRate, uint32_t msPreBuffer) { + // Allocate a buffer to keep msPreBuffer milliseconds of audio + bufferSize = msPreBuffer * sampleRate / 1000; + if (bufferSize < 2048) + bufferSize = 2048; + buffer = (uint32_t *) malloc(bufferSize * sizeof(uint32_t)); + last = buffer + bufferSize; + + // Buffering starts from the beginning + running = last; + current = buffer; + next = buffer; + + // Run DAC + dac->begin(VARIANT_MCK / sampleRate); + dac->setOnTransmitEnd_CB(onTransmitEnd, this); +} + +void AudioClass::end() { + dac->end(); + free(buffer); +} + +size_t AudioClass::write(const uint32_t *data, size_t size) { + LockDAC lock(dac); + //Serial1.print("WRI("); + const uint32_t TAG = 0x10000000; + int i; + for (i=0; i < size && next != running; i++) { + *next = data[i] | TAG; + next++; + + // Wrap around circular buffer + if (next == last) + next = buffer; + } + debug(); + if (dac->canQueue()) { + enqueue(); + debug(); + } + //Serial1.print(")"); + return i; +} + +void AudioClass::enqueue() { + if (!dac->canQueue()) + // DMA queue full + return; + + if (current == next) + // No data to enqueue + return; + + // If wrapping happened + if (next < current) { + + uint32_t size = last - current; + + if (size < 1024) { + // enqueue the last part of the circular buffer + dac->queueBuffer(current, size); + current = buffer; + next = buffer; + } else { + // Enqueue only a block of 512 + dac->queueBuffer(current, 512); + current += 512; + } + return; + } + + bool aboutToWrap = (last - next) < 512; + uint32_t size = next - current; + + // If buffered data is less than 512 bytes + if (size < 512) { + + // Enqueue all + dac->queueBuffer(current, size); + + if (aboutToWrap) + next = buffer; + current = next; + + } else { + + if (aboutToWrap && size < 1024) { + // Enqueue all + dac->queueBuffer(current, size); + next = buffer; + current = buffer; + } else { + // Enqueue only a block of 512 + dac->queueBuffer(current, 512); + current += 512; + } + + } +} + +void AudioClass::onTransmitEnd(void *me) { + AudioClass *audio = reinterpret_cast(me); + + //Serial1.print("INT("); + audio->enqueue(); + + // Update running pointer + audio->running = audio->dac->getCurrentQueuePointer(); + + audio->debug(); + //Serial1.print(")"); +} + +AudioClass Audio(DAC); diff --git a/hardware/arduino/sam/libraries/Audio/Audio.h b/hardware/arduino/sam/libraries/Audio/Audio.h new file mode 100644 index 000000000..93ae0e780 --- /dev/null +++ b/hardware/arduino/sam/libraries/Audio/Audio.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012 by Cristian Maglie + * Audio library for Arduino Due. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#ifndef AUDIO_H +#define AUDIO_H + +#include "Arduino.h" +#include "Print.h" +#include "DAC.h" + +class AudioClass : public Print { +public: + AudioClass(DACClass &_dac) : dac(&_dac) { }; + void begin(uint32_t sampleRate, uint32_t msPreBuffer); + void end(); + + virtual size_t write(uint8_t c) { /* not implemented */ }; + virtual size_t write(const uint8_t *data, size_t size) { return write(reinterpret_cast(data), size/4) * 4; }; + virtual size_t write(const uint16_t *data, size_t size) { return write(reinterpret_cast(data), size/2) * 2; }; + virtual size_t write(const int16_t *data, size_t size) { return write(reinterpret_cast(data), size/2) * 2; }; + virtual size_t write(const uint32_t *data, size_t size); + + void debug() { +// Serial1.print(running-buffer, DEC); +// Serial1.print(" "); +// Serial1.print(current-buffer, DEC); +// Serial1.print(" "); +// Serial1.print(next-buffer, DEC); +// Serial1.print(" "); +// Serial1.println(last-buffer, DEC); + } +private: + void enqueue(); + static void onTransmitEnd(void *me); + uint32_t bufferSize; + uint32_t *buffer; + uint32_t *last; + uint32_t * volatile running; + uint32_t * volatile current; + uint32_t * volatile next; + + uint32_t *cook(const uint32_t *buffer, size_t size); + + DACClass *dac; +}; + +extern AudioClass Audio; + +#endif diff --git a/hardware/arduino/sam/libraries/Audio/DAC.cpp b/hardware/arduino/sam/libraries/Audio/DAC.cpp new file mode 100644 index 000000000..766d7e4c4 --- /dev/null +++ b/hardware/arduino/sam/libraries/Audio/DAC.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2012 by Cristian Maglie + * DAC library for Arduino Due. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#include + +void DACClass::begin(uint32_t period) { + // Enable clock for DAC + pmc_enable_periph_clk(dacId); + + dacc_reset(dac); + + // Set transfer mode to double word + dacc_set_transfer_mode(dac, 1); + + // Power save: + // sleep mode - 0 (disabled) + // fast wakeup - 0 (disabled) + dacc_set_power_save(dac, 0, 0); + + // DAC refresh/startup timings: + // refresh - 0x08 (1024*8 dacc clocks) + // max speed mode - 0 (disabled) + // startup time - 0x10 (1024 dacc clocks) + dacc_set_timing(dac, 0x08, 0, DACC_MR_STARTUP_1024); + + // Flexible channel selection with tags + dacc_enable_flexible_selection(dac); + + // Set up analog current + dacc_set_analog_control(dac, + DACC_ACR_IBCTLCH0(0x02) | + DACC_ACR_IBCTLCH1(0x02) | + DACC_ACR_IBCTLDACCORE(0x01)); + + // Enable output channels + dacc_enable_channel(dac, 0); + dacc_enable_channel(dac, 1); + + // Configure Timer Counter to trigger DAC + // -------------------------------------- + pmc_enable_periph_clk(ID_TC1); + TC_Configure(TC0, 1, + TC_CMR_TCCLKS_TIMER_CLOCK2 | // Clock at MCR/8 + TC_CMR_WAVE | // Waveform mode + TC_CMR_WAVSEL_UP_RC | // Counter running up and reset when equals to RC + TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR); + const uint32_t TC = period / 8; + TC_SetRA(TC0, 1, TC / 2); + TC_SetRC(TC0, 1, TC); + TC_Start(TC0, 1); + + // Configure clock source for DAC (2 = TC0 Output Chan. 1) + dacc_set_trigger(dac, 2); + + // Configure pins + PIO_Configure(g_APinDescription[DA0].pPort, + g_APinDescription[DA0].ulPinType, + g_APinDescription[DA0].ulPin, + g_APinDescription[DA0].ulPinConfiguration); + PIO_Configure(g_APinDescription[DA1].pPort, + g_APinDescription[DA1].ulPinType, + g_APinDescription[DA1].ulPin, + g_APinDescription[DA1].ulPinConfiguration); + + // Enable interrupt controller for DAC + dacc_disable_interrupt(dac, 0xFFFFFFFF); + NVIC_DisableIRQ(isrId); + NVIC_ClearPendingIRQ(isrId); + NVIC_SetPriority(isrId, 0); + NVIC_EnableIRQ(isrId); +} + +void DACClass::end() { + TC_Stop(TC0, 1); + NVIC_DisableIRQ(isrId); + dacc_disable_channel(dac, 0); + dacc_disable_channel(dac, 1); +} + +bool DACClass::canQueue() { + if ((dac->DACC_TCR == 0) && (dac->DACC_TNCR == 0)) + return true; + if (dac->DACC_TNCR == 0) + return true; + return false; +} + +size_t DACClass::queueBuffer(const uint32_t *buffer, size_t size) { + // Try the first PDC buffer + if ((dac->DACC_TCR == 0) && (dac->DACC_TNCR == 0)) { + dac->DACC_TPR = (uint32_t) buffer; + dac->DACC_TCR = size; + dac->DACC_PTCR = DACC_PTCR_TXTEN; + if (cb) + dacc_enable_interrupt(dac, DACC_IER_ENDTX); + return size; + } + + // Try the second PDC buffer + if (dac->DACC_TNCR == 0) { + dac->DACC_TNPR = (uint32_t) buffer; + dac->DACC_TNCR = size; + dac->DACC_PTCR = DACC_PTCR_TXTEN; + if (cb) + dacc_enable_interrupt(dac, DACC_IER_ENDTX); + return size; + } + + // PDC buffers full, try again later... + return 0; +} + +uint32_t *DACClass::getCurrentQueuePointer() { + return reinterpret_cast(dac->DACC_TPR); +} + +void DACClass::setOnTransmitEnd_CB(OnTransmitEnd_CB _cb, void *_data) { + cb = _cb; + cbData = _data; + if (!cb) + dacc_disable_interrupt(dac, DACC_IDR_ENDTX); +} + +void DACClass::onService() { + uint32_t sr = dac->DACC_ISR; +// if (sr & DACC_ISR_ENDTX) { + // There is a free slot, enqueue data + dacc_disable_interrupt(dac, DACC_IDR_ENDTX); + if (cb) + cb(cbData); +// } +} + +DACClass DAC(DACC_INTERFACE, DACC_INTERFACE_ID, DACC_ISR_ID); + +void DACC_ISR_HANDLER(void) { + DAC.onService(); +} + diff --git a/hardware/arduino/sam/libraries/Audio/DAC.h b/hardware/arduino/sam/libraries/Audio/DAC.h new file mode 100644 index 000000000..d49658415 --- /dev/null +++ b/hardware/arduino/sam/libraries/Audio/DAC.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012 by Cristian Maglie + * DAC library for Arduino Due. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#ifndef DAC_INCLUDED +#define DAC_INCLUDED + +#include "Arduino.h" + +typedef void (*OnTransmitEnd_CB)(void *data); + +class DACClass +{ +public: + DACClass(Dacc *_dac, uint32_t _dacId, IRQn_Type _isrId) : + dac(_dac), dacId(_dacId), isrId(_isrId), cb(NULL) { }; + void begin(uint32_t period); + void end(); + bool canQueue(); + size_t queueBuffer(const uint32_t *buffer, size_t size); + uint32_t *getCurrentQueuePointer(); + void setOnTransmitEnd_CB(OnTransmitEnd_CB _cb, void *data); + void onService(); + + void enableInterrupts() { NVIC_EnableIRQ(isrId); }; + void disableInterrupts() { NVIC_DisableIRQ(isrId); }; + +private: + Dacc *dac; + uint32_t dacId; + IRQn_Type isrId; + OnTransmitEnd_CB cb; + void *cbData; +}; + +extern DACClass DAC; + +#endif diff --git a/hardware/arduino/sam/libraries/Audio/keywords.txt b/hardware/arduino/sam/libraries/Audio/keywords.txt new file mode 100644 index 000000000..e69de29bb diff --git a/hardware/arduino/sam/system/libsam/include/tc.h b/hardware/arduino/sam/system/libsam/include/tc.h index ef69ec09b..e19388648 100644 --- a/hardware/arduino/sam/system/libsam/include/tc.h +++ b/hardware/arduino/sam/system/libsam/include/tc.h @@ -68,6 +68,12 @@ extern void TC_Stop( Tc *pTc, uint32_t dwChannel ) ; extern uint32_t TC_FindMckDivisor( uint32_t dwFreq, uint32_t dwMCk, uint32_t *dwDiv, uint32_t *dwTcClks, uint32_t dwBoardMCK ) ; +extern void TC_SetRA(Tc *tc, uint32_t chan, uint32_t v) ; + +extern void TC_SetRB(Tc *tc, uint32_t chan, uint32_t v) ; + +extern void TC_SetRC(Tc *tc, uint32_t chan, uint32_t v) ; + #ifdef __cplusplus } #endif diff --git a/hardware/arduino/sam/system/libsam/source/tc.c b/hardware/arduino/sam/system/libsam/source/tc.c index c3f935ee7..94b5caf79 100644 --- a/hardware/arduino/sam/system/libsam/source/tc.c +++ b/hardware/arduino/sam/system/libsam/source/tc.c @@ -173,3 +173,15 @@ extern uint32_t TC_FindMckDivisor( uint32_t dwFreq, uint32_t dwMCk, uint32_t *dw return 1 ; } +void TC_SetRA(Tc *tc, uint32_t chan, uint32_t v) { + tc->TC_CHANNEL[chan].TC_RA = v; +} + +void TC_SetRB(Tc *tc, uint32_t chan, uint32_t v) { + tc->TC_CHANNEL[chan].TC_RB = v; +} + +void TC_SetRC(Tc *tc, uint32_t chan, uint32_t v) { + tc->TC_CHANNEL[chan].TC_RC = v; +} + diff --git a/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a b/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a index df3d8bd04..a25e77ed9 100644 Binary files a/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a and b/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a differ diff --git a/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a.txt b/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a.txt index 6a04614cb..d9b649544 100644 --- a/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a.txt +++ b/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a.txt @@ -72,6 +72,7 @@ pmc.o: 00000000 T pmc_switch_udpck_to_upllck pwmc.o: +00000000 r C.9.7272 00000000 t FindClockConfiguration 00000000 T PWMC_ConfigureChannel 00000000 T PWMC_ConfigureChannelExt @@ -99,14 +100,14 @@ pwmc.o: 00000000 T PWMC_SetSyncChannelUpdateUnlock 00000000 T PWMC_WriteBuffer U __assert_func -00000000 r __func__.3295 -00000000 r __func__.3306 -00000000 r __func__.3321 -00000000 r __func__.3332 -00000000 r __func__.3343 -00000000 r __func__.3350 -00000000 r __func__.3434 -00000000 r __func__.3440 +00000000 r __func__.5968 +00000000 r __func__.5979 +00000000 r __func__.5994 +00000000 r __func__.6005 +00000000 r __func__.6016 +00000000 r __func__.6023 +00000000 r __func__.6107 +00000000 r __func__.6113 rtc.o: 00000000 T RTC_ClearSCCR @@ -122,9 +123,9 @@ rtc.o: 00000000 T RTC_SetTime 00000000 T RTC_SetTimeAlarm U __assert_func -00000000 r __func__.3292 -00000000 r __func__.3301 -00000000 r __func__.3306 +00000000 r __func__.5965 +00000000 r __func__.5974 +00000000 r __func__.5979 rtt.o: 00000000 T RTT_EnableIT @@ -133,8 +134,8 @@ rtt.o: 00000000 T RTT_SetAlarm 00000000 T RTT_SetPrescaler U __assert_func -00000000 r __func__.3299 -00000000 r __func__.3307 +00000000 r __func__.5972 +00000000 r __func__.5980 spi.o: 00000000 T SPI_Configure @@ -152,12 +153,15 @@ spi.o: tc.o: 00000000 T TC_Configure 00000000 T TC_FindMckDivisor +00000000 T TC_SetRA +00000000 T TC_SetRB +00000000 T TC_SetRC 00000000 T TC_Start 00000000 T TC_Stop U __assert_func -00000000 r __func__.3294 -00000000 r __func__.3300 -00000000 r __func__.3306 +00000000 r __func__.5967 +00000000 r __func__.5973 +00000000 r __func__.5979 timetick.o: 00000000 T GetTickCount @@ -184,18 +188,18 @@ twi.o: 00000000 T TWI_TransferComplete 00000000 T TWI_WriteByte U __assert_func -00000000 r __func__.3659 -00000000 r __func__.3674 -00000000 r __func__.3678 -00000000 r __func__.3685 -00000000 r __func__.3689 -00000000 r __func__.3694 -00000000 r __func__.3702 -00000000 r __func__.3716 -00000000 r __func__.3721 -00000000 r __func__.3725 -00000000 r __func__.3730 -00000000 r __func__.3734 +00000000 r __func__.6340 +00000000 r __func__.6355 +00000000 r __func__.6359 +00000000 r __func__.6366 +00000000 r __func__.6370 +00000000 r __func__.6375 +00000000 r __func__.6383 +00000000 r __func__.6397 +00000000 r __func__.6402 +00000000 r __func__.6406 +00000000 r __func__.6411 +00000000 r __func__.6415 usart.o: 00000000 T USART_Configure @@ -214,7 +218,7 @@ usart.o: 00000000 T USART_Write 00000000 T USART_WriteBuffer U __assert_func -00000000 r __func__.3580 +00000000 r __func__.6261 wdt.o: 00000000 T WDT_Disable diff --git a/hardware/arduino/sam/variants/arduino_due_x/variant.h b/hardware/arduino/sam/variants/arduino_due_x/variant.h index a26fb4fc1..e9e5dff05 100644 --- a/hardware/arduino/sam/variants/arduino_due_x/variant.h +++ b/hardware/arduino/sam/variants/arduino_due_x/variant.h @@ -170,6 +170,8 @@ static const uint8_t CANTX0 = 69; #define DACC_INTERFACE DACC #define DACC_INTERFACE_ID ID_DACC #define DACC_RESOLUTION 12 +#define DACC_ISR_HANDLER DACC_Handler +#define DACC_ISR_ID DACC_IRQn /* * PWM