ADC2 not maintaining sincronisation

Post here first, or if you can't find a relevant section!
Post Reply
jeremy777
Posts: 5
Joined: Thu Sep 14, 2017 7:02 pm

ADC2 not maintaining sincronisation

Post by jeremy777 » Thu Sep 14, 2017 10:14 pm

I'm reading 10 channels with regular simultaneous mode continuously via DMA to a circular buffer of 10kB with the half-done and completed interrupts calling an isr which processes the data. All that is working fine, except that the data supplied from ADC2 is coming is not staying in step with ADC1. The data from ADC1 is in its correct sequence. I've tried various configurations of the ADC's, with and without external triggers (SW_START or ADON start) looked at various other people's code including the other similar sounding but different issue from a couple of years ago in this forum and hardware examples pointed to from the manuals, tried every lead I can see... ADC2's data, collected in parallel with ADC1's, is continually slipping round. It is adding readings to it's sequence by repeating - sometimes the last one, sometimes the first, sometimes both - and then leaving out one from the middle... (see sanitised raw reading set below). I cannot see a way to make this happen, so I cannot see a way to make it NOT happen...

I very much hope there's someone out there who can see through this issue --- what am I doing wrong?

The important code is probably the ADC setup function - but I've included the whole source ... sorry if it's a bit messy, I don't usually show my code to anyone! There is a lot of probably unnecessary stuff in there by now, since I've been adding things trying to clear the problem.

Running on what I guess is a "blue-pill" maple mini clone with a 48pin STM32F103CBT6. I have several so I tried it on different ones just in case it was a hardware fault, but with the same result.

Code: Select all

// STM32F103C high resolution (multisampled) analog read

#include <libmaple/dma.h>
#define MAX_COUNT 64

// MAX_COUNT counts the number of chunks processed into a single value set. a chunk is 16*16 samples of all 10 channels. all ten channels take 10.8µs to sample, so a chunk takes 2773µs.
// 64 chunks takes 177.4ms, 5.6 values per second. bit depth is 12+4+4+6 = 26 bits.

uint32 X[2][16][16][5]; // data space for DMA buffer. each 32 bit word holds 2 12-bit values, total 512 reads for each input 256 in first half, 256 in second half
uint32 D[2][10]; // result buffer for the 10 values, twice so that the next value can be being calculated before the previous one is read
uint32 pk[20]; // a peek at the raw data for debugging
bool pkp = true;
byte count = 0; // how many input buffer halves have been summed so far - resets when we have enough of them
byte set = 0; // which set of results we are currently summing into
byte lastset = 0; // follows 'set' - posting loop checks these are same, if different post the set this points to then sets it to 'set'

void set_dma() {
  dma_init(DMA1);
  dma_setup_transfer(DMA1, DMA_CH1, &ADC1->regs->DR, DMA_SIZE_32BITS,
                     &X, DMA_SIZE_32BITS, DMA_MINC_MODE | DMA_CIRC_MODE | DMA_HALF_TRNS | DMA_TRNS_CMPLT );
  dma_set_num_transfers(DMA1, DMA_CH1, 2560);
  dma_set_priority(DMA1, DMA_CH1, DMA_PRIORITY_VERY_HIGH);   
  
  dma_enable(DMA1, DMA_CH1);   
}

