ADC muxing with DMA

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

I'm trying take a reading from 5 separate pins on my ATxmega256A3 ADC, and move them to SRAM via DMA and am having trouble coming up with a solution.

I thought the correct solution would be to use ADC conversion complete interrupt to change the Mux register of the ADC chan I am using after each conversion. Then have DMA automatically increment the register address so the value of that pin goes to the next address. But that does not seem to be working. The readings seem to overwrite each other after some amount of cycles.

Any ideas on what to try next?

buffer:

volatile uint16_t current_eye_reading[5];

ISR:

see following post

ADC setup:

void ADC_Setup(void){
	// setup ADC for continuous acquisition 12 bits, single ended on 
	// PORTA:0
	ADCA.PRESCALER = ADC_PRESCALER_DIV16_gc; //8mhz clock divided by 16 gives 500khz ADC clock 
	ADCA.CH0.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc; // configure single ended no gain on CH0
	//ADCA.CH0.INTCTRL = 0x1; // low level
	ADCA.CH0.MUXCTRL = ADC_CH_MUXNEG_PIN0_gc ;  //ADC pin 0
	ADCA.REFCTRL = ADC_REFSEL_VCC_gc;   // use internal Vref = VCC-0.6
	ADCA.CTRLB = ADC_FREERUN_bm;      // free running, 12 bit right justified
	ADCA.CH0.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;
	ADCA.CTRLA = ADC_ENABLE_bm;        // enable ADC
}

DMA setup:

void DMA_Setup(void){
	DMA.CTRL = DMA_ENABLE_bm;    // Enable, single buffer, round robin
	DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BURST_gc | DMA_CH_SRCDIR_INC_gc |  DMA_CH_DESTRELOAD_BLOCK_gc |  DMA_CH_DESTDIR_INC_gc ; //Reload source address after burst transfer and inc it. Reload the Dest address after burst and inc it.
	DMA.CH0.TRIGSRC= DMA_CH_TRIGSRC_ADCA_CH0_gc;  // ADCA CH0 is trigger source
	DMA.CH0.TRFCNT = 10;   // Buffer is ten bytes
	DMA.CH0.SRCADDR0 =(((uint16_t)(&ADCA.CH0.RES))>>0*8)&0xFF;
	DMA.CH0.SRCADDR1 =(((uint16_t)(&ADCA.CH0.RES))>>1*8)&0xFF;
	DMA.CH0.DESTADDR0  =(((uint16_t)ADCA_Buff)>>0*8) & 0xFF;
	DMA.CH0.DESTADDR1  =(((uint16_t)ADCA_Buff)>>1*8) & 0xFF;
	DMA.CH0.CTRLA = DMA_CH_ENABLE_bm | DMA_CH_REPEAT_bm |  DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc;  // Enable, repeat, 2 byte, burst 
}

You don't gno-me!

Last Edited: Thu. Apr 19, 2012 - 02:06 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

that ISR was way wrong... what was I thinking...

Here is a more better one:

ISR(ADCA_CH0_vect){ //this interrupt is called on completion of an ADC conversion
	uint8_t temp;
	temp=((ADCA.CH0.MUXCTRL & (ADC_CH_MUXPOS_gm))>>ADC_CH_MUXPOS_gp);
	temp+=1;
	if (temp == 5 ){
		temp = 0;
	}
	ADCA.CH0.MUXCTRL = (temp << ADC_CH_MUXPOS_gp);
}

You don't gno-me!

Last Edited: Thu. Apr 19, 2012 - 02:21 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I know you can set the ADC to convert up to 4 channels in a round robin type of sweep, after setting up the registers for the 4 channels . Remember that the ADC is a pipeline design, so it doesn't need an ISR or extra code to convert all 4 automatically once started . Setup the DMA to use channel 3 conversion complete to trigger the transfer and channel 0 result register will be DMA source .

