cancel
Showing results for 
Search instead for 
Did you mean: 

USART DMA TRIGGERS BUT READS EMPTY IN STM32G070 DMA UART RXD

APatr.1
Associate

I am developing libraries for STM32G070RB and starting with DMAs TX and RX; the interrupts trigger for both modes. 
I am using STM32G070RB, Cube IDE (1.19.0)


Description of task:
I wish to make the library optional for the user.

1. Option DMA (TX & RX) hardcoded to Channel 2 & 3.

2. Option receiving an interrupt and write polling. 

The MCU has limited DMA, so I am dedicating Channels 2 & 3 for the USART: a single buffer if interrupt mode was selected and a double buffer in DMA with the IDLE interrupt trigger. 

 Though a generic template class for the 4 USARTs, I am currently testing using USART1.

In the main.cpp file, I toggle PC1 to be sure the DMA and RXNE interrupts trigger. which appear fine. 
I also check within the handlerDMA and handlerIRQ, and all are working fine. 

The issue at hand:

In DMA mode, if I read the DMA buffer (__dmarxbuf), I will see the exact received character; I also confirmed that in the DMA countdown register. I implemented copy from __dmarxbuf to the second buffer when IDLE is triggered.

But funny, the buffer that is supposed to hold that data accessible by the user will always be empty, the head index always zero. This only means no data was populated. 

In first attempts, I used the C library "memcpy" this way. 
u16 __dma_pos = bsize - DMA1_Channel2->CNDTR;
memcpy(ubuf.bufrx, __dmarxbuf, __dma_pos );

In second attempts, I hardcoded the copying this way, but none worked. 

for (u16 i = 0; i < _len; i++) {

ubuf.bufrx[ubuf.head] = __dmarxbuf[(__dma_pos + i) % bsize];

head = (head + 1) % bsize;

}

This is the second day I am embarrassed by this issue.
Could anyone help point out what I am doing wrong?
Do I have a memory-related issue I need to be aware of?

I won't say it interrupts vector name mangling since I use C++ because my custom round robin scheduler is running; it actively manages my tasks. 

I don't think this is related to the reported issue with STM32H723 since STM32G070 does not have
similar complexity in memory and buses. Moreover, STM32H723 did not trouble me this way. :)

typedef struct{
	__attribute__((aligned(4))) u8 bufrx[255];
	__attribute__((aligned(4))) u8 buftx[255];
    u16 tail;
    u16 head;
    bool timeout;
}__attribute__ ((packed))ringbuf;

typedef struct {
    USART_TypeDef* usart; 	// UART peripheral
    IO txd, rxd;       		// TX/RX pin number
    AF af;
    IRQn_Type irq;
    u32 const _txreq, _rxreq;
}usart_io_map;

constexpr usart_io_map _usart_bus[]={
    {USART1, PA9, PA10, 	AF1, USART1_IRQn, 	DMAMUX_REQ_USART1_TX, DMAMUX_REQ_USART1_RX}, 		//
	{USART2, PA2, PA3, 		AF1, USART2_IRQn, 	DMAMUX_REQ_USART2_TX, DMAMUX_REQ_USART2_RX}, 		//
    {USART3, PB10,PB11, 	AF4, USART3_4_IRQn, DMAMUX_REQ_USART3_TX, DMAMUX_REQ_USART3_RX}, 	//
	{USART4, PA0, PA1, 		AF4, USART3_4_IRQn, DMAMUX_REQ_USART4_TX, DMAMUX_REQ_USART4_RX}}; 	//

typedef enum:u8 const{
	USE_USART1_PA9_P10 	=0,
	USE_USART2_PA2_PA3 	=1,
	USE_USART3_PB10_PB11=2,
	USE_USART4_PA0_PA1 	=3,
}USART_LIST;

template<USART_LIST const idxList>
class HUSART{

private:

	__IO ringbuf ubuf;
	static constexpr u16 bsize = static_cast<u16>(255);
	__attribute__((aligned(4))) __IO u8 __dmarxbuf[bsize] = {0};
	bool dmaset =false;
	static constexpr u8 idx = static_cast<int>(idxList);

	u32 __brr=500000;

