cancel
Showing results for 
Search instead for 
Did you mean: 

SPI with DMA: Add delay between bytes

JSmit.11
Associate II

Hi all,

I am working with a peripheral using SPI at 25MHz.

I would like to use DMA to send a continuous stream of data but the problem is that peripheral requires a 125ns delay between bytes.

Is there any way to achieve that with DMA?

I guess if i use DMA there will be no delays between bytes and thus it is not suitable for me.

Thanks

12 REPLIES 12

Thanks. I have implemented that successfully using DMAMUX.

Although it is harder to implement on a more simple CPU which has no dmamux channel and so dma can't be synchronized.

My idea is to use timer interrupt and on interrupt program dma to transfer one byte.

Hope that will take less that 125 ns.

> My idea is to use timer interrupt and on interrupt program dma to transfer one byte.

For a single byte you don't need DMA at all. Just write the data to the peripheral with CPU and it will be even faster.

MasterT
Senior III

I had similar problem with interfacing Analog Device ADC to STM32F7. (uCPU should support Combined PWM feature.)

ADC sampling rate is 1 MSPS, 16-bits, and it requires 500-700 nsec to complete analog-digital conversion. Whatever time left is for data clocking out. Idea I come up with, is using two timers. One is a master, defines frame rate, CS pulse. Another timer is gated, and provides SPI clock. SPI itself on stm32F configured in slave mode, so spi-CS connected to adc-CS and to Timer-CS driver pin. Same with spi-clock - Timer as a driver connected to spi-CLK & adc-CLK. To make it all to run synchronously, start with clock (25 MHz or whatever) than calculated required PWM for master though gated timer outputs exactly necessary number of pulses.

void sampl_rate(uint16_t ksps)
{
  uint16_t tim2_clok;
  uint16_t tim2_pwm1;
  uint16_t tim2_pwm2;
  uint16_t tim3_clok;
 
    if(ksps > 1500) ksps = 1500;
  
    tim3_clok = 2;          
  
    tim2_clok = roundf(108000.0 /(tim3_clok *ksps));
    tim2_clok *= tim3_clok; 
    
    tim2_pwm1 = 16 * tim3_clok +dly_trans;
    tim2_pwm2 =  dly_trans; // shift right 25 nsec
  
    TIM2->CR1   &= ~(TIM_CR1_CEN);
    TIM3->CR1   &= ~(TIM_CR1_CEN);
  
    TIM2->ARR   = tim2_clok -1;
    TIM3->ARR   = tim3_clok -1;
    
    TIM2->CCR1  = tim2_pwm1;
    TIM2->CCR2  = tim2_pwm2;
    TIM2->CCR3  = (tim2_pwm1 *5) /6;
    //Charge pump:
    TIM2->CCR4  = tim2_clok /2;
    TIM3->CCR2  = tim3_clok /2;
 
    TIM2->CNT   =   0;
    TIM3->CNT   =   0; 
    //  p. 41
    //  5. Reset TIM3 by writing ‘1 in UG bit (TIM3_EGR register).
    //  6. Reset TIM2 by writing ‘1 in UG bit (TIM2_EGR register).
    TIM2->EGR = (TIM_EGR_UG | TIM_EGR_CC2G | TIM_EGR_CC1G);
    TIM3->EGR = (TIM_EGR_UG | TIM_EGR_CC2G);
  
    TIM2->CR1 |=  (TIM_CR1_CEN);
    TIM3->CR1 |=  (TIM_CR1_CEN);
  
    resolutin = 108000.0f /(tim2_clok *32768.0f);
}
 
void SPI4_Init(void)
{
  hspi4.Instance                        = SPI4;
  hspi4.Init.Mode                       = SPI_MODE_SLAVE;
  hspi4.Init.Direction                  = SPI_DIRECTION_2LINES_RXONLY;
  hspi4.Init.DataSize                   = SPI_DATASIZE_16BIT;
  hspi4.Init.NSS                        = SPI_NSS_HARD_INPUT;
  hspi4.Init.BaudRatePrescaler          = SPI_BAUDRATEPRESCALER_2;
  hspi4.Init.TIMode                     = SPI_TIMODE_ENABLE;
  hspi4.Init.CRCCalculation             = SPI_CRCCALCULATION_DISABLE;
  hspi4.Init.CRCPolynomial              = 7;
  hspi4.Init.CRCLength                  = SPI_CRC_LENGTH_DATASIZE;
  hspi4.Init.NSSPMode                   = SPI_NSS_PULSE_DISABLE;
  
  if (HAL_SPI_Init(&hspi4) != HAL_OK)
  {
    Error_Handler();
  }
}
 
