Skip to main content
Associate
May 11, 2026
Question

STM32H7 vs STM32G4 DDS generation through external DAC AD5640 14-bit DAC w/ SPI

  • May 11, 2026
  • 13 replies
  • 580 views

Hello,

I have implemented a DDS sine wave generator where I use a timer interrupt to update DDS phase word and also use the same timer triggering a SPI transmission via DMA to an external DAC using a nucleo-g431rb. I am now doing the same thing with a nucleoh723zg on a different pcb, but cannot achieve the same performance. I've only uploaded 1 spectrum image of the H7 output but the problem is the image I uploaded is not the constant spectrum, it's center frequency changes -20 to 20 Hz every sweep.

I am using ~16MHz SPI speed and I have checked hardware of both pcbs, there is no problem with the communication and DAC CS jitter is very low (or none) for both MCUs.

I also checked the phase word variables on the run via uart in H7 and saw that there are reoccurring (not periodic though) jumps in phase word. But I use uart so cannot be exactly sure about that.

I'm sure there are no missing interrupts which I'm controlling by flipping a GPIO pin.

I can use a direct write to SPI data register or use a buffer and update it and give that buffer to the DMA triggered by the same timer, both approaches worked in G4 board and both are giving the same results in H7.

My code and DDS output spectrums and scope images are below. I am using LL functions everywhere except for some initialization stuff that are turned off shortly after.

For reference

I've tried lowering SPI clock and lowering system clock

I've had problems with DMA when I used Dcache then I closed it, now using DMA and directly writing to SPI DR gives the same results. 

Another thing that might be useful:

Last time I uploaded code to G4 was ~3 weeks ago, from the images I can see that there is no problem with the uploaded code or LUT in the ROM. However when I disconnect and reconnect H7, before uploading code again the signal in the scope is distorted, probably meaning some ROM corruption occurs when disconnecting or just powering up)

I'm thinking there is a problem with accessing phase accumulator variable (pa) in RAM but I turned off caching and also made all relevant variables volatile. Also I'm not using any optimization, and other things I'm supposed to do (sampling an external ADC via SPI etc.) are turned off, interrupt timings are not missed for sure.

 

//bi++; //buffer index (uint8_t)
pa += pp; //pa: phase accumulator, pp: phase word
di = (pa >> 17) & bm; // LUT size is 2^15 so 17 bit shift
//txBuffer[bi] = ((uint16_t) (amp*LUT[di])); //buffer used in G4 DMA
SPI5->TXDR = (uint16_t) (amp*LUT[di]);
/* //uart phase accumulator variable test, 
if (dac_i == dac_test_size_i){ //pa is stored in a seperate bufferand then sent in
 uart_start = 1; //another place to not interfere with interrupt 
}
else{
 dac_test_arr[dac_i] = pa;
 dac_i++;
}
//*/
LL_GPIO_TogglePin(GPIOB,GPIO_PIN_8); //interrupt control GPIO

 G4 spectrumG4 spectrumH7 spectrumH7 spectrumH7 scope before uploading new codeH7 scope before uploading new codeG4 scopeG4 scopeH7 scopeH7 scopePhase Accumulator values at every updatePhase Accumulator values at every update

13 replies

AScha.3
Super User
May 11, 2026

Hi,

whats the update rate of DDS ?

and which DAC ?

+

you should run on an H7 always with optimizer, i use -O2  always.

+ with d-cache off , helping .

 

BUT i would do it different on an H7 : to reduce time with INT

- make circular DMA , 2 x 1 K ; clocked by a timer ; DMA sending to SPI -> dac output ;

- in callbacks write the calculated phase values (1000 values)

So about 99% less busy with interrupt handling.

If you feel a post has answered your question, please click on " Best Answer ".
AA6Author
Associate
May 11, 2026

Hi,

I'm using AD5640 14-bit DAC, 

DDS update rate is around 300kHz for G4, I tried from 500kHz to very low frequencies for H7, none of which work. 

I have to sample 2 external ADCs via SPI very accurately and I use IIR filtering so I don't want to use bursts for data acquisition or DDS. I was able to do everything timed very accurately on G4, I bought an H7  for data processing speed so that I can sample faster.

Using any optimization was somehow messing with my recieve only master SPI channels (idk why) so that's why I'm not using them. 

 

AScha.3
Super User
May 11, 2026

So you dont want my recommendations, how to do....ok.

+

SPI5->TXDR = (uint16_t) (amp*LUT[di]);

