EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

C++ hardware library for (small) 32-bit micro-controllers

Started by Wouter van Ooijen December 4, 2013
On 08/12/13 10:50, Wouter van Ooijen wrote:
>> 1) what needs to be done at the hardware level, e.g. turn LED on/off, >> sleep, write text, draw rectangle, PWM an output to control a motor, >> read an ADC > > Sorry, forgot to ask. The above reads as a small part of a wish list.
Not a wishlist, merely the things I happened to think of while typing :)
> And probably the hardest part: I am especially looking for good interfaces.
Is the interface the 'lowest common denominator' or 'highest common multiple' of each class of device, e.g. ADC, or sleep, or ... In other words, how do you choose to disappoint your user :) No, I don't expect an ADC to be have the same interface as a display :)
On 08/12/13 10:50, Wouter van Ooijen wrote:
> And probably the hardest part: I am especially looking for good interfaces.
Considering the GUI part only... Would the interface allow me to easily create, say, a "soft oscilloscope", i.e. the equivalent of this "hard oscilloscope": http://www.surplusa2z.com/60311.JPG That includes: - drawing graticule - drawing textual annotations - drawing individual "random" pixels of different colour and brightness - drawing soft buttons (plus notifying my code when button clicked) I am *not* in the market for such an interface!
Wouter van Ooijen <wouter@voti.nl> writes:

>>> I am working on a portable C++ hardware library >> >> 1. Portable hardware is myth. > > I have a lot of hardware that I can carry if I want to :) > > Seriously (I am not sure you are, but I'll be), a lot of > hardware-related code can be portable, except for the frustrating > aspect of accessing the I/O pins (and dealing with timing). > >> 2. Universal solutions and modular designs don't work. > > I don't think any comment is needed. > >> 3. Trying to cover everything instead of doing particular task is waste >> of time and effort. > > First part is true, second part is nonsense, otherwise no libraries > would exist or be used. Library design is the art of balancing between > doing everything and doing a specific task well. > >>> So far I have found nice and efficient abstractions for I/O pins and I/O >>> ports >> >> Useless. Abstract not I/O operation but function that it does. >> >> #define RED_LED_ON SETBIT(PORTA, 7) > > I've been there, check for instance http://www.voti.nl/rfm73/. It > works up to a point. It runs into problems > > - when setting a pin involves more than a simple operation (BTW, PORTA > hints that you use a PIC, in which case this is plain wrong due to the > RMW problem!) > > - when you need more than one instance of your library (like > interfacing to two identical radio modules) > > - it uses macro's, which are evil (according to some they are THE > evil)
So far I have indeed done this using C macros. I can define them using standardized names so I can end up doing /* this header specific to each target architecture */ #include "pio.h" /* now can use generic versions of commands */ #define RED_LED PIO(A,1) #define SERIAL_DATA PIO(A,2) pio_out(RED_LED, 1); pio_dd(SERIAL_DATA, 0); /* bidirectional data pin */ ...And so forth. When it involves more than a simple operation, I find you end up having to do things in an application-specific way anyway. So to take your example of an SPI driven I/O expander, usually I would update this periodically rather than every time the application writes a bit. Yes, your mileage may vary but that is the point, you are likely to end up having to rewrite it each time in reality. [...]
> The two styles I mention (static class templates and (traditional) > classes with virtual functions) are both needed to get a balance > between (code and data) size and run-time flexibility.
I found it an interesting idea, and thanks for posting it. But a bit daunting to comprehend unless one is very well versed in c++ template metaprogramming, which I am not. I am reading the latest "The c++ programming language" (c++11 based). So I will look again after that. -- John Devereux
>> For instance, my A/D interface is (omitting a few details) >> >> template< int n_bits > > > What if n_bits doesn't fit in an "int" (i.e., the data type returned > by your ad_get() method)?
An A/D converter that returns more that 31 bits? IMHO a merger between Google and Microsoft next year is more realisitic! That aside, I think that could be solved with template specialization.
> What if n_bits *varies* during the course of execution?
That falls under the 'no abstraction can cover 100%'. A separate (upwards compatible) abstraction could cover that, but IMO it is not worth it to trouble the users of the most frequnet case with that option. Note that using just a few resolutions is covered by the abstraction if the A/D converter offers the template itself instead of one instantation.
> What if my converter technology takes a long time to > come up with a result? Does your ad_get() *block* > while waiting for the converter to yield its result?
Yes, this one does.
> How do I (developer) ensure my application isn;t > penalized by using your ADC interface (i.e., do > I have to rewrite it so that it suspends the invoking > task until the ADC result is available thereby allowing > other tasks to continue executing "while waiting"?
No, that is part of the details I left out.
> What if I don't run an MTOS/RTOS? > > I.e., has the presence of your library -- and the framework > it imposes/suggests -- forced me to compromise how I would > otherwise approach a problem?
Yes, always. That can not be avoided. But it does not force you to use the abstraction for the A/D because you use the abstraction of let's say the LCD. When you want to use the hardware to its limiot youn will nearly always have to go 'native'. But it is not often that a design uses all its hardware to the limit.
> Note, I'm not saying it does -- or doesn't. Rather, trying > to point out how hardware variations and exploits can complicate > trying to cram those abstractions into a generic wrapper. > (An ADC is an ADC, right?)
Anyway: thanks for a few new ideas - this is the sort of discussion I was hoping for. Wouter
On 08/12/13 09:52, Tom Gardner wrote:
> Presume you complete that proposal to your satisfaction. > > When I start new designs, my thought processes are along the lines of: > 1) what needs to be done at the hardware level, e.g. turn LED on/off, > sleep, write text, draw rectangle, PWM an output to control a motor, > read an ADC > 2) what is the level of abstraction that I want to use in my > application, e.g. setMotorSpeed, illuminateTarget, measureTemperature > displayTemperature > 3) what hardware do I have to use, both individual devices and > /combinations/ of devices > 4) what documentation, code examples and libraries are available > that I can more or less cut-and-paste into my code > 5) does the library contain complex algorithms or is it merely a > indirect access to the peripherals
Forgot to add: if I use another library/API, can I predict whether your library will/won't destructively interfere with the other libraries? Last time I used C++, which was a /long/ time ago, that was a serious concern. Another point: will your library work with my C++ compiler? Given the evident difficulty of producing /complete/ (and hopefully correct!) C++ compilers, that's not a theoretical concern.
Hi Wouter,