I suggest trying it with 2 channels first, easier to debug and keep straight what all needs to be setup . Output values to an LCD or PC . Don't setup the DMA until channel sweeping part works . See AVR1300 .

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

Last Edited: Thu. Apr 26, 2012 - 11:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wanted to avoid that because I would still have the last 5th channel left after that. It would make it like 4 conversions via round robin then I'd have to hack together a 5th for the remaining and then I'd still be in the same boat as I am now, right?

You don't gno-me!

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

I wrote what I did because you wanted 4 channels in your OP, but you've changed that to 5 . I don't know if you can convert multiple pins using ONE channel in free-running, it might start converting the same pin right away before the change to the next channel happens . My way uses the system maximally, for up to 4 channels ( 4 signals in the pipe and DMA moves FOUR result registers, instead of just ONE result in your code. ) . You might want to try using the 2nd ADC for the 5th signal and a 2nd DMA channel for it .

ADCA.CH0.MUXCTRL = ADC_CH_MUXNEG_PIN0_gc ;

This can't be right since you want single-ended . It should be positive MUX setting .

AVR-1300 wrote:
Care should be taken not to change any involved MUX settings when in free-running mode, as this would corrupt conversion results.

Quote:
I thought the correct solution would be to use ADC conversion complete interrupt to change the Mux register of the ADC chan I am using after each conversion.
The correct solution would use the features of the MCU and result in the least coding effort for you and the fastest conversions possible .

Your idea should work, but maybe not in free-running mode of 1 ADC channel while using the MUX . What kind of signals are you converting ?

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

Got tied up with something else but I am going to try the round ribbon ADC when I get a chance. Unfortunately all the pins that I need a reading from are on the first ADC. I would have to green wire the 5th to get it to the 2nd ADC. Which is not a total problem, but would obviously like to be avoided If possible. But ill cross that bridge when i get to it.

You don't gno-me!

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

Chupa wrote:
...I am going to try the round ribbon ADC when I get a chance.

[A Doctor's bedside manner-ON] It's really for the best ! [/OFF]

Having a ADC-pipeline and not using it is like not assembling the next car on an assembly-line until one car is completely done . My way you can do free-running for fastest conversions, just set and forget .

You could trigger the 5th signal to start its ADC'ing in several ways, but what messes it up for reading all 5 values from the array is that once the pipe fills, the 1st 4 signals will be converted in 1 ADC clock each . So for example, you could event system trigger the 5th to start when the 2nd signal's done ADC'ing and that way you KNOW you'd have all 5 when 4th's done . But it only works timing wise for 1st 4 ADC's, before the pipe's full . Something like that would work if you have a slow enough signal, compared to other 4 though .

OR you could do the structure I just mentioned but w/o free-running . So you'd have to start a set by setting the start bits after each set of 5 conversions/DMA moves .

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

OK! got round robin working. Now trying to get the DMA to move the data to RAM when the ADC is complete. And I cant quite figure it out. It appears like only half of the 16bit result is being moved, not sure why.

Just focusing on the first 2 ADC chans for now.

void ADC_RR_Setup(void){
	ADCA.CTRLA = ADC_ENABLE_bm;
	ADCA.CTRLB = ADC_FREERUN_bm | ADC_RESOLUTION_12BIT_gc;
	ADCA.REFCTRL = ADC_REFSEL_VCC_gc;
	ADCA.EVCTRL = ADC_SWEEP_0123_gc;
	ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc;
	
	ADCA.CH0.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;
//	ADCA.CH0.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;
	
	ADCA.CH1.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
//	ADCA.CH1.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;
	
	ADCA.CH2.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
//	ADCA.CH2.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;
	
	ADCA.CH3.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN3_gc;
//	ADCA.CH3.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;
	
}	
	DMA.CTRL = DMA_ENABLE_bm; 
	
	DMA.CH0.TRIGSRC= DMA_CH_TRIGSRC_ADCA_CH0_gc;  // ADCA CH0 is trigger source
	DMA.CH0.TRFCNT = 2;
	DMA.CH0.SRCADDR0 =(((uint16_t)(&ADCA.CH0.RES))>>0*8)&0xFF;
	DMA.CH0.SRCADDR1 =(((uint16_t)(&ADCA.CH0.RES))>>1*8)&0xFF;
	DMA.CH0.DESTADDR0  =(((uint16_t)&ADCA_Buff[0])>>0*8) & 0xFF;
	DMA.CH0.DESTADDR1  =(((uint16_t)&ADCA_Buff[0])>>1*8) & 0xFF;
	DMA.CH0.CTRLA = DMA_CH_ENABLE_bm | DMA_CH_REPEAT_bm | DMA_CH_BURSTLEN_2BYTE_gc; 
	
	DMA.CH1.TRIGSRC= DMA_CH_TRIGSRC_ADCA_CH1_gc;  // ADCA CH1 is trigger source
	DMA.CH1.TRFCNT = 2;
	DMA.CH1.SRCADDR0 =(((uint16_t)(&ADCA.CH1.RES))>>0*8)&0xFF;
	DMA.CH1.SRCADDR1 =(((uint16_t)(&ADCA.CH1.RES))>>1*8)&0xFF;
	DMA.CH1.DESTADDR0  =(((uint16_t)ADCA_Buff[1])>>0*8) & 0xFF;
	DMA.CH1.DESTADDR1  =(((uint16_t)ADCA_Buff[1])>>1*8) & 0xFF;
	DMA.CH1.CTRLA = DMA_CH_ENABLE_bm | DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc; 

the buffer is initialized as:

volatile uint16_t ADCA_Buff[4];

An example of the problem is the ADCA ch0 result is 0x027D and a DMA transfer leaves 0x007D in ADC_Buff[0]

You don't gno-me!

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

1) Must setup the DMA.CH0.ADDRCTRL register .

