2015-06-29 7 views
3

Я пытался какое-то время передать блок данных с моего компьютера на STM32L100C-DISCO по USART. По соображениям производительности это должно быть сделано с использованием DMA. Однако пока мне не удалось заставить его работать. Поскольку я не могу понять, что я могу делать неправильно, я решил, что спрошу здесь.Прямой доступ к памяти RX для STM32L1

Я использую libopencm3, но, к сожалению, их отличное отличное repository of examples не содержит одного для DMA на STM32L1xxx. Я проверил, что я рассмотрел все базы, когда дело доходит до параметров конфигурации, доступных в common DMA header file.

Естественно, я сослался на справочное руководство по STM32L1xxx, в которой говорится следующее запросов таблицу DMA1, что приводит меня верить канал 6 является то, что мне нужно использовать ..

DMA requests table

Поскольку я не был уверен в размерах памяти и периферии (например, USART2), я варьировался во всех комбинациях 8, 16 и 32 бит для обоих, но безрезультатно.

Без дальнейших церемоний; это минимальный рабочий (ну, а не рабочий ...) отрывок из того, что я пытаюсь сделать. Мне кажется, что я пропускаю что-то в конфигурации DMA, поскольку сам USART отлично работает.

На данный момент все оценивается.

Идея этого кода в основном заключается в том, чтобы зацикливаться навсегда, пока данные в буфере не будут полностью заменены, а затем, когда это будет, вывести его. От хоста я отправляю килобайт высоко узнаваемых данных, но все, что я получаю, - это искаженный мусор. Это что-то пишут, но не то, что я намереваюсь написать.

EDIT: Вот изображение карты памяти. 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_DR. Here 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