STM32 cubeMX: triggering SPI DMA interrupt using interrupt












0















I am currently practicing using SPI+DMA to send data to a SPI display.
The data sequence of the display is as following:



[pull CS low]->[pull D/C low]->[1 SPI byte of CMD]->[pull D/C high]->[n SPI byte of data]->[pull CS high]. Where the D/C pin is a GPIO pin.



My idea is that first pull CS and D/C low, then send 1 byte of CMD through HAL_SPI_Transmit_IT(); and pull the D/C pin high and start the DMA transfer in the SPI interrupt routine. And the CS pin will be pull high in DMA TxComplete interrupt.



My SPI is set with data length of 8 bits, and DMA setting is memory to peripheral and increment mode.



I am using cubeMX to generate code, and here is roughly my code:



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);
}
}


And here is my SPI interrupt routine



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 */
}


And here is my last part of DMA interrupt routine



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 */
}


In my current attempt, the main starts the spi CMD transmitting, and I expect that the DMA transmitting will be triggered by HAL_SPI_Transmit_DMA(). But the DMA can only be started once, which is the very first transmitting. And then HAL_SPI_Transmit_DMA() seems like return HAL_BUSY due to hspi->State != HAL_SPI_STATE_READY.



I am not sure where I did wrong. Can any one provide any hint, what is the proper way to drive interrupt based DMA transmission?



Thanks.



Update1



I got a strange result after looking into it.
Since I only have a logic analyzer as my debugging tool, I placed pin toggling as debugging massage. I put one in the SPI_IRQHandler as follow:



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);
}
}
}


And also placed another pin toggling in the end of HAL_SPI_Transmit_DMA().
I put it in the end of the function.



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;
}


As result:
the DMA transmission only works at the very first time, then no data is transferred through DMA. AND I only get once of DIS_DC_Pin toggled, with many times toggling of DIS_NRST_Pin. Which means, that the process did entered the if(hspi2.State == HAL_SPI_STATE_READY) in the interrupt routine, but not into the HAL_SPI_Transmit_DMA()???



screen shot of logic analyzer



How can this happen?










share|improve this question

























  • Does your SPI output any data after calling HAL_SPI_Transmit_IT? I hope your solution with HAL_Delay is only temporary. It is not very efficient ot use interrupts and then hope for them to be finished after a fixed delay.

    – A.R.C.
    Jan 2 at 8:41











  • Hi, this code is only for testing the mechanism, not meant to be the final implementation. Yes, the HAL_SPI_Transmit_IT() did transmit the 0xAA as intended. But for the HAL_SPI_Transmit_DMA(), it only works at the very first interrupt after boot up, then HAL_SPI_Transmit_DMA does not do anything more, it returns HAL_BUSY.

    – MinShu Huang
    Jan 2 at 10:52











  • By the way, I tried the HAL_SPI_Transmit_DMA() separately in the main, the function itself work, it sends out the amount of data, which is assigned. But it does not work when I put it in the interrupt routine as I described in my post.

    – MinShu Huang
    Jan 2 at 11:00











  • Have you tried debugging the HAL_SPI_IRQHandler() function? This function is responsible for setting the HAL status to e.g. HAL_OK or HAL_BUSY. If the status after this function is not ok, your transmit dma function will not work.

    – A.R.C.
    Jan 2 at 12:39











  • Thanks for your input, I did more debugging, I have an update in the original post, could you have a look?

    – MinShu Huang
    Jan 2 at 20:51
















0















I am currently practicing using SPI+DMA to send data to a SPI display.
The data sequence of the display is as following:



[pull CS low]->[pull D/C low]->[1 SPI byte of CMD]->[pull D/C high]->[n SPI byte of data]->[pull CS high]. Where the D/C pin is a GPIO pin.



My idea is that first pull CS and D/C low, then send 1 byte of CMD through HAL_SPI_Transmit_IT(); and pull the D/C pin high and start the DMA transfer in the SPI interrupt routine. And the CS pin will be pull high in DMA TxComplete interrupt.



My SPI is set with data length of 8 bits, and DMA setting is memory to peripheral and increment mode.



I am using cubeMX to generate code, and here is roughly my code:



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);
}
}


