Search code examples
stm32interruptdmaadc

Read out Sigma Delta ADC ADS1282 with STM32F767


i want to read out an 32 bit Sigma Delata ADC per DMA over the SPI Interface with an STM32F767. There is an Data Ready Signal which indicates that there is new Data available. enter image description here

My current idea is to start the DMA transfer with the an Interrupt triggered by this Data Ready Pin. The Code is a mixture of Bare Metal Programming and configuration via Cube IDE:

   void EXTI1_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI1_IRQn 0 */

  /* USER CODE END EXTI1_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
  /* USER CODE BEGIN EXTI1_IRQn 1 */

  data_ready_flag = 1;




  DMA2_Stream0->CR |= DMA_SxCR_EN; // DMA-Transfer aktivieren

  SPI1->CR2 |= SPI_CR2_TXDMAEN; // TXDMA-Request aktivieren
  SPI1->CR1 |= SPI_CR1_SPE; // SPI aktivieren


  EXTI->PR |= EXTI_PR_PR1;
  /* USER CODE END EXTI1_IRQn 1 */
}

void DMA2_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream0_IRQn 0 */

  /* USER CODE END DMA2_Stream0_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_spi1_rx);
  /* USER CODE BEGIN DMA2_Stream0_IRQn 1 */
  if (DMA2->LISR & DMA_LISR_TCIF0)
     {
         


      
      for (int i = 0; i < SPI_BUF_LEN; i++)
      {
          receivedDataX[i] = destinationBuffer[i];
      }




        
         DMA2->LIFCR |= DMA_LIFCR_CTCIF0;

         
         SPI1->CR2 &= ~SPI_CR2_TXDMAEN;
     }
  /* USER CODE END DMA2_Stream0_IRQn 1 */
}


int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI1_Init();
  MX_ETH_Init();
  MX_USART3_UART_Init();
  MX_USB_OTG_FS_PCD_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();
  MX_TIM9_Init();
  /* USER CODE BEGIN 2 */

  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); // Chip Select 1

  TIM3->CCR1 = 10;
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);

 

  /// Schritt 3: DMA für SPI1 aktivieren
  RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;

  // Schritt 4: DMA-Konfiguration
  DMA2_Stream0->PAR = (uint16_t)(&(SPI1->DR)); // Quelladresse: SPI1 Datenregister
  DMA2_Stream0->M0AR = (uint16_t)destinationBuffer; // Zieladresse: Speicherbereich
  DMA2_Stream0->NDTR = SPI_BUF_LEN; // Anzahl der zu übertragenden Daten
  DMA2_Stream0->CR = DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0; // Channel 0, Memory-Increment-Mode, Memory-to-Peripheral-Mode

  // Schritt 5: DMA-Stream konfigurieren und aktivieren
  DMA2_Stream0->CR |= DMA_SxCR_TCIE; // Transfer Complete Interrupt aktivieren
  NVIC_EnableIRQ(DMA2_Stream0_IRQn); // NVIC-Interrupt für DMA-Stream aktivieren




  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */



  }
  /* USER CODE END 3 */

If i try to receive the data per polling method it is working but with the DMA it is not (neither with pure configuration by Cube IDE nor by bare metal approach).

Does someone have an Idea on how to configure an DMA / SPI receive triggered by Interrupt in a proper way?

Best regards

Additionally the SPI configuration which was generated by Cube IDE:

    static void MX_SPI1_Init(void)
{

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  /* SPI1 parameter configuration*/
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}

Solution

  • Several remarks:

    • always start with reading out and checking against manual the related peripherals' registers
    • observe the SPI lines using oscilloscope/LA and check/post waveforms
    • divide and conquer; while walking before running. Try first SPI only (i.e. no USB and similar obstacles), polled (without Cube), while looping back MOSI to MISO, while observing signals and resulting data in debugger; then add DMA, then add interrupts. Note, debugger is intrusive, and observing some registers (mainly status, i.e. SPI_SR) in debugger (especially "live view") while operating the peripheral may have consequences.
    • you appear to try to set up DMA for SPI_Tx (DMA direction set as Memory-to-Peripheral, enabling SPI_CR2_TXDMAEN in SPI_CR2). However, DMA2_Stream0 which you try to set up, does not have trigger from SPI_Tx, see *DMA2 request mapping table in DMA chapter of RM.
    • ORing DMA_SxCR_CHSEL_0 into value for DMA_SxCR won't select Channel 0 but Channel 1 (see DMA_SxCR_CHSEL_0 definition - you set the Channel bitfield CHSEL to 0b0001)
    • while you do indeed need to set up Tx DMA to generate SPI clocks, you probably want to set up also Rx DMA to actually receive the data

    JW