void set_adc() {
  //adc_set_sample_rate(ADC1, ADC_SMPR_13_5);  // sets sample rate for all channels, all identical (critical in dual mode, even if only pairs sampled together need to be identical)
  //adc_set_sample_rate(ADC2, ADC_SMPR_13_5);

  adc_calibrate(ADC1);
  adc_calibrate(ADC2);

  ADC1->regs->CR2 = 0; // turn both ADC's off and clear settings
  ADC2->regs->CR2 = 0;
  ADC1->regs->CR1 = 0;
  ADC2->regs->CR1 = 0;
  ADC1->regs->SR = 0;
  ADC2->regs->SR = 0;
  ADC1->regs->SMPR1 = 0;
  ADC2->regs->SMPR1 = 0;
  ADC1->regs->SMPR2 = 0;
  ADC2->regs->SMPR2 = 0;
  ADC1->regs->HTR = 0xFFF;
  ADC2->regs->HTR = 0xFFF;
  ADC1->regs->LTR = 0;
  ADC2->regs->LTR = 0;
  ADC1->regs->SQR1 = 0;
  ADC2->regs->SQR1 = 0;
  ADC1->regs->SQR2 = 0;
  ADC2->regs->SQR2 = 0;
  ADC1->regs->SQR3 = 0;
  ADC2->regs->SQR3 = 0;
  ADC1->regs->JSQR = 0;
  ADC2->regs->JSQR = 0;
  
  ADC1->regs->CR1 = 1 << 8;        // Set scan mode  
  ADC1->regs->CR1 |= 6 << 16;      // and regular simultaneous mode
  ADC1->regs->CR2 = ( ADC_CR2_TSEREFE | ADC_CR2_CONT | ADC_CR2_DMA | (0b1111 << 17) ); // external trigger = software trigger
    
  ADC1->regs->SQR1 = 4 << 20; // regular sequence of 5
  ADC1->regs->SQR3 = 1 | (3 << 5) | (5 << 10) | (7 << 15) | (17 << 20);    // adc1 converts channels 1 3 5 7 and Vrefint
  ADC1->regs->SMPR2 = 0b010010010010010; // all five to 13.5 cycles sampletime (1 µs)
  

  ADC2->regs->CR1 = 1 << 8;         // Set scan mode
  ADC2->regs->CR1 |= 6 << 16;       // and regular simultaneous mode
  ADC2->regs->CR2 = ( ADC_CR2_CONT  | ADC_CR2_DMA | (0b1111 << 17) ); 

  ADC2->regs->SQR1 = 4 << 20; // regular sequence of 5
  ADC2->regs->SQR3 = 0 | ( 2 << 5) | (4 << 10) | (6 << 15) | (8 << 20);    // adc2 converts channels 0 2 4 6 8
  ADC2->regs->SMPR2 = 0b010010010010010; // all five to 13.5 cycles sampletime (1 µs)

  ADC1->regs->CR2 |= ADC_CR2_ADON; // this is now the first adc_on signal
  ADC2->regs->CR2 |= ADC_CR2_ADON; 
}

// set only the registers that need to be reset before each conversion
void adc_start() {
  //ADC1->regs->CR2 = ( ADC_CR2_CONT | ADC_CR2_DMA );
  //ADC2->regs->CR2 = ( ADC_CR2_CONT );
  //ADC1->regs->CR2 |= ADC_CR2_ADON;  // it is critical to enable ADC (bit 0=1) independently of all other changes to the CR2 register
  //ADC2->regs->CR2 |= ADC_CR2_ADON;  // enabling all at once (i.e. ADC_CR2_ADON | ADC_CR2_CONT | ADC_CR2_EXTSEL ) will cause problems when used with continuous mode   
  ADC1->regs->CR2 |= ADC_CR2_SWSTART; // trigger start with a SW_TRIGGER
}

bool flash = true;

void DMA1_isr()
{
  // check if first or second half done
  byte h = 0;
  if(DMA1->regs->ISR & 0b100 != 0)
  {
    h = 0; // half-transfer = first half of buffer complete
    DMA1->regs->IFCR = 0b100; // clear the flag
  } else if(DMA1->regs->ISR & 0b010 != 0)
  {
    h = 1; // transfer complete = second half of buffer done
    DMA1->regs->IFCR = 0b010; // clear the flag
  } else return; // if the ISR triggers with niether flag set, somethings wrong...
  uint32 sum[5];
  for (byte n = 0; n < 16; n++)
  {
    for(byte w = 0; w < 5; w++) sum[w] = 0;
    for(byte x = 0; x < 16; x++)
    {
      for(byte w = 0; w < 5; w++) sum[w] += X[h][n][x][w];
    }
    for(byte w = 0; w < 5; w++)
    {
      D[set][(w*2)+1] += sum[w] & 0xFFFF;
      D[set][w*2] += (sum[w] & 0xFFFF0000) >> 16;
    }
  }
  if(pkp) // provides a clean peek at the raw data, 4 rows from the start of the last prosessed chunk, for debugging
  {
    for(byte a = 0; a < 20; a++)
    {
      pk[a] = X[h][0][a/5][a%5];
    }
    pkp = false;
  }
  count++;
  if(count >= MAX_COUNT) // time for next set?
  {
    set = (set + 1) & 1;
    for (byte w = 0; w < 10; w++) D[set][w] = 0; // clear the results buffer
    count = 0;
    digitalWrite(33, flash); flash = !flash;
  }
}