2) Must set the "single" bit in the CH0_CTRLA register. I'd also say don't set its enable bit until the DMA is all setup and ready to go .

3) You say you're only testing with 2 channels for now for the DMA. If you don't mind, for any future posting while debugging this show code set for that...sweep channel_0_1, etc. ( that way we don't have distractions or worrying about what's enabled and what's not :wink: ) .

4) You don't need to reference the first array element in your source address register ( In case you thought you did . ) .

5) Volatile not needed for the array, the DMA knows the deal .

6) You only need ONE DMA channel for all 2/4 ADC values . Do a DMA trigger when channel 1 result's done, not ch_0 . I'd say comment out the DMA ch_1 stuff for now, may be able to use it for the 5th ADC value .

7) For 2 initial channels, tranfer_cnt register should be 4 bytes .

Do you have a JTAG debugger ? Or are you printing to an LCD/PC ?

It's 100% for round-robin in getting both bytes for all 4 channels, then ?

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

I am using a JTAG debugger. After making the changes you listed the reading is successfully being loaded into the first element of the array. But the 2nd element is not loading at all. Still always reading 00. The Chan1 of the ADC is running and there is a non 0 result in there.

Not sure what you mean by this.

Quote:
It's 100% for round-robin in getting both bytes for all 4 channels, then ?

Correct me if im wrong here but the idea is that the ADC is free running round Robbin on all 4 channels. When a conversion is complete on chan 1 (and inherently on all chans previous to that) DMA is going to copy the results of each ADC chan register to the dest address, which is an array. After each burst its going to increment the destination address by 1, which moves it to the next element of the array, and likewise for the source address. At the end of the transaction it will reload the original source and destination address, and wait for a conversion on ADC chan 1 to be complete again.

Here is the current code.

Quote:
int16_t ADCA_Buff[4];

