Serial1(USART2) reception using DMA [Solved]

Hi all,

I am working on a small project using Spark Core. Great product thank you Spark team!!!. :sunny:

In my project there is a need that DMA copies received serial data to memory. So far, by current setting it seems DMA controller copies received data to memory, I am checking it by constantly printing all rx_buffer.buffer to terminal.

My questions are:

  1. Where and how should I implement DMA1_Channel6 interrupt handler?
  2. Any suggestions or advices on USART2 reception using DMA?

Thanks :-).

PS: I am aware that latest I2C is using same DMA resource (DMA1_Channel6).


Current settings.

Brief explanations:

ONE

core-firmware/src/spark_wiring_usartserial.cpp
beginDMA(...) function was added. It is modified version of original begin(unsigned long baud) function.

TWO

core-firmware/inc/spark_wiring_usartserial.h
"private" is changed to public. For purpose of debugging rx_buffer.

THREE

core-firmware/src/application.cpp
Checking whether first element of buffer has changed. If so print all elements. Which are 64.

=====================================================================================

CODES:

From: spark_wiring_usartserial.cpp

....
#define USART2_DR_ADDRESS    ((uint32_t)0x40004404)
// It's DMA added version of begin();
void USARTSerial::beginDMA(unsigned long baud)
{
    // AFIO clock enable
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    // Enable USART Clock
    *usartMap->usart_apbReg |=  usartMap->usart_clock_en;

    NVIC_InitTypeDef NVIC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;

    //Configuring DMA1 on USART2 RX
    DMA_DeInit(DMA1_Channel6);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART2_DR_ADDRESS;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&(_rx_buffer.buffer);
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = SERIAL_BUFFER_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel6, &DMA_InitStructure);


    // Enable the USART Interrupt
    NVIC_InitStructure.NVIC_IRQChannel = usartMap->usart_int_n;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = USART2_IRQ_PRIORITY;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

    // Configure USART Rx as input floating
    pinMode(usartMap->usart_rx_pin, INPUT);

    // Configure USART Tx as alternate function push-pull
    pinMode(usartMap->usart_tx_pin, AF_OUTPUT_PUSHPULL);

    // Remap USARTn to alternate pins EG. USART1 to pins TX/PB6, RX/PB7
    GPIO_PinRemapConfig(usartMap->usart_pin_remap, ENABLE);

    // USART default configuration
    // USART configured as follow:
    // - BaudRate = (set baudRate as 9600 baud)
    // - Word Length = 8 Bits
    // - One Stop Bit
    // - No parity
    // - Hardware flow control disabled (RTS and CTS signals)
    // - Receive and transmit enabled
    USART_InitStructure.USART_BaudRate = baud;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    // Configure USART
    USART_Init(usartMap->usart_peripheral, &USART_InitStructure);

    // Enable USART Receive and Transmit interrupts
   //  //USART_ITConfig(usartMap->usart_peripheral, USART_IT_RXNE, ENABLE);


    USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE); 
    USART_ITConfig(usartMap->usart_peripheral, USART_IT_TXE, ENABLE);

    DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);

    // Enable the USART
    USART_Cmd(usartMap->usart_peripheral, ENABLE);
    DMA_Cmd(DMA1_Channel6, ENABLE); 
    USARTSerial_Enabled = true;
    transmitting = false;
} 
...

From: src/application.cpp

....
void setup(void)
{ 
    WiFi.on();
    Serial1.beginDMA(115200);
     .... //Some GPIO setting code
}
   
void loop()
{    
if (Serial1._rx_buffer.buffer[0] != 0)
        {
            for (uint8_t i = 0; i < SERIAL_BUFFER_SIZE; i++){
                Serial1.print(Serial1._rx_buffer.buffer[i]);
            }
            Serial1.println("");
            delay(1000);
     }
}

UPDATE: Solved

Hey guys, after reading few documents and studying over spark core code, I have found my answer.

1 Like

Awesome! Glad you managed to work it out @DBear. :smile:

1 Like

How did you solve the DMA1_Channel6 issue? I don't see how one can cover that using attachSystemInterrupt().

Hi @ensonic, I do not remember solution right now :blush: , could you state your problem/question in detail?

Hi @DBear, I am trying to figure how I can attach an interrupt handler for e.g. DMA1_Channel6. The normal STM32F2xx firmware uses void functions with weak linkage so that you simply have to define a function with the right name. This won’t work on the Photon since it dynamically loads (and binds) the user-code. And hence they added methods like attachSystemInterrupt(). Unfortunately they irq types they cover don’t seem to cover dma.