STM32 cubeMX: triggering SPI DMA interrupt using interrupt
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
add a comment |
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
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
add a comment |
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
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
stm32 spi dma
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
add a comment |
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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