STM32L1 的直接内存访问 RX
Direct memory access RX for the STM32L1
一段时间以来,我一直在尝试通过 USART 将数据块从我的计算机传输到 STM32L100C-DISCO
。出于性能原因,这将使用 DMA 完成。然而,到目前为止,我还无法让它工作。由于我似乎无法弄清楚我可能做错了什么,所以我想我应该在这里问一下。
不过我正在使用 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。
当然,我已经参考了 STM32L1xxx 的参考手册,其中提到了以下对 DMA1 的要求 table,这让我相信通道 6 是我需要使用的..
由于我不确定内存和外设(即 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;
}
对libopencm3和STM32L系列不熟悉,对STM32F系列比较熟悉。我知道外围设备存在差异,但我认为您的错误在于以下行:
dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) USART2_BASE);
此处您将 DMA 外设地址设置为 USART2_BASE
地址,但通常,您希望将其设置为 USART2 数据寄存器,该寄存器可能不在 USART2_BASE
。
我现在看到你的问题的一些评论已经指出了这一点,但还有一个问题是如何指示数据寄存器。对于 ST 外设库,外设有内存映射结构。 libopencm3 似乎有一个已定义的宏,您可以将其用于数据寄存器地址:USART2_DR
。 Here is the definition in the documentation
所以,我相信如果您将上面的行更改为以下行,它可能会解决您的问题:
dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) USART2_DR);
最后,这是我用来让它工作的配置。
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 存储库。你可以找到它here。我将确保在合并代码时更新 link。
一段时间以来,我一直在尝试通过 USART 将数据块从我的计算机传输到 STM32L100C-DISCO
。出于性能原因,这将使用 DMA 完成。然而,到目前为止,我还无法让它工作。由于我似乎无法弄清楚我可能做错了什么,所以我想我应该在这里问一下。
不过我正在使用 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。
当然,我已经参考了 STM32L1xxx 的参考手册,其中提到了以下对 DMA1 的要求 table,这让我相信通道 6 是我需要使用的..
由于我不确定内存和外设(即 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;
}
对libopencm3和STM32L系列不熟悉,对STM32F系列比较熟悉。我知道外围设备存在差异,但我认为您的错误在于以下行:
dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) USART2_BASE);
此处您将 DMA 外设地址设置为 USART2_BASE
地址,但通常,您希望将其设置为 USART2 数据寄存器,该寄存器可能不在 USART2_BASE
。
我现在看到你的问题的一些评论已经指出了这一点,但还有一个问题是如何指示数据寄存器。对于 ST 外设库,外设有内存映射结构。 libopencm3 似乎有一个已定义的宏,您可以将其用于数据寄存器地址:USART2_DR
。 Here is the definition in the documentation
所以,我相信如果您将上面的行更改为以下行,它可能会解决您的问题:
dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) USART2_DR);
最后,这是我用来让它工作的配置。
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 存储库。你可以找到它here。我将确保在合并代码时更新 link。