Quote:
void ADC_RR_Setup(void){
ADCA.CTRLA = ADC_ENABLE_bm;
ADCA.CTRLB = ADC_FREERUN_bm | ADC_RESOLUTION_12BIT_gc;
ADCA.REFCTRL = ADC_REFSEL_VCC_gc;
ADCA.EVCTRL = ADC_SWEEP_01_gc;
ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc;

ADCA.CH0.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;
// ADCA.CH0.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;

ADCA.CH1.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
// ADCA.CH1.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;

/*
ADCA.CH2.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
// ADCA.CH2.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;

ADCA.CH3.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN3_gc;
// ADCA.CH3.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc;

*/
}

Quote:
void DMA_Setup(void){
DMA.CTRL = DMA_ENABLE_bm;
DMA.CH0.CTRLA = DMA_CH_SINGLE_bm| DMA_CH_REPEAT_bm | DMA_CH_BURSTLEN_2BYTE_gc;
DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_TRANSACTION_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTRELOAD_TRANSACTION_gc | DMA_CH_DESTDIR_INC_gc;
DMA.CH0.TRIGSRC= DMA_CH_TRIGSRC_ADCA_CH1_gc; // ADCA CH0 is trigger source
DMA.CH0.TRFCNT = 4;
DMA.CH0.SRCADDR0 =(((uint16_t)(&ADCA.CH0.RES))>>0*8)&0xFF;
DMA.CH0.SRCADDR1 =(((uint16_t)(&ADCA.CH0.RES))>>1*8)&0xFF;
DMA.CH0.DESTADDR0 =(((uint16_t)&ADCA_Buff)>>0*8) & 0xFF;
DMA.CH0.DESTADDR1 =(((uint16_t)&ADCA_Buff)>>1*8) & 0xFF;

DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;

/*
DMA.CH1.TRIGSRC= DMA_CH_TRIGSRC_ADCA_CH1_gc; // ADCA CH1 is trigger source
DMA.CH1.TRFCNT = 2;
DMA.CH1.SRCADDR0 =(((uint16_t)(&ADCA.CH1.RES))>>0*8)&0xFF;
DMA.CH1.SRCADDR1 =(((uint16_t)(&ADCA.CH1.RES))>>1*8)&0xFF;
DMA.CH1.DESTADDR0 =(((uint16_t)ADCA_Buff[1])>>0*8) & 0xFF;
DMA.CH1.DESTADDR1 =(((uint16_t)ADCA_Buff[1])>>1*8) & 0xFF;
DMA.CH1.CTRLA = DMA_CH_ENABLE_bm | DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc;
*/
}

You don't gno-me!

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

Quote:
It's 100% for round-robin in getting both bytes for all 4 channels, then ?
I meant the 4 ( 2 ? ) result registers all had the correct 12 bit values, using RR .

Chupa wrote:
Correct me if im wrong here but the idea is that the ADC is free running round Robbin on all 4 channels. When a conversion is complete on chan 1 (and inherently on all chans previous to that) DMA is going to copy the results of each ADC chan register to the dest address, which is an array. After each burst its going to increment the destination address by 1, which moves it to the next element of the array, and likewise for the source address. At the end of the transaction it will reload the original source and destination address, and wait for a conversion on ADC chan 1 to be complete again.
Yes, for the 2 channels, triggering from result reg. chan. 1, you're debugging now. Same will apply when you try 4 channels by triggering on result reg. 3 . But the addresses ALWAYS increment by 1 byte, no matter what size the burst is set to . If 1st result is moved to array and it starts at say 0x2020, then it's incremented after EACH byte. So 0x2020,++inc ...0x2021 ( holds 2nd half of chan. 0 ), ++inc and now dest. points to 0x2022 for 1st half of chan. 1 .

Try reloading on BLOCK, instead of on TRANSACTION, for BOTH . Transaction never ends ( free-running ), so it might be the issue. Also, for your current code did the DMA source/dest. addresses reload after getting the 1st result ( chan. 0 ) or the 2nd ?

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

If the BLOCK change doesn't do it, maybe a DMA complete ISR() is needed, to clear that flag... ?

