0
\$\begingroup\$

Thanks to help from this site, my code can send and receive over the STM32F446RE Nucleo UART (using the Nucleo's built-in ST-Link / VCP). I'm now trying to move the receive to DMA, and seem to get a no-op: The uint8_t uart_recv_dma_buf0 remain all 00 (as shown in gdb), and the ISR is never called.

I've checked my code against the examples I can find online, and don't see anything missing or incorrect. (There's obviously a bit of a gap with each example, as each one I've found targets a somewhat different configuration).

What should be my next step in debugging?

#pragma once
#include <stdint.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/nvic.h>

#define UART_RECV_DMA_BUF_SIZE 8
volatile uint8_t uart_recv_dma_buf0[UART_RECV_DMA_BUF_SIZE];
volatile uint8_t uart_recv_dma_buf1[UART_RECV_DMA_BUF_SIZE];

// The values below are for USART2 on STM32F446xx
#define SER USART2
#define SER_RX_PERIPH_ADDR USART2_DR
// DMA Controller 1
#define SER_RX_DMA DMA1
#define SER_RX_DMA_RCC RCC_DMA1

// DMA Stream 5
#define SER_RX_DMA_STREAM DMA_STREAM5
#define SER_RX_DMA_IRQ NVIC_DMA1_STREAM5_IRQ
#define SER_RX_DMA_ISR dma1_stream5_isr

// DMA Channel 4
#define SER_RX_DMA_CHANNEL DMA_SxCR_CHSEL_4

void uart_recv_dma_init() {
  // Config
  rcc_periph_clock_enable(SER_RX_DMA_RCC);
  dma_stream_reset(SER_RX_DMA, SER_RX_DMA_STREAM);
  dma_set_priority(SER_RX_DMA, SER_RX_DMA_STREAM, DMA_SxCR_PL_LOW);
  dma_channel_select(SER_RX_DMA, SER_RX_DMA_STREAM, SER_RX_DMA_CHANNEL);
  dma_set_transfer_mode(SER_RX_DMA, SER_RX_DMA_STREAM, DMA_SxCR_DIR_PERIPHERAL_TO_MEM);
  dma_set_memory_size(SER_RX_DMA, SER_RX_DMA_STREAM, DMA_SxCR_MSIZE_8BIT); // 1 byte at a time
  dma_set_peripheral_size(SER_RX_DMA, SER_RX_DMA_STREAM, DMA_SxCR_MSIZE_8BIT);
  dma_enable_memory_increment_mode(SER_RX_DMA, SER_RX_DMA_STREAM); // Increment mem ptr each time
  dma_disable_peripheral_increment_mode(SER_RX_DMA, SER_RX_DMA_STREAM); // Periph addr is fixed
  dma_enable_circular_mode(SER_RX_DMA, SER_RX_DMA_STREAM);
  dma_enable_double_buffer_mode(SER_RX_DMA, SER_RX_DMA_STREAM);

  // Addresses
  dma_set_peripheral_address(SER_RX_DMA, SER_RX_DMA_STREAM, SER_RX_PERIPH_ADDR); // Read from USART DR
  dma_set_memory_address(SER_RX_DMA, SER_RX_DMA_STREAM, (uint32_t) uart_recv_dma_buf0); // And write to these addresses
  dma_set_memory_address_1(SER_RX_DMA, SER_RX_DMA_STREAM, (uint32_t) uart_recv_dma_buf1);
  dma_set_initial_target(SER_RX_DMA, SER_RX_DMA_STREAM, 0);
  dma_set_number_of_data(SER_RX_DMA, SER_RX_DMA_STREAM, UART_RECV_DMA_BUF_SIZE);
  
  // Interrupts
  nvic_enable_irq(SER_RX_DMA_IRQ);
  // TODO set priority
  dma_enable_transfer_complete_interrupt(SER_RX_DMA, SER_RX_DMA_STREAM);
  // TODO: Enable intrpt for idle, to process partial transfers
  
  // And launch!
  dma_enable_stream(SER_RX_DMA, SER_RX_DMA_STREAM);
  usart_enable_rx_dma(SER);
}

void SER_RX_DMA_ISR(void) {
  asm("bkpt");
}
```
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I bet you want to use DMA to read data from the address of UART peripheral data read register.

Not from the address that you read as a value from the UART peripheral data read register.

The next step of debugging may be to look at what values are read or written to each function/register and if they make sense as addresses or values.

\$\endgroup\$
1
  • \$\begingroup\$ Fantastic! This modification fixed it: // Warning: The libopencm3 macro USART2_DR gives the _val_ of the reg, not the _addr_ #define SER_RX_PERIPH_ADDR (USART2_BASE + 0x4) // addr of USART2_DR \$\endgroup\$ Commented Sep 28, 2023 at 21:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.