cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 Timer Triggered DMA SPI – automatic NSS > problem - workaround?

enrpr
Associate II

STM32F4 Timer Triggered DMA SPI – NSS Problem

I have a STM32F417IG microcontroller an external 16bit-DAC (TI DAC81404) that is supposed to generate a Signal with a sampling rate of 32kHz. The communication via SPI should not involve any CPU resources. That is why I want to use a timer triggered DMA to shift the data with a rate of 32kHz to the SPI data register in order to send the data to the DAC.

Information about the DAC

Whenever the DAC receives a channel address and the new corresponding 16bit value the DAC will renew its output voltage to the new received value. This is achieved by:

1.      Pulling the CS/NSS/SYNC – pin to low

2.      Sending the 24bit/3 byte long message and

3.      Pulling the CS back to a high state

The first 8bit of the message are containing among other information the information where the output voltage should be applied. The next and concurrently the last 16bit are containing the new value.

Information about STM32

Unfortunately the microcontroller of ST are having a hardware problem with the NSS-pin. Starting the communication via SPI the NSS-pin is pulled low. Now the pin is low as long as SPI is enabled (. (reference manual page 877). That is sadly not the right way for communicate with device that are in need of a rise of the NSS after each message. A “solution�? would be to toggle the NSS-pin manually as suggested in the manual (When a master is communicating with SPI slaves which need to be de-selected between transmissions, the NSS pin must be configured as GPIO or another GPIO must be used and toggled by software.)

Problem

If DMA is used the ordinary way the CPU is only used when starting the process. By toggling the NSS twice every 1/32000 s this leads to corresponding CPU interactions.

My question is whether I missed something in order to achieve a communication without CPU.

If not my goal is now to reduce the CPU processing time to a minimum. My pIan is to trigger DMA with a timer. So every 1/32k seconds the data register of SPI is filled with the 24bit data and sended to the DAC.

The NSS could be toggled by a timer interrupt.

I have problems achieving it because I do not know how to link the timer with the DMA of the SPI using HAL-functions. Can anyone help me?

5 REPLIES 5
KnarfB
Principal III

You can use one timer channel to trigger the next dma and another channel of the timer to generate a PWM pulse that can serve as SS. See here

https://community.st.com/s/question/0D53W00001NVgChSAL/interface-with-external-adc-via-spi-at-1mss

hth

KnarfB

TDK
Guru

> I have problems achieving it because I do not know how to link the timer with the DMA of the SPI using HAL-functions. Can anyone help me?

The HAL has a callback when the transmission is complete that you can use to raise the CS pin. If you are using two way communication, it would be done in HAL_SPI_TxRxCpltCallback. CubeMX will generate the initialization code for you, you'd need to implement this callback somewhere in user code.

Callbacks at 32kHz are somewhat fast but quite doable.

If you feel a post has answered your question, please click "Accept as Solution".
MasterT
Lead

I interfaced dac80501 to stm32F446re, similar 24-bits protocol. The trick is to use I2S instead of SPI mode. Here is part of config:

void I2S3_Init(void)
{
  hi2s3.Instance            = SPI3;
  hi2s3.Init.Mode           = I2S_MODE_MASTER_TX; 
  hi2s3.Init.Standard       = I2S_STANDARD_PCM_SHORT;
  hi2s3.Init.DataFormat     = I2S_DATAFORMAT_32B;
  hi2s3.Init.AudioFreq      = I2S_AUDIOFREQ_48K;
  hi2s3.Init.MCLKOutput     = I2S_MCLKOUTPUT_DISABLE; //I2S_MCLKOUTPUT_ENABLE;
  hi2s3.Init.CPOL           = I2S_CPOL_HIGH; 
  //hi2s3.Init.ClockSource    = I2S_CLOCK_PLL;
  hi2s3.Init.ClockSource    = I2S_CLOCK_PLLR; 
  hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
 
  if (HAL_I2S_Init(&hi2s3) != HAL_OK)
  {
    Error_Handler();
  }
}

and data array

void trans_out1(void)
{
  uint16_t msb16, lsb16;
  int32_t  temp  = 0;
  
  uint16_t indxb = 0;
  for( int i = 0; i < (INP_BUFF /2); i++) {  
 
    if(dc_output) {
      temp = magnt_set[1];
      }
    else {
      temp = waves[i];
      }
 
//    temp = (0x0000FFFF & temp) | 0x00080000; 
    temp = (0x0000FFFF & temp) | (((uint32_t)confg_reg) << 16); 
    //0 0 0 0 1 0 0 0 DAC DATA, Bit23 < - > Bit16
    
    temp <<= 1;
    msb16 = (temp >> 16);
    lsb16 = (0x0000FFFF & temp);
 
    out[indxb++] = msb16;
    out[indxb++] = lsb16;    
    }
}

confg_reg is "service" byte, configure somewhere in your code. CubeMX helps to generate config settings for DMA

Thank you. That is a good idea.

Interesting idea. I will check whether this is possible in my case.

Thank you :)