STM32L4 SPI Передача полного прерывания с использованием DMA срабатывает только один раз - PullRequest
0 голосов
/ 04 июня 2018

Я пытаюсь отправить массив из 10 байтов между двумя ядерными платами (NUCLEO-L432KCU), используя SPI и DMA.Моя цель - разработать код для ведомой платы с использованием API низкого уровня.Основная плата используется просто для тестирования, и, когда все будет работать, она будет заменена реальной системой.

Прежде чем продолжить, вот еще несколько подробностей о системе: отправитель настроен как главный.Код для мастера разработан с использованием HAL API.Выбор микросхемы на основной плате реализован с использованием GPIO.Приемник настроен как подчиненный с параметром Прием только подчиненный включен и Аппаратный вход NSS .Код инициализации генерируется автоматически G с использованием инструмента CubeMX.

В моей текущей реализации я могу получать данные на ведомой плате, но только один раз: на практике кажется, чтопрерывание срабатывает только один раз, и мне трудно понять, чего мне не хватает!

Я полагаю, что ошибка связана с очисткой некоторых флагов прерываний.Я просмотрел справочное руководство, но не могу понять, что я делаю неправильно.

Ниже приведен мой код как для отправителя, так и для получателя.


Код для отправителя

Примечание : Относительно отправителя я сообщаю только основную функцию, так как весь другой код генерируется автоматически.Кроме того, я проверил с помощью логического анализатора, что код работает.Пожалуйста, дайте мне знать, если вам нужно больше деталей.

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_SPI3_Init();
  MX_USART2_UART_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */

  uint8_t test[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
  HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,RESET);
  HAL_SPI_Transmit(&hspi1,test,sizeof(test),1000);
  HAL_GPIO_WritePin(SPI1_CS_GPIO_Port,SPI1_CS_Pin,SET);

  /* USER CODE END 2 */

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

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

Код для приемника Примечание: Конфигурация DMA и SPI в основномсделано автоматически инструментом CubeMX.Другие инициализации для моего проекта представлены в основной функции.

uint8_t aRxBuffer[10];
uint8_t received_buffer[100];
uint16_t cnt = 0;

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_SPI3_Init();
  MX_USART2_UART_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */

  // Custom configuration of DMA (after calling function MX_SPI3_INIT()
  // Configure address of the buffer for receiving data
  LL_DMA_ConfigAddresses(DMA2, LL_DMA_CHANNEL_1, LL_SPI_DMA_GetRegAddr(SPI3), (uint32_t)aRxBuffer,LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1));
  // Configure data length
  LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);
  // Enable DMA Transfer complete interrupt
  LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
  // LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);

  // We Want the SPI3 to receive 8-bit data
  // Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
  // See pag. 1221 of the TRM
  LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
  LL_SPI_EnableDMAReq_RX(SPI3);

  // Enable SPI_3
  LL_SPI_Enable(SPI3);
  // Enable DMA_2,CHANNEL_1
  LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);

  /* USER CODE END 2 */

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

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

Ниже приведен обработчик IRQ (закомментированный код представляет различные попытки заставить его работать!):

void DMA2_Channel1_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Channel1_IRQn 0 */

    // Transfer-complete interrupt management
    if(LL_DMA_IsActiveFlag_TC1(DMA2))
    {
        //LL_DMA_ClearFlag_TC1(DMA2);
        LL_DMA_ClearFlag_GI1(DMA2);
        /* Call function Transmission complete Callback */
        DMA1_TransmitComplete_Callback();
    }
    else if(LL_DMA_IsActiveFlag_TE1(DMA2))
    {
        /* Call Error function */
        int _error = 0;
    }


      // Enable SPI_3
      //LL_SPI_Disable(SPI3);
      // Enable DMA_2,CHANNEL_1
      //LL_DMA_DisableChannel(DMA2, LL_DMA_CHANNEL_1);

      //LL_DMA_EnableIT_TC(DMA2, LL_DMA_CHANNEL_1);
      // LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);

      // We Want the SPI3 to receive 8-bit data
      // Therefore we trigger the RXNE interrupt when the FIFO level is greater than or equal to 1/4 (8bit)
      // See pag. 1221 of the TRM
      //LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER);
      //LL_SPI_EnableDMAReq_RX(SPI3);

      // Enable SPI_3
      //LL_SPI_Enable(SPI3);
      // Enable DMA_2,CHANNEL_1
      LL_DMA_EnableChannel(DMA2, LL_DMA_CHANNEL_1);



    //  LL_DMA_EnableIT_TE(DMA2, LL_DMA_CHANNEL_1);

  /* USER CODE END DMA2_Channel1_IRQn 0 */

  /* USER CODE BEGIN DMA2_Channel1_IRQn 1 */

  /* USER CODE END DMA2_Channel1_IRQn 1 */
}