static void TIM2_Config(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = { 0};
  TIM_MasterConfigTypeDef sMasterConfig     = { 0};
  TIM_OC_InitTypeDef sConfigOC              = { 0};
 
    __HAL_RCC_TIM2_CLK_ENABLE();
 
  htim2.Instance                = TIM2;
  htim2.Init.Prescaler          = 0;
  htim2.Init.CounterMode        = TIM_COUNTERMODE_UP;
  htim2.Init.Period             = 0x07CF;
  htim2.Init.ClockDivision      = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload  = TIM_AUTORELOAD_PRELOAD_ENABLE;
 
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
    Error_Handler();
    }
    
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
    Error_Handler();
    }
 
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC2REF;
  sMasterConfig.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;
  
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
    Error_Handler();
    }
 
  sConfigOC.OCMode      = TIM_OCMODE_PWM1;
  sConfigOC.Pulse       = 0x03E8;
  sConfigOC.OCPolarity  = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode  = TIM_OCFAST_DISABLE;
  
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) {
    Error_Handler();
    }
 
  sConfigOC.OCMode      = TIM_OCMODE_COMBINED_PWM2;//TIM_OCMODE_PWM1;
  sConfigOC.Pulse       = 0x03B6;
  sConfigOC.OCPolarity  = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode  = TIM_OCFAST_DISABLE;
  
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) {
    Error_Handler();
    }
 
  sConfigOC.OCMode      = TIM_OCMODE_PWM1;
  sConfigOC.Pulse       = 0x3E8;
  sConfigOC.OCPolarity  = TIM_OCPOLARITY_LOW;
  sConfigOC.OCFastMode  = TIM_OCFAST_DISABLE;
  
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) {
    Error_Handler();
    }
 
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
 
  HAL_TIM_Base_Start(&htim2);
 
  if (HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1) != HAL_OK) {
    Error_Handler();
    }
 
  if (HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2) != HAL_OK) {
    Error_Handler();
    }
 
  if (HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3) != HAL_OK) {
    Error_Handler();
    }
 
  if (HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4) != HAL_OK) {
    Error_Handler();
    }
}
 
static void TIM3_Config(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = { 0};
  TIM_SlaveConfigTypeDef sSlaveConfig       = { 0};
  TIM_MasterConfigTypeDef sMasterConfig     = { 0};
  TIM_OC_InitTypeDef sConfigOC              = { 0};
 
  __HAL_RCC_TIM3_CLK_ENABLE();
 
  htim3.Instance                = TIM3;
  htim3.Init.Prescaler          = 0;
  htim3.Init.CounterMode        = TIM_COUNTERMODE_UP;
  htim3.Init.Period             = 0x31;
  htim3.Init.ClockDivision      = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload  = TIM_AUTORELOAD_PRELOAD_ENABLE;
 
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK) {
    Error_Handler();
    }
    
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK) {
    Error_Handler();
    }
    
  sSlaveConfig.SlaveMode    = TIM_SLAVEMODE_GATED;
  sSlaveConfig.InputTrigger = TIM_TS_ITR1;
  
  if (HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig) != HAL_OK) {
    Error_Handler();
    }
    
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;
  
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK) {
    Error_Handler();
    }
  
  sConfigOC.OCMode      = TIM_OCMODE_PWM1;
  sConfigOC.Pulse       = 0x19;
  sConfigOC.OCPolarity  = TIM_OCPOLARITY_LOW; 
  sConfigOC.OCFastMode  = TIM_OCFAST_DISABLE;
 
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) {
    Error_Handler();
    }
 
  HAL_TIM_Base_Start(&htim3);
 
  if (HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2) != HAL_OK) {
    Error_Handler();
    }
}