Author Topic: STM32: How can I go about outputting audio to a speaker?  (Read 13780 times)

0 Members and 1 Guest are viewing this topic.

Offline TC2Topic starter

  • Newbie
  • Posts: 9
  • Country: us
STM32: How can I go about outputting audio to a speaker?
« on: May 25, 2021, 09:00:23 pm »
I am hoping someone can point me in the right direction as so far I haven't been able to get help on other forums. I am trying to learn how to output audio on a STM32 board. The board I have is the STM32F746ZG NUCLEO board.

I have small (1-3) second audio files I plan to have stored on the internal flash of my STM32, so there is no external SD card or anything. They have 16 bit data @44.1kHz. I was able to convert to 8bit so I tried both 8 and 16 bit. I plan to output the audio to a PAM8302a amplifier which is connected to a speaker. I was following along with the AN3126 (Waveform generation using DAC) from STM. I was able to successfully generate waveforms and view them on my oscilloscope. But when I send audio data, it's just static. I tried to follow along this video, but it just sounds like static for the most part with a hint of sound in the background. Not sure what I'm doing wrong but I feel this is not the way to go about this.

So does anyone know what I should be using on the board or reading on? I see things on SAI, I2S, etc and honestly just don't know where to start. Am I missing components? I'm not interested in filtering and what not at the moment, that's something I feel can come after. I just want to at least be able to output something audible for now.

I have the code I used to generate the sine wave according to the doc along with my scope pic
Code: [Select]
   
    uint8_t samples = 100;
    void getSineVal(){
    for(int i = 0; i < samples; i++){
    sineVal[i] = (sin(2*i*PI/samples)+1)*((0xFFF+1)/2);
    }
    }
    getSineVal();
    HAL_TIM_Base_Start(&htim6);
    HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
    HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, sineVal, 100, DAC_ALIGN_12B_R);
    // sineVal being the data, 100 being the # of data points

The clock for TIM6 is at 80Mhz. The prescalar is 80-1, the period is 100-1, and the #samples is 100. So 80Mhz/(80 * 100 * 100) = 100Hz.

Here is the picture of the sine wave I generated at 100 Hz


Now here is the code I made recently for the audio
Code: [Select]
    uint8_t *p = (uint8_t *)&audioArray;
    ... // I didn't paste here but I move the pointer to the data section
          if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13) == GPIO_PIN_SET){
              uint8_t *pp = (uint8_t *)p;
              uint32_t wavBuffer1[512];
              uint32_t wavBuffer2[512];
              flg_dma_done = 1;
              int count = 65536;
              while(count > 0){
                  for(int i = 0; i < 512; i++){
                      wavBuffer2[i] = *pp;
                      pp++;
                  }
                  while(!flg_dma_done){ // check if the flag is done
                      __NOP();
                  }
                  flg_dma_done = 0; // clear/reset the flag
                  count = count - 512; // 512 bytes decrease
                  HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_1); // Stop the transfer
                  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)wavBuffer2, 512, DAC_ALIGN_12B_R); // Start transmission
                  for(int i = 0; i < 512; i++){
                      wavBuffer1[i] = *pp;
                      pp++;
                  }
                  while(!flg_dma_done){ // check if the flag is done
                      __NOP();
                  }
                  flg_dma_done = 0; // Clear/reset the flag
                  count = count - 512;
                  HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_1); // Stop the transfer
                  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)wavBuffer1, 512, DAC_ALIGN_12B_R); // Start transmission
              }
          }
    ...
    void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef* hdac)
    {
        flg_dma_done = 1;
    }
Here is an image of the schematic


Thanks for reading. I've been on this for a month to a month and a half now when I can and am just completely lost. It's a personal project for me, I've recently graduated in the past year and I'm trying to learn STM32. Thanks.

P.S: Yes, I do have the DAC in buffer mode, and the sine wave produces an audible tone.
« Last Edit: May 25, 2021, 09:38:54 pm by TC2 »
 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #1 on: May 25, 2021, 10:22:02 pm »
