В настоящее время я практикую использование SPI + DMA для отправки данных на дисплей SPI.Последовательность данных на дисплее следующая:
[pull CS low] -> [pull D / C low] -> [1 байт SPI CMD] -> [pull D / C high] ->[n SPI байт данных] -> [тянуть CS высоко].Там, где вывод D / C является выводом GPIO.
Моя идея состоит в том, чтобы сначала вывести низкий уровень CS и D / C, а затем отправить 1 байт CMD через HAL_SPI_Transmit_IT();
, вытащить вывод D / C и запуститьпередача DMA в процедуре прерывания SPI.И вывод CS будет выдвигаться высоко в прерывании DMA TxComplete.
Мой SPI установлен с длиной данных 8 бит, а настройкой DMA является память для периферийного и инкрементного режима.
Я использую cubeMX для генерации кода, и вот примерно мой код:
uint8_t displayData[DIS_DATA_BUFF_SIZE];
int main(void)
{
...init stuff from cubeMX
cmdBuffer[0].cmd = 0xAA;
cmdBuffer[0].amountOfData = 10;
cmdBuffer[0].pDataStart = displayData;
while (1)
{
HAL_Delay(500);
cmdBuffer[0].status = IN_USE;
pExecuteCmd = &cmdBuffer[0];
SPI_START();
DIS_CMD_MODE_ON();
HAL_SPI_Transmit_IT(&hspi2, &pExecuteCmd->cmd, 1);
}
}
А вот моя процедура прерывания SPI
void SPI2_IRQHandler(void)
{
/* USER CODE BEGIN SPI2_IRQn 0 */
uint8_t startDMA = 0;
if(__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE)){
if(pExecuteCmd->status == EXE_CMD){
DIS_CMD_MODE_OFF();
if(pExecuteCmd->amountOfData == 0){
SPI_END();
pExecuteCmd->status = EMPTY;
}else{
pExecuteCmd->status = EXE_DATA;
startDMA = 1;
}
}
else if(pExecuteCmd->status == IN_USE){
pExecuteCmd->status = EXE_CMD;
}
}
/* USER CODE END SPI2_IRQn 0 */
HAL_SPI_IRQHandler(&hspi2);
if(startDMA)
{
HAL_SPI_Transmit_DMA(&hspi2, pExecuteCmd->pDataStart,
pExecuteCmd->amountOfData);
}
/* USER CODE BEGIN SPI2_IRQn 1 */
/* USER CODE END SPI2_IRQn 1 */
}
А вот мойпоследняя часть процедуры прерывания DMA
void DMA1_Channel5_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel5_IRQn 0 */
if(__HAL_DMA_GET_FLAG(&hdma_spi2_tx, DMA_FLAG_TC5)){
SPI_END();
pExecuteCmd->status = EMPTY;
}
/* USER CODE END DMA1_Channel5_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_spi2_tx);
/* USER CODE BEGIN DMA1_Channel5_IRQn 1 */
/* USER CODE END DMA1_Channel5_IRQn 1 */
}
В моей текущей попытке main запускает передачу spi CMD, и я ожидаю, что передача DMA будет запущена HAL_SPI_Transmit_DMA()
.Но DMA может быть запущен только один раз, что является самой первой передачей.И тогда HAL_SPI_Transmit_DMA()
кажется возвращением HAL_BUSY
из-за hspi->State != HAL_SPI_STATE_READY
.
Я не уверен, где я сделал не так.Может ли кто-нибудь дать подсказку, как правильно управлять передачей DMA на основе прерываний?
Спасибо.
Update1
Я получил странный результат после просмотра.Так как в качестве инструмента отладки у меня есть только логический анализатор, я включил переключение булавок в качестве отладочного массажа.Я поместил один в SPI_IRQHandler следующим образом:
void SPI2_IRQHandler(void)
{
/* USER CODE BEGIN SPI2_IRQn 0 */
uint8_t startDMA = 0;
if(__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE)){
if(pExecuteCmd->status == EXE_CMD){
DIS_CMD_MODE_OFF();
if(pExecuteCmd->amountOfData == 0){
SPI_END();
pExecuteCmd->status = EMPTY;
}else{
pExecuteCmd->status = EXE_DATA;
startDMA = 1;
}
}
else if(pExecuteCmd->status == IN_USE){
pExecuteCmd->status = EXE_CMD;
}
}
/* USER CODE END SPI2_IRQn 0 */
HAL_SPI_IRQHandler(&hspi2);
if(startDMA)
{
if(hspi2.State == HAL_SPI_STATE_READY){
HAL_GPIO_TogglePin(DIS_NRST_GPIO_Port, DIS_NRST_Pin);
HAL_GPIO_TogglePin(DIS_NRST_GPIO_Port, DIS_NRST_Pin);
//^^^^^^^toggle pin showing the state is READY^^^^^//
HAL_SPI_Transmit_DMA(&hspi2, pExecuteCmd->pDataStart,
pExecuteCmd->amountOfData);
}
}
}
И также поместил еще один контакт, переключающийся в конце HAL_SPI_Transmit_DMA ().Я поставил его в конце функции.
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
{
HAL_StatusTypeDef errorcode = HAL_OK;
/* Check Direction parameter */
assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));
/* Process Locked */
__HAL_LOCK(hspi);
if(hspi->State != HAL_SPI_STATE_READY)
{
errorcode = HAL_BUSY;
goto error;
}
if((pData == NULL) || (Size == 0U))
{
errorcode = HAL_ERROR;
goto error;
}
/* Set the transaction information */
hspi->State = HAL_SPI_STATE_BUSY_TX;
hspi->ErrorCode = HAL_SPI_ERROR_NONE;
hspi->pTxBuffPtr = (uint8_t *)pData;
hspi->TxXferSize = Size;
hspi->TxXferCount = Size;
/* Init field not used in handle to zero */
hspi->pRxBuffPtr = (uint8_t *)NULL;
hspi->TxISR = NULL;
hspi->RxISR = NULL;
hspi->RxXferSize = 0U;
hspi->RxXferCount = 0U;
/* Configure communication direction : 1Line */
if(hspi->Init.Direction == SPI_DIRECTION_1LINE)
{
SPI_1LINE_TX(hspi);
}
#if (USE_SPI_CRC != 0U)
/* Reset CRC Calculation */
if(hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
SPI_RESET_CRC(hspi);
}
#endif /* USE_SPI_CRC */
/* Set the SPI TxDMA Half transfer complete callback */
hspi->hdmatx->XferHalfCpltCallback = SPI_DMAHalfTransmitCplt;
/* Set the SPI TxDMA transfer complete callback */
hspi->hdmatx->XferCpltCallback = SPI_DMATransmitCplt;
/* Set the DMA error callback */
hspi->hdmatx->XferErrorCallback = SPI_DMAError;
/* Set the DMA AbortCpltCallback */
hspi->hdmatx->XferAbortCallback = NULL;
/* Enable the Tx DMA Stream */
HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->DR, hspi->TxXferCount);
/* Check if the SPI is already enabled */
if((hspi->Instance->CR1 &SPI_CR1_SPE) != SPI_CR1_SPE)
{
/* Enable SPI peripheral */
__HAL_SPI_ENABLE(hspi);
}
/* Enable the SPI Error Interrupt Bit */
SET_BIT(hspi->Instance->CR2, SPI_CR2_ERRIE);
/* Enable Tx DMA Request */
SET_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);
error :
/* Process Unlocked */
__HAL_UNLOCK(hspi);
HAL_GPIO_WritePin(DIS_DC_GPIO_Port, DIS_DC_Pin, RESET);
HAL_GPIO_WritePin(DIS_DC_GPIO_Port, DIS_DC_Pin, SET);
return errorcode;
}
В результате: передача DMA работает только в самый первый раз, затем через DMA данные не передаются.И я получаю только один раз переключение DIS_DC_Pin, и много раз переключение DIS_NRST_Pin.Это означает, что процесс вошел в if(hspi2.State == HAL_SPI_STATE_READY)
в процедуре прерывания, но не в HAL_SPI_Transmit_DMA()
???
снимок экрана логического анализатора
Как это может произойти?