cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F407 ADC Problem with sample rate above 8 kHz

Raymond Buck
Senior

I am using the STM32F407G-DISC1 board in hopes of using ADC to send audio via UDP to a remote client. Before I actually test the UDP process, I am using ADC to encode the audio and pass it to the onboard DAC. So far I have not been very successful. The Discovery board is running at 168 MHz.

I am using ADC1 in the interrupt mode with Timer 3 as the trigger. DAC is triggered by software in the ADC complete callback function. As long as I keep the ADC running at 8 kHz, everything works well. The DAC output sine wave looks on the scope as what you would expect to see, more or less a sine wave. However, I would like to sample at 16 kHz to get a cleaner signal.

I have Ethernet up and running and have a UDP client running on my desktop. UDP sends the signal and the desktop client receives the packets. So that process appears to be working. My ultimate goal is to use another STM32F407G-DISC1 board as the client. I will receive the UDP data on that board and pass it off to the DAC. So I should end up with the same audio out of the DAC on the second board.

However, if I set the ADC to anything above 8 kHz, it messes with the MX_LWIP_Process() call on the first board. If I set it to 10 kHz I usually get 3 good replies when I ping the board. If I set the ADC to 16 kHz, most of the time I only get 2 good replies. At 8 kHz I get all 4 replies with less than 1 ms turn around time. The problem appears that the ADC interrupt happens too quickly for the main loop to reliably run the MX_LWIP_Process() code.

Is there any way to work around that problem by somehow using DMA to process the ADC packets and hand them off to the Ethernet send process? I did setup an ADC to DAC DMA test program and it runs fine without blocking the MX_LWIP_Process() call. However, there appears to be no way to let DMA send the ADC conversions to the Ethernet transmit process.

ADC code is shown here:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
  adcvalue = HAL_ADC_GetValue(&hadc1); // adcvalue is uint16_t global variable
  // adcbuff is uint16_t global array, buffcount is uint16_t global variable
  adcbuff[buffcount] = adcvalue; // place value read in current buffer position
  HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
  HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_8B_R, adcbuff[buffcount]);
  HAL_GPIO_TogglePin(CLK_GPIO_Port, CLK_Pin); // so O'scope can measure ADC update frequency
  udp_test_send(); // send adcbuff[buffcount] out as UDP packet
}

Any suggestions would be appreciated.

6 REPLIES 6

Normally people would use a larger DMA buffer in a circular mode, with an interrupt every half buffer fill, ie an HT and TC interrupt. The TIM triggering the ADC, and the ADC EOC triggering the DMA transfer.

The DAC could be trigger by the same TIM but with a slightly different phase offset on a second channel, such that the data fetched was freshly written nye the ADC.

Data transfer of the completed halves handed to another task at the DMA HT/TC interrupt.​

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Raymond Buck
Senior

Clive, I actually have a large buffer (not DMA memory, just RAM) configured for circular mode. The code I show above is a simplified version of what I am doing. I have a 16k buffer that I start feeding to the DAC when it hits the 8K point. The DAC is just temporary code that I am using to confirm the ADC process works properly. It will not be used in the final code as the ADC conversion data will be handed off to the Ethernet UDP transmit process. Timer 2 is triggering the ADC now and that is where the problem is.

At 8 kHz the timer interrupt occurs every 125 usecs. Anything shorter than 125 usecs doesn't allow enough time to return to main to call the MX_LWIP_Process() process reliably. Since I am dealing with audio, it has to be done in real time. I don't think I can wait for the buffer to be filled, hand it off to the Ethernet process and then wait for the buffer to be filled again to hand it off to the Ethernet process. Would that not cause breakup of the audio? And since Ethernet cannot work with DMA, my only choice is to process each ADC conversion as it happens. Or am I approaching the problem in the wrong fashion?

I can post the entire code if necessary. Basically when the buffer is half full I start sending the ADC data to the DAC starting at buffer[0]. The DAC always runs 8K behind the ADC conversions. I track both the ADC writes and DAC reads and when they hit the 16K point I reset the pointer to 0.

Mikhail Z
Senior II

On F407 Ethernet really cannot work with DMA?

Raymond Buck
Senior

Not as far as I can tell. If you look at the DMA setup in CubeMX, Ethernet is not one of the options.

A couple of years ago I did a similar project using a Microchip PIC16F16f1718 running at 32 MHz. The PIC chip handled the ADC and DAC portions of the project without breaking a sweat. The ADC output at the send side was fed to the UART running at 115200 baud. At the receive end of the link the PIC received the data from the UART and sent it to the DAC. Both the transmit and receive end of the links used a USR-TCP232-T2 to handle the UART to Ethernet conversions. The system has been in service for almost 3 years with no problems. The USR-TCP232-T2 is shown in this link: https://www.aliexpress.com/item/32278722461.html

I was hoping to create a similar device using the STM32407 to handle the ADC and DAC conversion plus use the built in Ethernet engine to handle the communications. But that may be beyond what the STM32 running at 168 MHz can handle. It is beginning to look like I may have to continue to use an external Ethernet module and just let the processor handle the ADC and DAC conversion. That would mean I do not need a processor that has the horsepower that the STM32 provides.

TDK
Guru

The ethernet has its own dedicated DMA that it uses. You don't get a choice, it's always in use. However, the overhead required to handle TCP/IP connection logic requires the CPU.

Perhaps buffer the results and only send over ethernet when the buffer reaches 25% full. I don't think calling an ISR at 8kHz that uses LWIP functions is efficient.

If you feel a post has answered your question, please click "Accept as Solution".
Raymond Buck
Senior

I am already buffering the data and attempt to send it out when the buffer reaches 50% full. However, there is not enough time between the 125 usec interrupts for the MX_LWIP_Process() function to run reliably, I believe MX_LWIP_Process() is being called consistently but then the ADC interrupt comes along and messes up the MX_LWIP_Process() call and causes it to fail. If the STM32 could run at 400 MHz it could probably handle the tasks.