void setup() {
  // zero array (not really necessary but doing it anyway)
  for(byte w = 0; w < 2; w++) for(byte x = 0; x < 16; x++) for(byte y = 0; y < 16; y++) for(byte z = 0; z < 5; z++) X[w][x][y][z]=0;

  Serial.begin(115200);

  pinMode(3,INPUT_ANALOG);
  pinMode(4,INPUT_ANALOG);
  pinMode(5,INPUT_ANALOG);
  pinMode(6,INPUT_ANALOG);
  pinMode(7,INPUT_ANALOG);
  pinMode(8,INPUT_ANALOG);
  pinMode(9,INPUT_ANALOG);
  pinMode(10,INPUT_ANALOG);
  pinMode(11,INPUT_ANALOG);

  pinMode(33, OUTPUT);
  
  // set up DMA: from: ADC1 data, word, no increment; to: X[], word, increment; number 2560; circular buffer; half-done and completed interrupts.
  set_dma();
  dma_attach_interrupt(DMA1, DMA_CH1, &DMA1_isr);

  // set up ADC: all 9 channels + Vrefint, 13.5 cycle sampletimes, continuous, scan, DMA, regular symultaneous mode
  set_adc();
  delay(10);
  adc_start();
}

void loop() {
  // put your main code here, to run repeatedly:
  bool nodata = true;
  for (int x = 0; x < 1000; x++)
  {
    delay(10);
    if (lastset != set)
    {
      nodata = false;
      Serial.print(millis());
      Serial.print(" ");
      for (byte y = 0; y < 10; y++)
      {
        Serial.print(D[lastset][y]);
        Serial.print(" ");
      }
      Serial.println();
      // some raw data for debugging
      while(pkp); // should be false by the time we get here
      Serial.print(millis());
      Serial.print(" ");
      for (byte y = 0; y < 20; y++)
      {
        Serial.print((pk[y] & 0xFFFF0000) >> 16);
        Serial.print(" ");
        Serial.print(pk[y] & 0xFFFF);
        Serial.print(" ");
      }
      Serial.println();
      pkp = true; // request next peek
      
      lastset = set;
    }
  }
  if(nodata) Serial.println("No data! somethings not working...");
}

The following is a sanitised version of the raw output from the code above. the blocks of 4 lines are each from the beginning of a separate chunk of data, the contents of pk[] tabulated for easy reading. The input lines are connected to a resistor ladder to get a clear sequence, with Ain0 (pin11) connected to Vcc and Ain8 to GND. The first line just happens to be in correct order, purely by chance since this is not the beginning of the capture which by this stage has been ongoing for over a minute. Just occasionally there is a line that is correct...

Code: Select all

ADC2	ADC1-1	ADC2	ADC1-3	ADC2	ADC1-5	ADC2	ADC1-7	ADC2	ADC1-17

4095	3581	3068	2555	2044	1565	1045	518	0	1496
4084	3580	3070	2556	3070	1570	2045	519	1049	1494
3	3582	4095	2556	3066	1560	3066	521	2045	1498
0	3580	4095	2554	4095	1563	3069	517	2043	1497
									
3067	3580	1052	2554	0	1561	0	518	4093	1497
3069	3582	2046	2556	1048	1561	0	518	0	1497
4095	3584	3067	2557	2045	1561	1049	517	0	1497
4095	3582	3067	2555	3067	1569	2047	518	1049	1496
									
2046	3581	0	2555	0	1561	4095	517	4095	1497
3069	3580	1054	2555	0	1561	0	519	4095	1498
3067	3584	2045	2556	1051	1561	0	518	0	1498
4094	3582	3067	2555	2045	1561	1049	517	0	1497
									