Edit: What you may need is DMA_SEL bits to be set ( DMA ISR() MIGHT still be needed to clear the flag... ? ) :

DMA.CTRL = DMA_ENABLE_bm | (0x01 << 6 ); /Set for DMA req., ch_0 or ch_1 .

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

I think the problem is that when the original source address, ADCA ch0 result (0x0224), is inc'd it goes to 0x0225, which is NOT the ADCA ch1 result address.
The ADCA ch1 result address is 0x022C. So it appears that this current plan is not going to work.

I suppose it would require changing the source address with an interrupt after the DMA transfer is done. Or how I originally had it using a separate DMA chan for each ADC chan.

Any other ideas?

You don't gno-me!

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

There are TWO sets of result registers you can use and the 0x0224 is the wrong one . Look at the end of the ADC section in Manual A . You want the compiler to use the result reg. beginning at 0x0210, CH0RESL . This is how the DMA will be able to read all four values . Also, see my edit in my last post , if you missed it.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

Remember to use code tags when posting code . You need this :

DMA.CH0.SRCADDR0 =(((uint16_t)(&ADCA.CH0RES))>>0*8)&0xFF;
DMA.CH0.SRCADDR1 =(((uint16_t)(&ADCA.CH0RES))>> 8)&0xFF;

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

Last Edited: Fri. Apr 27, 2012 - 01:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

indianajones11 wrote:
There are TWO sets of result registers you can use and the 0x0224 is the wrong one . Look at the end of the ADC section in Manual A . You want the compiler to use the result reg. beginning at 0x0210, CH0RESL . This is how the DMA will be able to read all four values . Also, see my edit in my last post , if you missed it.

That was the only remaining problem. It seems to be working lovely now. I appreciate all the help! Thank you very much!

Ok so now there are 3 more pins I would like to get readings from on ADCA: ADCA4 - ADCA6. What do you think the best solution is to achieve that?

Here is everything now. Sweeping 4 pins on ADCA, and DMA transferring the result after all 4 are done.

void DMA_Setup(void){
	DMA.CTRL = DMA_ENABLE_bm; 
	DMA.CH0.CTRLA =  DMA_CH_REPEAT_bm | DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc; 
	DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTRELOAD_BLOCK_gc | DMA_CH_DESTDIR_INC_gc;
	DMA.CH0.TRIGSRC= DMA_CH_TRIGSRC_ADCA_CH3_gc;  // ADCA CH0 is trigger sour	
	DMA.CH0.TRFCNT = 8;
	
	
	DMA.CH0.SRCADDR0 =(((uint16_t)(&ADCA.CH0RES))>>0*8)&0xFF;
	DMA.CH0.SRCADDR1 =(((uint16_t)(&ADCA.CH0RES))>>1*8)&0xFF;
	DMA.CH0.DESTADDR0  =(((uint16_t)&ADCA_Buff)>>0*8) & 0xFF;
	DMA.CH0.DESTADDR1  =(((uint16_t)&ADCA_Buff)>>1*8) & 0xFF;

	DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
}
void ADC_RR_Setup(void){
	ADCA.CTRLA = ADC_ENABLE_bm;
	ADCA.CTRLB = ADC_FREERUN_bm | ADC_RESOLUTION_12BIT_gc;
	ADCA.REFCTRL = ADC_REFSEL_VCC_gc;
	ADCA.EVCTRL = ADC_SWEEP_0123_gc;
	ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc;
	
	ADCA.CH0.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;
	
	ADCA.CH1.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;

	ADCA.CH2.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
	
	ADCA.CH3.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN3_gc;	
}	

You don't gno-me!

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

You're welcome . So, you want 3 more pins...ttl of 7, then ? The best is either put the other 3 on ADCB port and then it's just more of the same thing like on ADCA or you'll have to set the 1st 4 for manual conversion ( NO free-run ) ADCA -> all done -> DMA -> to array . Then MUXCTRL to other 3 pins, adjust DMA for 3 channels, and do same sequence for those . When done, start over .

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

