USART serial driver with DMA

Go To Last Post
2 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm writing a driver to use a USART peripheral for serial communications. I would like the driver to always be listening for incoming messages and put them in a buffer in memory as soon as they are received. Ideally the application should consume the messages from the buffer shortly after they are received. DMA is used to transfer incoming messages from the receive holding register (RHR) to buffer in memory. An interrupt is generated only when the DMA has filled the buffer. The ISR resets the DMA destination address back to the start of the buffer and begins the transfer again.

 

The issue I'm having is that the DMA is not transferring individual bytes from the RHR register to memory. Instead, it is transferring after each 4th byte is received (ie. a whole word). This is a problem. When a single byte message is received I want it to become available to the application straight away, rather than waiting for another 3 bytes to be received.

 

Any suggestions on how to get around this?

 

Here are some of the XDMAC channel settings I am using that might be relevant;

XDMAC_CUBC = 64 (my buffer size)

XDMAC_CC_DWIDTH = 0 (byte)

XDMAC_CC_CSIZE = 0 (1 data transferred)

XDMAC_CC_MBSIZE = 0 (burst size set to 1)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi!
You can try to modify my example for I2S receiver, which receives stereo audio data into 4 word long buffer AudioBuffer[4]={AudioLeft0, AudioRight0, AudioLeft1, AudioRight1} with the frequency of 192 kHz.
Hope that replacing some variables will let you to work with UART0:
XDAMC_CHANNEL_I2SC0_RX_Left    XDAMC_CHANNEL_HWID_UART0_RX
XDMAC_CC_DWIDTH_WORD            XDMAC_CC_DWIDTH_BYTE
I2SC0->I2SC_RHR                          UART0->UART_RHR

/** XDMA channel used in this example. */
#define XDMA_CH_I2SC_RX    0

/** XDMA Descriptor */
#define TOTAL_BUFFERS        2
#define MICROBLOCK_LEN      2

COMPILER_ALIGNED(32) lld_view1 linklist_read[TOTAL_BUFFERS]={0};
COMPILER_ALIGNED(32) volatile static uint32_t AudioBuffer[TOTAL_BUFFERS*MICROBLOCK_LEN]={0};

static void configure_I2SC_rx_xdma(void)
{
	uint32_t *src;
	uint8_t i;

	xdmac_channel_config_t xdmac_channel_cfg = {0};

	/* Initialize and enable DMA controller */
	pmc_enable_periph_clk(ID_XDMAC);

	/* Initialize channel config */
	xdmac_channel_cfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN
				  | XDMAC_CC_MBSIZE_SINGLE
				  | XDMAC_CC_DSYNC_PER2MEM
				  | XDMAC_CC_CSIZE_CHK_1
				  | XDMAC_CC_DWIDTH_WORD
				  | XDMAC_CC_SWREQ_HWR_CONNECTED
				  | XDMAC_CC_SIF_AHB_IF1
				  | XDMAC_CC_DIF_AHB_IF0
				  | XDMAC_CC_SAM_FIXED_AM
				  | XDMAC_CC_DAM_INCREMENTED_AM
				  | XDMAC_CC_PERID(XDAMC_CHANNEL_I2SC0_RX_Left);
	xdmac_configure_transfer(XDMAC, XDMA_CH_I2SC_RX, &xdmac_channel_cfg);

	/* Initialize linked list descriptor */
	src = (uint32_t*)&AudioBuffer[0];
	for(i = 0; i < TOTAL_BUFFERS; i++)
	{
		linklist_read[i].mbr_ubc =	XDMAC_UBC_NVIEW_NDV1 |
						XDMAC_UBC_NDE_FETCH_EN |
						XDMAC_UBC_NSEN_UNCHANGED |
						XDMAC_UBC_NDEN_UPDATED |
						XDMAC_CUBC_UBLEN(MICROBLOCK_LEN);

		linklist_read[i].mbr_sa  = (uint32_t)&(I2SC0->I2SC_RHR);
		linklist_read[i].mbr_da = (uint32_t)(src);
		if ( i == (TOTAL_BUFFERS - 1))
                {
			linklist_read[i].mbr_nda = (uint32_t)&linklist_read[0];
		} else {
			linklist_read[i].mbr_nda = (uint32_t)&linklist_read[i + 1];
		}
		src += MICROBLOCK_LEN ;
	}

	xdmac_channel_set_descriptor_control(XDMAC, XDMA_CH_I2SC_RX, XDMAC_CNDC_NDVIEW_NDV1 |
	                                                             XDMAC_CNDC_NDE_DSCR_FETCH_EN |
	                                                             XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED |
	                                                             XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED);
	xdmac_channel_set_descriptor_addr(XDMAC, XDMA_CH_I2SC_RX, (uint32_t)(&linklist_read[0]), 0);

 	xdmac_enable_interrupt(XDMAC, XDMA_CH_I2SC_RX);
 	xdmac_channel_enable_interrupt(XDMAC, XDMA_CH_I2SC_RX, XDMAC_CIE_BIE);
      //xdmac_channel_enable(XDMAC, XDMA_CH_I2SC_RX);

	 /*Enable XDMA interrupt */
	NVIC_ClearPendingIRQ(XDMAC_IRQn);
	NVIC_SetPriority(XDMAC_IRQn ,1);
 	NVIC_EnableIRQ(XDMAC_IRQn);
}

void XDMAC_Handler(void)
{        
    XDMAC->XDMAC_CHID[XDMA_CH_I2SC_RX].XDMAC_CIS;                   
    NVIC_DisableIRQ(XDMAC_IRQn);
}

 

Last Edited: Tue. Mar 3, 2020 - 03:28 PM