c. return(adc2_value<<16 + adc1_value);
d. return adc1_value in first call and adc2_value in the second
Maple and STM32F103 dual simultaneous sampling
(71 posts) (14 voices)-
Posted 4 years ago #
-
Being an non-programmer end user, the backbones of the app are somewhat out of my scope.
As a user, I think the dual adc command should be clean - transparent to the user.
In other words, when reading an analog pin, the user should not care about how the data is received under the hood.I suggest one of two modified command options:
Option 1:
analogRead(analogPin, [Optional: ADC1 or ADC2]); In this case the pin number and ADC 1 or 2 are selected in this read command.Option 2:
pinMode(analogPin, INPUT_ANALOG[1 or 2]); In this case the input pin is pre-prepared and will always refer to the adc it was set for.As far as I know, in the STM32 dual mode, both ADC1 and ADC2 values are packed together in ADC1 register. The separation of the values from the common register should naturally be transparent to the user, and based on one of the a/m options.
samtalPosted 4 years ago # -
Okay! This is good stuff. Let me try to make a bit more progress, and see what happens.
cren
There are 2 ways I can think of doing it. Getting the function to pass back a pointer to a 2 member array, or passing pointers of where you want the data stored.
Some C API's so use the first method, but I don't like them. There is a static which the user has to remember to copy, so I prefer method 2, pass in a pointer to space that the user manages. They can choose how to do that, and it is a common C idiom.
anton
c. return(adc2_value<<16 + adc1_value);
d. return adc1_value in first call and adc2_value in the secondI am okay with that. It needs the user to unpack. While I am happy with that, but I am not a typical user. IMHO it amounts to something very similar to returning a two member struct, and the struct hides the unpacking code.
I don't understand how d works.
I assume there some API, which we haven't discussed yet, which sets up Dual ADC capture 'mode', and it keeps track of which ADC signals can be dual captured, and knows whether there is an outstanding value to be retrieved? Would you outline how it might look, i.e. give a little fragment of code (like a unit test?)samtel
As a user, I think the dual adc command should be clean - transparent to the user.
In other words, when reading an analog pin, the user should not care about how the data is received under the hood.I strongly agree with you.
analogRead(analogPin, [Optional: ADC1 or ADC2]); In this case the pin number and ADC 1 or 2 are selected in this read command.
It is enough to know that a specific ADC should be used, but it doesn't carry enough information for the system to know that a dual ADC capture is required.
A 'magic' value meaning ADC1_and_ADC2 would help, but it still needs the names of both input pins.So it could look something like:
... get the value ... = analogRead(ADC1_and_ADC2, analogPinA, analogPinB);
(I am using "ADC1_and_ADC2" just as a way to discriminate which mode we are using)
or
... get the value ... = dualAnalogRead(analogPinA, analogPinB);
(which, I am tending to prefer)Option 2:
pinMode(analogPin, INPUT_ANALOG[1 or 2]); In this case the input pin is pre-prepared and will always refer to the adc it was set for.okay, so there is some other API which needs to be described and understood.
Do you want to describe that?For now, lets explore dualAnalogRead(analogPinA, analogPinB);
and come back to these other ideas if we need to get to something better.I would make the case that dualAnalogRead preserves the spirit of the Arduino library call. It can atomically handle the whole sequence events in one call (and hence is simple to write, debug and hopefully use); it doesn't demand any previous set up.
So, retuning to the point about getting values back out of a function.
If the API call is to carry enough information to tell the library code exactly everything it needs to do the job in one function call (from the users perspective), then it could return both values. We have 4 C-like proposals:
1. return a struct,
2. return two values bit-packed into something big enough; in this case a 32-bit unsigned integer works perfectly, and would be good upto 16bit ADC's !-),
3. hand over a pointer to a user variable which the function fills in, and
4. get handed back a pointer to internal/static storage which the user must copy.there are two basic flavours of 3. it could be a struct as in 1, or a bit-packed value as in 2.
I don't know enough about the code generation in the gcc compiler, but if the compiler is smart enough there is likely little difference between 1 and 2. 1 may use more space if struct's are not packed (in which case I'd check-out bit fields, which might make 1 and 2 identical in the generated code, but with different syntax for the user)
I prefer 1 or 2 to 3 or 4. IMHO 1 and 2 are simpler, with fewer opportunities for mistakes.
So I will try to flesh out those two.So proposal 1 might look like:
// Declarations: typedef struct {uint16 adcA, adcB; } DualADC; // use: DualADC dualvalue = dualAnalogeRead(int analogPinA, int analogPinB); unit16 valA = dualValue.adcA; unit16 valB = dualValue.adcB;
and proposal 2 might look like (you could do your own unpacking too):
// Declarations: inline uint16 getADCA(uint32 dualValue) { return dualValue>>16; } inline uint16 getADCB(uint32 dualValue) { return dualValue & 0xFFFF; } // use: uint32 dualvalue = dualAnalogeRead(int analogPinA, int analogPinB); unit16 valA = getADCA(dualValue); unit16 valB = getADCB(dualValue);
Comments?
(full disclosure: I am not a member of LeafLabs staff)
Posted 4 years ago # -
In the STM32, the ADC1 is master, the ADC2 is slave.
Once initially defined, a call to a ADC1 analog channel group will automatically and simultaneously sample the corresponding ADC2 group of channels.
Once defined in the setup, it is therefore not required that the regular analogRead command will specify either ADC1 or ADC2.
In other words, a new dual ADC command needs to concentrate on the initial setup only, defining which of the secondary (ADC2) channels will correspond to the ADC1 master channel groups.
In the main loop command, all that is needed is as before: Run a regular analogRead command of one channel (arduino stile). The ADC2 will automatically follow.
Extract from ST RM0008 Reference manual, Page 208, Par. 11.9.2
Regular simultaneous mode Analog-to-digital converter (ADC) RM0008, 228/1072 Doc ID 13902 Rev 11:
11.9.2 Regular simultaneous mode
This mode is performed on a regular channel group. The source of the external trigger
comes from the regular group mux of ADC1 (selected by the EXTSEL[2:0] bits in the
ADC1_CR2 register). A simultaneous trigger is provided to the ADC2.At the end of conversion event on ADC1 or ADC2:
● A 32-bit DMA transfer request is generated (if DMA bit is set) which transfers to SRAM
the ADC1_DR 32-bit register containing the ADC2 converted data in the upper
halfword and the ADC1 converted data in the lower halfword.
● An EOC interrupt is generated (if enabled on one of the two ADC interfaces) when
ADC1/ADC2 regular channels are all converted.And one more comment to the LeafLabs people:
Your blessed work is far from complete until ALL STM32 capabilities are implemented by you or anyone capable and willing to help. As an example, the Dual ADC has a large number of additional features that are not even mentioned as options of the Maple.
Another thing is to significantly improve the UI of the Maple IDE.
Personally, I am somewhat disappointed with the limited capabilities as compared with the STM32 ARM features. The Maple was released too early and immature, but I am optimistic for the future of the concept that helps me a lot.Posted 4 years ago # -
Samtal -
In the STM32, the ADC1 is master, the ADC2 is slave.
Once initially defined, a call to a ADC1 analog channel group will automatically and simultaneously sample the corresponding ADC2 group of channels.
Okay.
Once defined in the setup, it is therefore not required that the regular analogRead command will specify either ADC1 or ADC2.
In other words, a new dual ADC command needs to concentrate on the initial setup only, defining which of the secondary (ADC2) channels will correspond to the ADC1 master channel groups.
Okay. So what does that API look like?
Then what does a program have to do to get out of that mode, and just do single samples?I think the API has to be flexible enough to allow a user to easily use the ADC's in a variety of modes.
Let me take a plausible example, and suggest what the code might look like.
Let's imagine I am trying to make a high-quality bat-call recorder which will also give me some indication (by measuring time lag) of where the bat is in relation to 3 microphones. (This is a real project I came across last year. Fascinating. Bats are very sophisticated.)
The microphones are connected to 3 ADC inputs, carefully chosen to allow dual ADC sampling. I want to use dual sampling partly to get as high a sampling rate as possible, and partly to get two position samples coincident in time (I'd like all three this way, but I'll be satisfied with two).
I think I can do this with something like:
struct {int adc1, adc2, adc3; } results[MAX]; ... loop() { for (int i=0; i<MAX; i++) { DualADC r1and2 = dualAnalogRead(adc1, adc2); results[i].adc3 = analogRead(adc3); results[i].adc1 = r1and2.adcA; results[i].adc2 = r1and2.adcB; } // other stuff to process that data }
The code would be almost identical for the alternative proposed API above.
(Warning: That code has not been compiled or tested)I was attempting to define as simple an API as practical that someone could implement.
It is certainly worth having a more sophisticated API to support the richness of STM32F ADC sampling modes, and maybe you need some of them. For anyone to have a chance of implementing the right thing, the requirements need to be clear.The RM0008 manual defines a huge set of things the STM32F can do.
The list is very long, and we need a way to prioritise the list.The general rule of thumb (Pareto principle) suggests "80% of the value is in 20% of the work, and the other 20% of the value will take 80% of the work". So it is an axiom of product development that we try to find the most valuable 80% that only costs 20%, and we don't try to reach 100%. Put another way, the cost of 100% of a product's feature set = 5 products with 80% feature sets.
IMHO, it is more helpful to start from real use case or scenario, rather than the manual.
That way there is something concrete that can be used to triage development activity.The Maple was released too early and immature, but I am optimistic for the future of the concept that helps me a lot.
IMHO, this is always going to be an issue with new Open Source projects.
These projects must go public in order to enrol support from a wider community, and encourage members of that community to support it and add value.
IMHO it is even harder for Open Source hardware because the economics of making hardware requires volume to make it affordable.HTH
(full disclosure: I am not a member of LeafLabs staff)
EDIT: the paper on bat-position is at http://physics.wm.edu/Seniorthesis/Senior%20Theses%202004/Loncich-Kristen.pdf
I think it might be fun to make some further progress on those bat experiments (if I ever get any time for fun :-)Posted 4 years ago # -
I've always looked at Arduino and Maple as "gentle" ways to get into microcontroller programming, not necessarily endpoints in themselves. The basic libraries get you up and running quickly, and work for a lot of simple applications. If you start doing more advanced projects, and want to take advantage of exotic processor-specific features, I see no problem with being forced into the datasheet and register twiddling. That the libraries are open source helps with this even more, because you can easily go see how this stuff is done.
Even with the Arduino, a lot of the more elaborate functionality ends up in third-party libraries, not part of the Arduino core itself.
Personally, I'd rather Leaf spend their time on the FPGA goodies. ;)
Posted 4 years ago # -
gbulmer
Thanks for your continued efforts.
How can we proceed from here?
As I stated, I am not in the level to build or modify the Maple IDE or compiler, but I do wish someone knowledgeable would help to develop the Maple dual ADC capability.
As to trevden's comment -
I do not agree with your opinion.
The main advantage of the Arduino / Maple concept is not that experts can do things faster and easier, but rather the fact that non-experts and armatures with very little programming knowledge can build applications that until now were not possible for them.
(Myself, I am experienced electronic hardware engineer and applications developer / inventor, but not a software expert, and the Arduino concept is for me a significant advantage)
It is a necessary step towards the time when everyone will be capable of designing and building basic electronics.
using C (or C++, or C#) is not the solution.
Experts would rather use professional development boards with full programming (C, C#, C++, .NET. Linux, Unix, VB, assembler….), where every available command can be implemented.
I therefore expect LeafLab's supporters to embed even the more sophisticated features into the simple IDE / compiler for the users of low level programming capabilities, thereby making it available to all.Posted 4 years ago # -
samtal - I sorry to say I am too busy too help. It is functionality which I have on future plans, but I have a lot of work to do to get there. Also, my plans do change.
There is another piece of information that someone implementing this might need to know, which is how frequently must it take the samples? That would help choose between DMA or code.
My plan will be to code it, so it will be similar to the existing analogRead.
I guess we could consolidate all the manual/datasheet registers and values to configure the ADC's onto the wiki. That'll save hours of work, and give folks a chance to check they are correct (many hours of work).
I've created a place holder which refers back here at:
http://wiki.leaflabs.com/index.php?title=Dual_Simultaneous_SamplingPosted 4 years ago # -
Hey Guys,
I need this feature as well, I need to use DUAL SYNC sampling and hopefully the DMA As well. I get why its not implemented on Maple; because for most uses it's not needed at all. gbulmer I was wondering if you could share the work you have done so I can base what I do off of that. hopefully with the help of everybody I would be able to get it to work. I'm not sure if I would be able to integrate with wirish... i was hoping to work with using libmaple.
thanks,
oh yea any pointers would be appreciated :)
Posted 4 years ago # -
I would implement it as part of a larger framework that includes DMA and DMA Buffering:
I have not included the return types as this is just a sketch
The idea is to move the non mission critical code to places where the class dereferncing doesnt have speed impact:
class HardwareResourceParams
{
public:
HardwareResourceParams();};
class HardwareResource
{
public:
Start();
Stop();
Setup(HardwareResourceParams * params);};
class DMABuffer
{};
// There may be multiple ways of implementing DMA for variouss purposes
class DMABase : public HardwareResource
{
public:
DMABase();buffer
};
// There may be multiple ways of implementing DMA for variouss purposes
// Class implementing DMA Reading with double bouffering
class DMAReader : public HardwareResource
{
public:
DMAReader();int read(void * buffer, int n);
buffer
};
// Class implementing DMA Writing with double bouffering
class DMAWriter : public HardwareResource
{
public:
DMAWriter();int write(void * buffer, int n);
DMABuffer buffer;
};
And finally we have:
class ADC : public HardwareResource
{
public:
ADC(){ // }
Setup(...);// read a single word
int Read();
};
class DAC : public HardwareResource
{
public:
DAC(){ // }
Setup(...);// Send a single word
Write(int data);
};
with usage such as
DACParams dacParams;
DAC dac1;
DMAWriterParams dmaWriterParmas;
DMAWriter dmaWriter;
bool sendnext = false;void onDMABufferSent()
{
sendnext = true;
}void setup()
{
dmaWriterParmas.bufferSize = 128;
dmaWriterParmas.bits = 16;
dmaWriterParmas.onBufferSent = onDMABufferSent;
dmaWriter.Setup(dmaWriterParmas);dacParams.port = 1;
dacParams.bits = 8;
dacParams.enableDMA = true;
dacParams.dmaWriter = &dmaWriter;dac1.Setup(dacParams);
}void loop()
{
if (ready)
{
ready = false;
dmaWriter.write( ...... );
}
}
This is a very very sketch nevertheless the most felxible IMHO. might have some overhead but the DMA would take care of the real time implications.
Posted 4 years ago # -
Hi newyorkbrass,
Welcome to the forum!
One request: can you (and others as well) please refrain from pasting long code listings into the forum? They make it harder to follow the thread of discussion and are generally unsightly. Small snippets are OK, but longer things like the above post should be put somewhere else and linked to.
There are a variety of alternatives that buy you syntax highlighting etc., pastebin being among the most popular:
Alternatively, GitHub Gists ( https://gist.github.com/ ) are pretty nice. You can post them anonymously, but they really shine if you have a GitHub account, since even though they're easy to use (just copy, paste, submit), they're actually full git repositories behind the scenes, so you can keep updating your code as the discussion goes on. I put your sketch (after a couple of minor syntax error fixups) into a Gist as an example:
Posted 4 years ago # -
mbolivar - thanks for the gist tip.
Might it be worthwhile using the forum topic id to be part of the gist file name, then folks could 'index' into the forum using likely looking gist code snippets?
Posted 4 years ago # -
gbulmer,
How about including a link to the forum post in the gist? I've done that.
By the way, I put it up anonymously, so anyone can edit it.
Posted 4 years ago # -
How about including a link to the forum post in the gist? I've done that.
Yup! That works,
I hoped there might be some filtering/sorting on file name, which might be tidy. But anyway back from gist to here helps.Posted 4 years ago # -
I put your sketch (after a couple of minor syntax error fixups) into a Gist as an example:
Use case? :)
I think top down...
Posted 4 years ago #
Reply »
You must log in to post.