On 12/8/2013 7:29 AM, Wouter van Ooijen wrote:
>>> For instance, my A/D interface is (omitting a few details) >>> >>> template< int n_bits > >> >> What if n_bits doesn't fit in an "int" (i.e., the data type returned >> by your ad_get() method)? > > An A/D converter that returns more that 31 bits? IMHO a merger between > Google and Microsoft next year is more realisitic!
That's not what your code says! static int ad_get(); I assumed your choice of "int" -- instead of "long" -- was deliberate; to allow the library to use the preferred word size of the machine instead of forcing a 32b result for ad_get(). BTW, 24b ADC's are widely available; 32b devices are also available: <http://www.esstech.com/PDF/Sabre32%20ADC%20Series%20PF%20111222.pdf> There's a fair bit of hand-waving, there, but you *can* get a 32b result! I guess Google *and* Microsoft ARE merging!! :> Poor google... And with integration, there's no theoretical limit (though there are practical ones).
> That aside, I think that could be solved with template specialization. > >> What if n_bits *varies* during the course of execution? > > That falls under the 'no abstraction can cover 100%'. A separate > (upwards compatible) abstraction could cover that, but IMO it is not > worth it to trouble the users of the most frequnet case with that option.
But you're going to find lots of cases where your abstractions don't fit. Serial ports that have different modem control signals available (from one port to the next), serial ports that are send-only, PWM's with gating functions -- or without, dividers that have minimum and maximum values that aren't '0' and INTMAX, etc.
> Note that using just a few resolutions is covered by the abstraction if > the A/D converter offers the template itself instead of one instantation. > >> What if my converter technology takes a long time to >> come up with a result? Does your ad_get() *block* >> while waiting for the converter to yield its result? > > Yes, this one does. > >> How do I (developer) ensure my application isn;t >> penalized by using your ADC interface (i.e., do >> I have to rewrite it so that it suspends the invoking >> task until the ADC result is available thereby allowing >> other tasks to continue executing "while waiting"? > > No, that is part of the details I left out. > >> What if I don't run an MTOS/RTOS? >> >> I.e., has the presence of your library -- and the framework >> it imposes/suggests -- forced me to compromise how I would >> otherwise approach a problem? > > Yes, always. That can not be avoided. But it does not force you to use > the abstraction for the A/D because you use the abstraction of let's say
What if the LCD and ADC use some common piece of hardware? E.g., a simplex LCD where the backglass frequency is derived from a timer that also runs the converter?
> the LCD. When you want to use the hardware to its limiot youn will > nearly always have to go 'native'. But it is not often that a design > uses all its hardware to the limit.
I beg to differ. Especially on resource starved designs, the available hardware resources find themselves exploited heavily. "I can use this spare counter as an edge triggered IRQ input" "I can use this spare DIO as a flow control signal for the UART" etc.
>> Note, I'm not saying it does -- or doesn't. Rather, trying >> to point out how hardware variations and exploits can complicate >> trying to cram those abstractions into a generic wrapper. >> (An ADC is an ADC, right?) > > Anyway: thanks for a few new ideas - this is the sort of discussion I > was hoping for.
Gold luck! --don
Hi Dimiter,

On 12/8/2013 5:15 AM, dp wrote:
> On Sunday, December 8, 2013 1:55:59 PM UTC+2, Don Y wrote:
>> [We've finally touched 0C!] > > We got switched from a mild autumn into a harsh winter > overnight by the end of November... It is not a worst case > winter yet (-10 to -20) but way harsher than last year.
Yeah, but you *expect* The Cold! <grin> (I actually miss it -- when I am prepared for it! -- especially the fruits)
>> Fitting the tool to its user is the key. If you intend a tool to be >> used in a different way than the user will *want* to use it, you've >> got an impedance mismatch :> > > Yes, this is a major part of it. Then when the tool/author combination > gets around 20 years old more effects can be observed, too. > The most obvious one being the fact that you keep on developing > the tools/language to suit what you need; I have been lucky enough to not > have to throw away much if anything written so far so things do pile up.
I tend not to reuse code as much as *designs*. "How did I do this the last time" instead of "where is the *code* I wrote last time" And, more importantly, what did I *learn* the last time so that I can make improvements *this* time.
> A may be less obvious one is that having to maintain/remember all > that stuff you wrote last 20 years all the time (a few tens of megabytes > of sources, about 1.5M lines (non multilined as C :-) in my case) > tends to keep you alert and in good shape; I am not sure if this is > any less important than anything else really.
"Inertia". Keeps you from straying too far, too fast! :> [I actually *may*, soon, have the workspaces cleared off enough to send you some pix. Of course, "cleared off" is not a stable state -- I expect things to change almost instantaneously thereafter! <frown>]
On 12/8/2013 9:37 AM, Don Y wrote:
> Gold luck!
(not to be confused with SILVER luck! :< )
>> The two styles I mention (static class templates and (traditional) >> classes with virtual functions) are both needed to get a balance >> between (code and data) size and run-time flexibility. > > I found it an interesting idea, and thanks for posting it. > > But a bit daunting to comprehend unless one is very well versed in c++ > template metaprogramming, which I am not. I am reading the latest "The > c++ programming language" (c++11 based). So I will look again after > that.
There is very little metaprogramming involved in th basics. My interface for an open-collector/drain input/output pin is (leaving out initialization and type identifification) just struct pin_out { static void set( bool ); static bool get(); }; Again leaving out some details, the template for a PCF8574 (8-bit I2C I/O expander) takes 2 such pins (SCL and SDA) and provides 8 such pins. template< class scl, class sda > struct pcf8574 { typedef ... pin0; ... typedef ... pin7; }; So if I want to connect one PCF8574 to two pins of the micro-controller, and and a second one to two pins of the first one, I declare typedef pcf8574< target::pin_0_4, target::pin_0_5 > chip1; typedef pcf8574< chip1::pin0, chip1::pin1 > chip2; now I can use the pins on chip2 ( eg. chip::pin0::set(1) ) and each use will be written to the pin via the cascaded I2C busses. No metaprogramming, just straightforward templates. Wouter
> Question for a library implementer developing a > library that will work with more than one device: > "does your library library expose the union or > intersection of all the devices' capabilities?"
For me: definitely intersection, and probably a lot less than that. Wouter

The 2024 Embedded Online Conference