In your diagram you do not have a complete circuit. Is there an implied ground wire that is not shown?
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1838
  • Country: se
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #2 on: May 26, 2021, 08:18:12 am »
I did not check the code (and there should be no difference whether a sine or something else is generated, as long as we are talking about pre-calculated data), but I have some doubts on the HW side:
  • Is the sine wave taken from the DAC or the amplifier output?
  • If the former: remember that the amplifier has a gain of ~24 dB, so your 3.3 V p2p sine will become a very nice square wave; you should attenuate the signal with a resistor divider (or lower the digital values, but that would increase quantization noise).
  • Also, a DC blocking cap is needed on the amplifier input, but if you are using a module, that might be already included.
SAI and I2S are audio synchronous serial interfaces, and they are used with a separate codec - there's no codec on a Nucleo board.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline ucanel

  • Regular Contributor
  • *
  • Posts: 134
  • Country: tr
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #3 on: May 26, 2021, 09:01:42 am »
You may have flash read problem.
Check the values you copy to the ram in the debugger.
 

Offline TC2Topic starter

  • Newbie
  • Posts: 9
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #4 on: May 26, 2021, 09:57:25 pm »
In your diagram you do not have a complete circuit. Is there an implied ground wire that is not shown?
Hi, you are right, it is not complete, I was meaning to just show the general connections. The STM32 is connected to my PC so it shares the ground I believe? The amp is connected to the bench PSU but to the positive and negative, not the earth ground.

I did not check the code (and there should be no difference whether a sine or something else is generated, as long as we are talking about pre-calculated data), but I have some doubts on the HW side:
  • Is the sine wave taken from the DAC or the amplifier output?
  • If the former: remember that the amplifier has a gain of ~24 dB, so your 3.3 V p2p sine will become a very nice square wave; you should attenuate the signal with a resistor divider (or lower the digital values, but that would increase quantization noise).
  • Also, a DC blocking cap is needed on the amplifier input, but if you are using a module, that might be already included.
SAI and I2S are audio synchronous serial interfaces, and they are used with a separate codec - there's no codec on a Nucleo board.

The sine wave is taken from the DAC, it is not from the PAM amplifier. But wow, this is something I've forgotten from my classes that could happen.
You are right, the datasheet discusses choosing a DC input capacitor. I do have a module and will have to double check this week. But without it, would it be that much noise where it makes the audio inaudible?

You may have flash read problem.
Check the values you copy to the ram in the debugger.

Hiya. In debug mode when I was debugging, I observed the pointer that points to the audioArray. It seems to get the correct values and assign it to the buffers, so not sure if that is an issue.
« Last Edit: May 26, 2021, 10:15:19 pm by TC2 »
 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #5 on: May 26, 2021, 10:02:23 pm »
Hi, you are right, it is not complete, I was meaning to just show the general connections. The STM32 is connected to my PC so it shares the ground I believe? The amp is connected to the bench PSU but to the positive and negative, not the earth ground.

It is critical that both the STM32 board and the amplifier board share a common ground. Do not rely on the earth ground of your PC through the USB cable, even if that works in some situations it is just asking for trouble. Connect a wire from the ground node on the STM32 board to the ground on the amplifier board. You will want to have a DC blocking capacitor too in series with the audio line too, value is not critical, 10uF electrolytic is probably reasonable.
 

Online DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6470
  • Country: es
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #6 on: May 26, 2021, 11:26:36 pm »
I recently used a cheap pcm5102 i2s dac($4), worked great in dma mode while reading and decoding MP3 files from a pendrive.
If you are interested I'll share the project when I get back to home in a few days.
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline ucanel

  • Regular Contributor
  • *
  • Posts: 134
  • Country: tr
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #7 on: May 27, 2021, 12:17:05 am »
Last answer at that link:
https://community.st.com/s/question/0D50X0000BEXUjV/haldacstartdma-stops-on-start-calling-haldacerrorcallbackch1

If that is not the issue,
what does oscilloscope shows for the memory to dac version?

Maybe the timing is an issue you are interrupting the dac (start stop for each 512 bytes).

Dac output new value frequency and the audio sample frequency must be same that need to be checked.

