Spark Logic

Problems with regular simultaneous dual ADC conversion with DMA transfer

robca Tue Nov 24, 2015 5:06 pm

EDIT: I finally solved this, please see http://www.stm32duino.com/viewtopic.php … 8474#p8474 for more info

I’m at the end of my wits :? I have read the ADC and DMA docs at least 5 times each. Looked at the RegSimul_DualMode example from ST (and that was fun enough, since it uses completely different conventions than the Arduino/Maple libraries but in the end I understand what it does)… and I still can’t make it work… help, please.

Problem statement: on a Maple Mini, using STM32duino, convert 4 analog channels (connected to pins D3-D6, i.e. IN8-5, respectively) and transfer the data in memory using DMA (I need fast and predictable timing).

I basically would need to combine “dual simultaneous mode” with “continuous scan”, in order to capture ~2000 samples from each channel. But I fear it might not be possible, and the only mention I found is here (no reply, though) https://my.st.com/public/STe2ecommuniti … x?tags=adc

The code below sets a sequence for each ADC: in SQR1 sequence length is 1 (i.e. 2 conversions), and I set the SQR2 registers to have ADC1 convert IN8 while ADC2 converts IN7, then ADC1 convert IN6 while ADC2 converts IN5. I set regular (not injected) SW triggers.

After setting the ADC1 and ADC2 registers and the DMA1 Channel 1, in theory setting ADC1->regs->CR2 |= ADC_CR2_SWSTART; should start the conversion for the master ADC1 and slave ADC2 and in parallel start the DMA (one 32 bit DMA transfer for each conversion: in dual mode the ADC1 SR register holds both the values for ADC1 and ADC2).

What I noticed, though, is that when ADC_CR2_CONT is enabled, the ADC continuously scans, and the DMA keeps sending data to my buffer. What that means is the the order of the odd and even channels in the buffer can be off. Basically, when ADC_CR2_CONT is enabled, the ADC keeps scanning all the time, and that triggers DMA transfers in a random order.

If I don’t use ADC_CR2_CONT, I can only sequence a limited number of channels (and even if I repeat the sequence, I can only still do 16 conversions per ADC max (using SQR1-3). Or I can convert 2 channels (one on ADC1, one on ADC2) continuously in parallel…

But there seems no way to do scan and dual simultaneous at the same time… am I missing something? Any suggestion? If this doesn’t work, I can use two Maple Mini synchronized to capture 4 channels, but it makes everything harder to code (one as a master, one as a slave, with the conversion triggered at the same time, and transferring data)

Here’s my non-working code (if you uncomment the delay() statement you will see the problem)

EDIT: removed non-working code, see http://www.stm32duino.com/viewtopic.php?f=3&t=757&p=8474#p8474 for working code

Replies

mrn Thu May 05, 2016 10:00 pm

@robca: a comment on DoA estimation:
in case of periodic signal detection, it is possible to derive a 2D representation,
where one dimension is frequency(pitch), the other one is DoA(direction of arrival),
and colours(intensity) represent the probability that a source is present at given Freq and DoA.
this 2D position-pitch plane is extremely robust against non-periodic noise.

Image

more details here:
https://signalprocessingideas.wordpress … opi-plane/

in case you are interested, i got it implemented in octave.
mrn.

robca Thu May 05, 2016 11:18 pm

Thanks for the suggestion, that’s really interesting…

But would that work with a single percussive sound (like a shot)? there’s really no frequency to speak of, in those cases

I fully implemented a sound location algorithm, btw. Works pretty well: 4 microphones, uses hyperbolic location (heavily modified version of the multilateration algorithm defined in IT++: https://sourceforge.net/projects/itpp/). The slowest part is cross correlating the sample (I’m using a general cross correlation algorithm, optimized for my sampling strategy) . Depending on the precision I want to achieve, I can get a location in anywhere between 1 and 3 seconds after the sound capture.

I could maybe speed up cross correlation using FFTs, but the devices I’m using simply do not have enough memory to do so. And even using a device with more memory might not improve cross correlation enough to justify the effort: an F1 using integer math is not that fast calculating FFTs

Unfortunately “life happened” before I completed the project and cleaned it up for publishing… Hope to publish it soon, though

mrn Sat May 07, 2016 7:12 pm

But would that work with a single percussive sound (like a shot)? there’s really no frequency to speak of, in those cases
I checked the recording you posted few months ago, it seems to be really non-periodic :)
Unfortunately, the method would give you almost the same result as the cross-correlation based estimation.
There would be one single vertical line present in the decomposition, meaning, the estimation would be spread over the whole frequency range.

Regarding the speed of the cross-correlation: once you know roughly the DoA of the source, you can reduce the calculation of the cross-correlation to only those time-lag candidates (DoA-candidates), which are close to the source position from the previous estimation.

Example: there is a strong peak at 50 Degrees indicating a source, then from the next frame onward, it is enough to compute the cross-correlation for those values,, which are close to 50 Degrees (let say to scan from 30 degs to 60 degs) instead of scanning the whole (0-180)Degs space for a given mic pair. in case of moving sources, of course, it is necessary to update those values.

good luck!
M.

robca Mon May 09, 2016 12:36 am

I’m actually not using DOA (as in an angle), but TDOA (Time Difference Of Arrival). Cross correlation for those 4 signals won’t be helped much by knowing the angle, since the signal can still reach the microphones at very different times. I mean, it can help, but I’m not sure it will compensate for the time that it would take to run another algorithm before attempting cross correlation

Since there was no code or example at the link you provided, it’s really hard to say how long (and how much memory) would it require… If you have more info, I’d be interested

robca Mon May 16, 2016 3:20 pm

Thanks.

Do you really use the xcorr() function in your algorithm? If so, well, that’s the only slow part in my code :). I had to implement a fast version of xcorr for my data sets, and I managed to reduce the time to calculate cross correlation to ~15% of the more generic algorithm for the same data set (accepting some limitations that, in my case, are not significant)