OK! I think im going to do the 2nd option because it will be very difficult to green wire the board.

The idea here is to setup the ADC to do a single sweep on the first set of pins. Let DMA transfer the results to the array. With DMA is done it will generate an interrupt where I will change the mux pins for the ADC as well as the dest address for DMA, then run a single sweep again. Then and then repeat the process for the first set of pins and so on and so fourth.

I cant figure out why its only doing doing the first set of conversions. The ADC does not seem to want to start up again. When debugging it everything seems o
be correct, but the ADC never runs a 2nd time.

uint16_t ADCA_BuffA[4], ADCA_BuffB[4];
ISR(DMA_CH0_vect){
	if(conversion == 0 ){
		conversion = 1;
		ADCA.CTRLA = ADC_FLUSH_bm;
		ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN4_gc;	
		ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN5_gc;
		ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN6_gc;
		ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN7_gc;
		
		DMA.CH0.DESTADDR0  =(((uint16_t)&ADCA_BuffB)>>0*8) & 0xFF;
		DMA.CH0.DESTADDR1  =(((uint16_t)&ADCA_BuffB)>>1*8) & 0xFF;
	}
	else{
		conversion = 0;
		ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;	
		ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
		ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
		ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN3_gc;	
		
		DMA.CH0.DESTADDR0  =(((uint16_t)&ADCA_BuffA)>>0*8) & 0xFF;
		DMA.CH0.DESTADDR1  =(((uint16_t)&ADCA_BuffA)>>1*8) & 0xFF;
	}
	ADCA.CTRLA = ADC_CH0START_bm | ADC_CH1START_bm | ADC_CH2START_bm | ADC_CH3START_bm | ADC_ENABLE_bm;
}
void DMA_Setup(void){
	DMA.CTRL = DMA_ENABLE_bm; 
	DMA.CH0.CTRLB = DMA_CH_TRNINTLVL_LO_gc;
	DMA.CH0.CTRLA =  DMA_CH_REPEAT_bm | DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_2BYTE_gc; 
	DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTRELOAD_BLOCK_gc | DMA_CH_DESTDIR_INC_gc;
	DMA.CH0.TRIGSRC= DMA_CH_TRIGSRC_ADCA_CH3_gc;  // ADCA CH0 is trigger sour	
	DMA.CH0.TRFCNT = 8;
	
	DMA.CH0.SRCADDR0 =(((uint16_t)(&ADCA.CH0RES))>>0*8)&0xFF;
	DMA.CH0.SRCADDR1 =(((uint16_t)(&ADCA.CH0RES))>>1*8)&0xFF;
	DMA.CH0.DESTADDR0  =(((uint16_t)&ADCA_BuffA)>>0*8) & 0xFF;
	DMA.CH0.DESTADDR1  =(((uint16_t)&ADCA_BuffA)>>1*8) & 0xFF;

	DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
}
void ADC_RR_Setup(void){
	ADCA.CTRLA = ADC_ENABLE_bm;
	ADCA.CTRLB =  ADC_RESOLUTION_12BIT_gc;
	ADCA.REFCTRL = ADC_REFSEL_VCC_gc;
	ADCA.EVCTRL = ADC_SWEEP_0123_gc;
	ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc;

	ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;	
	ADCA.CH0.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	
	ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
	ADCA.CH1.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	
	ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
	ADCA.CH2.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	
	ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN3_gc;	
	ADCA.CH3.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
}	

You don't gno-me!

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

Chupa, do me a favor and change the burstlen = 8 instead of 2. I just want to see if it'll work, even for the version you now have up to the point of where it is working . It should since it's all contiguous peripheral addresses.

1) Clear the TRNIF bit in the ISR(), since it's not auto-cleared. Make this the 1st thing done in the ISR() .