And if you can make it work there is a better way by using Full and Half Transfer completed Callback
in this case you do not need to use two wavbuffers,
use one wavBuffer with first 512 registered,
start dac dma, (dma circular mode)
you just need to renew first half of the wsvBuffer values at the half transfer complete callback and
renew second half of the wavBuffer values at the Full TCC.
So you will not start stop the Dac Dma until the end of the all values sended.
« Last Edit: May 27, 2021, 12:36:34 am by ucanel »
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 4948
  • Country: dk
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #8 on: May 27, 2021, 12:22:14 am »
check the data format, e.g. offset binary vs. two-complement
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 9559
  • Country: fi
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #9 on: May 27, 2021, 01:04:17 pm »
As the oscillosscope is already showing a nice sine wave as the output of the DAC, it makes no sense to debug anything before that; it's not a microcontroller or code question at all, the problem is what comes after the DAC output, i.e., connection to the amplifier and the amplifier itself.

Really, I can't think anything else than
* Excessive gain resulting in massive distortion in the amplifier
* Lack of AC coupling
* Miswiring or abusing the amplifier chip in some way.
 

Offline ledtester

  • Super Contributor
  • ***
  • Posts: 3439
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #10 on: May 27, 2021, 01:20:15 pm »

Here is an image of the schematic



What's SD (shutdown) connected to?

Are you using the bare chip or a PAM8302 module like from Adafruit/ebay/aliexpress?

Can you test with a different chip/module?
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 16146
  • Country: fr
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #11 on: May 27, 2021, 03:29:05 pm »
As the oscillosscope is already showing a nice sine wave as the output of the DAC, it makes no sense to debug anything before that;

Exactly my thought, but I've found the OP's explanation not very clear. So first thing they should do: use the "problematic" setup exactly (DAC connected to powered on amp, same MCU code, etc) and look at the DAC signal with a scope (while it's connected to the amp!) If everything looks correct, then obviously it's an amp setup issue. If the signal doesn't look correct, but does when you disconnect the DAC from the amp, then there's something faulty in your amp or your connections somehow. As always, debug one thing at a time and with some logic.
 

Offline TC2Topic starter

  • Newbie
  • Posts: 9
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #12 on: May 27, 2021, 09:52:30 pm »
It is critical that both the STM32 board and the amplifier board share a common ground. Do not rely on the earth ground of your PC through the USB cable, even if that works in some situations it is just asking for trouble. Connect a wire from the ground node on the STM32 board to the ground on the amplifier board. You will want to have a DC blocking capacitor too in series with the audio line too, value is not critical, 10uF electrolytic is probably reasonable.

Hiya. Okay, I connected GND from the STM32 to the GND of the amplifier, and the speaker negative terminal to the ground as well. I guess that makes the - terminal ground basically then? I added the capacitors which got rid of the static noise at the speaker, and adding the connection of the GND from the STM32 produces an audible tone.

Exactly my thought, but I've found the OP's explanation not very clear. So first thing they should do: use the "problematic" setup exactly (DAC connected to powered on amp, same MCU code, etc) and look at the DAC signal with a scope (while it's connected to the amp!) If everything looks correct, then obviously it's an amp setup issue. If the signal doesn't look correct, but does when you disconnect the DAC from the amp, then there's something faulty in your amp or your connections somehow. As always, debug one thing at a time and with some logic.
The sinewav I generated is a 441Hz Sine Wave which I checked from the STM32 DAC output and it looked fine. On the +,- output of the amplifier without the speaker connected, I get the following output.

Is this the "nice" square wave that @newbrain mentioned? As you all can see, it doesn't look that nice! These "square waves" (if it is indeed a square wave) appear for around 30ms, then flatlines for around 260ms.


What's SD (shutdown) connected to?

Are you using the bare chip or a PAM8302 module like from Adafruit/ebay/aliexpress?

Can you test with a different chip/module?


I read to leave SD floating if you don't plan to use the shutdown feature.
I'm using a module from adafruit.
I do not have another module.

I recently used a cheap pcm5102 i2s dac($4), worked great in dma mode while reading and decoding MP3 files from a pendrive.
If you are interested I'll share the project when I get back to home in a few days.
Thanks for the offer, but I am looking to specifically use the STM board's DAC to do this  :) . But if there is something that I can learn from your project maybe it is not a bad idea.

As for responses related to the code, I will poke around as per the responses.

I will mention that the @ucanel that the DAC frequency does indeed match the audio sampling frequency :)

I hope I don't seem disinterested in responding because of how long it's taking me, I just have some other things going on, so I appreciate the help :)
« Last Edit: May 27, 2021, 09:56:35 pm by TC2 »
 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #13 on: May 27, 2021, 11:01:12 pm »
