AnsweredAssumed Answered

Problems with spdif rx and DMA

Question asked by Dieter Steinwedel on Feb 19, 2017

Dear all,

 

currently, I implement a program on the "stm32f769 disc1", that receives data via spdif rx using DMA. To understand my problems, I have added my sample code at the end.The code should be easily to understand: I configure spdif rx with dma and enable the reception. Second, I add an output timer. The timer callback dumps the received data and also displays some status bits. Unfortunally, the code doesn't work proper:

  1. The dumped out data is not spdif data. If I switch to spdif polling, the dumped data differs completely. Most data using DMA are 0, even music is received.
  2. When I disconnect the device from the spdif input, the spdif callback handlers stop working. Even after reconnecting, the callbacks are not called.

 

I would be glad about help!

 

#include "stm32f7xx.h"
#include "stm32f769i_discovery.h"
#include "stm32f769i_discovery_lcd.h"

// LCD specific configurations
#define LAYER0_ADDRESS                (LCD_FB_START_ADDRESS)

// DMA specific configurations
#define DMAx_CLK_ENABLE()            __HAL_RCC_DMA1_CLK_ENABLE()
#define SPDIF_DT_RX_DMA_STREAM        DMA1_Stream1
#define SPDIF_DT_RX_DMA_CHANNEL        DMA_CHANNEL_0
#define SPDIF_DT_DMA_RX_IRQn        DMA1_Stream1_IRQn
#define SPDIF_DMA_RX_IRQHandler        DMA1_Stream1_IRQHandler

#define BUFFER_SIZE                 100
#define HALF_BUFFER_SIZE             BUFFER_SIZE / 2

static uint32_t                     dma_buffer[BUFFER_SIZE];

static DMA_HandleTypeDef             hdma_rx;

// Output specific configurations
#define PRINT_TIMER_INSTANCE         TIM5
#define PRINT_TIMER_CLK_ENABLE()     __HAL_RCC_TIM5_CLK_ENABLE()
#define PRINT_TIMER_IRQn            TIM5_IRQn
#define PRINT_TIMER_IRQHandler()    TIM5_IRQHandler()

static uint32_t                     printing = 0;
static uint32_t                     print_buffer[HALF_BUFFER_SIZE];
static TIM_HandleTypeDef             print_timer;

// SPDIFRX specific configurations
#define SPDIF_INPUT                    SPDIFRX_INPUT_IN1
#define SPDIF_GPIO_PIN                GPIO_PIN_12
#define SPDIF_GPIO                    GPIOG

#define SPDIF_LED_TOGGLE_CYCLES     100

static uint32_t                     spdifLedCyclesCounter = 0;

static SPDIFRX_HandleTypeDef        spdifrxHandle;

// Util functions =============================================================
const char* to_string(uint32_t x) {
    static char b[64];
    b[0] = '\0';

    for (uint8_t i = 0; i < 32; i++) {
        uint32_t z = (1 << (i));
        strcat(b, x & z ? "1" : "0");
        if (i > 1 && ((i + 1) % 8 == 0)) {
            strcat(b, "  ");
        }
        if (i > 1 && ((i + 1) % 4 == 0)) {
            strcat(b, " ");
        }
    }

    return b;
}

void Error_Handler(void) {
    BSP_LED_On(LED1);
    while (1);
}

static void SystemClock_Config(void) {
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_OscInitTypeDef RCC_OscInitStruct;
  HAL_StatusTypeDef  ret = HAL_OK;

  /* Enable Power Control clock */
  __HAL_RCC_PWR_CLK_ENABLE();

  /* The voltage scaling allows optimizing the power consumption when the device is
     clocked below the maximum system frequency, to update the voltage scaling value
     regarding system frequency refer to product datasheet.  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /* Enable HSE Oscillator and activate PLL with HSE as source */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 432;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 9;
  RCC_OscInitStruct.PLL.PLLR = 7;

  ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
  if (ret != HAL_OK) {
      Error_Handler();
  }

  /* Activate the OverDrive to reach the 216 MHz Frequency */
  ret = HAL_PWREx_EnableOverDrive();
  if (ret != HAL_OK) {
      Error_Handler();
  }

  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
  if (ret != HAL_OK) {
      Error_Handler();
  }
}

