From e412c71ec298e473a1945baaea6c4da51b7e38fa Mon Sep 17 00:00:00 2001 From: unicod Date: Sat, 21 Mar 2026 00:14:22 +0100 Subject: [PATCH] DAC objects for playing on both codec - TODO replace static variables - TODO fix: codec-B louder, noise even if muted --- Core/Src/main.c | 73 ++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/Core/Src/main.c b/Core/Src/main.c index 6ac8bf8..8ae9c27 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -39,6 +39,16 @@ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ +typedef struct { + const DMA_TypeDef* dma; // GPDMA instance (GPDMA1 or GPDMA2) + uint32_t dmaChannel; // DMA channel used for this audio DAC + uint32_t* dmaBuf; // DMA buffer for audio samples (32-bit words, left justified 16-bit samples in upper half, stereo interleaved) + uint32_t dmaBufSizeBytes; // Size of DMA buffer in bytes + uint32_t dmaBufSampleCnt; // Number of audio samples (per channel) in DMA buffer (dmaBufSizeBytes / 4 / 2) + uint32_t PrevSampleIdx; // Previous sample index processed in DMA buffer, used to detect new samples for processing + uint8_t AudioChSel; // Audio channel selection by user for this DAC +} AudioDac; + /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ @@ -76,6 +86,9 @@ uint32_t I2S2TxDmaBuf[I2S2_TXDMA_BUF_SAMPLE_CNT][2] = {0}; uint32_t I2S3RxDmaBuf[I2S3_RXDMA_BUF_SAMPLE_CNT][2] = {0}; uint32_t I2S3TxDmaBuf[I2S3_TXDMA_BUF_SAMPLE_CNT][2] = {0}; +AudioDac AudioDac_A = {GPDMA2, LL_DMA_CHANNEL_0, (uint32_t*)I2S2TxDmaBuf, sizeof(I2S2TxDmaBuf), I2S2_TXDMA_BUF_SAMPLE_CNT, 0, 3}; +AudioDac AudioDac_B = {GPDMA2, LL_DMA_CHANNEL_2, (uint32_t*)I2S3TxDmaBuf, sizeof(I2S3TxDmaBuf), I2S3_TXDMA_BUF_SAMPLE_CNT, 0, 4}; + #define AUDIO_TX_CHAN_CNT 2 // Audio channel count for sending on RS485 bus #define AUDIO_BUF_SAMPLE_CNT 256 // Audio buffer size in samples (per channel) @@ -86,7 +99,6 @@ uint16_t AudioRdPos = 0; // Read position in audio buffer (in samples) uint16_t AudioDatCnt = 0; // Number of valid audio samples in buffer (per channel), updated by producer (audio ADC) and consumer (RS485 bus) tasks -uint8_t ChSel = 0; uint8_t Vol = 0; /* USER CODE END PV */ @@ -241,7 +253,7 @@ void TLV320_AIC3204_Init() { } -void Fill_I2S_Buffer(uint32_t *buf, uint32_t start_sample, uint32_t sample_count) { +void Fill_I2S_Buffer(AudioDac* dac, uint32_t start_sample, uint32_t sample_count) { static uint8_t playing = 0; uint_fast8_t doubling = 0; uint_fast8_t dropping = 0; @@ -249,7 +261,7 @@ void Fill_I2S_Buffer(uint32_t *buf, uint32_t start_sample, uint32_t sample_count for (uint32_t i = 0; i < sample_count; i++) { int16_t pcmsample = 0; - switch (ChSel) { + switch (dac->AudioChSel) { case 0: case 1: { static uint32_t Phase = 0; @@ -263,7 +275,7 @@ void Fill_I2S_Buffer(uint32_t *buf, uint32_t start_sample, uint32_t sample_count pcmsample = pcm1 + ((pcm2 - pcm1) * frac >> 14); // linear interpolation pcmsample = pcmsample / 2; // reduce amplitude Phase = (Phase + PhaseInc) % (ARRAY_COUNT(Wave_Sin) << 14); // phase accumulator with wrap-around - if (ChSel) PhaseInc += IncDir; // Chsel: 0=fixed freq, 1=sweep up and down + if (dac->AudioChSel) PhaseInc += IncDir; // Chsel: 0=fixed freq, 1=sweep up and down if (PhaseInc < ( 4UL << 14)) IncDir = 1; // minimum frequency reached, change direction to up if (PhaseInc > (16UL << 14)) IncDir = -1; // maximum frequency reached, change direction to down }break; @@ -272,7 +284,7 @@ void Fill_I2S_Buffer(uint32_t *buf, uint32_t start_sample, uint32_t sample_count case 5: { static uint16_t Phase = 0; pcmsample = Wave_Organ[Phase] / 4; // organ waveform - uint16_t phase_inc = (ChSel == 3) ? 4 : ((ChSel == 4) ? 8 : 16); // different frequency for different channel selection + uint16_t phase_inc = (dac->AudioChSel == 3) ? 4 : ((dac->AudioChSel == 4) ? 8 : 16); // different frequency for different channel selection Phase = (Phase + phase_inc) % ARRAY_COUNT(Wave_Organ); }break; case 6: { @@ -312,15 +324,15 @@ void Fill_I2S_Buffer(uint32_t *buf, uint32_t start_sample, uint32_t sample_count } int32_t frame = pcmsample << 16; // convert to 32-bit sample with 16-bit left justified uint32_t pos = (start_sample + i) * 2; // Stereo: L,R - buf[pos + 0] = frame/4; // Left - buf[pos + 1] = frame/4; // Right + dac->dmaBuf[pos + 0] = frame/4; // Left + dac->dmaBuf[pos + 1] = frame/4; // Right if (doubling == 10) { if (idmaBuf[pos + 0] = frame/4; // Left (duplicate sample) + dac->dmaBuf[pos + 1] = frame/4; // Right i++; if (playing) printf("+"); } @@ -338,27 +350,24 @@ void Fill_I2S_Buffer(uint32_t *buf, uint32_t start_sample, uint32_t sample_count } } +void AudioDAC_Task(AudioDac* dac) { + uint32_t dma_ptr = dac->dmaBufSizeBytes - LL_DMA_GetBlkDataLength(dac->dma, dac->dmaChannel); + uint32_t sample_idx = dma_ptr / 8; // Word = 4 bytes * 2 channels = 8 bytes per sample -void AudioDAC_Task(void) { - static uint32_t old_sample_index = 0; - // DMA pointer --> Word-Index - uint32_t dma_ptr = 2048 - LL_DMA_GetBlkDataLength(GPDMA2, LL_DMA_CHANNEL_0); - uint32_t sample_index = dma_ptr / 8; // Word = 4 bytes * 2 channels = 8 bytes per sample - - if (sample_index != old_sample_index) { + if (sample_idx != dac->PrevSampleIdx) { uint32_t free_samples; - if (sample_index > old_sample_index) { - free_samples = sample_index - old_sample_index; // normal case: DMA is in between old and new index - Fill_I2S_Buffer(I2S2TxDmaBuf[0], old_sample_index, free_samples); + if (sample_idx > dac->PrevSampleIdx) { + free_samples = sample_idx - dac->PrevSampleIdx; // normal case: DMA is in between old and new index + Fill_I2S_Buffer(dac, dac->PrevSampleIdx, free_samples); } else { - free_samples = I2S2_TXDMA_BUF_SAMPLE_CNT - old_sample_index; // Wrap-around - Fill_I2S_Buffer(I2S2TxDmaBuf[0], old_sample_index, free_samples); // first the rest until end of buffer - if (sample_index > 0) { - Fill_I2S_Buffer(I2S2TxDmaBuf[0], 0, sample_index); + free_samples = dac->dmaBufSampleCnt - dac->PrevSampleIdx; // Wrap-around + Fill_I2S_Buffer(dac, dac->PrevSampleIdx, free_samples); // first the rest until end of buffer + if (sample_idx > 0) { + Fill_I2S_Buffer(dac, 0, sample_idx); } } - old_sample_index = sample_index; + dac->PrevSampleIdx = sample_idx; } //printf("dma_ptr:%u dma_word_index:%u sample_index:%u\n", dma_ptr, dma_word_index, sample_index); } @@ -369,7 +378,7 @@ void Proc_I2S_Buffer(uint32_t *buf, uint32_t start_sample, uint32_t sample_count uint32_t pos = (start_sample + i) * 2; // Stereo: L,R int16_t left = buf[pos + 0] >> 16; // convert back to 16-bit sample from 32-bit left justified int16_t right = buf[pos + 1] >> 16; // convert back to 16-bit sample from 32-bit left justified - int16_t sample = (ChSel == 7) ? left : right; + int16_t sample = (AudioDac_A.AudioChSel == 7) ? left : right; if (AudioDatCnt < AUDIO_BUF_SAMPLE_CNT-1) { AudioDatCnt++; AudioWrPos++; // increment position @@ -528,15 +537,12 @@ int main(void) if (TickChk(&Tick1secRef, 1000)) { // execute every 1s LD2_Toggle(); static uint8_t cnt = 0; - DispPutDigit(2, 'A'+cnt, 0); + DispPutDigit(3, ' ', cnt&1); // dot on/off for testing cnt = (cnt + 1) % 8; } static uint32_t Tick100msRef = 0; if (TickChk(&Tick100msRef, 100)) { // execute every 100ms - static uint8_t dot = 0; - dot ^= 1; // toggle dot - DispPutDigit(3, ' ', dot); } int16_t ch = Uart5_GetByte(); @@ -571,14 +577,19 @@ int main(void) TlvWriteReg(CODEC_A, TLV_PAGE_0, 12, 0xF0); // Enable ADC high-pass filter } if (isdigit(c)) { - ChSel = c - '0'; + AudioDac_A.AudioChSel = c - '0'; DispPutDigit(0, c, 0); } + if (c >= 'A' && c <= 'I') { + AudioDac_B.AudioChSel = c - 'A'; + DispPutDigit(2, '0' + AudioDac_B.AudioChSel, 0); + } } Usart2_DMA_Task(); // handle USART2 DMA rx/tx Usart3_DMA_Task(); // handle USART3 DMA rx/tx - AudioDAC_Task(); // handle audio data feeding to I2S Tx buffer + AudioDAC_Task(&AudioDac_A); // handle audio data processing for Codec-A (I2S2) + AudioDAC_Task(&AudioDac_B); // handle audio data processing for Codec-B (I2S3) AudioADC_Task(); // handle audio data processing from I2S Rx buffer