2015-06-29 54 views
3

我一直在尝试一段时间,通过USART将我的计算机的数据块传输到STM32L100C-DISCO。出于性能原因,这是使用DMA完成的。但是,到目前为止,我还没有得到它的工作。因为我似乎无法弄清楚我可能做错了什么,所以我想我会在这里问。STM32L1的直接存储器访问RX

我使用的是libopencm3,但不幸的是,他们其他优秀的repository of examplesSTM32L1xxx上似乎没有包含一个用于DMA的DMA。虽然我提到common DMA header file中的配置选项,但我检查了我涵盖了所有的基础。

当然,我所指的STM32L1xxx的参考手册,其中提到了DMA1以下请求表,导致我相信频道6是什么,我需要使用..

DMA requests table

因为我不确定内存和外设的大小(例如USART2),所以我对这两种方法的所有8位,16位和32位组合都有所不同,但无济于事。

毫不费力;这是一个最小的工作(呃,不工作..)我试图做的节选。我觉得我忽略了DMA配置中的某些东西,因为USART本身工作正常。

在这一点上,任何感激。

这段代码背后的想法基本上是循环直到缓冲区中的数据完全被替换,然后当它输出时。从主持人,我发送了一千字节的高度可识别的数据,但我收回的只是格式错误的垃圾。它写的东西,但不是我打算写它。

编辑:这里是内存映射的图片。 USART2_BASE的计算结果为0x4000 4400,所以这似乎也是好的。

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; 
} 
+0

只是一个想法:不是一个解决方案,但如果你可以计算RX需要多长时间,看看它是否与波特率(最小0.08秒)一致,可以显示错误事件是否触发DMA(一个假定波特率是正确的,因为你有非DMA工作)。 –

+0

我不确定我完全理解你的意思,但我试图将双方的波特率降低到9600,并没有解决问题(或者给我提供有意义的输出)。调用'usart_send_blocking'和'usart_recv_blocking'确实可以正常工作。 – Joost

+0

添加; 9600被选为在谨慎方面犯错 - 我认为这将是一个安全的下限。 – Joost

回答

1

最后,这是我用得到它的工作配置。

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

然后,当转让完成后,dma1_channel6_isr功能的覆盖被调用,所有的数据是buffer

我已将完整的工作代码作为拉取请求提交给libopencm3示例存储库。你可以找到它here。我将确保在代码合并时更新链接。

+0

作为增强示例的一个建议,以及在异步DMA方面经常缺乏的东西,应该是支持接收超时。一些USART外设有一个可以在内部生成的超时中断,在某些部分,您需要使用定时器中断。用上面的配置,我知道这就是你要做的,如果收到31个字符,什么都不会回应。使用接收超时时,如果未收到其他字符,则会回显31个字符,然后可以重新配置并接收最多32个字符。 – rjp

+0

这不适用于我目前的情况(因为缺少单个字节是终止的原因),但我可以将其包含在示例中。感谢您的建议。 – Joost

3

我不熟悉libopencm3或STM32L系列,但我熟悉的STM32F系列。我知道有外设的差异,但我相信你的错误就在于下面一行:

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

这里你设置你的DMA外设地址为USART2_BASE地址,但通常情况下,你会想将它设置为USART2数据寄存器,这可能不正确,在USART2_BASE

我现在看到您的问题中的一些评论已经指出了这一点,但如何指示数据寄存器仍然存在问题。对于ST外设库,外设有内存映射结构。看起来libopencm3有一个已定义的宏,可用于数据寄存器地址:USART2_DRHere is the definition in the documentation

所以,我相信,如果你改变上面以下行,它可能解决您的问题:

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

嗯,看来我错误的解释'USART2_BASE'是唯一可用。正确安装libopencm3之后,而不是使用(现在变成一个)可用文件的一小部分子集,“USART2_DR”确实解决了。我现在通过'usart_recv_blocking'获取最后一个字符,而不是垃圾,重复。这并不是我想要的地方(我可能仍然错过了配置较小的东西),但它正在接近。 – Joost

+0

自从我使用STM32 DMA控制器以来已经有一段时间了,但听起来像是由于某种原因,USART没有正确控制DMA通道上的流量,而DMA控制器只是读出'datasize'字节从USART2_DR开始,而不是USART。再次回到libopencm3文档,它看起来像'dma_set_peripheral_flow_control'是调用设置DMA控制器以适应USART。 http://libopencm3.github.io/docs/latest/stm32l1/html/group__dma__file.html#gaf667ccb9a78c8fe76f2cf256fa153b6b – rjp

+0

不幸的是,流量控制需要额外的硬件,在我目前的情况下无法正常工作(即我的控制台没有RTS/CTS引脚) – Joost