	void __peri_reg_reset(){
	    u32 reg_count = sizeof(USART_TypeDef) / sizeof(u32);
	    volatile u32 *peripheral_base = (volatile u32 *)USART1;
	    for (u32 i = 0; i < reg_count; i++) {
	    		*(peripheral_base + i)  &=~ 0xFFFFFFFF;  // Clear all
	       }
	}
	void __irqsetup() {
	        USART1->CR1 =
					USART_CR1_TE		//Enable TX
					| USART_CR1_RE		//Enable RX
					| USART_CR1_UE		//Enable UART
					| USART_CR1_RXNEIE_RXFNEIE	//Enable RX Interrupt
					;
	        USART1->CR3 |= USART_CR3_OVRDIS;
	    }
	 void __dmasetup(){
	        dmaset = true;
			RCC->AHBENR |= RCC_AHBSMENR_DMA1SMEN;
			DMA1->IFCR = DMA_IFCR_CGIF2 | DMA_IFCR_CGIF3; // Clear all flags

			//Transmit
			DMA1_Channel3->CCR &=~0xFFFFFFFF;	//DMA_CCR_EN;
			DMAMUX1_Channel2->CCR =
					(DMAMUX1_Channel2->CCR & ~0x3FUL) |(_usart_bus[idx]._txreq & 0x3F)
					;
			DMA1_Channel3->CPAR = (u32)&USART1->TDR;
			DMA1_Channel3->CMAR = (u32)ubuf.buftx;
			DMA1_Channel3->CCR = DMA_CCR_MINC     // Memory increment
						| DMA_CCR_DIR       // Memory to peripheral
						| (0x0 << DMA_CCR_PSIZE_Pos| 0x0 << DMA_CCR_MSIZE_Pos)	//Data mem to peri 8bit
						| DMA_CCR_TCIE     // Transfer complete interrupt
						| DMA_CCR_TEIE     // Transfer error interrupt
						;

			//Receiving
			DMA1_Channel2->CCR &=~DMA_CCR_EN;
			DMAMUX1_Channel1->CCR = (DMAMUX1_Channel1->CCR & ~0x3FUL) |(_usart_bus[idx]._rxreq & 0x3F);
			DMA1_Channel2->CPAR = (u32)&USART1->RDR;
			DMA1_Channel2->CMAR = (u32)__dmarxbuf;
			DMA1_Channel2->CNDTR = 10;//bsize;
			DMA1_Channel2->CCR =
					DMA_CCR_MINC       //  Memory increment
					| DMA_CCR_CIRC     //  Circular mode
					| (0x0 << DMA_CCR_PSIZE_Pos | 0x0 << DMA_CCR_MSIZE_Pos)	// Data from peri to mem 8bit
					| DMA_CCR_TCIE      //  Transfer complete interrupt
					| DMA_CCR_TEIE      //  Transfer error interrupt
					;
			DMA1_Channel2->CCR &= ~DMA_CCR_DIR;	 //  Peri to Memory

			USART1->CR3 =
					  USART_CR3_DMAT		// Enable DMA for TX
					| USART_CR3_DMAR		// Enable DMA for RX
					| USART_CR3_OVRDIS 		// Enable disable overrun
					;
			DMA1_Channel2->CCR |= DMA_CCR_EN;  	// Enable RX DMA
			USART1->CR1 =
					  USART_CR1_TE			//Enable TX
					| USART_CR1_RE			//Enable RX
					| USART_CR1_IDLEIE		//Enable IDLE Interrupt
					| USART_CR1_UE			//Enable UART
					;
			NVIC_SetPriority(DMA1_Channel2_3_IRQn, 2);	// 2 RXD, 3 TXD
			NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
	    }
	u32 __compute_time_out(u16 strLen){
		if(__brr==0) return 0;
		u32 time_ms = (10000UL + __brr - 1) / __brr;  // Rounded up
		const u32 time_per_char_ms= (time_ms == 0) ? 1 : (u16)time_ms;
		return (u32)(time_per_char_ms * strLen) + 50;
	}
	int __dmawrite(u8* ptr, int len=1){
		if (len > 0 && len < bsize) {
			memcpy((u8*)ubuf.buftx, ptr, len);
			DMA1_Channel3->CCR &= ~DMA_CCR_EN;
			DMA1_Channel3->CMAR = (u32)ubuf.buftx;
			DMA1_Channel3->CNDTR = len;
			DMA1_Channel3->CCR |= DMA_CCR_EN;
		}
	    return (len);
	}
public:

	friend void DMA1_Channel2_3_IRQHandler(void);
	friend void USART1_IRQHandler(void);
	friend void USART2_IRQHandler(void);

