XMEGA, DMA, synchronous parallel input

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

Guys,

 

you might be my last hope...

 

I've asked already on another board but we couldn't solve the problem.

 

Data source delivers 16 bit words in two parallel 8 bit portions, each with rising clock edge.
Data shall be read using DMA from PORT A and written to memory.
Timer D0 is generating the clock, PWM channel D providing external clock, PWM channel C a slighty delayed trigger for DMA channel 0.

Looks like that:

external clock  ____|¯¯¯¯¯¯|____|¯¯¯¯¯¯|_  output inverted
DMA trigger     ¯¯¯¯¯¯|____|¯¯¯¯¯¯|____|¯

But DMA is writing nonsense into memory.
Obviously DMA channel is transferring the same input byte several times consecutively.

Example code:

  rjmp start

  .org DMA_CH0_vect
  reti

start:
;setup timer D0
;clock period
  ldi r16,20
  sts (TCD0_PER),r16
  ldi r16,0
  sts (TCD0_PER+1),r16
;PWM compare channel D for external clock
  ldi r16,8
  sts (TCD0_CCD),r16
  ldi r16,0
  sts (TCD0_CCD+1),r16
;PWM compare channel C for DMA trigger
  ldi r16,12
  sts (TCD0_CCC),r16
  ldi r16,0
  sts (TCD0_CCC+1),r16
  ldi r16,1<<TC0_CCDEN_bp|TC_WGMODE_SS_gc
  sts (TCD0_CTRLB),r16

;setup DMA channel 0
  ldi r16,1<<DMA_ENABLE_bp|DMA_DBUFMODE_DISABLED_gc|DMA_PRIMODE_CH0123_gc
  sts (DMA_CTRL),r16
  ldi r16,DMA_CH_SRCRELOAD_NONE_gc|DMA_CH_SRCDIR_FIXED_gc|DMA_CH_DESTRELOAD_NONE_gc|DMA_CH_DESTDIR_INC_gc
  sts (DMA_CH0_ADDRCTRL),r16
  ldi r16,DMA_CH_TRIGSRC_TCD0_CCC_gc  ;triggered from Timer D0 PWM compare channel C
  sts (DMA_CH0_TRIGSRC),r16
  ldi r16,LOW(4096)
  sts (DMA_CH0_TRFCNT),r16
  ldi r16,HIGH(4096)
  sts (DMA_CH0_TRFCNT+1),r16
  ldi r16,1
  sts (DMA_CH0_REPCNT),r16
  ldi r16,LOW(PORTA_IN)
  sts (DMA_CH0_SRCADDR0),r16
  ldi r16,HIGH(PORTA_IN)
  sts (DMA_CH0_SRCADDR1),r16
  ldi r16,BYTE3(PORTA_IN)
  sts (DMA_CH0_SRCADDR2),r16
  ldi r16,LOW(SRAM_START)
  sts (DMA_CH0_DESTADDR0),r16
  ldi r16,HIGH(SRAM_START)
  sts (DMA_CH0_DESTADDR1),r16
  ldi r16,BYTE3(SRAM_START)
  sts (DMA_CH0_DESTADDR2),r16
  ldi r16,1<<DMA_CH_ENABLE_bp|1<<DMA_CH_REPEAT_bp|DMA_CH_BURSTLEN_1BYTE_gc
  sts (DMA_CH0_CTRLA),r16
  ldi r16,DMA_CH_TRNINTLVL_HI_gc
  sts (DMA_CH0_CTRLB),r16

;invert PORTD.3 output (timer D0 PWM channel D)
  ldi r16,1<<PORT_INVEN_bp
  sts (PORTD_PIN3CTRL),r16

  ldi r16,1<<PMIC_HILVLEN_bp
  sts (PMIC_CTRL),r16
  ldi r16,1<<SLEEP_SMODE_IDLE_gc|1<<SLEEP_SEN_bp
  sts (SLEEP_CTRL),r16

;enable timer D0
  ldi r16,TC_CLKSEL_DIV1_gc
  sts (TCD0_CTRLA),r16

  sei
  sleep

  ldi r16,TC_CLKSEL_OFF_gc
  sts (TCD0_CTRLA),r16

stop:  rjmp stop

Code is running on Xplained with XMEGA128A1.

 

Using that code the memory pattern looks like this:

 

1313 1313 6767 6767 1313 1313 6767 6767

 

Logic analyzer showed that everything on the interface ran as expected, the bytes were clocked once per rising edge.

 

Changing the timer setting to somewhat slower clock, say, from 20-8-10 to 100-40-60, has stored something like this into memory:

 

7B7B 7B7B 7B7B 7B7B 7B7B 7B7B 7B7B 7B7B 7B7B 7BAC ACAC ACAC ACAC ACAC ACAC ACAC ACAC ACAC ACAC

 

Also here the everything was running well on the interface.

Somebody recommended to use an event channel to trigger the DMA.

Well, tried that.

;route Timer D0 compare C to Event Channel 0
  ldi r16,EVSYS_CHMUX_TCD0_CCC_gc
  sts (EVSYS_CH0MUX),r16
 
  ldi r16,DMA_CH_TRIGSRC_EVSYS_CH0_gc ;trigger source Event Channel 0
  sts (DMA_CH0_TRIGSRC),r16

Didn't improve anything...

 

So, anybody here having the crucial hint?

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

Why use the timer to trigger the DMA? Wouldn't it make more sense to use a pin change trigger to detect the falling edge?

 

I seems like you are triggering on both edges or something like that. Maybe you are triggering on both channels C and D? Really you probably only need channel D anyway, because when it triggers DMA there is a slight delay before the transfer actually happens.

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

mojo-chan wrote:
Why use the timer to trigger the DMA? Wouldn't it make more sense to use a pin change trigger to detect the falling edge?

The timer is actually sourcing the clock.

 

mojo-chan wrote:
I seems like you are triggering on both edges or something like that.

Well, DMA has been set explicitly to be triggered by timer compare C, either directly or via event channel. There's nothing more.

And even triggering on both edges couldn't explain the number of identical bytes written especially with a slower clock IMHO.

 

mojo-chan wrote:
Really you probably only need channel D anyway, because when it triggers DMA there is a slight delay before the transfer actually happens.

Sure, once I got it to work I can change to a single combined clock/trigger.

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

Made it.

 

Thinking again of 

mojo-chan wrote:
Wouldn't it make more sense to use a pin change trigger to detect the falling edge?

I changed the DMA trigger to do so.

	ldi r16,1<<PORT_INVEN_bp|PORT_ISC_FALLING_gc
	sts (PORTD_PIN3CTRL),r16

	ldi r16,EVSYS_CHMUX_PORTD_PIN3_gc
	sts (EVSYS_CH0MUX),r16

At the first try it didn't improve the situation.

Scanning the DMAC description again I found the crucial setting.

    ldi r16,1<<DMA_CH_ENABLE_bp|1<<DMA_CH_REPEAT_bp|1<<DMA_CH_SINGLE_bp|DMA_CH_BURSTLEN_1BYTE_gc
    sts (DMA_CH0_CTRLA),r16

It's called Single-Shot Data transfer and performs a single burst transfer on the transfer trigger.

Just for the record: Even with that setting it didn't work correctly when triggering directly from the timer compare C.

 

Unfortunately, it turned out in the end that even with tuned timer setting the DMA is still slower than reading the data "by foot".

Just like that:

	sbi CLK
	in r16,PINA
	cbi CLK
	st X+,r16

Thus I leave the DMA stuff as is for now..