And here is my SPI interrupt routine



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 */
}


And here is my last part of DMA interrupt routine



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 */
}


In my current attempt, the main starts the spi CMD transmitting, and I expect that the DMA transmitting will be triggered by HAL_SPI_Transmit_DMA(). But the DMA can only be started once, which is the very first transmitting. And then HAL_SPI_Transmit_DMA() seems like return HAL_BUSY due to hspi->State != HAL_SPI_STATE_READY.



I am not sure where I did wrong. Can any one provide any hint, what is the proper way to drive interrupt based DMA transmission?



Thanks.



Update1



I got a strange result after looking into it.
Since I only have a logic analyzer as my debugging tool, I placed pin toggling as debugging massage. I put one in the SPI_IRQHandler as follow:



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);
}
}
}


And also placed another pin toggling in the end of HAL_SPI_Transmit_DMA().
I put it in the end of the function.



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;
}


As result:
the DMA transmission only works at the very first time, then no data is transferred through DMA. AND I only get once of DIS_DC_Pin toggled, with many times toggling of DIS_NRST_Pin. Which means, that the process did entered the if(hspi2.State == HAL_SPI_STATE_READY) in the interrupt routine, but not into the HAL_SPI_Transmit_DMA()???



screen shot of logic analyzer



How can this happen?










share|improve this question

























  • Does your SPI output any data after calling HAL_SPI_Transmit_IT? I hope your solution with HAL_Delay is only temporary. It is not very efficient ot use interrupts and then hope for them to be finished after a fixed delay.

    – A.R.C.
    Jan 2 at 8:41











  • Hi, this code is only for testing the mechanism, not meant to be the final implementation. Yes, the HAL_SPI_Transmit_IT() did transmit the 0xAA as intended. But for the HAL_SPI_Transmit_DMA(), it only works at the very first interrupt after boot up, then HAL_SPI_Transmit_DMA does not do anything more, it returns HAL_BUSY.

    – MinShu Huang
    Jan 2 at 10:52











  • By the way, I tried the HAL_SPI_Transmit_DMA() separately in the main, the function itself work, it sends out the amount of data, which is assigned. But it does not work when I put it in the interrupt routine as I described in my post.

    – MinShu Huang
    Jan 2 at 11:00











  • Have you tried debugging the HAL_SPI_IRQHandler() function? This function is responsible for setting the HAL status to e.g. HAL_OK or HAL_BUSY. If the status after this function is not ok, your transmit dma function will not work.

    – A.R.C.
    Jan 2 at 12:39











  • Thanks for your input, I did more debugging, I have an update in the original post, could you have a look?

    – MinShu Huang
    Jan 2 at 20:51














0












0








0








I am currently practicing using SPI+DMA to send data to a SPI display.
The data sequence of the display is as following:



[pull CS low]->[pull D/C low]->[1 SPI byte of CMD]->[pull D/C high]->[n SPI byte of data]->[pull CS high]. Where the D/C pin is a GPIO pin.



My idea is that first pull CS and D/C low, then send 1 byte of CMD through HAL_SPI_Transmit_IT(); and pull the D/C pin high and start the DMA transfer in the SPI interrupt routine. And the CS pin will be pull high in DMA TxComplete interrupt.



My SPI is set with data length of 8 bits, and DMA setting is memory to peripheral and increment mode.



I am using cubeMX to generate code, and here is roughly my code:



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);
}
}


And here is my SPI interrupt routine



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 */
}


And here is my last part of DMA interrupt routine



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 */
}


In my current attempt, the main starts the spi CMD transmitting, and I expect that the DMA transmitting will be triggered by HAL_SPI_Transmit_DMA(). But the DMA can only be started once, which is the very first transmitting. And then HAL_SPI_Transmit_DMA() seems like return HAL_BUSY due to hspi->State != HAL_SPI_STATE_READY.



I am not sure where I did wrong. Can any one provide any hint, what is the proper way to drive interrupt based DMA transmission?



Thanks.



Update1



I got a strange result after looking into it.
Since I only have a logic analyzer as my debugging tool, I placed pin toggling as debugging massage. I put one in the SPI_IRQHandler as follow:



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);
}
}
}


