Direct Memory Access RX for STM32L1

I have been trying to transfer a block of data from my computer to STM32L100C-DISCO

over USART for some time now . For performance reasons, this should be done using DMA. However, I have not been able to get it to work so far. Since I can't figure out what I might be doing wrong, I figured I'd ask here.

I'm using libopencm3 , but unfortunately their excellent example store result doesn't seem to contain one for DMA on STM32L1xxx

. I have verified that I have looked at all the bases when it comes to configuration options available in the general DMA header file .

Naturally, I mentioned the reference manual for STM32L1xxx which mentions the following query table for DMA1, which leads me to believe channel 6 is what I need to use.

DMA requests table

Since I was not aware of the memory and peripheral sizes (i.e. USART2), I changed in all combinations of 8, 16 and 32 bits for both, to no avail.

Without further ado; this is a minimal working (well, not working ...) excerpt from what I am trying to do. I feel like I am missing something in the DMA configuration as the USART itself works fine.

At this moment, everything is evaluated.

The idea behind this code is basically to loop forever until the data in the buffer is completely swapped, and then output it when it does. I am sending a kilobyte of highly recognizable data from the host, but all I get is garbled garbage. He writes something, but not what I intend to write.

EDIT: Pictured here is a memory map. USART2_BASE

is evaluated as 0x4000 4400

, so it seems to be ok.

memory map

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include "libopencm3/stm32/usart.h"
#include <libopencm3/stm32/dma.h>

const int buflength = 1024;

uint8_t buffer[1024];

static void clock_setup(void)
{
    rcc_clock_setup_pll(&clock_config[CLOCK_VRANGE1_HSI_PLL_32MHZ]);
    rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_GPIOAEN);
    rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USART2EN);
    rcc_periph_clock_enable(RCC_DMA1);

}

static void gpio_setup(void)
{
    gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO3);
    gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2);
    gpio_set_af(GPIOA, GPIO_AF7, GPIO3);
    gpio_set_af(GPIOA, GPIO_AF7, GPIO2);
}

static void usart_setup(void)
{
    usart_set_baudrate(USART2, 115200);
    usart_set_databits(USART2, 8);
    usart_set_stopbits(USART2, USART_STOPBITS_1);
    usart_set_mode(USART2, USART_MODE_TX_RX);
    usart_set_parity(USART2, USART_PARITY_NONE);
    usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);

    usart_enable(USART2);
}

static void dma_setup(void)
{
    dma_channel_reset(DMA1, DMA_CHANNEL6);
    dma_set_priority(DMA1, DMA_CHANNEL6, DMA_CCR_PL_VERY_HIGH);
    dma_set_memory_size(DMA1, DMA_CHANNEL6, DMA_CCR_MSIZE_8BIT);
    dma_set_peripheral_size(DMA1, DMA_CHANNEL6, DMA_CCR_PSIZE_8BIT);
    dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL6);
    dma_disable_peripheral_increment_mode(DMA1, DMA_CHANNEL6);
    dma_enable_circular_mode(DMA1, DMA_CHANNEL6);
    dma_set_read_from_peripheral(DMA1, DMA_CHANNEL6);

    dma_disable_transfer_error_interrupt(DMA1, DMA_CHANNEL6);
    dma_disable_half_transfer_interrupt(DMA1, DMA_CHANNEL6);
    dma_disable_transfer_complete_interrupt(DMA1, DMA_CHANNEL6);

    dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) USART2_BASE);
    dma_set_memory_address(DMA1, DMA_CHANNEL6, (uint32_t) buffer);
    dma_set_number_of_data(DMA1, DMA_CHANNEL6, buflength);

    dma_enable_channel(DMA1, DMA_CHANNEL6);
}

int main(void)
{
    int i;
    for (i = 0; i < buflength; i++) {
        buffer[i] = 65;
    }
    clock_setup();
    gpio_setup();
    usart_setup();
    dma_setup();

    usart_enable_rx_dma(USART2);
    char flag = 1;
    while (flag) {
        flag = 0;
        for (i = 0; i < buflength; i++) {
            if (buffer[i] == 65) {
                flag = 1;
            }
        }
    }
    usart_disable_rx_dma(USART2);

    for (i = 0; i < buflength; i++) {
        usart_send_blocking(USART2, buffer[i]);
    }
    usart_send_blocking(USART2, '\n');

    return 0;
}

      

+3


source to share


2 answers


At the end of the day, this is the configuration I used to make it work.

const int datasize = 32;

char buffer[32];

static void dma_setup(void)
{
    dma_channel_reset(DMA1, DMA_CHANNEL6);

    nvic_enable_irq(NVIC_DMA1_CHANNEL6_IRQ);

    // USART2_DR (not USART2_BASE) is where the data will be received
    dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) &USART2_DR);
    dma_set_read_from_peripheral(DMA1, DMA_CHANNEL6);

    // should be 8 bit for USART2 as well as for the STM32L1
    dma_set_peripheral_size(DMA1, DMA_CHANNEL6, DMA_CCR_PSIZE_8BIT);
    dma_set_memory_size(DMA1, DMA_CHANNEL6, DMA_CCR_MSIZE_8BIT);

    dma_set_priority(DMA1, DMA_CHANNEL6, DMA_CCR_PL_VERY_HIGH);

    // should be disabled for USART2, but varies for other peripherals
    dma_disable_peripheral_increment_mode(DMA1, DMA_CHANNEL6);
    // should be enabled, otherwise buffer[0] is overwritten
    dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL6);

    dma_set_memory_address(DMA1, DMA_CHANNEL6, (uint32_t) &buffer);
    dma_set_number_of_data(DMA1, DMA_CHANNEL6, datasize);

    dma_disable_transfer_error_interrupt(DMA1, DMA_CHANNEL6);
    dma_disable_half_transfer_interrupt(DMA1, DMA_CHANNEL6);
    dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL6);

    usart_enable_rx_dma(USART2);
    dma_enable_channel(DMA1, DMA_CHANNEL6);
}

      



Then, when the transfer is complete, the function override is called dma1_channel6_isr

and all the data is in buffer

.

I have provided complete working code as a pull request to the libopencm3 repository. You can find it here . I will definitely update the link when the code is merged.

+1


source


I am not familiar with libopencm3 or STM32L series, but familiar with STM32F series. I know there are differences in peripherals, but I believe your mistake lies in the following line:

dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) USART2_BASE);

      

This is where you set your peripheral DMA address to the address USART2_BASE

, but usually you want to set it to the USART2 data register, which might be wrong on USART2_BASE

.



Now I see that some of the comments on your question have indicated this, but there is still the question of how to specify the data case. With the ST peripheral library, memory mapped structures are available for peripherals. It seems that libopencm3 has a specific macro that you can use for the data register address: USART2_DR

. Here is the definition in the documentation

So, I believe that if you change the line above to the following, it might solve your problem:

dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) USART2_DR);

      

+3


source







All Articles