Hiya. Okay, I connected GND from the STM32 to the GND of the amplifier, and the speaker negative terminal to the ground as well. I guess that makes the - terminal ground basically then? I added the capacitors which got rid of the static noise at the speaker, and adding the connection of the GND from the STM32 produces an audible tone.

Yes, the - terminal is ground, that's typically what - implies. I don't want to confuse you by mentioning this, but technically "ground" can be any node you want, and just serves to identify a point to which all other voltage measurements are referenced to, but typically in most circuits it will be the node that the - terminal of the battery or power supply connects to.

Sounds like you're all set then, you have a complete circuit with the input to the amplifier and capacitor to block the DC offset and now you get a tone which probably means that other audio will play fine too.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 16146
  • Country: fr
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #14 on: May 28, 2021, 12:50:27 am »
The sinewav I generated is a 441Hz Sine Wave which I checked from the STM32 DAC output and it looked fine. On the +,- output of the amplifier without the speaker connected, I get the following output.

Is this the "nice" square wave that @newbrain mentioned? As you all can see, it doesn't look that nice! These "square waves" (if it is indeed a square wave) appear for around 30ms, then flatlines for around 260ms.

It's a Class-D amplifier. So it doesn't look all that surprising. Running it without any speaker connected will give you a nasty looking output. That is normal.

From the datasheet, it shouldn't require any output capacitor, as it's a "differential" output. Of course, for that to work properly, the speaker must be connected to the + and - outputs of the amplifier IC. Do NOT ground any of the speaker's terminals!

But it does require an input capacitor - to accomodate the internal biasing. It's clearly stated in the datasheet.

 

Offline james_s

  • Super Contributor
  • ***
  • Posts: 21611
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #15 on: May 28, 2021, 04:34:18 am »
I should have looked closer at that schematic, when I said the - terminal I meant what they're showing as A-, which is tied to Gnd. I haven't looked at the datasheet but that looks like a differential input and they're just tying one side to ground to make it single ended.
 

Offline boB

  • Frequent Contributor
  • **
  • Posts: 341
  • Country: us
    • my work www
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #16 on: May 28, 2021, 06:56:50 am »

Is the signal AC coupled ?   Maybe you need to use a capacitor to isolate the DC output of the DAC ?

Is the speaker cone being pushed out or sucking in all the way ?  That would be a sine of DC.


Is the speaker good enough to reproduce 440 Hz ?   Maybe try a bit higher frequency ?  1 or 2 kHz ?

The first scope shot looked great.  The second one, if it is a switching amp might need an L-C output filter ?

Either way, I would think you would hear "something " ?

boB
K7IQ
 

Offline TC2Topic starter

  • Newbie
  • Posts: 9
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #17 on: May 28, 2021, 11:22:30 pm »
Sounds like you're all set then, you have a complete circuit with the input to the amplifier and capacitor to block the DC offset and now you get a tone which probably means that other audio will play fine too.

Unfortunately it does not seem to be the case

Last answer at that link:
https://community.st.com/s/question/0D50X0000BEXUjV/haldacstartdma-stops-on-start-calling-haldacerrorcallbackch

I have set it to word, it was set to byte. Now there when I send the audio, no output at the speaker. A byte makes the static sound and I get a weird output on the scope from the DAC directly, but on word I just get around a 3V flat output for a while.

Either way, I'm getting an underrun from the DAC_SR (dac status register) on channel 1 which I'm using. I checked the DMA registers and it doesn't have any error flags up for the transfers, it says the transfer is complete.



Is the speaker good enough to reproduce 440 Hz ?   Maybe try a bit higher frequency ?  1 or 2 kHz ?

boB

I produced a 1kHz wave and I can hear a tone from the speaker. Though I should mention I can't really produce sine waves higher. I had this problem before, and I realized the timer I was using (TIM6) was set with either HSI or PLLCLK, and I was using HSE. Then I was able to produce higher frequency sine waves, but now I again have the issue where it's flat lining if I try to make for example a 4kHz sine wave.

Edit: Okay referring to me generating the sine wave, this issue has gone away when I removed the Start_Dma from outside the while loop into inside it. I have a 22.5kHz wave on my screen at the moment for example. Why do the higher frequency sine waves only generate inside the while loop versus being outside in the initialization area? NOTE: My sine wave generation code is in my main project, where the audio I'm currently working on inside a separate project, just to figure it out before implementing, so that's why I'm saying the sine wave is not in the while loop. My audio project has the Start_Dma in the while loop, but I don't know why it's flat lining there as I mentioned above. Anyways, I applied the 22.55kHz to the amplifier and can hear a extremely high pitch sound as expected.