And also placed another pin toggling in the end of HAL_SPI_Transmit_DMA().
I put it in the end of the function.



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;
}


As result:
the DMA transmission only works at the very first time, then no data is transferred through DMA. AND I only get once of DIS_DC_Pin toggled, with many times toggling of DIS_NRST_Pin. Which means, that the process did entered the if(hspi2.State == HAL_SPI_STATE_READY) in the interrupt routine, but not into the HAL_SPI_Transmit_DMA()???



screen shot of logic analyzer



How can this happen?










share|improve this question
















I am currently practicing using SPI+DMA to send data to a SPI display.
The data sequence of the display is as following:



[pull CS low]->[pull D/C low]->[1 SPI byte of CMD]->[pull D/C high]->[n SPI byte of data]->[pull CS high]. Where the D/C pin is a GPIO pin.



My idea is that first pull CS and D/C low, then send 1 byte of CMD through HAL_SPI_Transmit_IT(); and pull the D/C pin high and start the DMA transfer in the SPI interrupt routine. And the CS pin will be pull high in DMA TxComplete interrupt.



My SPI is set with data length of 8 bits, and DMA setting is memory to peripheral and increment mode.



I am using cubeMX to generate code, and here is roughly my code:



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);
}
}


And here is my SPI interrupt routine



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 */
}


And here is my last part of DMA interrupt routine



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 */
}


In my current attempt, the main starts the spi CMD transmitting, and I expect that the DMA transmitting will be triggered by HAL_SPI_Transmit_DMA(). But the DMA can only be started once, which is the very first transmitting. And then HAL_SPI_Transmit_DMA() seems like return HAL_BUSY due to hspi->State != HAL_SPI_STATE_READY.



I am not sure where I did wrong. Can any one provide any hint, what is the proper way to drive interrupt based DMA transmission?



Thanks.



Update1



I got a strange result after looking into it.
Since I only have a logic analyzer as my debugging tool, I placed pin toggling as debugging massage. I put one in the SPI_IRQHandler as follow:



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);
}
}
}


And also placed another pin toggling in the end of HAL_SPI_Transmit_DMA().
I put it in the end of the function.



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;
}


As result:
the DMA transmission only works at the very first time, then no data is transferred through DMA. AND I only get once of DIS_DC_Pin toggled, with many times toggling of DIS_NRST_Pin. Which means, that the process did entered the if(hspi2.State == HAL_SPI_STATE_READY) in the interrupt routine, but not into the HAL_SPI_Transmit_DMA()???



screen shot of logic analyzer



How can this happen?







stm32 spi dma






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 2 at 20:50







MinShu Huang

















asked Jan 1 at 22:59









MinShu HuangMinShu Huang

86




86













  • Does your SPI output any data after calling HAL_SPI_Transmit_IT? I hope your solution with HAL_Delay is only temporary. It is not very efficient ot use interrupts and then hope for them to be finished after a fixed delay.

    – A.R.C.
    Jan 2 at 8:41











  • Hi, this code is only for testing the mechanism, not meant to be the final implementation. Yes, the HAL_SPI_Transmit_IT() did transmit the 0xAA as intended. But for the HAL_SPI_Transmit_DMA(), it only works at the very first interrupt after boot up, then HAL_SPI_Transmit_DMA does not do anything more, it returns HAL_BUSY.

    – MinShu Huang
    Jan 2 at 10:52











  • By the way, I tried the HAL_SPI_Transmit_DMA() separately in the main, the function itself work, it sends out the amount of data, which is assigned. But it does not work when I put it in the interrupt routine as I described in my post.

    – MinShu Huang
    Jan 2 at 11:00











  • Have you tried debugging the HAL_SPI_IRQHandler() function? This function is responsible for setting the HAL status to e.g. HAL_OK or HAL_BUSY. If the status after this function is not ok, your transmit dma function will not work.

    – A.R.C.
    Jan 2 at 12:39











  • Thanks for your input, I did more debugging, I have an update in the original post, could you have a look?

    – MinShu Huang
    Jan 2 at 20:51



















  • Does your SPI output any data after calling HAL_SPI_Transmit_IT? I hope your solution with HAL_Delay is only temporary. It is not very efficient ot use interrupts and then hope for them to be finished after a fixed delay.

    – A.R.C.
    Jan 2 at 8:41











  • Hi, this code is only for testing the mechanism, not meant to be the final implementation. Yes, the HAL_SPI_Transmit_IT() did transmit the 0xAA as intended. But for the HAL_SPI_Transmit_DMA(), it only works at the very first interrupt after boot up, then HAL_SPI_Transmit_DMA does not do anything more, it returns HAL_BUSY.

    – MinShu Huang
    Jan 2 at 10:52











  • By the way, I tried the HAL_SPI_Transmit_DMA() separately in the main, the function itself work, it sends out the amount of data, which is assigned. But it does not work when I put it in the interrupt routine as I described in my post.

    – MinShu Huang
    Jan 2 at 11:00











  • Have you tried debugging the HAL_SPI_IRQHandler() function? This function is responsible for setting the HAL status to e.g. HAL_OK or HAL_BUSY. If the status after this function is not ok, your transmit dma function will not work.

    – A.R.C.
    Jan 2 at 12:39











  • Thanks for your input, I did more debugging, I have an update in the original post, could you have a look?

    – MinShu Huang
    Jan 2 at 20:51

















