Проблемы с SPI в STM32F0 (HAL)
Posted by Nosaer | Posted in Комплектующие , Электроника , ARM , STM32
Отличительной особенностью в работе с SPI в серии F0, является использование 16 битного режима при передаче данных:
Не смотря на то, какой режим передачи вы выберете в STM32CubeMX, передаваться будет всегда по 2 байта. Разница только в том, что если в 8 битном режиме остался 1 байт для передачи данных, то он будет отправлен отдельно.
Для режима приемо-передачи есть один нюанс, это флаг SPI_FLAG_RXNE. Вся загвоздка в том, что пока аппаратно не будет получено 2 байта, этот флаг не будет выставлен. А следовательно передача в этом месте зависнет и будет ждать прихода еще одного байта.
Для этого при передаче последнего байта, флаг SPI_FLAG_RXNE выставляется самостоятельно (программно).
Но проблема в том, что это не работает. Хотя и реализовано вроде бы все правильно.
Но при работе и использовании функций на передачу и приемо-передачу отладчик вываливается с ошибкой в вечный цикл.
Проблема распространенная. Часто встречался с ней на форумах, но в большинстве своем решение сводится к тому, чтобы отказаться в данном случае от HAL и переписать функцию на SPL или CMSIS.
Для функции HAL_SPI_Transmit, мне в свое время помогло полное отключение оптимизации. Но это явно не тот выход.
Решение у меня в данном случае такое:
Имеем:
Возможно стоило бы как то более глубже вникнуть в проблему, и понять почему именно получаю ошибку, но мне было проще поступить таким образом. Если у кого то будет более элегантное или более простое решение, буду рад ознакомиться с этим.
Data buffer address alignment restriction:Функции в HAL написаны так, чтобы при передаче массива данных по SPI данные передавались пачками по 2 байта.
(#) 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().
Не смотря на то, какой режим передачи вы выберете в 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.
Возможно стоило бы как то более глубже вникнуть в проблему, и понять почему именно получаю ошибку, но мне было проще поступить таким образом. Если у кого то будет более элегантное или более простое решение, буду рад ознакомиться с этим.