Outside of xcorr, my algorithm is running in only a few msec, and doesn’t use any additional memory. Just to give a sense, of the less than 20Kb Ram available, I’m using 16Kb for the samples data, so only a couple of Kb are left for additional variables. There’s no room for a lookup table or much else

If we had plenty of memory, it would be easier to implement a FFT based cross correlation, using integer-based FFT with lookup tables. But there’s simply no room for any lookup table in an F103 (yes, I could use a device with more memory, which is what I plan to do in the future)

Anyway: the weak part of my code is having to implement xcorr() in an efficient way

mrn Mon May 16, 2016 6:17 pm

Yes, there is really an XCorr fnct before the po-pi decomposition :)
Here is the github link, there should be all files you will need to run the demo-decomposition.
There is the main routine, “popi_demo”, written and tested in octave.
Then the audio samples, the lookup tables, in txt format, and there is even an xcorr function i used, from Paul Kienzle.

https://github.com/m-r-n/popi-toolbox

I would be interested in the cross-correlation algorithm you used, as I will need soon one when rewriting this decomposition for an embedded platform.
Regards, mrn.

ahull Mon May 16, 2016 10:18 pm

Just thought I’d get in my probably useless tuppence worth. Surely lookup tables are typically static, and would therefore be best kept in flash rather than RAM (i.e. declared as consts rather than variables).. or have I missed the point.

robca Tue May 17, 2016 3:29 am

Andy: yes, a lookup table could be stored statically, you are right. But if you need to perform matrix math (I’m not that good at reading Octave code, so I’m not sure), it might be slow to retrieve all that data.

In any case, that code doesn’t help me any :) it’s for a periodic frequency, and designed to help locate a speaker in a room (2D space), using 2 microphones. My code works on percussive sounds, with a 4 microphone array, in full 3D, and depending on the microphone array size, can work from anything from a few cm to many km (assuming a sound loud enough to be heard clearly above the noise threshold). We are interested in completely different problems

Here’s my code. I store all my samples as 16 bit integers in an “interleaved array” (i.e. sample1_ch1, sample1_ch2, sample1_ch3, sample1_ch4, sample2_ch1, sample2_ch2, sample2_ch3, sample2_ch4, etc). So I need to traverse my array in increments of 4.

The gcc() function is a modified general cross correlation. Out of my 4 samples, one is identified as the “trigger channel” (the first microphone that receives a signal), and I always cross correlate the trigger channel with the 3 other samples. By doing so, I can cut the correlation scan to half the space (because I will never be in a case where the trigger channel will have a negative correlation value). I also only scan 1/3rd of the signal, because my trigger signal is percussive, and only the first 1/3rd of the sample contains significant information for the cross correlation.