check the data format, e.g. offset binary vs. two-complement
Doesn't this only matter for signed values? I'm using unsigned.
« Last Edit: May 29, 2021, 06:44:40 pm by TC2 »
 

Offline TC2Topic starter

  • Newbie
  • Posts: 9
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #18 on: June 01, 2021, 10:24:31 pm »
And if you can make it work there is a better way by using Full and Half Transfer completed Callback
in this case you do not need to use two wavbuffers,
use one wavBuffer with first 512 registered,
start dac dma, (dma circular mode)
you just need to renew first half of the wsvBuffer values at the half transfer complete callback and
renew second half of the wavBuffer values at the Full TCC.
So you will not start stop the Dac Dma until the end of the all values sended.
I'm a bit confused on how half callback and callback works.
Okay so I have a 512 byte sized buffer wavBuffer.
I will fill wavBuffer with the first 512 values of X data points of the WAV.
I start the DAC DMA in circular mode, passing wavBuffer, length 1024.
I am confused on what I do at the half call back. Do I now refill wavBuffer with the next 512 bytes?
Then at full TCC, I pass the next 512, and it keeps repeating until there is no more audio data to be sent?

Also, is it assumed I need to change the digital values by multiply them by (0xFF+1)/2?
« Last Edit: June 01, 2021, 10:46:17 pm by TC2 »
 

Offline ucanel

  • Regular Contributor
  • *
  • Posts: 134
  • Country: tr
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #19 on: June 01, 2021, 11:13:42 pm »
Sorry i explained it for 512 byte for one circle DMA stream.
If i need to explain it according to your original code:

We have 1024 bytes to send to DAC via DMA.
Code: [Select]
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)wavBuffer1024, 1024, DAC_ALIGN_12B_R); // Start transmission
wavBuffer1024 is our wav values buffer.

Just for the first samples we fully load the 1024 samples to wavBuffer1024.
And start the dac dma.
When this dma dac reaches the 512th sample it gives us a half complete callback
says us that i have send the half of your buffer.
So we have our buffers first half free of use, what we do
we are preparing the next 1024 bytes
because our dac dma is now sending first 1024 bytes' second half,
we are thinking ahead of time and right now
we need to fill the wavBuffer1024's first half with second 1024 wav values first 512 bytes.
When the full transfer complete callback fires we already prepared the next 1024 wav values first half to the wavBuffer1024 so
now we have a time to fill second half of the wavBuffer1024 with second 1024 wav values last 512bytes.

It confused me too for the first time.
If you think it on the paper it is obvious.
You have a interrupt that tells you your buffer's first half is sended and then tells you your buffer's all of it sended
and you need to fill that buffer's first half while it's second half is being send,
and you need to fill that buffer's second half while it's first half is being send.


                 
« Last Edit: June 01, 2021, 11:18:03 pm by ucanel »
 

Offline TC2Topic starter

  • Newbie
  • Posts: 9
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #20 on: June 02, 2021, 01:36:41 am »
Sorry i explained it for 512 byte for one circle DMA stream.
If i need to explain it according to your original code:

We have 1024 bytes to send to DAC via DMA.
Code: [Select]
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)wavBuffer1024, 1024, DAC_ALIGN_12B_R); // Start transmission
wavBuffer1024 is our wav values buffer.

Just for the first samples we fully load the 1024 samples to wavBuffer1024.
And start the dac dma.
When this dma dac reaches the 512th sample it gives us a half complete callback
says us that i have send the half of your buffer.
So we have our buffers first half free of use, what we do
we are preparing the next 1024 bytes
because our dac dma is now sending first 1024 bytes' second half,
we are thinking ahead of time and right now
we need to fill the wavBuffer1024's first half with second 1024 wav values first 512 bytes.
When the full transfer complete callback fires we already prepared the next 1024 wav values first half to the wavBuffer1024 so
now we have a time to fill second half of the wavBuffer1024 with second 1024 wav values last 512bytes.