2) You're keeping most of the app. code hidden, which is your right, BUT you STILL have to post enough info to get help, and figure out what parts need to be posted . HOW and WHERE is "conversion" declared ? Are you workin' PMIC and sei, correctly, etc ?

3) Re-enable the enable bit in CH0_DMA_CTRLA register in the ISR() .

4) You had int for your array in the working code, do the sign settings line-up for this version ( ADC and array ) ?

5) I doubt that you need to flush, since we know exactly 4 conversions will hit, or the sweep bits for this config. Those apply ONLY if using event-trigger or free-running mode .

6) If this doesn't do it, JTAG the relevant ADC and DMA registers and post here the values they have at the end of the ISR().

What kind of signals are you ADC'ing ?

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

Last Edited: Sat. Apr 28, 2012 - 04:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Those changes fixed everything. I believe its operating as I described in the previous post now.

I try to post only relevant sections to keep down on the amount of clutter, and make less reading for those trying to help. Its more that than a privacy issue. I forget that you have no idea what else is going on in my program at times and just assume that you know that everything else is operating normally even though I didn't give you any reason to believe it was. That's my bad. But it all works now with your guidance.

Changed BURSTLEN to 8 bytes and it works fine.

Again I really appreciate your help with this. I had read the DMA section 3 times over and was still going nowhere. You saved me a lot of headache.

Posting final code of the working solution.

This solution will run the ADCA on pins 0-3 and use DMA to copy the results to the array ADCA_BuffA. It will then run ADCA on pins 4-7 and copy the results via DMA to ADCA_BuffB. And repeat this process indefinitely.

Vars:

uint16_t ADCA_BuffA[4], ADCA_BuffB[4];

Ints need to be enabled somewhere in your program:

PMIC.CTRL = PMIC_LOLVLEX_bm; // enable low level interrupts
	sei(); // enable interrupts

DMA ISR:

ISR(DMA_CH0_vect){
	if(conversion == 0 ){
		conversion = 1;
		ADCA.CTRLA = ADC_FLUSH_bm;
		ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN4_gc;	
		ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN5_gc;
		ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN6_gc;
		ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN7_gc;
		
		DMA.CH0.DESTADDR0  =(((uint16_t)&ADCA_BuffB)>>0*8) & 0xFF;
		DMA.CH0.DESTADDR1  =(((uint16_t)&ADCA_BuffB)>>1*8) & 0xFF;
	}
	else{
		conversion = 0;
		ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;	
		ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
		ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
		ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN3_gc;	
		
		DMA.CH0.DESTADDR0  =(((uint16_t)&ADCA_BuffA)>>0*8) & 0xFF;
		DMA.CH0.DESTADDR1  =(((uint16_t)&ADCA_BuffA)>>1*8) & 0xFF;
	}
	DMA.CH0.CTRLB |= DMA_CH_TRNIF_bm;
	DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
	ADCA.CTRLA = ADC_CH0START_bm | ADC_CH1START_bm | ADC_CH2START_bm | ADC_CH3START_bm | ADC_ENABLE_bm;
}

DMA setup:

void DMA_Setup(void){
	DMA.CTRL = DMA_ENABLE_bm; 
	DMA.CH0.CTRLB = DMA_CH_TRNINTLVL_LO_gc;
	DMA.CH0.CTRLA =  DMA_CH_REPEAT_bm | DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_8BYTE_gc; 
	DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTRELOAD_BLOCK_gc | DMA_CH_DESTDIR_INC_gc;
	DMA.CH0.TRIGSRC= DMA_CH_TRIGSRC_ADCA_CH3_gc;  // ADCA CH0 is trigger sour	
	DMA.CH0.TRFCNT = 8;
	
	DMA.CH0.SRCADDR0 =(((uint16_t)(&ADCA.CH0RES))>>0*8)&0xFF;
	DMA.CH0.SRCADDR1 =(((uint16_t)(&ADCA.CH0RES))>>1*8)&0xFF;
	DMA.CH0.DESTADDR0  =(((uint16_t)&ADCA_BuffA)>>0*8) & 0xFF;
	DMA.CH0.DESTADDR1  =(((uint16_t)&ADCA_BuffA)>>1*8) & 0xFF;

	DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
}

