2017-01-08 11:53 AM
Edit: compilable example code posted below in comments. The problem now with this code is that too many messages are being sent in addition to the wrong data being sent.
I am using VisualGDB and a STM32F303RET6.
I am having trouble trying to get the UART transmitter to work with the DMA controller. The UART transmitter works fine when I do a single transmission by manually updating the TDR. But, when I use the DMA controller, the data that is transmitted is not what is expected.
It appears to me that the data being sent is coming from a memory address other than the address that I have told the DMA controller to point to in CMAR. But, when looking at the address of the data and the address in the CMAR, it matches. So, I am not sure what the problem is but I do wonder if the problem is something I am not understanding with C and C++. Since VisualGDB operates in C++ files, I write C++ wrappers for the C functions that do the heavy lifting with modifying the registers. When, I tried using a data pointer for the memory address to read from that was compiled under the C compiler, I could not get the DMA controller to operate correctly. When I pass the DMA controller a memory address from a pointer that was compiled under the C++ compiler, it works but sends the wrong data.
Hopefully, someone can help me.
Here is my code to setup the DMA controller for UART1.
void c_uart_setup_dma(uint8_t port, uint8_t * data) { uint16_t reg; uint32_t * tmp; volatile uint32_t src_address; volatile uint32_t dest_address; DMA_TypeDef * dma; DMA_Channel_TypeDef * dma_chan; USART_TypeDef * uart; // Assign the DMA channel and DMA controller based on the UART port c_uart_setdma(port, dma, dma_chan); // Assign the pointer for the UART port based on which one we want to modify c_uart_setport(port, uart); // set the source and destination address locations tmp = (uint32_t*)&data; src_address = *(uint32_t*)tmp; dest_address = (uint32_t)&uart->TDR; // test to see if this changes the data at the correct location *data = 0; *(data + 1) = 0; *(data + 2) = 0; // configure the DMA channel // set the RCC clock for the DMA controller RCC->AHBENR |= 0x01; // enable DMA1 RCC->AHBENR |= 0x02; // enable DMA2 // set the peripheral register address data will be read from/written to dma_chan->CPAR = dest_address; // set the memory address data will be read from/written to dma_chan->CMAR = src_address; // configure the total number of data items to be transferred dma_chan->CNDTR = (uint16_t)0x03; // configure the channel priority, data transfer direction, mode, data size, and interrupt reg = DMA_CCR_PL | DMA_CCR_DIR | DMA_CCR_TEIE | DMA_CCR_TCIE | DMA_CCR_MINC; dma_chan->CCR |= reg; // activate the DMA channel dma_chan->CCR |= DMA_CCR_EN; // clear the TC flag uart->ICR |= USART_ICR_TCCF; // enable the DMA transmitter in the UART //uart->CR3 |= USART_CR3_DMAT; // enable the DMA IRQ c_uart_set_dma_irq(port); }And here is what the debug shows when stopped at the end of this setup function:
Here is the code for my transmit function:
void c_uart_transmit_dma(uint8_t port)
{ uint32_t * source; uint32_t address; USART_TypeDef * uart; DMA_TypeDef * dma; DMA_Channel_TypeDef * dma_chan; // set the UART port c_uart_setport(port, uart); // set the DMA channel c_uart_setdma(port, dma, dma_chan); // disable the DMA channel dma_chan->CCR &= ~DMA_CCR_EN; // set the number of bytes to transfer dma_chan->CNDTR |= 0x03; // activate the DMA channel dma_chan->CCR |= DMA_CCR_EN; // clear the TC flag by writing the TCCF bit in ICR uart->ICR |= USART_ICR_TCCF; // enable the DMA transmitter in the UART uart->CR3 |= USART_CR3_DMAT; }Any help would be greatly appreciated!
2017-01-08 12:58 PM
This is a mess. How is c_uart_setup_dma() called and what is c_uart_transmit_dma() supposed to do?
Better than explaining the above, construct and post a minimal but complete compilable code exhibiting the problem.
JW
2017-01-08 02:13 PM
Allright. Let me see if I can put something together.
2017-01-08 03:51 PM
,
,
Ok this should work.
extern 'C',
{,
♯ include <,stm32f3xx_hal.h>,,
},
,
uint8_t * out_data,,
,
extern 'C' void DMA_transmit(),,
extern 'C' void DMA_setup(uint8_t * data),,
void initialize_data(),,
,
extern 'C',
void set_systemclock(),
{,
, , ,uint32_t freq = 0,,
, , ,uint32_t mask,,
, , ,,
, , ,freq = HAL_RCC_GetSysClockFreq(), , , ,// Check the system clock frequency,
, , ,,
, , ,// configure and set the PLL,
, , ,RCC->,CFGR |= (2 <,<, 0), , , , , , ,// SWS: PLL used as system clock,
, , ,RCC->,CFGR |= (0x7 <,<, 18), , , ,// PLLMUL: input x 9,
, , ,RCC->,CR |= (1 <,<, 24), , , , , , ,// PLLON: Enable the PLL,
, , ,,
, , ,// wait for the PLL to be locked and ready,
, , ,mask = (1 <,<, 25),,
, , ,mask &,= RCC->,CR,,
, , ,while (mask == 0),
, , ,{,
, , , , , ,mask = (1 <,<, 25),,
, , , , , ,mask &,= RCC->,CR,,
, , ,},
, , ,,
, , ,freq = HAL_RCC_GetSysClockFreq(), , , ,// Check the system clock frequency,
},
,
extern 'C',
void SysTick_Handler(void),
{,
, , ,HAL_IncTick(),,
, , ,HAL_SYSTICK_IRQHandler(),,
},
,
,
extern 'C',
int main(void),
{,
, , ,HAL_Init(),,
,
, , ,,
, , ,uint32_t system_freq,,
, , ,set_systemclock(), , , , , , , , , , , , , , , , , , , , , ,// set the system clock: HSI ->, PLL = 72 MHz,
, , ,system_freq = HAL_RCC_GetSysClockFreq(), , , ,// Check the system clock frequency,
, , ,,
, , ,initialize_data(),,
, , ,DMA_setup(out_data),,
, , ,,
, , ,for (,,),
, , ,{,
, , , , , ,,
, , , , , ,HAL_Delay(200),,
, , , , , ,DMA_transmit(),,
, , , , , ,,
, , ,},
},
,
,
void initialize_data(),
{,
, , ,out_data = new uint8_t[3],,
, , ,,
, , ,out_data[0] = 2,,
, , ,out_data[1] = 4,,
, , ,out_data[2] = 8,,
},
,
void DMA_setup(uint8_t * data),
{,
, , ,uint16_t reg,,
, , ,uint32_t * tmp,,
, , ,volatile uint32_t src_address,,
, , ,volatile uint32_t dest_address,,
,
, , ,,
, , ,// turn on the RCC clock for the GPIO port,
, , ,RCC->,AHBENR |= (1 <,<, 17),,
, , ,,
, , ,// initialize pin,
, , ,GPIOA->,OSPEEDR |= (3 <,<, (9 * 2)), , , ,// high speed,
, , ,GPIOA->,OTYPER |= (0 <,<, 9), , , , , , , , , ,// output state push pull,
, , ,GPIOA->,PUPDR |= (2 <,<, (9 * 2)), , , , , , ,// set output with pull down,
, , ,GPIOA->,MODER |= (2 <,<, (9 * 2)), , , , , , ,// configure port mode,
, , ,GPIOA->,AFR[1] |= (7 <,<, (1 * 4)), , , , , , ,// connect the I/O to the desired alternate function,
, , ,,
, , ,// turn on the RCC clock for the UART,
, , ,RCC->,APB2ENR |= (1 <,<, 14),,
, , ,,
, , ,// set the buadrate,
, , ,uint32_t sysclock = HAL_RCC_GetSysClockFreq(),,
, , ,uint16_t baudrate = (sysclock / 2) / baudrate, // divide by 2 because AHB bus is half?? , , ,,
, , ,USART1->,BRR = baudrate,,
, , ,,
, , ,// configure and initialize the USART,
, , ,USART1->,CR1 |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_TCIE),,
, , ,,
, , ,// clear the TC flag,
, , ,USART1->,ICR |= USART_ICR_TCCF,,
, , ,,
, , ,// set the source and destination address locations,
, , ,tmp = (uint32_t*)&,data,,
, , ,src_address = *(uint32_t*)tmp,,
, , ,dest_address = (uint32_t)&,USART1->,TDR,,
, , ,,
, , ,// configure the DMA channel,
, , ,// set the RCC clock for the DMA controller,
, , ,RCC->,AHBENR |= 0x01, // enable DMA1,
, , ,RCC->,AHBENR |= 0x02, // enable DMA2,
, , ,,
, , ,// set the peripheral register address data will be read from/written to,
, , ,DMA1_Channel4->,CPAR = dest_address,,
,
, , ,// set the memory address data will be read from/written to,
, , ,DMA1_Channel4->,CMAR = src_address,,
, , ,,
, , ,// configure the total number of data items to be transferred,
, , ,DMA1_Channel4->,CNDTR |= (uint16_t)0x03,,
, , ,,
, , ,// configure the channel priority, data transfer direction, mode, data size, and interrupt,
, , ,reg = DMA_CCR_PL | DMA_CCR_DIR | DMA_CCR_TEIE | DMA_CCR_TCIE | DMA_CCR_MINC,,
, , ,DMA1_Channel4->,CCR |= reg,,
, , ,,
, , ,// activate the DMA channel,
, , ,DMA1_Channel4->,CCR |= DMA_CCR_EN,,
, , ,,
, , ,// clear the TC flag,
, , ,USART1->,ICR |= USART_ICR_TCCF,,
, , ,,
, , ,// enable the DMA IRQ,
, , ,NVIC_ClearPendingIRQ(DMA1_Channel4_IRQn),,
, , ,NVIC_SetPriority(DMA1_Channel4_IRQn, 13),,
, , ,NVIC_EnableIRQ(DMA1_Channel4_IRQn),,
},
,
,
void DMA_transmit(),
{,
, , ,// disable the DMA channel,
, , ,DMA1_Channel4->,CCR &,= ~DMA_CCR_EN,,
, , ,,
, , ,// set the number of bytes to transfer,
, , ,DMA1_Channel4->,CNDTR |= (uint16_t)0x03,,
, , ,,
, , ,// activate the DMA channel,
, , ,DMA1_Channel4->,CCR |= DMA_CCR_EN,,
, , ,,
, , ,// clear the TC flag by writing the TCCF bit in ICR,
, , ,USART1->,ICR |= USART_ICR_TCCF,,
, , ,,
, , ,// enable the DMA transmit request in UART,
, , ,USART1->,CR3 |= USART_CR3_DMAT,,
},
,
,
extern 'C' void DMA1_Channel4_IRQHandler(),
{,
, , ,,
, , ,// disable the DMA transmit request in UART,
, , ,USART1->,CR3 &,= ~USART_CR3_DMAT,,
, , ,,
, , ,// clear the interrupt flag that was set by hardware,
, , ,DMA1->,IFCR |= DMA_IFCR_CGIF4 | DMA_IFCR_CTEIF4,,
}2017-01-09 09:05 PM
Edit: compilable example code posted below in comments. The problem now with this code is that too many messages are being sent in addition to the wrong data being sent.
I don't see anything wrong in the code.
I wouldn't enable DMA if DMA_setup() since you don't intend to transmit at that point.
And I definitively wouldn't OR in
DMA1_Channel4->CNDTR |= (uint16_t)0x03;
in DMA_transmit(),
But IMO neither of these grant described symptoms.
Can't this be UART-related issue anyway, such as baudrate mismatch problem, or problematic transmission circuitry (ground fault for example)? You could try to transmit a similar (but not the same to be distinguishable) message 'manually' by polling, in the same program, before starting DMA.
Also, I am no C++ expert. From C standpoint the play with tmp and src_address in DMA_setup() is unnecessary,
src_address = (uint32_t)data;
should work the same; again, this should not be source of described problems.
Also, I'd try with a static/global data buffer, rather than put it on heap (can't heap collide with stack?)
JW
2017-01-10 02:10 PM
Hey Jan,
Big thanks for looking into this for me. I was able to get everything working. It turns out that the correct data was being sent, I was just not interpreting it correctly in the logic analyzer. One of the problems, as you guessed, was a misconfiguration in the UART. I did not enable 'send MSB first' which is what the MIDI protocol on the logic analyzer was expecting. I retried my code but used 1, 2, 4 on the data to be sent and noticed the correct high-low on the data stream but saw that it was 'backwards'.
Between that and moving the code for declaring the pointer to the data stream in the C++ portion of the code, everything is working now.