	HUSART(){}
	void handlerDMA(void){
	 if (DMA1->ISR & DMA_ISR_TCIF3) {
			DMA1->IFCR = DMA_IFCR_CTCIF3;	//DMA_IFCR_CGIF3
			DMA1_Channel3->CCR &= ~DMA_CCR_EN;
		}
	if (DMA1->ISR & DMA_ISR_TEIF3) {
		DMA1->IFCR = DMA_IFCR_CTEIF3;
		DMA1_Channel3->CCR &= ~DMA_CCR_EN;
	}
	if (DMA1->ISR & DMA_ISR_TCIF2){
			DMA1->IFCR = DMA_IFCR_CTCIF2;
		}
	if (DMA1->ISR & DMA_ISR_TEIF2) {
			DMA1->IFCR = DMA_IFCR_CTEIF2;
		}
	}
	void handlerIRQ(void) {
		static u16 __dma_pos = 0;
	    u32 const isr = USART1->ISR;
	    // --- Error Handling ---
	    if (isr & USART_ISR_ORE) {
	        USART1->ICR |= USART_ICR_ORECF;
	    }
	    if (isr & USART_ISR_FE) {
	        USART1->ICR |= USART_ICR_FECF | USART_ICR_PECF;
	    }

	    //If IDLE Line Detection or RXNE Handling
	    if (((USART1->CR1 & USART_CR1_IDLEIE) && (isr & USART_ISR_IDLE)) ||		//If idle line triggered
	    	((USART1->CR1 & USART_CR1_RTOIE) && (isr & USART_ISR_RTOF))			//If Timeout triggered
	    ) {
	        USART1->ICR = USART_ICR_IDLECF | USART_ICR_RTOCF; //Clear both IDLE and RTO
	        u16 _len = bsize - DMA1_Channel2->CNDTR;
	        u16 head = 0;
	        for (u16 i = 0; i < _len; i++) {
	        	ubuf.bufrx[ubuf.head] = __dmarxbuf[(__dma_pos + i) % bsize];
	            head = (head + 1) % bsize;
	        }
	        __dma_pos = _len;
	        ubuf.head = (head + _len) % bsize;

	        // Restart DMA
	        DMA1_Channel2->CNDTR = 10;//bsize;
	        DMA1_Channel2->CCR &= ~DMA_CCR_EN;
	        DMA1_Channel2->CCR |= DMA_CCR_EN;
	    }else if ((USART1->CR1&USART_CR1_RXNEIE_RXFNEIE)&&	//If RX Interrupt enabled
	    	 (USART_ISR_RXNE_RXFNE & isr) &&				//If Rx triggered
			 (dmaset ==false))								//If not DMA mode
	    	{
	    		u16 head = ubuf.head;
				u8 data = (USART1->RDR);
				ubuf.bufrx[head] = data;
				ubuf.head = (head + 1) % bsize;
	    	}
	}
	void setup(bool const dma=true) {
	    CLOCK::peri_rcc_clock((u32)USART1);
	    __brr = 500000;
	    constexpr IO ptx = _usart_bus[idx].txd;
	    constexpr IO prx = _usart_bus[idx].rxd;
	    constexpr AF paf = _usart_bus[idx].af;

	    DRIVER::PIN<ptx, IO_ALT, paf>().high(); // TX
	    DRIVER::PIN<prx, IO_ALT, paf>().high(); // RX

	    USART1->CR1 = 0;
	    USART1->CR2 = 0;
	    USART1->CR3 = 0;
	    USART1->BRR = F_CPU/__brr;	//F_CPU= 64,000,000

	    if(!dma) {
	    	__irqsetup();
	    }else {__dmasetup();}

		NVIC_SetPriority(USART1_IRQn, 2);
	    NVIC_EnableIRQ(USART1_IRQn);
	    while (!(USART1->ISR & USART_ISR_TEACK) || !(USART1->ISR & USART_ISR_REACK));
	    flush();
	}
	int write(u8* ptr, int len=1){

		if (dmaset)return (__dmawrite(ptr, len));
		while (*ptr) {
		while (!(USART1->ISR & USART_ISR_TC));
		USART1->TDR = *ptr++;
		}
		return (len);
	}
	void print(const u8* fmt, ...) {
		char buf[bsize];
		va_list args;
		va_start(args, fmt);
		int len =vsnprintf(buf, sizeof(buf), (char*)fmt, args);
		va_end(args);
		if(dmaset){
			__dmawrite((u8*)buf, len);
		}else {write((u8*)buf, len);}
	}
	auto __get_buf()const {
		return (ubuf.bufrx);
	}
	u8 readline(u8* bufrxd, u16 strLen=1, bool const IFwait=false){

	  u32 __timeout = __compute_time_out(strLen);
	  u32 _ticks_ms = 0;
	  if(IFwait){
		  _ticks_ms = __get_ticks();
		  while(__get_ticks() - _ticks_ms < __timeout);
	  }
	  u16 idxbuf = 0; u16 charLen = 0;
	  u8 cc=0;
	  bool lineFlag=false;
	  do{
		  cc = ubuf.bufrx[idxbuf++];
		  switch(cc){
			  case '\n': case '\r':
			  bufrxd[charLen]='\0';
			  lineFlag = true; break;
			  default:{ bufrxd[charLen++]= cc; break;}
		  }
		}while(lineFlag ==false);
		return(charLen);
    }
     inline u16 IFdata(){
     	u16 nhead = ubuf.head;
     	u16 ntail = ubuf.tail;
     	return(nhead - ntail);
    }
    u8 read(){
    	if (!IFdata()) return 0xFF;
    	u8 data;
    	u16 tail = ubuf.tail;
    	data = ubuf.bufrx[tail];
    	ubuf.tail = (tail + 1) % bsize;
    	return data;
    }
    void flush(){
    	memset((u8*)ubuf.bufrx, '\0', bsize);
    	memset((u8*)ubuf.bufrx, '\0', bsize);
    	memset((u8*)ubuf.bufrx, '\0', bsize);
    	ubuf.head = 0;  ubuf.tail = 0;
    }
};



extern "C" void DMA1_Channel2_3_IRQHandler(void){
	__usart1.handlerDMA();
	if(DMA1->ISR & DMA_ISR_TCIF2)GPIOC->ODR ^= (1<<1);
}
extern "C" void USART1_IRQHandler(void) {
	__usart1.handlerIRQ();
	GPIOC->ODR ^= 1<< 1;
}

 

 

 

0 REPLIES 0