Does your SPI output any data after calling HAL_SPI_Transmit_IT? I hope your solution with HAL_Delay is only temporary. It is not very efficient ot use interrupts and then hope for them to be finished after a fixed delay.

– A.R.C.
Jan 2 at 8:41





Does your SPI output any data after calling HAL_SPI_Transmit_IT? I hope your solution with HAL_Delay is only temporary. It is not very efficient ot use interrupts and then hope for them to be finished after a fixed delay.

– A.R.C.
Jan 2 at 8:41













Hi, this code is only for testing the mechanism, not meant to be the final implementation. Yes, the HAL_SPI_Transmit_IT() did transmit the 0xAA as intended. But for the HAL_SPI_Transmit_DMA(), it only works at the very first interrupt after boot up, then HAL_SPI_Transmit_DMA does not do anything more, it returns HAL_BUSY.

– MinShu Huang
Jan 2 at 10:52





Hi, this code is only for testing the mechanism, not meant to be the final implementation. Yes, the HAL_SPI_Transmit_IT() did transmit the 0xAA as intended. But for the HAL_SPI_Transmit_DMA(), it only works at the very first interrupt after boot up, then HAL_SPI_Transmit_DMA does not do anything more, it returns HAL_BUSY.

– MinShu Huang
Jan 2 at 10:52













By the way, I tried the HAL_SPI_Transmit_DMA() separately in the main, the function itself work, it sends out the amount of data, which is assigned. But it does not work when I put it in the interrupt routine as I described in my post.

– MinShu Huang
Jan 2 at 11:00





By the way, I tried the HAL_SPI_Transmit_DMA() separately in the main, the function itself work, it sends out the amount of data, which is assigned. But it does not work when I put it in the interrupt routine as I described in my post.

– MinShu Huang
Jan 2 at 11:00













Have you tried debugging the HAL_SPI_IRQHandler() function? This function is responsible for setting the HAL status to e.g. HAL_OK or HAL_BUSY. If the status after this function is not ok, your transmit dma function will not work.

– A.R.C.
Jan 2 at 12:39





Have you tried debugging the HAL_SPI_IRQHandler() function? This function is responsible for setting the HAL status to e.g. HAL_OK or HAL_BUSY. If the status after this function is not ok, your transmit dma function will not work.

– A.R.C.
Jan 2 at 12:39













Thanks for your input, I did more debugging, I have an update in the original post, could you have a look?

– MinShu Huang
Jan 2 at 20:51





Thanks for your input, I did more debugging, I have an update in the original post, could you have a look?

– MinShu Huang
Jan 2 at 20:51












0






active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999597%2fstm32-cubemx-triggering-spi-dma-interrupt-using-interrupt%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999597%2fstm32-cubemx-triggering-spi-dma-interrupt-using-interrupt%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

'app-layout' is not a known element: how to share Component with different Modules

android studio warns about leanback feature tag usage required on manifest while using Unity exported app?

SQL update select statement