2015-06-29 55 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