Проблемы с SPI в STM32F0 (HAL)

Posted by Nosaer | Posted in , , ,

    Отличительной особенностью в работе с SPI в серии F0, является использование 16 битного режима при передаче данных:
Data buffer address alignment restriction:
      (#) In case more than 1 byte is requested to be transferred, the HAL SPI uses 16-bit access for data buffer.
          But there is no support for unaligned accesses on the Cortex-M0 processor.
          So, if the user wants to transfer more than 1 byte, it shall ensure that 16-bit aligned address is used for:
          (##) pData parameter in HAL_SPI_Transmit(), HAL_SPI_Transmit_IT(), HAL_SPI_Receive() and HAL_SPI_Receive_IT()
          (##) pTxData and pRxData parameters in HAL_SPI_TransmitReceive() and HAL_SPI_TransmitReceive_IT()
      (#) There is no such restriction when going through DMA by using HAL_SPI_Transmit_DMA(), HAL_SPI_Receive_DMA()
          and HAL_SPI_TransmitReceive_DMA().
    Функции  в HAL написаны так, чтобы при передаче массива данных по SPI данные передавались пачками по 2 байта.
    Не смотря на то, какой режим передачи вы выберете в STM32CubeMX, передаваться будет всегда по 2 байта. Разница только в том, что если в 8 битном режиме остался 1 байт для передачи данных, то он будет отправлен отдельно.
    Для режима приемо-передачи есть один нюанс, это флаг SPI_FLAG_RXNE. Вся загвоздка в том, что пока аппаратно не будет получено 2 байта, этот флаг не будет выставлен. А следовательно передача в этом месте зависнет и будет ждать прихода еще одного байта.
Для этого при передаче последнего байта, флаг SPI_FLAG_RXNE выставляется самостоятельно (программно).
    Но проблема в том, что это не работает.  Хотя и реализовано вроде бы все правильно.
Но при работе и использовании функций на передачу и приемо-передачу отладчик вываливается с ошибкой в вечный цикл.
    Проблема распространенная. Часто встречался с ней на форумах, но в большинстве своем решение сводится к тому, чтобы отказаться в данном случае от HAL и переписать функцию на SPL или CMSIS.
    Для функции HAL_SPI_Transmit, мне в свое время помогло полное отключение оптимизации. Но это явно не тот выход.
Решение у меня в данном случае такое:

Имеем:
  /* Transmit and Receive data in 8 Bit mode */
  else
  {
    if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01U))
    {
      if (hspi->TxXferCount > 1U)
      {
        hspi->Instance->DR = *((uint16_t *)pTxData);
        pTxData += sizeof(uint16_t);
        hspi->TxXferCount -= 2U;
      }
      else
      {
        *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++);
        hspi->TxXferCount--;
      }
    }
    while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U))
    {
      /* check TXE flag */
      if (txallowed && (hspi->TxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)))
      {
        if (hspi->TxXferCount > 1U)
        {
          hspi->Instance->DR = *((uint16_t *)pTxData);
          pTxData += sizeof(uint16_t);
          hspi->TxXferCount -= 2U;
        }
        else
        {
          *(__IO uint8_t *)&hspi->Instance->DR = (*pTxData++);
          hspi->TxXferCount--;
        }
        /* Next Data is a reception (Rx). Tx not allowed */
        txallowed = 0U;
      }
      /* Wait until RXNE flag is reset */
      if ((hspi->RxXferCount > 0U) && (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)))
      {
        if (hspi->RxXferCount > 1U)
        {
          *((uint16_t *)pRxData) = hspi->Instance->DR;
          pRxData += sizeof(uint16_t);
          hspi->RxXferCount -= 2U;
          if (hspi->RxXferCount <= 1U)
          {
            /* set fiforxthresold before to switch on 8 bit data size */
            SET_BIT(hspi->Instance->CR2, SPI_RXFIFO_THRESHOLD);
          }
        }
        else
        {
          (*(uint8_t *)pRxData++) = *(__IO uint8_t *)&hspi->Instance->DR;
          hspi->RxXferCount--;
        }
        /* Next Data is a Transmission (Tx). Tx is allowed */
        txallowed = 1U;
      }
      if ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >=  Timeout))
      {
        errorcode = HAL_TIMEOUT;
        goto error;
      }
    }
  }

  После преобразования, имеем:
  /* Transmit and Receive data in 8 Bit mode */
else
{
while((hspi->TxXferCount > 0) || (hspi->RxXferCount > 0))
{
  /* check TXE flag */
  if((hspi->TxXferCount > 0) && ((hspi->Instance->SR & SPI_FLAG_TXE) == SPI_FLAG_TXE))
  {
    *(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr++);
    hspi->TxXferCount--;
    /* Enable CRC Transmission */
    if((hspi->TxXferCount == 0) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE))
    {
    hspi->Instance->CR1 |= SPI_CR1_CRCNEXT;
    }
  }
      /* Wait until RXNE flag is reset */
      if((hspi->RxXferCount > 0) && ((hspi->Instance->SR & SPI_FLAG_RXNE) == SPI_FLAG_RXNE))
      {
        (*hspi->pRxBuffPtr++) =  *(__IO uint8_t *)&hspi->Instance->DR;
        hspi->RxXferCount--;
      }
      if ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >=  Timeout))
      {
        errorcode = HAL_TIMEOUT;
        goto error;
      }
    }
  }

    Если вникнуть в изменения, то можно увидеть что я исключил из функции проверки на количество байт для передачи и все время передаю по 1 байту, вместо 2.
    Возможно стоило бы как то более глубже вникнуть в проблему, и понять почему именно получаю ошибку, но мне было проще поступить таким образом. Если у кого то будет более элегантное или более простое решение, буду рад ознакомиться с этим. 



Comments (0)

Отправить комментарий