SAME54, Event driven DMA SPI transfer problem.

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

 

Hi guys, i have a strange problem I can't solve.

 

The SAME54 supposed to cyclically send packs of data through the SPI port. No receiving needed.

TC4 is generating an Event at regular basis. This event supposed to trigger a couple of bytes long SPI transfer using DMA.

 

Clock arrangement:

XTAL1-> GCLK0->MCLK

XTAL1-> GCLK1->TC4->EVENT

XTAL1-> GCLK3->SPI

 

 

It works properly, but ONLY if the MCLK is at least twice SLOWER (div 2) than the SPI GCLK3  ( it sends i.e. two byte array each time Event is fired)

If the GLCK0 (MCLK) is set to the same frequency as SPI (or if SPI shares same GCLK with MCLK) the DMA sends only first byte from the array.

 

Here is my DMA  config:

//------------------------------------------------------------------------------
//!  \brief		DMAC init
//------------------------------------------------------------------------------
static void dmac_init(void){

	hri_mclk_set_AHBMASK_DMAC_bit(MCLK);

	// Reset
	hri_dmac_clear_CTRL_DMAENABLE_bit(DMAC);
	hri_dmac_clear_CRCCTRL_reg(DMAC, DMAC_CRCCTRL_CRCSRC_Msk);
	hri_dmac_set_CTRL_SWRST_bit(DMAC);
	while (hri_dmac_get_CTRL_SWRST_bit(DMAC)){
	}

	hri_dmac_write_CTRL_reg(DMAC,
	(1 << DMAC_CTRL_LVLEN0_Pos)								// Priority Level 0 Enable/Disable
	| (1 << DMAC_CTRL_LVLEN1_Pos)							// Priority Level 1 Enable/Disable
	| (1 << DMAC_CTRL_LVLEN2_Pos)							// Priority Level 2 Enable/Disable
	| (1 << DMAC_CTRL_LVLEN3_Pos));							// Priority Level 3 Enable/Disable

	hri_dmac_write_DBGCTRL_DBGRUN_bit(DMAC, 1);				// Debug Run

	hri_dmac_write_PRICTRL0_reg(DMAC,
	DMAC_PRICTRL0_LVLPRI0(1)								// Level 0 Channel Priority Number <0x00-0xFF>
	| DMAC_PRICTRL0_LVLPRI1(2)								// Level 1 Channel Priority Number <0x00-0xFF>
	| DMAC_PRICTRL0_LVLPRI2(3)								// Level 2 Channel Priority Number <0x00-0xFF>
	| DMAC_PRICTRL0_LVLPRI3(4)								// Level 3 Channel Priority Number <0x00-0xFF>
	| (0 << DMAC_PRICTRL0_RRLVLEN0_Pos)						// Level 0 Round-Robin Arbitration
	| (0 << DMAC_PRICTRL0_RRLVLEN1_Pos)						// Level 1 Round-Robin Arbitration
	| (0 << DMAC_PRICTRL0_RRLVLEN2_Pos)						// Level 2 Round-Robin Arbitration
	| (0 << DMAC_PRICTRL0_RRLVLEN3_Pos));					// Level 3 Round-Robin Arbitration

	// Base addresses
	hri_dmac_write_BASEADDR_reg(DMAC, (uint32_t)descriptor_section);
	hri_dmac_write_WRBADDR_reg(DMAC, (uint32_t)write_back_section);

	// IRQ priority
	for (uint8_t i = 0; i < 5; i++) {
		NVIC_DisableIRQ(DMAC_0_IRQn + i);
		NVIC_SetPriority(DMAC_0_IRQn + i, 4);				// PGADD due to FreerRtos
		NVIC_ClearPendingIRQ(DMAC_0_IRQn + i);
		NVIC_EnableIRQ(DMAC_0_IRQn + i);
	}

	// DMA enable
	hri_dmac_set_CTRL_DMAENABLE_bit(DMAC);
}