ADC setup:

void ADC_RR_Setup(void){
	ADCA.CTRLA = ADC_ENABLE_bm;
	ADCA.CTRLB =  ADC_RESOLUTION_12BIT_gc;
	ADCA.REFCTRL = ADC_REFSEL_VCC_gc;
	ADCA.EVCTRL = ADC_SWEEP_0123_gc;
	ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc;

	ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;	
	ADCA.CH0.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	
	ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc;
	ADCA.CH1.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	
	ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc;
	ADCA.CH2.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
	
	ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN3_gc;	
	ADCA.CH3.CTRL = ADC_CH_START_bm | ADC_CH_INPUTMODE_SINGLEENDED_gc;
}

You don't gno-me!

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

It's a GOOD feelin', right ? Good work ! Thanks for the burstlen verify .

I wrote:
What kind of signals are you ADC'ing ?
I've earned an answer to my question ? :wink:

Another favor: would you comment out the flush bit code ( that code line writes a 0 to the main ADC enable bit, I'm surprised it doesn't shutoff the ADC module ) and also the sweep code and let us know if it still works right ?

I think you may be able to use free-running, with a twist . In both parts of the ISR()'s if(), as the last step in each section add the flush bit code and set for free-running mode in your adc_init(). Everything below :

DMA.CH0.CTRLB |= DMA_CH_TRNIF_bm;

you should be able to comment out .

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

Video of what I'm doing here:
http://www.youtube.com/watch?v=aIox-FH2yVY

I'm using it with a tennis ball cannon I made a couple years back. I have IR beams with IR LEDs and IR receivers thought the different stages as the ball is moving though the cannon. This will give me a closed loop system as to when each pneumatic cylinder needs to be activated to move the ball to the next stage. As it stands in this video all the pneumatic cylinders are just actuated based on time. So if there was a jam there was no way to tell and the system would just keep blank firing.

There are 5 or 6 IR "eyes" to check where the ball is, and a MEMS pressure sensor to get a reason on the air tank. My idea is once the system it turned on you will calibrate the eyes by taking a baseline reading, then sending a ball though step by step, and taking a reading at each eye. That So ill have an array of ADC values of when the IR beams are clear and when the IR beams are blocked by a ball for each eye. And then when in a firing sequence the program will know when to actuate the next pneumatic cylinder to move the ball.

I think i tried the changes you suggested and it didn't work. I think I'm confused what you want me to remove/add to the ISR. And also set the ADC to freerunning?

You don't gno-me!

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

Your cannon is very clever ! Are you a mechanical engineer and how long to design/build it ? LOL, I agree with you, I love the sound too ! How fast can it fire a ball ?

Quote:

I think i tried the changes you suggested and it didn't work. I think I'm confused what you want me to remove/add to the ISR.
Comment out the flush bit code in the ISR() and the sweep code line in ADC_setup(), I just wanted to see if that would work .
Quote:
And also set the ADC to freerunning?
Yeah, I think it can be done by flushing the ADC in the ISR as the last line of each if() section, having the sweep bits enabled and free-running all set in ADC_setup() . Then the last 2(?) code lines of the ISR() shouldn't be needed then (when you did working sweep code w/o the DMA, those settings didn't have to be reapplied ) .

For the code below, you can make it part of the 1st write to the register using '|' . Making it the last thing to do wasn't needed, as I thought . It would be an issue if you were triggering the DMA manually, though .

DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm; 

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

No i'm not a Mech eng. It was just a weekend project a couple years ago. Just kind of designed it on the fly.

I don't think the suggestions you gave are working. The ISR is never executed when I made the changes.

You don't gno-me!