It confused me too for the first time.
If you think it on the paper it is obvious.
You have a interrupt that tells you your buffer's first half is sended and then tells you your buffer's all of it sended
and you need to fill that buffer's first half while it's second half is being send,
and you need to fill that buffer's second half while it's first half is being send.


Okay, this is what I came up with. Btw, I used 1024 because I wasn't clear whether the document I talked about in my original post meant 512 was the half, so total is 1024 or 512 is total. I will go back to my 512 example.
Code: [Select]
uint8_t *p = (uint8_t *)&audioArray;
uint8_t *pp; // 8, not 16 since it skips bytes
int count = 65536;
uint8_t volatile wavBuffer1[512];

int main(){
  HAL_TIM_Base_Start(&htim6);
  HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
  initializePointer(); // This function moves *p to the data
  while (1)
  {
  if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET){ // Button on board
  for(int i = 0; i < 512; i++){
  wavBuffer1[i] = *pp;
  pp++;
  }
  count -= 512;
  while(count > 0){
  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)wavBuffer1, 512, DAC_ALIGN_12B_R);
  }
                 count = 65536; // Reset count
                 HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_1);
                 pp = (uint8_t *)p; // Reset pointer back to beginning of audio data
  }
    }
}

void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef* hdac){
for(int i = 0; i < 255; i++){
wavBuffer1[i] = *pp;
pp++;
}
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_11); // for testing
}

void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef* hdac)
{
for(int i = 255; i < 512; i++){
wavBuffer1[i] = *pp;
pp++;
}
count -= 512;
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_11); // for testing
}

Did I understand the concept correctly? If so, my audio sounds like this now https://sndup.net/53qz . Barely audible. Here is also the output before it goes through the amplifier

Here it is afterit goes through the amplifier



Note I took these snapshots at random points of the reading.
Edit: I guess my brain turned to mush after 8 hours. I should of just hooked up my second probe and put the waves side by side. Sorry, I will take another reading when I can.
« Last Edit: June 02, 2021, 04:50:36 am by TC2 »
 
The following users thanked this post: ucanel

Offline ucanel

  • Regular Contributor
  • *
  • Posts: 134
  • Country: tr
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #21 on: June 02, 2021, 10:21:26 am »
Yes more like it.
Maybe there is some little problems i added ? mark in your code.
A question you have 65535 samples
what is your sample rate?
For example if you have sample rate 8 kHz ( bare minimum for audio)
it means you have 8000 samples per second
and your 65k memory can hold almost 8 seconds of audio data.
You may also check audio play duration for to be sure.

If your pointer settings and iterations and your sample rate is correct you now need to be hearing the good sound.

Code: [Select]
uint8_t *p = (uint8_t *)&audioArray;
uint8_t *pp; // 8, not 16 since it skips bytes

? int32t count = 65536;

uint8_t volatile wavBuffer1[512];

int main(){
  HAL_TIM_Base_Start(&htim6);
  HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
  initializePointer(); // This function moves *p to the data
  while (1)
  {
  if(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET){ // Button on board
  for(int i = 0; i < 512; i++){
  wavBuffer1[i] = *pp;
  pp++;
  }
  count -= 512;
  //you just need to start dma once
// if you start it again and again
// it may cause jitter
  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)wavBuffer1, 512, DAC_ALIGN_12B_R);
? while(count > 0){  };
                 count = 65536; // Reset count
                 HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_1);
                 pp = (uint8_t *)p; // Reset pointer back to beginning of audio data
  }
    }
}

void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef* hdac){
?for(int i = 0; i <= 255; i++){   //first  512/2
wavBuffer1[i] = *pp;
pp++;
}
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_11); // for testing
}

void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef* hdac)
{
? for(int i = 256; i <= 511; i++){ // second half  512/2
wavBuffer1[i] = *pp;
pp++;
}
count -= 512;
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_11); // for testing
}

Also check whether it enters calbacks if not
you may need to use
HAL_DAC_Start_DMA_IT
instead of
HAL_DAC_Start_DMA.
I do not remember correctly, you may need to look at the callback examples.

Edit:
Code: [Select]

  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)wavBuffer1, 512, DAC_ALIGN_12B_R);
DAC_ALIGN_12B_R
If your samples are 8bits you need to change this parameter.
« Last Edit: June 02, 2021, 08:13:46 pm by ucanel »
 