//------------------------------------------------------------------------------
//!  \brief		DMA Channel config. SPI
//------------------------------------------------------------------------------
static void dmac_channel_SPITX_config(void){

	// DMA channel n config
	hri_dmac_write_CHCTRLA_reg(DMAC, DMA_CH_SPI_TX,
	(0 << DMAC_CHCTRLA_SWRST_Pos)
	| (0 << DMAC_CHCTRLA_ENABLE_Pos)
	| (1 << DMAC_CHCTRLA_RUNSTDBY_Pos)
	| DMAC_CHCTRLA_TRIGSRC(0x11)								// source of a trigger.  <0x11=> SERCOM6 TX Trigger, <0x39=> TC4 Match/Compare 0 Trigger
	| DMAC_CHCTRLA_TRIGACT(DMAC_CHCTRLA_TRIGACT_BLOCK_Val)		// trigger action used for a transfer.
	| DMAC_CHCTRLA_BURSTLEN(DMAC_CHCTRLA_BURSTLEN_SINGLE_Val)	// Burst Length
	| DMAC_CHCTRLA_THRESHOLD(DMAC_CHCTRLA_THRESHOLD_1BEAT_Val));// Destination write starts after X beats source address read */

	// Channel n Priority Level. 0=lowest, 7=highest
	hri_dmac_write_CHPRILVL_reg(DMAC, DMA_CH_SPI_TX,  DMAC_CHPRILVL_PRILVL(DMAC_CHPRILVL_PRILVL_LVL1_Val));

	// Channel n Event Control
	hri_dmac_write_CHEVCTRL_reg(DMAC, DMA_CH_SPI_TX,
	DMAC_CHEVCTRL_EVACT(DMAC_CHEVCTRL_EVACT_TRIG_Val)			// Channel Event Input Action
	| DMAC_CHEVCTRL_EVOMODE(DMAC_CHEVCTRL_EVOMODE_DEFAULT_Val)	// Channel Event Output Mode
	| (1 << DMAC_CHEVCTRL_EVIE_Pos)								// Channel Event Input Enable
	| (0 << DMAC_CHEVCTRL_EVOE_Pos));							// Channel Event Output Enable
}
//------------------------------------------------------------------------------
//!  \brief		DMA Descriptor config. SPI
//------------------------------------------------------------------------------
static void dmac_spi_tx_descriptor_0_config(void){

	// Descriptor 2 config
	hri_dmacdescriptor_write_BTCTRL_reg(&descriptor_section[2],
	(1 << DMAC_BTCTRL_VALID_Pos)								/**< \brief (DMAC_BTCTRL) Descriptor Valid */
	| DMAC_BTCTRL_EVOSEL(DMAC_BTCTRL_EVOSEL_DISABLE_Val)		/**< \brief (DMAC_BTCTRL) Event generation  */
	| DMAC_BTCTRL_BLOCKACT(DMAC_BTCTRL_BLOCKACT_NOACT_Val)		// what actions the DMAC should take after a block transfer has completed
	| DMAC_BTCTRL_BEATSIZE(DMAC_BTCTRL_BEATSIZE_BYTE_Val)		// A beat is the size of one data transfer bus access, and the setting apply to	both read and write accesses.
	| (1 << DMAC_BTCTRL_SRCINC_Pos)								// Source Address Increment Enable
	| (0 << DMAC_BTCTRL_DSTINC_Pos)								// Destination Address Increment Enable
	| (0 << DMAC_BTCTRL_STEPSEL_Pos)							// This bit selects if source or destination addresses are using the step size settings.
	| DMAC_BTCTRL_STEPSIZE( DMAC_BTCTRL_STEPSIZE_X1 ));			// address increment step size. The setting apply to source or destination address, depending on STEPSEL setting.

	// Data Source
	hri_dmacdescriptor_write_SRCADDR_reg(&descriptor_section[2],(uint32_t) &spi_tx_buffer); 

	// Data destination
	hri_dmacdescriptor_write_DSTADDR_reg(&descriptor_section[2],(uint32_t) &SERCOM6->SPI.DATA.reg);

	// Block Transfer Count
	_dma_set_block_size(&descriptor_section[2], 2); 

	// Next Descriptor Address
	hri_dmacdescriptor_write_DESCADDR_reg(&descriptor_section[2], (uint32_t) &descriptor_section[2]);
}

 

SPI config:

 