0	3579	4095	2551	4095	1563	3067	518	3067	1497
1050	3580	0	2555	4095	1560	4095	518	3066	1498
2042	3585	0	2556	0	1561	4093	517	4093	1499
3068	3580	1054	2555	0	1562	0	517	4082	1497
									
4095	3580	3067	2554	3067	1570	2045	518	1046	1499
0	3580	4093	2556	3067	1562	3067	522	2044	1498
0	3584	4094	2557	4094	1563	3069	518	3069	1498
1050	3580	0	2555	4094	1561	4094	518	3068	1497
									
4095	3581	3067	2551	2045	1560	1048	517	0	1498
4095	3582	3066	2554	3066	1570	2044	517	1048	1499
0	3580	4095	2555	3067	1561	3067	521	2047	1498
0	3581	4095	2555	4095	1563	3070	517	2045	1499
									
3066	3581	1052	2554	0	1559	0	517	4093	1497
3068	3580	2045	2555	1048	1561	0	518	0	1498
4095	3584	3068	2557	2044	1561	1049	517	0	1501
4084	3574	3069	2556	3069	1569	2047	517	1048	1498
									
1049	3581	0	2553	4095	1561	4095	519	3067	1499
2046	3580	0	2556	0	1561	4093	518	4093	1498
3068	3585	1053	2556	0	1561	0	518	4093	1499
3068	3580	2045	2556	1048	1560	0	517	0	1498
									
0	3583	4094	2554	4094	1562	3068	516	3068	1497
1049	3582	0	2554	4095	1560	4095	518	3069	1496
2044	3583	0	2556	0	1561	4095	516	4095	1497
3068	3582	1051	2556	0	1561	0	517	4095	1498
									
4094	3581	3068	2556	3068	1571	2046	517	1048	1499
0	3580	4095	2555	3066	1561	3066	522	2045	1498
0	3583	4095	2556	4095	1563	3068	517	3068	1498
1050	3580	0	2556	4095	1560	4095	518	3068	1497
									
3067	3581	2044	2555	1048	1561	0	517	0	1497
4094	3578	3066	2560	2044	1561	1049	518	0	1498
4095	3585	3066	2555	3066	1570	2045	520	1050	1498
0	3580	4095	2556	3070	1561	3070	522	2044	1498
									
2044	3581	0	2554	0	1561	4094	518	4094	1497
3068	3580	1054	2559	2	1560	2	518	4093	1497
3067	3584	2045	2555	1049	1561	0	517	0	1498
4095	3580	3067	2556	2046	1560	1049	517	0	1497
									
1048	3579	0	2556	4095	1562	4095	519	3071	1501
2041	3580	0	2555	0	1562	4095	518	4095	1497
3067	3585	1053	2556	0	1560	0	517	4095	1498
3068	3582	2045	2556	1050	1560	0	517	0	1497
									
0	3580	4093	2548	3068	1561	3068	522	2045	1499
0	3580	4094	2555	4094	1563	3067	518	3067	1498
1049	3584	0	2556	4095	1561	4095	518	3068	1498
2044	3580	0	2556	0	1561	4095	518	4095	1497
									
4094	3577	3066	2577	2044	1561	1048	517	0	1499
4094	3580	3067	2555	3067	1570	2045	518	1046	1498
0	3582	4093	2555	3067	1561	3067	521	2045	1499
0	3580	4092	2554	4092	1563	3067	517	3067	1497
									
3068	3582	2043	2554	1050	1561	0	516	0	1497
4095	3581	3068	2556	2044	1560	1048	517	0	1497
4095	3582	3068	2556	3068	1569	2044	516	1048	1497
0	3579	4089	2557	3068	1561	3068	521	2045	1498
									
2044	3581	0	2555	0	1559	4095	517	4095	1499
3068	3580	1052	2555	0	1560	0	519	4095	1498
3067	3584	2044	2556	1049	1562	0	517	0	1498
4095	3580	3068	2556	2044	1560	1048	517	0	1497
									
0	3581	4094	2555	4094	1559	3067	518	3067	1497
1049	3580	0	2555	4095	1561	4095	519	3067	1498
2044	3584	0	2556	0	1561	4095	517	4095	1499
3067	3580	1052	2556	0	1561	0	518	4095	1496