you write to SPI dr , as in G4 , but on H7 is different:

AScha3_0-1778523041519.png

I think only by using DMA->SPI you will get a perfect fix timing.

If you feel a post has answered your question, please click on " Best Answer ".
MasterT
Lead II
May 11, 2026

Nucleo-H7 by default getting clock from St-Link-V3  HSI, that is internal RC oscillator. Don't know about G431, but nucleo-G474re has crystall 24 MHz, and consequently clock is very clean. I'd suggest to re-program your H7 to 8.33 MHz clock from St_Link, switching to crystal instead of RC. There is programmer application and instructions on this ST.com site

Since you have access to SA, it 'd make sense to conduct some measurements before/ after you reconfigure clock base. Set GPIO pin to be driven by  timer-PWM and compare spectral width on G4 and H7.

I use MPU to configure memory to run with DMA on H7, and I also always activate D & I cache.

It also 'd make sense to make DDS using internal DAC, since it' exclude SPI complication. When it runs smoothly - no issues with memory/DMA etc observed, than switch to external DAC.

BTW, internal DAC's is not so bad after all, especially when buffered by external OP_amp, check out:

AN4566 Application note
Extending the DAC performance of STM32 microcontrollers

 

AA6Author
Associate
May 12, 2026

Hello again,

I'm using HSE crystal clock at 8 MHz, tried 8.33 today, didn't change anything. 

I've also tried -O2 optimization, no luck. I added -Wall, -Wextra, -pedantic flags for additional warnings, nothing there as well. But they might give something when I turn on SPI receive channels again.

I used to manage DMA buffer update in a different timer with same period as my DAC updating timer when I first started with G4, then I had noticed I didn't need that. So, I also tried using an additional timer in H7 today but it gave the same result.

I tried using DMA with Dcache&Icache enabled and fixed the transmission with SCB_ClearDcache_by_Addr(), but the result is the same spectrum.

I am sure I have no timing problems in hardware as my SPI CS signal shows no jitter in scope, and I remember I was able to get away with a little jitter when developing with G4, though I fixed it later. Also I think you would agree that the signal from the scope doesn't shout noisy spectrum at first glance, I've only noticed it when I tried to sweep a high Q resonator, spectrum analyzer is not my usual debugging tool.

Would you suggest another way to track variables "pa" or "di" other than uart? As I said any continuously filled test section of that buffer shouldn't have any jumps.

 

MasterT
Lead II
May 12, 2026

I think, you 've missed the point. Clock tree starts from st-link-v3 than arrives to HSE (bypass mode) on nucleo H7. So to do it right, reprogram st-link to output 8.33 MHz, than reconfigure HSE /PLL to new source, so PLL 'd stay in allowed range <960 MHz or so. See picture, and link

My scope does show jittery clock for new board before I make updates. Rigol 200 MHz, timers or MCO has to be set above 40-50 MHz (GPIO very fast speed). Scope also has SA mode, likely all modern DSO have. Try.

H753 upgrade.png

Why to generate "pa" "di" in real-time? H7 has a lot of memory, synthesize LUT and pass array to DMA. Below is my code to generate sine wave with harmonics for test purposes.

 for( uint32_t i = 0; i < BUF_SIZE; i++) { 
 double accm = 0.0;
 for( uint32_t j = 1; j < 8; j++) { 
 //for( uint32_t j = 1; j < 4; j++) { 
 freq = j * dac_freq1;
 magn = magnt_set[dac_n][j];
 faza = phase_set[dac_n][j];
 //wave = sinf(( i * freq *(2 *3.1415926535 /BUF_SIZE)) + (ANGL_RAD * faza));
 wave = sin(( i * freq *(2 *3.1415926535 /BUF_SIZE)) + (ANGL_RAD * faza));
 wave *= magn;
 accm += wave;
 }
 accm *= 2047.0f;
 //accm += 2048.0f;
 buf_dacs[dac_n][i] = roundf( accm );
 }

Code runs on nucleo-G474re, 

AA6Author
Associate
May 12, 2026

Update: I had another nucleo board (H7S3L8) lying around so I tried it today.

I'm only using the boot project with internal flash memory (LUT is smaller because H7S3 has only 64kB flash) and I managed to get it working (clean spectrum) using the same interrupt routines, DMA and SPI writes.

Now I will start again with H723ZG (new .ioc and project) and if it doesn't work I'll order another one and see where the problem lies but I think starting clean should do the trick. I'll update the thread when I find the source of the problem in 1-2 weeks.