2014-04-03 11:53 AM
All,
We are using theSTM32F051C8T6
.I have some comments and questions below but I mainly wanted to get a thumbs up to make sure I was on the right track with some of my conclusions. Thank you for taking the time to read this.
We are following the ST application note AN3126. Which is titled “Audio and waveform generation using the DAC�?.
The goal is to read data from external SPI flash, moving this data into memory and using the DMA controller to push the data out to the DAC to play the audio. This will be very similar to when we will be using the ADC to sample the data, use the DMA controller to move into memory and have the CPU record the audio to the SPI flash.
What I would like to confirm that the STM32F051C8T6 will work correctly for this application and that I am not missing something huge!
16 kHz Playback / Record
We would like to play the audio back at a sample rate of 16 kHz. This should be that each data point that is used for the DAC is updated every 6.25E-5 seconds. (.0000625 seconds). For a 12-bit value that will take 2 bytes each of memory so let’s call this 128 data points. This also applies for recording and using the ADC.
128 (12-bit data points) x .0000625 seconds = 0.008 seconds.
Does this look correct?
Transferring data to/from external SPI Flash
Looking at page 16 of the application note there is an example on how to do the buffering.
While the first buffer from memory is being used to play data out to the DAC via DMA the CPU needs to be busy preparing the next buffer. This will be handed over to the DMA so the DAC has continuous playback. To do this correctly I believe that I need to be able to pull 256 bytes from the serial flash in less than 0.008 seconds.
It’s 256 bytes because I need to read 2 bytes for each 12-bit DAC value. I can only write 256 bytes at a time for the serial flash but I am able to do a continuous read.
I did some timing testing to see how long it would take to read and write a page of data on the SPI flash.
Below I show an image that shows the total write time (write enable, page program, check for write in progress) of 210 us for a page (256 bytes) of the SPI flash. The total read time (image not shown) is 334 us that surprised me. I was expecting the write to take longer.
But based on this I have 0.008 seconds to either push or pull data to my SPI flash. This would be if I am either playing data out to the DAC (read from flash) or sampling the microphone to record audio with the ADC (write to flash).
Based on these initial results:
Write
For the write 210 us = 0.000210 seconds
0.000210 seconds < 0.008 seconds (The write is a factor of 38 times faster than the sampling time )
The worst case situation is when the page program command just by itself might take up to 3 ms. (0.003 seconds + 0.000210 seconds = .00321 seconds)
0.00321 seconds < 0.008 seconds (The write is a factor of 2.49 times faster than the sampling time )
Read
For the read 334 us = 0.000334 seconds
0.000334 seconds < 0.008 (The read is a factor of about 23 times faster than the playback interval)
Polling vs Interrupts
Looking at the SPI transfer more closely I see delays between each byte of data.
The gaps in between the bytes of data being sent out look to be longer than the data itself.
Gaps are about 944 ns between the read bytes and about 432 ns for the write bytes.
For each byte that I send out I poll to check that the transmit buffer is empty then I send the byte:
// Wait until transmit buffer is empty than continue
while( !(SPI1->SR & 0x02) );
SPI_SendData8(SPI1, data);
For the read I send the data out and then wait to receive a byte clocked in:
SPI_SendData8(SPI1, 0X00);
// Wait to receive the first byte back.
while( !(SPI1->SR & 0x01) );
Is there a more optimal way to do this? Should I be using the RXFIFO/ TXFIFO? What about if I use interrupts to check the status instead of polling? How much faster would this be?
For audio playback I need to use a timer to drive the sample time of 16 kHz of the DAC. In the other direction using this same timer when using the ADC. I am planning on using Timer2 (TIM2_TRGO) to do this for both the ADC and DAC. Not at the same time. For the datasheet botht he ADC and DAC are abled to be triggered with TIMER 2.
Will there be any problems with doing this?
Thank you.2014-04-04 01:08 AM
Mike,
the way you entered into details ... sounds good ! I don't see any reason for this project not going to success, anyway i am pretty sure that you know that the devil is in details, its hard to give you any warranty ;) On the SPI side, I'am confident that you can achieve a constant data flow without gaps between words. There are (at least) two ways to achieve this: 1) use the internal TXFIFO or RXFIFO. You must keep the TXFIFO not empty during the transfer by waiting the half-full(or half-empty) signal, then fill the empty half. It is a bit tricky because you need to pipeline up to 4 TX bytes before receiving their counter part (that means starting with TX in advance and continue to receive late after last tx). 2) much better is to use DMA, one channel for RX, one channel for TX, start the beast and simply wait the transfer to finish. Regarding DMA, you must be careful when designing the HW, because you want to have simultaneous DAC/ADC/SPIRX/SPITX channel working. From the RM (DMA request mapping) you will probably use SPI2 and not SPI1 to avoid conflict with DAC channel. Last, regarding interrupt vs polling, it up to you. Every data transfer is fast to fire, and you do not have background task, it is possible to poll. Using interrupts will help you to add more features later (background task, going low-power, etc ...) BR2014-04-05 09:26 AM
Thank you for your comments on the internal FIFO, DMA and interrupts.
I am going to move forward with how I have things setup now because the timings seem sufficient. Thank you. -Mike