I am trying register level programming for STM32F446RE Nucleo-64 board. I want to receive data through UART in circular mode using DMA. I am getting DMA Transfer error interrupt every time I try to send a byte through PC COM port to STM32F446RE. I am using Tera Term Terminal for sending the byte over COM Port at Baud rate of 115200.
I am using UART2 because its pins are connected to usb through ST-Link micro. which I can access through PC via virtual COM Port.
I am suspecting the sequence which I am using for enabling bits are faulty. Here is the sequence I am using: UART RX EN >> DMAR EN >> DMA Stream EN >> UART EN
.
Is it the correct sequence to configure UART with DMA for reception?
So far I have implemented:
main.c
#include <stdint.h>
#include <string.h>
#include "stm32f446xx.h"
void HT_Complete_callback(void);
void FT_Complete_callback(void);
void TE_error_callback(void);
void FE_error_callback(void);
void DME_error_callback(void);
void IDLE_IRQ_callback(void);
void DMA_init(void);
void DMA_Config (uint32_t srcAdd, uint32_t destAdd, uint16_t datasize);
void UART2_init (void);
void UART2_config (void);
#define RXSIZE 20
#define pDest (SRAM2_BASE + 0x800)
int main(void)
{
UART2_init ();
DMA_init();
DMA_Config (USART2->DR,(uint32_t) pDest, RXSIZE);
UART2_config ();
/* Loop forever */
while (1)
{
}
}
void UART2_init (void)
{
RCC_TypeDef* pRCC = RCC;
GPIO_TypeDef* pGPIOA = GPIOA;
// 1. Enable RCC Clock for UART and GPIOA pins PA2-TX and PA3-RX
pRCC->AHB1ENR |= (1 << 0);
pRCC->APB1ENR |= (1 << 17);
// 2. Configure PA2 and PA3 pins as alternate function pins moder register
pGPIOA->MODER |= (0x2 << 4);
pGPIOA->MODER |= (0x2 << 6);
pGPIOA->AFR[0] |= (7<<8);
pGPIOA->AFR[0] |= (7<<12);
// Enable internal pull-up for PA2
pGPIOA->PUPDR |= (0x1 << 4);
// Enable internal pull-up for PA3
pGPIOA->PUPDR |= (0x1 << 6);
}
void UART2_config (void)
{
USART_TypeDef* pUART2 = USART2;
DMA_Stream_TypeDef* pRXStream = DMA1_Stream5; //RX
pUART2->CR1 = 0x00; // Clear ALL
pUART2->CR1 |= (1 << 4); // Enable IDLE Interrupt
NVIC_EnableIRQ(USART2_IRQn);
//Configure baud rate
pUART2->BRR = 0x8B;
// 10. Enable RX engine
pUART2->CR1 |= (1 << 2);
pUART2->CR3 |= (1 << 6); //DMR bit enable
// 4. Enable the DMA1 stream 5
pRXStream->CR &= ~(1<<0);
while((pRXStream->CR & (1<<0)));
pRXStream->CR |= 1<<0; // 4. Enable the DMA1 stream 5
// 11. Enable UART engine
pUART2->CR1 |= (1 << 13);
}
void HT_Complete_callback(void)
{
while (1); //TODO: Implement callback
}
void FT_Complete_callback(void)
{
while (1); //TODO: Implement callback
}
void DMA_init(void)
{
// 0. Pointers for the registers
RCC_TypeDef* pRCC = RCC; //pointer to RCC_Typedef
DMA_Stream_TypeDef* pRXStream = DMA1_Stream5; //RX
// 1. Enable DMA1 Clock
pRCC->AHB1ENR |= (1 << 21);
pRXStream->CR = 0x0; //clear reg before config
pRXStream->CR |= (1 << 27); // channel 4 select
// 2. Enable DMA Interrupts
DMA1->HIFCR = 0x00; //clear flags first
pRXStream->CR |= (1<<1); // TCIE, HTIE, TEIE, DMEIE Enabled
pRXStream->CR |= (1<<2);
pRXStream->CR |= (1<<3);
pRXStream->CR |= (1<<4);
NVIC_EnableIRQ(DMA1_Stream5_IRQn);
// 3. Set the Data Direction
pRXStream->CR |= (1<<6); // Peripheral to Memory
// 4. Enable the circular mode (CIRC)
pRXStream->CR |= 1<<8;
// 5. Enable the Memory Increment (MINC)
pRXStream->CR |= 1<<10;
// 6. Set the Peripheral data size (PSIZE)
pRXStream->CR &= ~(3<<11); // 00 : 8 Bit Data
// 7. Set the Memory data size (MSIZE)
pRXStream->CR &= ~(3<<13); // 00 : 8 Bit Data
// 8. Set the Priority Level
pRXStream->CR &= ~(3<<16); // PL = 0
}
void DMA_Config (uint32_t srcAdd, uint32_t destAdd, uint16_t datasize)
{
DMA_Stream_TypeDef* pRXStream = DMA1_Stream5; //RX
// 1. Set the data size in CNDTR Register
pRXStream->NDTR = datasize;
// 2. Set the peripheral address in PAR Register
pRXStream->PAR = srcAdd;
// 3. Set the Memory address in M0AR Register
pRXStream->M0AR = destAdd;
}
void TE_error_callback(void)
{
while (1); //TODO: Implement callback
}
void FE_error_callback(void)
{
while (1); //TODO: Implement callback
}
void DME_error_callback(void)
{
while (1); //TODO: Implement callback
}
void IDLE_IRQ_callback(void)
{
while (1); //TODO: Implement callback
}
stm32f446_it.c
#include "stm32f446xx.h"
#include <stdint.h>
#define is_it_HT() DMA1->HISR & ( 1 << 10)
#define is_it_FT() DMA1->HISR & ( 1 << 11)
#define is_it_TE() DMA1->HISR & ( 1 << 9)
#define is_it_FE() DMA1->HISR & ( 1 << 6)
#define is_it_DME() DMA1->HISR & ( 1 << 8)
#define is_it_IDLE() USART2->SR & ( 1 << 4)
extern void HT_Complete_callback(void);
extern void FT_Complete_callback(void);
extern void TE_error_callback(void);
extern void FE_error_callback(void);
extern void DME_error_callback(void);
extern void IDLE_IRQ_callback(void);
void USART2_IRQHandler(void)
{
if(is_it_IDLE())
{
USART2->SR &= ~( 1 << 4); // clear IDLE bit
}
else
{
;
}
}
void DMA1_Stream5_IRQHandler(void)
{
if( is_it_HT() )
{
DMA1->HIFCR |= ( 1 << 10);
HT_Complete_callback();
}else if(is_it_FT() )
{
DMA1->HIFCR |= ( 1 << 11);
FT_Complete_callback();
}else if ( is_it_TE() )
{
DMA1->HIFCR |= ( 1 << 9);
TE_error_callback();
}else if (is_it_FE() )
{
DMA1->HIFCR |= ( 1 << 6);
FE_error_callback();
}else if( is_it_DME() )
{
DMA1->HIFCR |= ( 1 << 8);
DME_error_callback();
}else{
;
}
}
I tried debugging the code while transmitting a byte through PC. Initialization of the code executes well it even goes to super while loop. as soon as send a byte through PC. It goes to DMA transfer interrupt handler.
I am trying register level programming for STM32F446RE Nucleo-64 board. I want to receive data through UART in circular mode using DMA. I am getting DMA Transfer error interrupt every time I try to send a byte through PC COM port to STM32F446RE. I am using Tera Term Terminal for sending the byte over COM Port at Baud rate of 115200.
I am using UART2 because its pins are connected to usb through ST-Link micro. which I can access through PC via virtual COM Port.
I am suspecting the sequence which I am using for enabling bits are faulty. Here is the sequence I am using: UART RX EN >> DMAR EN >> DMA Stream EN >> UART EN
.
Is it the correct sequence to configure UART with DMA for reception?
So far I have implemented:
main.c
#include <stdint.h>
#include <string.h>
#include "stm32f446xx.h"
void HT_Complete_callback(void);
void FT_Complete_callback(void);
void TE_error_callback(void);
void FE_error_callback(void);
void DME_error_callback(void);
void IDLE_IRQ_callback(void);
void DMA_init(void);
void DMA_Config (uint32_t srcAdd, uint32_t destAdd, uint16_t datasize);
void UART2_init (void);
void UART2_config (void);
#define RXSIZE 20
#define pDest (SRAM2_BASE + 0x800)
int main(void)
{
UART2_init ();
DMA_init();
DMA_Config (USART2->DR,(uint32_t) pDest, RXSIZE);
UART2_config ();
/* Loop forever */
while (1)
{
}
}
void UART2_init (void)
{
RCC_TypeDef* pRCC = RCC;
GPIO_TypeDef* pGPIOA = GPIOA;
// 1. Enable RCC Clock for UART and GPIOA pins PA2-TX and PA3-RX
pRCC->AHB1ENR |= (1 << 0);
pRCC->APB1ENR |= (1 << 17);
// 2. Configure PA2 and PA3 pins as alternate function pins moder register
pGPIOA->MODER |= (0x2 << 4);
pGPIOA->MODER |= (0x2 << 6);
pGPIOA->AFR[0] |= (7<<8);
pGPIOA->AFR[0] |= (7<<12);
// Enable internal pull-up for PA2
pGPIOA->PUPDR |= (0x1 << 4);
// Enable internal pull-up for PA3
pGPIOA->PUPDR |= (0x1 << 6);
}
void UART2_config (void)
{
USART_TypeDef* pUART2 = USART2;
DMA_Stream_TypeDef* pRXStream = DMA1_Stream5; //RX
pUART2->CR1 = 0x00; // Clear ALL
pUART2->CR1 |= (1 << 4); // Enable IDLE Interrupt
NVIC_EnableIRQ(USART2_IRQn);
//Configure baud rate
pUART2->BRR = 0x8B;
// 10. Enable RX engine
pUART2->CR1 |= (1 << 2);
pUART2->CR3 |= (1 << 6); //DMR bit enable
// 4. Enable the DMA1 stream 5
pRXStream->CR &= ~(1<<0);
while((pRXStream->CR & (1<<0)));
pRXStream->CR |= 1<<0; // 4. Enable the DMA1 stream 5
// 11. Enable UART engine
pUART2->CR1 |= (1 << 13);
}
void HT_Complete_callback(void)
{
while (1); //TODO: Implement callback
}
void FT_Complete_callback(void)
{
while (1); //TODO: Implement callback
}
void DMA_init(void)
{
// 0. Pointers for the registers
RCC_TypeDef* pRCC = RCC; //pointer to RCC_Typedef
DMA_Stream_TypeDef* pRXStream = DMA1_Stream5; //RX
// 1. Enable DMA1 Clock
pRCC->AHB1ENR |= (1 << 21);
pRXStream->CR = 0x0; //clear reg before config
pRXStream->CR |= (1 << 27); // channel 4 select
// 2. Enable DMA Interrupts
DMA1->HIFCR = 0x00; //clear flags first
pRXStream->CR |= (1<<1); // TCIE, HTIE, TEIE, DMEIE Enabled
pRXStream->CR |= (1<<2);
pRXStream->CR |= (1<<3);
pRXStream->CR |= (1<<4);
NVIC_EnableIRQ(DMA1_Stream5_IRQn);
// 3. Set the Data Direction
pRXStream->CR |= (1<<6); // Peripheral to Memory
// 4. Enable the circular mode (CIRC)
pRXStream->CR |= 1<<8;
// 5. Enable the Memory Increment (MINC)
pRXStream->CR |= 1<<10;
// 6. Set the Peripheral data size (PSIZE)
pRXStream->CR &= ~(3<<11); // 00 : 8 Bit Data
// 7. Set the Memory data size (MSIZE)
pRXStream->CR &= ~(3<<13); // 00 : 8 Bit Data
// 8. Set the Priority Level
pRXStream->CR &= ~(3<<16); // PL = 0
}
void DMA_Config (uint32_t srcAdd, uint32_t destAdd, uint16_t datasize)
{
DMA_Stream_TypeDef* pRXStream = DMA1_Stream5; //RX
// 1. Set the data size in CNDTR Register
pRXStream->NDTR = datasize;
// 2. Set the peripheral address in PAR Register
pRXStream->PAR = srcAdd;
// 3. Set the Memory address in M0AR Register
pRXStream->M0AR = destAdd;
}
void TE_error_callback(void)
{
while (1); //TODO: Implement callback
}
void FE_error_callback(void)
{
while (1); //TODO: Implement callback
}
void DME_error_callback(void)
{
while (1); //TODO: Implement callback
}
void IDLE_IRQ_callback(void)
{
while (1); //TODO: Implement callback
}
stm32f446_it.c
#include "stm32f446xx.h"
#include <stdint.h>
#define is_it_HT() DMA1->HISR & ( 1 << 10)
#define is_it_FT() DMA1->HISR & ( 1 << 11)
#define is_it_TE() DMA1->HISR & ( 1 << 9)
#define is_it_FE() DMA1->HISR & ( 1 << 6)
#define is_it_DME() DMA1->HISR & ( 1 << 8)
#define is_it_IDLE() USART2->SR & ( 1 << 4)
extern void HT_Complete_callback(void);
extern void FT_Complete_callback(void);
extern void TE_error_callback(void);
extern void FE_error_callback(void);
extern void DME_error_callback(void);
extern void IDLE_IRQ_callback(void);
void USART2_IRQHandler(void)
{
if(is_it_IDLE())
{
USART2->SR &= ~( 1 << 4); // clear IDLE bit
}
else
{
;
}
}
void DMA1_Stream5_IRQHandler(void)
{
if( is_it_HT() )
{
DMA1->HIFCR |= ( 1 << 10);
HT_Complete_callback();
}else if(is_it_FT() )
{
DMA1->HIFCR |= ( 1 << 11);
FT_Complete_callback();
}else if ( is_it_TE() )
{
DMA1->HIFCR |= ( 1 << 9);
TE_error_callback();
}else if (is_it_FE() )
{
DMA1->HIFCR |= ( 1 << 6);
FE_error_callback();
}else if( is_it_DME() )
{
DMA1->HIFCR |= ( 1 << 8);
DME_error_callback();
}else{
;
}
}
I tried debugging the code while transmitting a byte through PC. Initialization of the code executes well it even goes to super while loop. as soon as send a byte through PC. It goes to DMA transfer interrupt handler.
Share Improve this question edited Feb 5 at 11:47 Lundin 214k45 gold badges274 silver badges430 bronze badges asked Feb 5 at 10:49 Rupesh kadamRupesh kadam 111 bronze badge 2- Are you getting any of the error codes you are checking for? – Lundin Commented Feb 5 at 11:46
- Look at the examples in the STM32CubeF4 package. Review how the example configures the UART and DMA via the STM32F4xx HAL, and compare that with your code. Read the example project readme.txt https://github.com/STMicroelectronics/STM32CubeF4/tree/master/Projects/STM32446E_EVAL/Examples/UART/UART_HyperTerminal_DMA – kkrambo Commented Feb 5 at 13:28
1 Answer
Reset to default 0Transfer error occurs when DMA tries to read/write an inaccessible location. Your function
void DMA_Config (uint32_t srcAdd, uint32_t destAdd, uint16_t datasize)
expects address as the first argument. Change this
DMA_Config (USART2->DR,(uint32_t) pDest, RXSIZE);
to
DMA_Config ((uint32_t)&(USART2->DR),(uint32_t) pDest, RXSIZE);