3

一段时间以来,我一直在尝试将数据块从我的计算机传输到STM32L100C-DISCOover USART。出于性能原因,这将使用 DMA 来完成。然而,到目前为止,我还没有让它工作。由于我似乎无法弄清楚我可能做错了什么,所以我想我会在这里问。

我正在使用libopencm3,但不幸的是,他们原本出色的示例存储库似乎不包含STM32L1xxx. 不过,我检查了我是否涵盖了通用 DMA 头文件中可用的配置选项的所有基础。

自然,我参考了 STM32L1xxx 的参考手册,其中提到了 DMA1 的以下请求表,这让我相信通道 6 是我需要使用的。

DMA 请求表

由于我不确定内存和外围设备(即 USART2)的大小,因此我在 8 位、16 位和 32 位的所有组合中进行了更改,但无济于事。

无需再费周折; 这是我正在尝试做的最小工作(嗯,不工作..)的摘录。我觉得我忽略了 DMA 配置中的某些内容,因为 USART 本身可以正常工作。

在这一点上,任何事情都值得赞赏。

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

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

内存映射

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

2 回答 2

4

我不熟悉libopencm3或STM32L系列,但熟悉STM32F系列。我知道外围设备存在差异,但我相信您的错误在于以下几行:

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

在这里,您将 DMA 外设地址设置为该USART2_BASE地址,但通常,您希望将其设置为 USART2 数据寄存器,该寄存器可能不在USART2_BASE.

我现在看到对您的问题的一些评论已经指出了这一点,但是仍然存在如何指示数据寄存器的问题。使用 ST 外设库,有外设的内存映射结构。似乎 libopencm3 有一个已定义的宏,您可以将其用于数据寄存器地址:USART2_DR. 这是文档中的定义

因此,我相信如果您将上面的行更改为以下内容,它可能会解决您的问题:

dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) USART2_DR);
于 2015-07-01T17:12:10.530 回答
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-example 存储库。你可以在这里找到它。我将确保在代码合并时更新链接。

于 2015-07-08T07:45:08.220 回答