void HAL_SPDIFRX_MspInit(SPDIFRX_HandleTypeDef *hspdif) {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_PeriphCLKInitTypeDef rcc_clkex;

    __HAL_RCC_SPDIFRX_CLK_ENABLE();

    rcc_clkex.PeriphClockSelection = RCC_PERIPHCLK_SPDIFRX;
    rcc_clkex.I2sClockSelection = RCC_I2SCLKSOURCE_PLLI2S;
    rcc_clkex.PLLI2S.PLLI2SN = 400;
    rcc_clkex.PLLI2S.PLLI2SP = RCC_PLLI2SP_DIV2;
    HAL_RCCEx_PeriphCLKConfig(&rcc_clkex);

    /* Enable and set SPDIF Interrupt */
    HAL_NVIC_SetPriority(SPDIF_RX_IRQn, 0, 1);
    HAL_NVIC_EnableIRQ(SPDIF_RX_IRQn);

    /* GPIOs Configuration */
    GPIO_InitStructure.Pin = SPDIF_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStructure.Alternate = GPIO_AF7_SPDIFRX;
    HAL_GPIO_Init(SPDIF_GPIO, &GPIO_InitStructure);

    /* Enable DMA clock */
    DMAx_CLK_ENABLE();

    /* Configure the DMA */
    hdma_rx.Instance = SPDIF_DT_RX_DMA_STREAM;

    hdma_rx.Init.Channel = SPDIF_DT_RX_DMA_CHANNEL;
    hdma_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    hdma_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_rx.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
    hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_rx.Init.Mode = DMA_CIRCULAR;
    hdma_rx.Init.Priority = DMA_PRIORITY_HIGH;

    HAL_DMA_Init(&hdma_rx);

    /* Configure the NVIC for DMA */
    HAL_NVIC_SetPriority(SPDIF_DT_DMA_RX_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(SPDIF_DT_DMA_RX_IRQn);

    /* Associate the initialized DMA handle to the the SPI handle */
    __HAL_LINKDMA(hspdif, hdmaDrRx, hdma_rx);
}

// SPDIF callback functions ===================================================
void SPDIF_DMA_RX_IRQHandler() {
  HAL_DMA_IRQHandler(&hdma_rx);
}

void HAL_SPDIFRX_RxHalfCpltCallback(SPDIFRX_HandleTypeDef *hspdif) {
    if (!printing) {
        memcpy(&print_buffer, dma_buffer, HALF_BUFFER_SIZE * 4);
    }
}

void HAL_SPDIFRX_RxCpltCallback(SPDIFRX_HandleTypeDef *hspdif) {
    if (!printing) {
        memcpy(&print_buffer,  (uint32_t *) dma_buffer[HALF_BUFFER_SIZE], HALF_BUFFER_SIZE * 4);
    }
    if (SPDIF_LED_TOGGLE_CYCLES >= SPDIF_LED_TOGGLE_CYCLES) {
            BSP_LED_Toggle(LED1);
            spdifLedCyclesCounter = 0;
    }
    spdifLedCyclesCounter++;
}

// Print timer callbacks ======================================================

void PRINT_TIMER_IRQHandler() {
    HAL_TIM_IRQHandler(&print_timer);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (htim->Instance == PRINT_TIMER_INSTANCE) {
        BSP_LED_Toggle(LED2);

        printing = 1;

        char str[50];

        // Print data
        int start = 00;
        int end = start + 40;
        for (uint32_t i = start; i < end; i++) {
            if (i % 2) {
                BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
            } else {
                BSP_LCD_SetTextColor(LCD_COLOR_YELLOW);
            }
            uint32_t data = dma_buffer[i * 2];
            sprintf(str, "%02d. ", i);
            strcat(str, to_string(data));
            BSP_LCD_DisplayStringAt(0, LINE(i - start), (uint8_t *) str, LEFT_MODE);
        }

        // Print sample rate
        BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
        uint32_t sr = 5 * 200000000 / (((spdifrxHandle.Instance->SR >> 16) & 0x7fff) * 64);
        sprintf(str, "Sample Rate: %dHz", sr);
        BSP_LCD_DisplayStringAt(400, LINE(0), (uint8_t *) str, LEFT_MODE);

        // Print bit width
        uint32_t bit = 0;
        for (uint32_t j = start; j < end; j++) {
            bit |= (dma_buffer[j]) & 0x0000ff00;
        }
        if (bit) {
            BSP_LCD_DisplayStringAt(400, LINE(1), (uint8_t *) "Bit with: 24 bit", LEFT_MODE);
        } else {
            BSP_LCD_DisplayStringAt(400, LINE(1), (uint8_t *) "Bit with: 16 bit", LEFT_MODE);
        }

        printing = 0;

        // Printing status bits
        uint32_t status = spdifrxHandle.Instance->SR;

        sprintf(str, "Read data register not empty: %d", (status & 1));
        BSP_LCD_DisplayStringAt(400, LINE(2), (uint8_t *) str, LEFT_MODE);

        sprintf(str, "Control buffer register not empty: %d", ((status >> 1) & 1));
        BSP_LCD_DisplayStringAt(400, LINE(3), (uint8_t *) str, LEFT_MODE);

        sprintf(str, "Parity error: %d", ((status >> 2) & 1));
        BSP_LCD_DisplayStringAt(400, LINE(4), (uint8_t *) str, LEFT_MODE);

        sprintf(str, "Overrun error: %d", ((status >> 3) & 1));
        BSP_LCD_DisplayStringAt(400, LINE(5), (uint8_t *) str, LEFT_MODE);

        sprintf(str, "Synchronization block detected: %d", ((status >> 4) & 1));
        BSP_LCD_DisplayStringAt(400, LINE(6), (uint8_t *) str, LEFT_MODE);

        sprintf(str, "Synchronization done: %d", ((status >> 5) & 1));
        BSP_LCD_DisplayStringAt(400, LINE(7), (uint8_t *) str, LEFT_MODE);

        sprintf(str, "Framing error: %d", ((status >> 6) & 1));
        BSP_LCD_DisplayStringAt(400, LINE(8), (uint8_t *) str, LEFT_MODE);

        sprintf(str, "Synchronization error: %d", ((status >> 7) & 1));
        BSP_LCD_DisplayStringAt(400, LINE(9), (uint8_t *) str, LEFT_MODE);

        sprintf(str, "Timeout error: %d", ((status >> 8) & 1));
        BSP_LCD_DisplayStringAt(400, LINE(10), (uint8_t *) str, LEFT_MODE);
    }
}

// main function ==============================================================

int main(void) {
    HAL_Init();

    SystemClock_Config();

    /* Initializing LEDs */
    BSP_LED_Init(LED1);
    BSP_LED_Init(LED2);

    /* Initializing LCD */
    BSP_LCD_Init();
    BSP_LCD_LayerDefaultInit(0, LAYER0_ADDRESS);
    BSP_LCD_SelectLayer(0);
    BSP_LCD_Clear(LCD_COLOR_BLACK);
    BSP_LCD_SetFont(&Font12);
    BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
    BSP_LCD_SetBackColor(LCD_COLOR_BLACK);

    /* Configure the output timer */
    print_timer.Instance = PRINT_TIMER_INSTANCE;
    print_timer.Init.Prescaler = 99999;
    print_timer.Init.Period = 999;
    PRINT_TIMER_CLK_ENABLE();
    HAL_NVIC_SetPriority(PRINT_TIMER_IRQn, 2, 0); //Enable the peripheral IRQ
    HAL_NVIC_EnableIRQ(PRINT_TIMER_IRQn);
    HAL_TIM_Base_Init(&print_timer); //Configure the timer
    HAL_TIM_Base_Start_IT(&print_timer); //Start the timer

    /* Configure SPDIFRX Peripheral */
    spdifrxHandle.Instance = SPDIFRX;
    HAL_SPDIFRX_DeInit(&spdifrxHandle);

    spdifrxHandle.Init.InputSelection = SPDIF_INPUT;
    spdifrxHandle.Init.Retries = SPDIFRX_MAXRETRIES_15;
    spdifrxHandle.Init.WaitForActivity = SPDIFRX_WAITFORACTIVITY_OFF;
    spdifrxHandle.Init.ChannelSelection = SPDIFRX_CHANNEL_A;
    spdifrxHandle.Init.DataFormat = SPDIFRX_DATAFORMAT_MSB; // Format: MCU Spec 37.5.5 (LSB) or 37.5.6 (MSB)
    spdifrxHandle.Init.StereoMode = SPDIFRX_STEREOMODE_ENABLE;
    spdifrxHandle.Init.PreambleTypeMask = SPDIFRX_PREAMBLETYPEMASK_OFF;
    spdifrxHandle.Init.ChannelStatusMask = SPDIFRX_CHANNELSTATUS_OFF;
    spdifrxHandle.Init.ParityErrorMask = SPDIFRX_PARITYERRORMASK_OFF;
    spdifrxHandle.Init.ValidityBitMask = SPDIFRX_VALIDITYMASK_OFF;
    if (HAL_SPDIFRX_Init(&spdifrxHandle) != HAL_OK) {
        Error_Handler();
    }

    /* Receive spdif data per DMA */
    if (HAL_SPDIFRX_ReceiveDataFlow_DMA(&spdifrxHandle, (uint32_t *) dma_buffer, BUFFER_SIZE)) {
        Error_Handler();
    }

   // If you replace the last 3 lines with following code, the data dump looks fine; but it isn't dma
   // HAL_SPDIFRX_ReceiveDataFlow(&spdifrxHandle, (uint32_t *)dma_buffer, BUFFER_SIZE, 0xff);
   // memcpy(&print_buffer, dma_buffer, HALF_BUFFER_SIZE * 4);

    while (1);

}

Outcomes