Davis
Davis

Reputation: 308

Dynamic DMA transmit

I have the following snipper to generate sound and in while loop, I want to change it dynamically so it creates different frequency sound during sound gerenation.

for(uint16_t i = 0; i < sample_N; i++)
{
    dataI2S[i*2] = i*5000; 
    dataI2S[i*2 + 1] =i*5000; 
} 

HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t *)dataI2S, sample_N*2);

while (1)
{
    ps = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
    HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t *)dataI2S, sample_N*b);

    b++;
    if (b == 5)
        b = 1;

    if(ps){
        generate();
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, ps);
    } else {
        stop();
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, 0);   
    }
}

It generates sound with only the first sample_N*b value. It does not change dynamically.

How can I change HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t *)dataI2S, sample_N*b); dynamically?

void DMA1_Stream5_IRQHandler(void) {
if(x % 2 == 1)
    HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t *)dataI2S, sample_N*2);
else 
    HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t *)dataI2S2, sample_N2*2);
    x++;
HAL_DMA_IRQHandler(&hdma_spi3_tx);

}

Upvotes: 4

Views: 650

Answers (1)

Maxim Sagaydachny
Maxim Sagaydachny

Reputation: 2218

when you tell controller to transmit data over DMA it just registers your wish(instructions) and do not transmit anything while being inside HAL_I2S_Transmit_DMA function.

Your loop will do multiple iterations while single buffer gets transmitted by controller (in parallel to your code execution).

So telling controller to transmit new data while it is transmitting can cause side-effects.

You need to register interrupt handler which will tell you that transmission completed.

Then you need to register your subsequent "wish" again to sustain non-broken stream of data.

Usually it does not make sense to change data in the buffer while controller transmits them. Usually we use two buffers for transmission of real data:

  • prepare data in buffer1
  • hand off buffer1 to controller for transmission
  • prepare data in buffer2 while controller transmits data from buffer1
  • when interrupt tells you that transmit of buffer1 is complete you tell controller to use buffer2
  • while it transmits data from bugger2 you again prepare data in buffer1
  • and so on

You can either set global flag from interrupt handler and check for it in the loop (and initiate subsequent tx from your loop) or use message queue to pass messages from interrupt handler into your loop.

static void MX_I2S3_Init(void) {
    ...some code
    hdma_spi3_tx.Instance = DMA1_Stream0;
    HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
}

void DMA1_Stream0_IRQHandler(void) {
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, 1);
}

Upvotes: 5

Related Questions