Not sure how relevant this optimization would be in your case… otherwise you’ll have to use the general cross correlation algorithm (here’s is a version, but it’s really a pretty wasteful version http://www.jot.fm/issues/issue_2010_03/column2/)

#define SAMPLES_CH 2048 //ADC Samples per channel (each 16 bit)
#define SAMPLES_BUF SAMPLES_CH*4 //Total size of 16 bit buffer
#define SAMPLES_32 SAMPLES_CH*2 //ADC buffer (2 CH per conversion)

int32_t adcbuf32[SAMPLES_32+1]; // buffer to hold samples, ADC1 16bit, ADC2 16 bit
int16_t *adcbuf16 = (int16_t *)&adcbuf32; //same buffer, but as 16 bits

int16_t gcc(int16_t *trigger, int16_t *array2)
{
int32_t dotprod = 0;
int32_t maxv = 0;
int pos = 0;

for (int j = 0; j < SAMPLES_BUF; j+=4) // creates a sliding window (positive only, since first array is always trigger). Array is made of four 16 bits elements, need to move in 4 increments
{
dotprod = 0;
for (int i = 0; i < MIN(SAMPLES_BUF/3, SAMPLES_BUF-j); i+=4) // no need to check all samples for trigger microphone, roughly first 1/3th should be enough; stop when i+j=SAMPLES_BUF
{
dotprod += trigger[i] * array2[i+j]; //weighting function. If more than 2048 samples per channel, might overflow
}

if (dotprod > maxv)
{
maxv = dotprod;
pos = j/4;
}
}

return pos;
}

mrn Thu May 19, 2016 6:39 pm

hi!
thanks for hint (storing lookup tables), the details and the code!

– regarding the differences between the two methods:
although the pitch-dependent DoA estimation code uses only 2 mic signals, by triangulating two pairs of microphones you can get to similar representation, as with the TDOA method. The pitch is just one extra dimension, (just like a parameter,) and yes, it is redundant (useless) in case of your problem. Reason: for percussive sounds there would be no significant point in the decomposition indicating a single frequency at a single tdoa (or doa) value, but a vertical line, spreading over the whole frequency range. Of course, at the correct doa (tdoa) value.

– regarding the octave code: there are three steps:
– pairwise cross-correlation of mic-signal frames (result: vector sl),
– dot product calculation between the cross-correlation vector (sl) and the 3 lookup-tables (popiLUT_L, popiLUT_0, popiLUT_R), result : 3 matrices
– and finally summing the three dot-product-matrices, result: one single matrix (cut1) with the same resolution.
Note: there is a png image demonstrating this in the repository.

– regarding the lookup tables:
the idea to store them with the executable sounds great, however, I also have no idea how fast is it to calculate dot-products of vectors stored in the RAM and matrices stored in the flash. have to try :)
– the lookup tables can be re-calculated for any mic-distance (cm to meters, as you say) and to any pitch range (just in case one day you decide to analyze periodic signals).

cheers!

ahull Thu May 19, 2016 8:13 pm

mrn wrote:hi!
– regarding the lookup tables:
the idea to store them with the executable sounds great, however, I also have no idea how fast is it to calculate dot-products of vectors stored in the RAM and matrices stored in the flash. have to try :)
– the lookup tables can be re-calculated for any mic-distance (cm to meters, as you say) and to any pitch range (just in case one day you decide to analyze periodic signals).

cheers!

robca Fri May 20, 2016 7:04 pm

mrn wrote:
– and finally summing the three dot-product-matrices, result: one single matrix (cut1) with the same resolution.

Pito Thu Jul 13, 2017 2:57 pm

@robca – I’ve been playing with the above code you did for simult 2 ADCs, 2 channel each.. (so 4 values).
I’ve been trying with simult 2 ADCs but 1 channel per each ADC (so 2 values per sample).
I’ve done following changes:
#define CHANNELS_PER_ADC 1 // number of channels for each ADC. Must match values in ADCx_Sequence array below
#define NUM_SAMPLES 8192 // number of samples for each ADCx. Each channel will be sampled NUM_SAMPLES/CHANNELS_PER_ADC
#define SAMPLE_RATE ADC_SMPR_71_5 // when using dual mode, each pair of channels must have same rate. Here all channels have the same

uint32 adcbuf[NUM_SAMPLES+1]; // buffer to hold samples, ADC1 16bit, ADC2 16 bit
uint8 ADC1_Sequence[]={8,0,0,0,0,0}; // ADC1 channels sequence, left to right. Unused values must be 0. Note that these are ADC channels, not pins
uint8 ADC2_Sequence[]={7,0,0,0,0,0}; // ADC2 channels sequence, left to right. Unused values must be 0

zmemw16 Thu Jul 13, 2017 7:31 pm

with apologies to agatha c, but why didn’t they ask @ahull :?:

ahull Fri Jul 14, 2017 8:51 am

[zmemw16 – Thu Jul 13, 2017 7:31 pm] –
with apologies to agatha c, but why didn’t they ask @ahull :?:

Don’t ask him, he knows nothing ;)

Sign in to reply