Ниже приводится инициализация SPI и DMA (автоматически сгенерированная):

/* SPI1 init function */
void MX_SPI1_Init(void)
{
  LL_SPI_InitTypeDef SPI_InitStruct;

  LL_GPIO_InitTypeDef GPIO_InitStruct;
  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);

  /**SPI1 GPIO Configuration  
  PA1   ------> SPI1_SCK
  PA7   ------> SPI1_MOSI 
  */
  GPIO_InitStruct.Pin = SCLK1_to_SpW_Pin|MOSI1_to_SpW_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
  SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
  SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
  SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
  SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
  SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
  SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
  SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
  SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
  SPI_InitStruct.CRCPoly = 7;
  LL_SPI_Init(SPI1, &SPI_InitStruct);

  LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);

  LL_SPI_EnableNSSPulseMgt(SPI1);

}
/* SPI3 init function */
void MX_SPI3_Init(void)
{
  LL_SPI_InitTypeDef SPI_InitStruct;

  LL_GPIO_InitTypeDef GPIO_InitStruct;
  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI3);

  /**SPI3 GPIO Configuration  
  PA4   ------> SPI3_NSS
  PB3 (JTDO-TRACESWO)   ------> SPI3_SCK
  PB5   ------> SPI3_MOSI 
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = SCLK_from_SpW_Pin|MOSI_from_SpW_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_6;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* SPI3 DMA Init */

  /* SPI3_RX Init */
  LL_DMA_SetPeriphRequest(DMA2, LL_DMA_CHANNEL_1, LL_DMA_REQUEST_3);

  LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

  LL_DMA_SetChannelPriorityLevel(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_LOW);

  LL_DMA_SetMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL);

  LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_BYTE);

  LL_DMA_SetMemorySize(DMA2, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_BYTE);

  /* SPI3 interrupt Init */
  NVIC_SetPriority(SPI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(SPI3_IRQn);

  SPI_InitStruct.TransferDirection = LL_SPI_SIMPLEX_RX;
  SPI_InitStruct.Mode = LL_SPI_MODE_SLAVE;
  SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_4BIT;
  SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
  SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
  SPI_InitStruct.NSS = LL_SPI_NSS_HARD_INPUT;
  SPI_InitStruct.BitOrder = LL_SPI_LSB_FIRST;
  SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
  SPI_InitStruct.CRCPoly = 7;
  LL_SPI_Init(SPI3, &SPI_InitStruct);

  LL_SPI_SetStandard(SPI3, LL_SPI_PROTOCOL_MOTOROLA);

  LL_SPI_DisableNSSPulseMgt(SPI3);

}

Спасибо.

1 Ответ

0 голосов
/ 04 июня 2018

Я недавно внедрил подобную систему и надеюсь, что смогу помочь.У меня есть несколько вопросов, комментариев, которые могут решить вашу проблему, но это трудно сделать, не будучи там.

  • Знаете ли вы, что это SPI или DMA, что это неправильно?Происходит ли прерывание SPI на ведомом устройстве?Это будет означать, что DMA неисправен, а не SPI.Важно точно знать, где происходит сбой системы.
  • LL_SPI_SetRxFIFOThreshold(SPI3,LL_SPI_RX_FIFO_TH_QUARTER); необходимо, но это следует делать в течение init
  • . Флаг TCIF должен быть сброшен (как вы это сделали) во время IRQ.
  • Вы должны установить SPI для запуска DMA (я не вижу его в вашем коде), используя регистр SPI_CR2_RXDMAEN.Это также следует делать во время init, если вы не знаете, когда будете получать данные.
  • По той же причине я думаю, что вы должны включить канал DMA во время init и оставить его включенным.

Я надеюсь, что один из этих комментариев поможет.Если нет, мы попробуем еще раз.

Редактировать: Хорошая работа.Я рад, что вы справились с этим, решив большинство вопросов.На основе предоставленной вами информации я выяснил, в чем заключалась основная проблема с буфером.

Вы устанавливаете DMA на получение 10 байтов с помощью:

LL_DMA_SetDataLength(DMA2, LL_DMA_CHANNEL_1,10);

Это устанавливает внутренний счетчик DMA на 10Для каждого полученного байта счетчик уменьшается на единицу, пока не достигнет нуля.Это то, что позволяет ему считать 10 байтов.В обычном режиме , если вы хотите получить еще 10 байтов, вам нужно отправить эту команду еще раз.В режиме циклический это значение будет сброшено автоматически на 10, что означает, что он может получить еще 10 байтов.

Поэтому, если вы ожидаете, что всегда получает 10 байт, тогда режим cicular должен работать как раз для вас.Если не , вам придется использовать нормальный режим и указать MCU, сколько байтов вы ожидаете (немного сложнее).

...