Offline AndyC_772

  • Super Contributor
  • ***
  • Posts: 4343
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #22 on: June 02, 2021, 01:01:13 pm »
Here is also the output before it goes through the amplifier

That looks to me as though your audio data is using signed binary, but the DAC expects unsigned.

Try adding 0x8000 to every sample and see if that helps.

Offline TC2Topic starter

  • Newbie
  • Posts: 9
  • Country: us
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #23 on: June 02, 2021, 11:21:36 pm »
what is your sample rate?

Also check whether it enters calbacks if not
you may need to use
HAL_DAC_Start_DMA_IT
instead of
HAL_DAC_Start_DMA.
I do not remember correctly, you may need to look at the callback examples.
Edit:
Code: [Select]

  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)wavBuffer1, 512, DAC_ALIGN_12B_R);
DAC_ALIGN_12B_R
If your samples are 8bits you need to change this parameter.

My sample rate now is 44.1kHz. I do not see any interrupt functions for the DAC according to my manual. But if you take a look at my callbacks, I actually toggle a pin for testing to see if it reaches the callback, and I hooked up my oscilloscope. I observed a square wave when I pressed the button until the audio finished, so I indeed believe it's working. I also switched it to the 8B, sorry about that.

Well I have good news! I can now hear audio!!  :) :) :) :). Thanks for the help with creating the callbacks :)

That looks to me as though your audio data is using signed binary, but the DAC expects unsigned.
Try adding 0x8000 to every sample and see if that helps.

I was attempting to but I noticed in the debugger it wasn't adding 0x8000 even though I had it. Then I realized oh right, my wavBuffer1 is 8bit, let me change it to 16 bit. Once I did, I heard audio! Even without the 0x8000 I can hear audio now.

How were you able to tell it was signed from the scope? My stuff is put away right now so I can't exactly answer it myself at the moment. Also I'm a bit confused on how the data was signed, since in my audiodata array I have it declared as an unsigned 8 bit array, and the program I used WavToCode I set the output to unsigned. Am I confused on something?

Also another thing is my sound is audible, but the sound level sounds a bit low compared to the sine wave which the sound of the tone was very loud. What could be a cause?
 

Offline ucanel

  • Regular Contributor
  • *
  • Posts: 134
  • Country: tr
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #24 on: June 03, 2021, 10:08:59 am »
Quote
...
Well I have good news! I can now hear audio!!  :) :) :) :). Thanks for the help with creating the callbacks :)
...
Glad to hear that. I would also be happy to hear what it sounds like.
Maybe you can share with us a working code template, cubemx setting etc.

About sound level:
It is probably because of your wav sample values 0~255
and dac expects 0~4096.
If i recall correctly you can not change the dac scale.
DACoutput = (VREF+) x (DOR/4096)

DOR corresponds to (comes from) your wav buffer values.

So you may try to use 16bit wav buffer,
shift your 0~255 wav values to 4bit left beforehand and save it to the flash memory like that,
use 12B_R dac mode.
(maybe just using 12B_R dac mode with unmodified wav values may do the trick try them both)
If it does not solves the problem there is another thing to look at.
It is about buffered dac modeand dac output channel load,
at figure 10 in that document:

AN3126 Application note
Audio and waveform generation using the DAC in STM32 products

Next thing to do,
the dac module is not common, many low end mcus does not have it or just have one dac module.
You may have achive the same thing with PWM module.
Timer triggered Dma, callbacks, etc. are almost the same,
you will set a stand alone PWM with output frequency at least wav frequency * 2,
you will set a timer triggered dma in circular mode,
that dma will send the wav value buffer to PWM CCRx,
at the pwm pin output you will filter the pwm carrier frequency with a simple RC filter.
 

Offline AndyC_772

  • Super Contributor
  • ***
  • Posts: 4343
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: STM32: How can I go about outputting audio to a speaker?
« Reply #25 on: June 03, 2021, 05:44:12 pm »
How were you able to tell it was signed from the scope?

In a word, experience. I've seen this bug before.

Look closely at the shape of the waveform. It has very distinct upper and lower limits, and it spends most of the time close to one or the other of them. This is exactly what happens during quiet periods, which would correctly have values 0x8000 +/- some small value 'a', but instead end up taking values that are within 'a' of either 0x0000 or 0xffff.


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf