You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
3.6KB

  1. #include "Audio.h"
  2. #include "arm_math.h"
  3. // #define PDB_CONFIG (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT)
  4. // #define PDB_PERIOD 1087 // 48e6 / 44100
  5. #if defined(__MK20DX256__)
  6. DMAMEM static uint16_t dac_buffer[AUDIO_BLOCK_SAMPLES*2];
  7. audio_block_t * AudioOutputAnalog::block_left_1st = NULL;
  8. audio_block_t * AudioOutputAnalog::block_left_2nd = NULL;
  9. bool AudioOutputAnalog::update_responsibility = false;
  10. void AudioOutputAnalog::begin(void)
  11. {
  12. SIM_SCGC2 |= SIM_SCGC2_DAC0;
  13. DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; // 3.3V VDDA is DACREF_2
  14. // slowly ramp up to DC voltage, approx 1/4 second
  15. for (int16_t i=0; i<128; i++) {
  16. analogWrite(A14, i);
  17. delay(2);
  18. }
  19. // set the programmable delay block to trigger DMA requests
  20. SIM_SCGC6 |= SIM_SCGC6_PDB;
  21. PDB0_IDLY = 1;
  22. PDB0_MOD = PDB_PERIOD;
  23. PDB0_SC = PDB_CONFIG | PDB_SC_LDOK;
  24. PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG | PDB_SC_PDBIE | PDB_SC_DMAEN;
  25. SIM_SCGC7 |= SIM_SCGC7_DMA;
  26. SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
  27. DMA_CR = 0;
  28. DMA_TCD4_SADDR = dac_buffer;
  29. DMA_TCD4_SOFF = 2;
  30. DMA_TCD4_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
  31. DMA_TCD4_NBYTES_MLNO = 2;
  32. DMA_TCD4_SLAST = -sizeof(dac_buffer);
  33. DMA_TCD4_DADDR = &DAC0_DAT0L;
  34. DMA_TCD4_DOFF = 0;
  35. DMA_TCD4_CITER_ELINKNO = sizeof(dac_buffer) / 2;
  36. DMA_TCD4_DLASTSGA = 0;
  37. DMA_TCD4_BITER_ELINKNO = sizeof(dac_buffer) / 2;
  38. DMA_TCD4_CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  39. DMAMUX0_CHCFG4 = DMAMUX_DISABLE;
  40. DMAMUX0_CHCFG4 = DMAMUX_SOURCE_PDB | DMAMUX_ENABLE;
  41. update_responsibility = update_setup();
  42. DMA_SERQ = 4;
  43. NVIC_ENABLE_IRQ(IRQ_DMA_CH4);
  44. }
  45. void AudioOutputAnalog::analogReference(int ref)
  46. {
  47. // TODO: this should ramp gradually to the new DC level
  48. if (ref == INTERNAL) {
  49. DAC0_C0 &= ~DAC_C0_DACRFS; // 1.2V
  50. } else {
  51. DAC0_C0 |= DAC_C0_DACRFS; // 3.3V
  52. }
  53. }
  54. void AudioOutputAnalog::update(void)
  55. {
  56. audio_block_t *block;
  57. block = receiveReadOnly(0); // input 0
  58. if (block) {
  59. __disable_irq();
  60. if (block_left_1st == NULL) {
  61. block_left_1st = block;
  62. __enable_irq();
  63. } else if (block_left_2nd == NULL) {
  64. block_left_2nd = block;
  65. __enable_irq();
  66. } else {
  67. audio_block_t *tmp = block_left_1st;
  68. block_left_1st = block_left_2nd;
  69. block_left_2nd = block;
  70. __enable_irq();
  71. release(tmp);
  72. }
  73. }
  74. }
  75. // TODO: the DAC has much higher bandwidth than the datasheet says
  76. // can we output a 2X oversampled output, for easier filtering?
  77. void dma_ch4_isr(void)
  78. {
  79. const int16_t *src, *end;
  80. int16_t *dest;
  81. audio_block_t *block;
  82. uint32_t saddr;
  83. saddr = (uint32_t)DMA_TCD4_SADDR;
  84. DMA_CINT = 4;
  85. if (saddr < (uint32_t)dac_buffer + sizeof(dac_buffer) / 2) {
  86. // DMA is transmitting the first half of the buffer
  87. // so we must fill the second half
  88. dest = (int16_t *)&dac_buffer[AUDIO_BLOCK_SAMPLES];
  89. end = (int16_t *)&dac_buffer[AUDIO_BLOCK_SAMPLES*2];
  90. } else {
  91. // DMA is transmitting the second half of the buffer
  92. // so we must fill the first half
  93. dest = (int16_t *)dac_buffer;
  94. end = (int16_t *)&dac_buffer[AUDIO_BLOCK_SAMPLES];
  95. }
  96. block = AudioOutputAnalog::block_left_1st;
  97. if (block) {
  98. src = block->data;
  99. do {
  100. // TODO: this should probably dither
  101. *dest++ = ((*src++) + 32767) >> 4;
  102. } while (dest < end);
  103. AudioStream::release(block);
  104. AudioOutputAnalog::block_left_1st = AudioOutputAnalog::block_left_2nd;
  105. AudioOutputAnalog::block_left_2nd = NULL;
  106. } else {
  107. do {
  108. *dest++ = 2047;
  109. } while (dest < end);
  110. }
  111. if (AudioOutputAnalog::update_responsibility) AudioStream::update_all();
  112. }
  113. #else
  114. void AudioOutputAnalog::begin(void)
  115. {
  116. }
  117. void AudioOutputAnalog::update(void)
  118. {
  119. audio_block_t *block;
  120. block = receiveReadOnly(0); // input 0
  121. if (block) release(block);
  122. }
  123. #endif // defined(__MK20DX256__)