jeremy777
Posts: 5
Joined: Thu Sep 14, 2017 7:02 pm

Re: ADC2 not maintaining sincronisation

Post by jeremy777 » Fri Sep 15, 2017 11:35 am

I have solved my own problem...

I misunderstood the manual entry on the sample time register, thinking it was sample times for each item in the regular sequence. it actually is the sample time for each channel... ADC1 was giving its results correctly because it was in control of the DMA. ADC2 was sampling more of its channels with longer window times, so sometimes it was not updating its results and its previous result was left in the upper half of the register, and sometimes it produced a result and then another before the DMA happened so one result was missing... all the time loosing step with ADC1.

I got as far as trying to synchronise the two with timer3_trgo in discontinuous mode and was still getting double-readings and missing readings, but the pattern became intelligible at that point :)

adc setup now looks like this, and all works fine:

Code: Select all

void set_adc() {

  adc_calibrate(ADC1);
  adc_calibrate(ADC2);

  ADC1->regs->CR2 = 0; // ensure both ADC's are off
  ADC2->regs->CR2 = 0;

  ADC1->regs->CR1 = 1 << 8;        // Set scan mode  
  ADC1->regs->CR1 |= 6 << 16;      // and regular simultaneous mode
  ADC1->regs->CR2 = ( ADC_CR2_TSEREFE | ADC_CR2_CONT | ADC_CR2_DMA | (0b1111 << 17) ); // external trigger = sw_trigger
    
  ADC1->regs->SQR1 = 4 << 20; // regular sequence of 5
  ADC1->regs->SQR3 = 1 | (3 << 5) | (5 << 10) | (7 << 15) | (17 << 20);    // adc1 converts channels 1 3 5 7 and Vrefint
  ADC1->regs->SMPR2 = 0b010010010010010010010010010; // all 9 to 13.5 cycles sampletime (1 µs)
  ADC1->regs->SMPR1 = 0b010 << 21; // also 17(Vrefint) 13.5 cycles
  

  ADC2->regs->CR1 = 1 << 8;         // Set scan mode 
  ADC2->regs->CR2 = ( ADC_CR2_CONT | (0b1111 << 17) ); // external trigger = sw_trigger

  ADC2->regs->SQR1 = 4 << 20; // regular sequence of 5
  ADC2->regs->SQR3 = 0 | ( 2 << 5) | (4 << 10) | (6 << 15) | (8 << 20);    // adc2 converts channels 0 2 4 6 8
  ADC2->regs->SMPR2 = 0b010010010010010010010010010; // all 9 to 13.5 cycles sampletime (1 µs)

  ADC1->regs->CR2 |= ADC_CR2_ADON; // this is now the first adc_on signal
  ADC2->regs->CR2 |= ADC_CR2_ADON; 
}

victor_pv
Posts: 1648
Joined: Mon Apr 27, 2015 12:12 pm

Re: ADC2 not maintaining sincronisation

Post by victor_pv » Fri Sep 15, 2017 1:29 pm

So the change is in the ADC1->regs->SMPR2 and ADC2->regs->SMPR2, settings, right?

jeremy777
Posts: 5
Joined: Thu Sep 14, 2017 7:02 pm

Re: ADC2 not maintaining sincronisation

Post by jeremy777 » Sat Oct 14, 2017 11:13 pm

yes, that's right.

I also noticed another slight error when I started bending this code to another project of mine, a 36-channel MIDI controller board. The page detection at the start of the ISR needs explicit bracketing otherwise it always thinks it's doing page one and never page two. Didn't matter much till page one and page two were different multiplexed rows of knobs...

Code: Select all

void DMA1_isr()
{
  // check if first or second half done
  byte h = 0;
  if((DMA1->regs->ISR & 0b100) != 0) // change: added brackets
  {
    h = 0; // half-transfer = first half of buffer complete
    DMA1->regs->IFCR = 0b100; // clear the flag
  } else if((DMA1->regs->ISR & 0b010) != 0) // change: added brackets
  {
    h = 1; // transfer complete = second half of buffer done
    DMA1->regs->IFCR = 0b010; // clear the flag
  } else return; // if the ISR triggers with niether flag set, somethings wrong...

.........

Post Reply