2

I'm trying to implement communication between stm32f103 and SIM900A using FreeRTOS (mutexes and stream buffers), DMA and USART3.

I've enabled USART_IT_IDLE USART3 interrupt to be able to detect end of SIM900 transmittion and make force firing of DMA transmission complete interrupt to copy data from memory into FreeRtos's streamBuffer.

This article said that this is possible.

/**
 * \brief       Global interrupt handler for USART2
 */
void USART2_IRQHandler(void) {
    /* Check for IDLE flag */
    if (USART2->SR & USART_FLAG_IDLE) {         /* We want IDLE flag only */
        /* This part is important */
        /* Clear IDLE flag by reading status register first */
        /* And follow by reading data register */
        volatile uint32_t tmp;                  /* Must be volatile to prevent optimizations */
        tmp = USART2->SR;                       /* Read status register */
        tmp = USART2->DR;                       /* Read data register */
        (void)tmp;                              /* Prevent compiler warnings */
        DMA1_Stream5->CR &= ~DMA_SxCR_EN;       /* Disabling DMA will force transfer complete interrupt if enabled */
    }
}

But my debugger says: no

Here is my code:

#define BUFF_SIZE 16

uint8_t RX_BUFFER[BUFF_SIZE] = {"\0"};
uint8_t TX_BUFFER[BUFF_SIZE] = {"\0"};

....

// USART TX channel
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(USART3->DR);
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&TX_BUFFER[0];
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStruct.DMA_BufferSize = sizeof(TX_BUFFER);
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_Low;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel2, &DMA_InitStruct);
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&RX_BUFFER[0];
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_BufferSize = sizeof(RX_BUFFER);
DMA_Init(DMA1_Channel3, &DMA_InitStruct);

// USART RX channel
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&RX_BUFFER[0];
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_BufferSize = sizeof(RX_BUFFER);
DMA_Init(DMA1_Channel3, &DMA_InitStruct);

// Interrupts
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE);
USART_DMACmd(USART3, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);
USART_Cmd(USART3, ENABLE);
DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA1_Channel3, ENABLE);

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY + 10;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_Init(&NVIC_InitStructure);

....

void USART3_IRQHandler() {
    if (USART_GetITStatus(USART3, USART_IT_IDLE) == SET) {
        volatile uint32_t tmp = USART3->SR;
        tmp = USART3->DR;
        (void) tmp;


        // HERE interrupt should be fired!
        DMA1_Channel3->CCR &= (uint16_t)(~DMA_CCR1_EN); 
        USART_ClearITPendingBit(USART3, USART_IT_IDLE);
    }
}

void DMA1_Channel3_IRQHandler(void) {
    if (DMA_GetITStatus(DMA1_IT_TC3) == SET) {
        uint8_t len = BUFF_SIZE - DMA1_Channel3->CNDTR;
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        xStreamBufferSendFromISR(xUSART3ReceiveStreamBuffer, &RX_BUFFER, len, &xHigherPriorityTaskWoken);
        for (uint8_t i = 0; i < len; i++) {
            RX_BUFFER[i] = '\0';
        }

        DMA1_Channel3->CNDTR = BUFF_SIZE;
        DMA_Cmd(DMA1_Channel3, ENABLE);
        DMA_ClearITPendingBit(DMA1_IT_TC3);
    }
}

Why I can't force DMA1_IT_TC3 interrupt? Or can it be forced on STM32F103?

Bence Kaulics
  • 7,066
  • 7
  • 33
  • 63
  • What do you mean that your debugger says no? Bear in mind that interrupts are disabled if you're stepping through code (if they weren't, you'd never manage to step from one line to the next). – cooperised Aug 09 '18 at 20:47
  • Any point of doing it this strange way. Interrupts or DMA. – 0___________ Aug 09 '18 at 21:49
  • in your uart2 isr you use DMA1_Stream5, but in your init code you use DMA1_Channel3 and uart3. What up with that? – Jeroen3 Aug 10 '18 at 06:34
  • @P__J__ If I understand correctly, it's DMA for when you don't know the incoming data size. You want to use DMA for speed, but you can't rely on the DMA controller generating an interrupt because the incoming data might not (and probably won't) completely fill the buffer, so you use the USART idle detection to disable the DMA and force an early interrupt. – cooperised Aug 10 '18 at 09:44
  • @cooperised "What do you mean that your debugger says no?" I've add breakpoints into DMA1_Channel3_IRQHandler and debugger doesn't stoped there and xUSART3ReceiveStreamBuffer was empty. And i've checked that USART3_IRQHandler was invoked correctly on IDLE state detected – Nick Muntyanov Aug 10 '18 at 11:52
  • @Jeroen3 DMA1_Stream5 and USART2 in example that i've used to write my own code. USART3 RX uses DMA1_Channel3 In STM32F103, so that's why I write my code in this way – Nick Muntyanov Aug 10 '18 at 11:56
  • 1
    @cooperised I think i've found an answer. I've make a little googling and found that my MCU doesn't support dynamic DMA TC interrupt firing. https://community.st.com/s/question/0D50X00009XkWuTSAV/disabling-dma-has-no-effect-on-dma-transfer-compelete-flag-tcif – Nick Muntyanov Aug 10 '18 at 11:57
  • You should still be able to disable the DMA, as you're doing, and then read the remaining data from the buffer manually. A bit more code, but definitely doable. – cooperised Aug 11 '18 at 07:46

0 Answers0