Reputation: 11
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.
Upvotes: 1
Views: 69
Reputation: 706
Transfer 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);
Upvotes: 0