//------------------------------------------------------------------------------
//!  \brief		Initialize SPI. Use ASF 4 config
//!
//------------------------------------------------------------------------------
static void spi_init(void){

	void *const hw = SERCOM6;

	// Configure Clock
	hri_gclk_write_PCHCTRL_reg(GCLK, SERCOM6_GCLK_ID_CORE, GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
	hri_gclk_write_PCHCTRL_reg(GCLK, SERCOM6_GCLK_ID_SLOW, GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
	hri_mclk_set_APBDMASK_SERCOM6_bit(MCLK);

	// Configure module
	hri_sercomspi_wait_for_sync(hw, SERCOM_SPI_SYNCBUSY_SWRST);
	hri_sercomspi_write_CTRLA_reg(hw,
		(0 << SERCOM_SPI_CTRLA_DORD_Pos)
		| (1 << SERCOM_SPI_CTRLA_CPOL_Pos)
		| (0 << SERCOM_SPI_CTRLA_CPHA_Pos)
//		| (CONF_SERCOM_6_SPI_AMODE_EN ? SERCOM_SPI_CTRLA_FORM(2) : SERCOM_SPI_CTRLA_FORM(0))	// Address mode disabled in master mode
		| (SERCOM_SPI_CTRLA_DOPO(0x2))			// Transmit Data Pinout. <0x0=>PAD[0,1,2]_DO_SCK_SS  <0x1=>reserved  <0x2=>PAD[3,1,2]_DO_SCK_SS  <0x3=>reserved
		| (SERCOM_SPI_CTRLA_DIPO(0x0))			// Receive Data Pinout. <0x0=>PAD[0] <0x1=>PAD[1] <0x2=>PAD[2] <0x3=>PAD[3]
		| (0 << SERCOM_SPI_CTRLA_IBON_Pos)		// Immediate Buffer Overflow Notification
		| (1 << SERCOM_SPI_CTRLA_RUNSTDBY_Pos)
		| (SERCOM_SPI_CTRLA_MODE(0x03)));		// Set module in SPI Master mode

	hri_sercomspi_write_CTRLB_reg(hw,
		(1 << SERCOM_SPI_CTRLB_RXEN_Pos)		// Receive buffer enable
		| (0 << SERCOM_SPI_CTRLB_MSSEN_Pos)		// Master Slave Enable Select
		| (0 << SERCOM_SPI_CTRLB_SSDE_Pos)		// Address mode disabled in master mode
		| (0 << SERCOM_SPI_CTRLB_PLOADEN_Pos)	// Address mode disabled in master mode
		| (SERCOM_SPI_CTRLB_AMODE(0))			// Address mode disabled in master mode
		| (SERCOM_SPI_CTRLB_CHSIZE(0)));		// <o> Character Size. <0x0=>8 bits <0x1=>9 bits

	hri_sercomspi_write_BAUD_reg(hw, 2);//(uint8_t) (((float)819200 / (float)(2 * 400000)) - 1));		// GCLK=819200. BAUD=40000, but it sets to maximum at current CLK
	hri_sercomspi_write_DBGCTRL_reg(hw, 0 << SERCOM_SPI_DBGCTRL_DBGSTOP_Pos);

	// Port
	// Set pin direction to input ( MISO )
	gpio_set_pin_direction(PC04, GPIO_DIRECTION_IN);
	gpio_set_pin_pull_mode(PC04, GPIO_PULL_OFF);
	gpio_set_pin_function(PC04, PINMUX_PC04C_SERCOM6_PAD0);
	gpio_set_pin_level(PC05, false);

	// Set pin direction to output ( SCK )
	gpio_set_pin_direction(PC05, GPIO_DIRECTION_OUT);
	gpio_set_pin_function(PC05, PINMUX_PC05C_SERCOM6_PAD1);

	// Set pin direction to output (MOSI )
	gpio_set_pin_direction(PC07, GPIO_DIRECTION_OUT);
	gpio_set_pin_function(PC07, PINMUX_PC07C_SERCOM6_PAD3);
	gpio_set_pin_level(PC07, false);

	// Set pin direction to output ( SS )
// 	gpio_set_pin_direction(PC14, GPIO_DIRECTION_OUT);
// 	gpio_set_pin_function(PC14, PINMUX_PC14D_SERCOM6_PAD2);
// 	gpio_set_pin_level(PC14, true);

	/* Enable module */
	while (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST));
	hri_sercomspi_set_CTRLA_ENABLE_bit(hw);

}

And Clock

 

 

//------------------------------------------------------------------------------
//!  \brief		Manual Clock configuration
//------------------------------------------------------------------------------
static void clock_gclk0_init(void){

	// Initializes generators
	hri_gclk_write_GENCTRL_reg(GCLK,	0,	// Generic clock generator index
	GCLK_GENCTRL_DIV(1)						// Generic clock generator division <0x0000-0xFFFF>		// To low will lose some DMA ISRs
	| (0 << GCLK_GENCTRL_RUNSTDBY_Pos)		// Run in Standby
	| (0 << GCLK_GENCTRL_DIVSEL_Pos)		// Divide Selection
	| (0 << GCLK_GENCTRL_OE_Pos)			// Output Enable
	| (0 << GCLK_GENCTRL_OOV_Pos)			// Output Off Value
	| (0 << GCLK_GENCTRL_IDC_Pos)			// Improve Duty Cycle
	| (1 << GCLK_GENCTRL_GENEN_Pos)			// Generic Clock Generator Enable
	| GCLK_GENCTRL_SRC_XOSC1);				// Generic clock generator Source

	//brief Initialize master clock generator
	hri_mclk_write_CPUDIV_reg(MCLK, MCLK_CPUDIV_DIV(MCLK_CPUDIV_DIV_DIV1_Val)); //	CPU Clock Division Factor
}

//------------------------------------------------------------------------------
//!  \brief		Manual Clock configuration - GLCK1 = xxxxx
//------------------------------------------------------------------------------
static void clock_gclk3_init(void){

	// Initializes generators
	hri_gclk_write_GENCTRL_reg(GCLK,	3,	// Generic clock generator index
	GCLK_GENCTRL_DIV(1)					// Generic clock generator division <0x0000-0xFFFF>
	| (0 << GCLK_GENCTRL_RUNSTDBY_Pos)		// Run in Standby
	| (0 << GCLK_GENCTRL_DIVSEL_Pos)		// Divide Selection
	| (0 << GCLK_GENCTRL_OE_Pos)			// Output Enable
	| (0 << GCLK_GENCTRL_OOV_Pos)			// Output Off Value
	| (0 << GCLK_GENCTRL_IDC_Pos)			// Improve Duty Cycle
	| (1 << GCLK_GENCTRL_GENEN_Pos)			// Generic Clock Generator Enable
	| GCLK_GENCTRL_SRC_XOSC1);				// Generic clock generator Source
}

 

The above configuration is NOT working properly:

Only single byte (instead of two is transfered). Event is fired at falling edge of the Yellow track.

 

 

ONce I change the GCLK0 Divider (or MCLK divider) to two or more, than whole array is transfered properly:

 

//------------------------------------------------------------------------------
//!  \brief		Manual Clock configuration
//------------------------------------------------------------------------------
static void clock_gclk0_init(void){

	// Initializes generators
	hri_gclk_write_GENCTRL_reg(GCLK,	0,	// Generic clock generator index
	GCLK_GENCTRL_DIV(2)						// Generic clock generator division <0x0000-0xFFFF>		// To low will lose some DMA ISRs
	| (0 << GCLK_GENCTRL_RUNSTDBY_Pos)		// Run in Standby
	| (0 << GCLK_GENCTRL_DIVSEL_Pos)		// Divide Selection
	| (0 << GCLK_GENCTRL_OE_Pos)			// Output Enable
	| (0 << GCLK_GENCTRL_OOV_Pos)			// Output Off Value
	| (0 << GCLK_GENCTRL_IDC_Pos)			// Improve Duty Cycle
	| (1 << GCLK_GENCTRL_GENEN_Pos)			// Generic Clock Generator Enable
	| GCLK_GENCTRL_SRC_XOSC1);				// Generic clock generator Source

	//brief Initialize master clock generator
	hri_mclk_write_CPUDIV_reg(MCLK, MCLK_CPUDIV_DIV(MCLK_CPUDIV_DIV_DIV1_Val)); //	CPU Clock Division Factor
}

 

 

 

I just can’t' get it to work using a single GCLK, or MCLK clk faster than SPI clock.

I tried different events (sync, async), DMA trigger settings, but nothing helps...

 

Am I missing something fundamental here?

 

 

This topic has a solution.
Last Edited: Fri. Oct 16, 2020 - 10:10 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

An update:

 

When I set SPI and MCLK from the same source (GCLK0), the DMA sents only one out of two bytes (as on the first oscilloscope shot).

If I increase the packet size to four, then two bytes are sent: byte 0 and byte 2 (see the attached screenhot).

It seems that the DMA is not catching all SPI TXC events? ....

 

 

 

 

Last Edited: Thu. Oct 15, 2020 - 07:56 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


It seems the problem was due to bad configuration for DMA and DMA EVENT INPUT:

 

SOLUTION:

DMAC_CHCTRLA_TRIGACT(DMAC_CHCTRLA_TRIGACT_BURST_Val)

DMAC_CHEVCTRL_EVACT(DMAC_CHEVCTRL_EVACT_RESUME_Val)

DMAC_BTCTRL_BLOCKACT(DMAC_BTCTRL_BLOCKACT_SUSPEND_Val)

 

Screenshot below.