Direct memory access RX for the STM32L1

I've been trying for a while now to transmit a block of data from my computer to an STM32L100C-DISCO over USART. For performance reasons, this is to be done using DMA. So far, however, I have not been able to get it to work. As I cannot seem to figure out what I might be doing wrong, I figured I'd ask here.

I'm using libopencm3, but unfortunately, their otherwise excellent repository of examples does not appear to contain one for DMA on the STM32L1xxx. I checked that I covered all the bases when it comes to the configuration options available in the common DMA header file, though.

Naturally, I've referred to the reference manual for the STM32L1xxx, which mentions the following requests table for DMA1, leading me to believe channel 6 is what I need to be using..

Direct memory access RX for the STM32L1

As I was unsure about the size of the memory and peripheral (i.e. USART2), I varied across all combinations of 8, 16 and 32 bit for both, but to no avail.

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

At this point, anything is appreciated.

The idea behind this code is basically to loop forever until the data in the buffer is replaced entirely, and then when it is, output it. From the host, I'm sending a kilobyte of highly recognisable data, but all I'm getting back is malformed garbage. It is writing something, but not what I intend for it to write.

EDIT: Here's a picture of the memory map. USART2_BASE evaluates to 0x4000 4400, so that seems to be all right as well.

Direct memory access RX for the STM32L1

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

-------------Problems Reply------------

I am not familiar with libopencm3 or the STM32L series, but I am familiar with the 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);

Here you are setting your DMA peripheral address to the USART2_BASE address, but typically, you would want to set this to the USART2 data register, which may not be right at USART2_BASE.

I see now that some of the comments on your question have pointed this out, but there is still a remaining question of how to indicate the data register. With the ST peripheral library, there are memory mapped structures for the peripherals. It appears that libopencm3 has a defined macro you can use for the data register address: USART2_DR. Here is the definition in the documentation

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

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

Category:c# Views:4 Time:2019-03-14

Related post

Copyright (C), All Rights Reserved.

processed in 0.149 (s). 11 q(s)