Вызов DMA Transfer Complete не синхронизирован с SPI на STM32F446 - PullRequest
0 голосов
/ 01 мая 2018

Я хочу использовать плату STM32F446RE-Nucleo для считывания АЦП MCP3201 с регулярной частотой. Я хочу установить связь с АЦП, используя SPI и DMA для записи данных непосредственно в память.

Мой код (показанный ниже) для STM32F446RE имеет следующую структуру:

  • Прерывание SysTick (установлено на 18 кГц) используется для запуска связи SPI. SPI2 и DMA1 включены, выходной сигнал CS низок, как того требует АЦП
  • Обратный вызов DMA Receive Complete используется для прекращения связи SPI после считывания 16-битной выборки. SPI2 и DMA1 отключены, а уровень CS повышен.

SPI правильно генерирует 16-тактовый тактовый сигнал, но сигнал CS поднимается слишком рано, как я вижу на осциллографе (тактовый сигнал вверху, сигнал CS внизу):

enter image description here

Что я делаю не так, что вызывает вызов DMA1_Stream3_IRQHandler после 2 бит, а не после получения всех 16 бит данных?

    #include "main.h"

    #define SYS_CLK_FREQ 100000000 //100 MHz
    #define SYSTICK_FREQ 18000 //18 kHz
    #define ADC_SCK_PIN  LL_GPIO_PIN_13 //PB13
    #define ADC_MISO_PIN LL_GPIO_PIN_14 //PB14
    #define ADC_CS_PIN   LL_GPIO_PIN_15 //PB15
    #define BUFFER_SIZE 16 //RX Buffer size

    __IO   uint32_t uwMeasuredFrequency = 0; //measured freq
    static uint32_t TimOutClock = 1; // TIM2 Clock
    static uint8_t bit = 0;
    static uint8_t initFinished = 0;
    uint16_t aRxBuffer[BUFFER_SIZE];

    // Private function prototypes 
    __STATIC_INLINE void     SystemClock_Config(void);
    __STATIC_INLINE void     Configure_Frequency(uint32_t Frequency);
    __STATIC_INLINE void     LED_Init(void);
    __STATIC_INLINE void     LED_Blinking(uint32_t Period);
    __STATIC_INLINE void     UserButton_Init(void);
    __STATIC_INLINE void     Configure_SPI2 (void);
    __STATIC_INLINE void     Configure_DMA1 (void);

    void DMA1_ReceiveComplete_Callback (void);


    /** @brief  Main program */
    int main(void) {
      SystemClock_Config(); // Configure the system clock to 100 MHz
      LED_Init();           // Initialize LED2
      UserButton_Init();    // Initialize button in EXTI mode 
      Configure_SPI2 ();
      Configure_DMA1 ();
      initFinished = 1;
      while (1) {}
    }

    /**
    * @brief  Periodic call, using the SysTick handler. This starts communication with the ADC, that will
    * send a data sample.
    * @param  None
    * @retval None
    */
    void SysTick_Handler (void)
    {
      //LL_GPIO_SetOutputPin (LED2_GPIO_PORT, LED2_PIN); 
      /* Activate SPI 2 */
      if (initFinished) {
         LL_GPIO_ResetOutputPin (GPIOB, ADC_CS_PIN); //Pull CS low, to start ADC sampling
         LL_SPI_Enable (SPI2);
         LL_DMA_EnableStream (DMA1, LL_DMA_STREAM_3);
      }
    }

    /**
    * @brief  Function called when a sample has been received from the ADC. It can be stored or processed.
    * @param  None
    * @retval None
    */
    void DMA1_ReceiveComplete_Callback (void) {
      LL_DMA_DisableStream (DMA1, LL_DMA_STREAM_3);
      LL_SPI_Disable (SPI2);
      uint16_t sample = aRxBuffer[0];
      //LL_GPIO_SetOutputPin (GPIOB, ADC_CS_PIN);
      LL_GPIO_TogglePin (GPIOB, ADC_CS_PIN);

    }

    /**
    * @brief  This function configures SPI2.
    * @note  This function is used to :
    *        -1- Enables GPIO clock and configures the SPI2 pins.
    *        -2- Configure SPI2 functional parameters.
    * @note   Peripheral configuration is minimal configuration from reset values.
    *         Thus, some useless LL unitary functions calls below are provided as
    *         commented examples - setting is default configuration from reset.
    * @param  None
    * @retval None
    */
    __STATIC_INLINE void Configure_SPI2 (void) {
      /* (1) Enables GPIO clock and configures the SPI2 pins ********************/

      /* Enable the peripheral clock of GPIOB */
      LL_AHB1_GRP1_EnableClock (LL_AHB1_GRP1_PERIPH_ALL); //(LL_AHB1_GRP1_PERIPH_GPIOB) ;


      /* Configure SCK Pin connected to pin PB13 of CN10 connector */
      LL_GPIO_SetPinMode    (GPIOB, ADC_SCK_PIN, LL_GPIO_MODE_ALTERNATE);
      LL_GPIO_SetAFPin_8_15 (GPIOB, ADC_SCK_PIN, LL_GPIO_AF_5);
      LL_GPIO_SetPinSpeed   (GPIOB, ADC_SCK_PIN, LL_GPIO_SPEED_FREQ_HIGH);
      LL_GPIO_SetPinPull    (GPIOB, ADC_SCK_PIN, LL_GPIO_PULL_DOWN);

      /* Configure MISO Pin connected to pin PB14 of CN10 connector */
      LL_GPIO_SetPinMode    (GPIOB, ADC_MISO_PIN, LL_GPIO_MODE_ALTERNATE);
      LL_GPIO_SetAFPin_8_15 (GPIOB, ADC_MISO_PIN, LL_GPIO_AF_5);
      LL_GPIO_SetPinSpeed   (GPIOB, ADC_MISO_PIN, LL_GPIO_SPEED_FREQ_HIGH);
      LL_GPIO_SetPinPull    (GPIOB, ADC_MISO_PIN, LL_GPIO_PULL_DOWN);


      /* Configure CS Pin connected to pin PB15*/
      LL_GPIO_SetPinMode (GPIOB, ADC_CS_PIN, LL_GPIO_MODE_OUTPUT);
      LL_GPIO_SetPinSpeed (GPIOB, ADC_CS_PIN, LL_GPIO_SPEED_FREQ_HIGH);
      LL_GPIO_SetPinPull (GPIOB, ADC_CS_PIN, LL_GPIO_PULL_NO);

      /* (2) Configure SPI2 functional parameters ********************************/
      /* Enable the peripheral clock of GPIOB */
      LL_APB1_GRP1_EnableClock (LL_APB1_GRP1_PERIPH_SPI2);

      /* Configure SPI2 communication */
      LL_SPI_SetBaudRatePrescaler (SPI2, LL_SPI_BAUDRATEPRESCALER_DIV64);
      LL_SPI_SetTransferDirection (SPI2, LL_SPI_SIMPLEX_RX);
      LL_SPI_SetClockPhase (SPI2, LL_SPI_PHASE_2EDGE);
      LL_SPI_SetClockPolarity (SPI2, LL_SPI_POLARITY_LOW);
      LL_SPI_SetTransferBitOrder (SPI2, LL_SPI_MSB_FIRST); //already the default value
      LL_SPI_SetDataWidth (SPI2, LL_SPI_DATAWIDTH_16BIT);
      LL_SPI_SetNSSMode (SPI2, LL_SPI_NSS_SOFT);
      LL_SPI_SetMode (SPI2, LL_SPI_MODE_MASTER);  //I am the master! Give me samples!

      /* Enable DMA RX Interrupt */
      LL_SPI_EnableDMAReq_RX (SPI2);
    }

    /**
    * @brief  This function configures the DMA1 Channels for SPI2
    * @note  This function is used to :
    *        -1- Enable DMA1 clock
    *        -2- Configure NVIC for DMA1 transfer complete/error interrupts
    *        -3- Configure the DMA1_Stream3 functional parameters
    *        -4- Enable DMA1 interrupts complete/error
    * @param   None
    * @retval  None
    */
    __STATIC_INLINE void Configure_DMA1 (void) {
      // DMA1 used for SPI2 Reception
      LL_AHB1_GRP1_EnableClock (LL_AHB1_GRP1_PERIPH_DMA1); // Enable DMA1-clock

      /* (2) Configure NVIC for DMA transfer complete/error interrupts */
      NVIC_SetPriority (DMA1_Stream3_IRQn, 0);
      NVIC_EnableIRQ (DMA1_Stream3_IRQn);

      /* (4) Configure the DMA1_Stream3 functional parameters */
      LL_DMA_ConfigTransfer (DMA1,
         LL_DMA_STREAM_3,
         LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_NORMAL |
         LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT |
         LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE);
      LL_DMA_ConfigAddresses (DMA1, 
         LL_DMA_STREAM_3,
         LL_SPI_DMA_GetRegAddr (SPI2),
         (uint32_t)aRxBuffer,
         LL_DMA_GetDataTransferDirection (DMA1, LL_DMA_STREAM_3));
      LL_DMA_SetDataLength (DMA1, LL_DMA_STREAM_3, 1); //We receive 1 sample (16-bit)

      LL_DMA_SetChannelSelection (DMA1, LL_DMA_STREAM_3, LL_DMA_CHANNEL_0);

      /* (5) Enable DMA interrupts complete/error */
      LL_DMA_EnableIT_TC (DMA1, LL_DMA_STREAM_3);
      LL_DMA_EnableIT_TE (DMA1, LL_DMA_STREAM_3);
    }



    /**
    * @brief  Initialize LED2.
    * @param  None
    * @retval None
    */
    __STATIC_INLINE void LED_Init(void) {
      LED2_GPIO_CLK_ENABLE();
      LL_GPIO_SetPinMode(LED2_GPIO_PORT, LED2_PIN, LL_GPIO_MODE_OUTPUT);
    }


    /**
    * @brief  Configures User push-button in GPIO or EXTI Line Mode.
    */
    __STATIC_INLINE void UserButton_Init(void) {
    USER_BUTTON_GPIO_CLK_ENABLE();

    LL_GPIO_SetPinMode(USER_BUTTON_GPIO_PORT, USER_BUTTON_PIN, LL_GPIO_MODE_INPUT);
    LL_GPIO_SetPinPull(USER_BUTTON_GPIO_PORT, USER_BUTTON_PIN, LL_GPIO_PULL_NO);

    USER_BUTTON_SYSCFG_SET_EXTI();

    USER_BUTTON_EXTI_LINE_ENABLE();
    USER_BUTTON_EXTI_FALLING_TRIG_ENABLE();

    NVIC_EnableIRQ(USER_BUTTON_EXTI_IRQn); 
    NVIC_SetPriority(USER_BUTTON_EXTI_IRQn,0x03);  
    }

    /**
    * @brief  System Clock Configuration
    *         The system Clock is configured as follow :
    *            System Clock source            = PLL (HSE)
    *            SYSCLK(Hz)                     = 100000000
    *            HCLK(Hz)                       = 100000000
    *            AHB Prescaler                  = 1
    *            APB1 Prescaler                 = 2
    *            APB2 Prescaler                 = 1
    *            HSE Frequency(Hz)              = 8000000
    *            PLL_M                          = 8
    *            PLL_N                          = 400
    *            PLL_P                          = 4
    *            VDD(V)                         = 3.3
    *            Main regulator output voltage  = Scale1 mode
    *            Flash Latency(WS)              = 3
    * @param  None
    * @retval None
    */
    void SystemClock_Config(void) {
      /* Enable HSE oscillator */
      LL_RCC_HSE_EnableBypass();
      LL_RCC_HSE_Enable();
      while(LL_RCC_HSE_IsReady() != 1) {};

      LL_FLASH_SetLatency(LL_FLASH_LATENCY_3);

      LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_8, 400, LL_RCC_PLLP_DIV_4);
      LL_RCC_PLL_Enable();
      while(LL_RCC_PLL_IsReady() != 1) {}

      /* Sysclk activation on the main PLL */
      LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
      LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
      while (LL_RCC_GetSysClkSource () != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {}

      /* Set APB1 & APB2 prescaler */
      LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
      LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);

      SysTick_Config (SYS_CLK_FREQ / SYSTICK_FREQ); // Set sysTick

      /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
      SystemCoreClock = SYS_CLK_FREQ;
    }

    /******************************************************************************/
    /*   USER IRQ HANDLER TREATMENT                                               */
    /******************************************************************************/
    /**
    * @brief  User button interrupt processing
    * @note   When the user key button is pressed the frequency of the  
    *         PWM signal generated by TIM2 is updated. 
    * @param  None
    * @retval None
    */
    void UserButton_Callback(void)
    {
    /* please do nothing yet*/
    }


    /**
    * @brief  This function handles external line 13 interrupt request.
    * @param  None
    * @retval None
    */
    void USER_BUTTON_IRQHANDLER (void)
    {
      /* Manage Flags */
      if (LL_EXTI_IsActiveFlag_0_31 (USER_BUTTON_EXTI_LINE) != RESET) {
         LL_EXTI_ClearFlag_0_31 (USER_BUTTON_EXTI_LINE);

         /* User button interrupt processing(function defined in main.c) */
         UserButton_Callback (); 
      }
    }

    void DMA1_Stream3_IRQHandler (void)
    {
      if (LL_DMA_IsActiveFlag_TC3 (DMA1))
      {
         LL_DMA_ClearFlag_TC3 (DMA1);
         /* Call function Reception complete Callback */
         DMA1_ReceiveComplete_Callback ();
      }
      else if (LL_DMA_IsActiveFlag_TE3 (DMA1))
      {
         /* TODO Call Error function */
      }

    }
...