C++ template specialization with SFR

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

Avr-libc explains how to pass an IO port as a parameter:
http://www.nongnu.org/avr-libc/u...

By declaring the parameter as

volatile uint8_t *port

one can do

*port |= mask;

.

Now I wish to have C++ templates specialized by SFR like:

typedef	volatile uint8_t	Register;

template< Register ® >
class RegBit
{
public:
    static inline void bitOn( uint8_t bitNo )
    {	reg |= _BV( bitNo );	}

    static inline void bitOff( uint8_t bitNo )
    {	reg &= ~_BV( bitNo );	}

    inline bool operator( )( uint8_t bitNo )	const
    {	return	reg & _BV( bitNo );	}
};

//...

// So I can do bit handling and other things like:
RegBit< DDRB >::bitOn( 0 );

But I see those compilation errors:

Quote:

templ.cpp: In function 'int main()':
templ.cpp:44: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
templ.cpp:44: error: `*' cannot appear in a constant-expression
templ.cpp:44: error: template argument 1 is invalid
templ.cpp:44: error: invalid type in declaration before '(' token

Changing the reference template parameter to a pointer one gives the same error.

From "avr/io.h" and its included files:

#define _SFR_IO8(io_addr) ((io_addr) + __SFR_OFFSET)

#define DDRB    _SFR_IO8(0x04)

__SFR_OFFSET is 0 or 0x20 depending on the MCU.

It would led to DDRB being replaced by the preprocessor with ((0x04) + 0x20).

So, the following two lines are equivalent:

	RegBit< *(volatile uint8_t *)((0x04) + 0x20) >::bitOn( 0 );
	RegBit< DDRB >::bitOn( 0 );

And I find out the '*' complained by the compiler.

If I do:

	RegBit< (volatile uint8_t *)((0x04) + 0x20) >::bitOn( 0 );

Compiler now says:

Quote:
templ.cpp: In function 'int main()':
templ.cpp:51: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
templ.cpp:51: error: template argument 1 is invalid
templ.cpp:51: error: invalid type in declaration before '(' token

Can anyone here help me, please???

I'm using the latest WinAVR (20081205, prior I was using 20080610).
Of course I've already tried several other things that I didn't post here, but if any aditional information could be useful, please ask.

Thanks!

[/code]

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

The template argument has to be a constant
and C++ is fussy about what it calls a constant.
io.h's definition of PORTB apparently doesn't qualify.
IIRC it involves coercing an int to a pointer.
If you try something like "extern volatile uint8_t portb;",
I think portb will be usable as a template argument.
If so, portb can be given an absolute address with a linker option.
As a check on correctness, you might want to put something like

if(&portb != &PORTB) panic();

in your code.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Just for the record something like PORTB is just a macro for:

(*(volatile uint8_t *)((0x3C) + 0x20))

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

I think I understand what is going on the compilers mind.

Quote:
a cast to a type other than an integral or enumeration type cannot appear in a constant-expression

Templates are solved in compile-time (differing from "generics" solution from interpreted languages as C#).
So, to achieve a real code generation at this time, all the template parameters, type and non-type ones, must be constant.

If I create something like:

struct UartRegs {
    char BAUDRATE;
} uart0regs = { 12 };

template
class Uart {
  public:
    Uart() {};

};

Uart uart0;

It will compile fine. No complains at all because uart0 is a reference to a statically allocated entity known at compile time.

Any SRF is:

(*(volatile uint8_t *)(mem_addr))

In the compiler cybernetic brain, what is seen is a pointer. A pointer can point to anywere, but the pointer itself can be a known place in memory, so a non-type tamplate taking a pointer is valid.

C++ compilers aren't meant to handle explicit addresses as we are used in embedded systems. Maybe it can't distinguish the #defined SFR and tries to pass a temprary pointer to the template, what is clearly not a constant-expression.

It would explain the error, but gives me no clue on how to fix or workaround it...

More ideas?
(Oh, course, thanks a lot for the help)

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

It's funny how much traffic on this board is about the parametertization of SFRs (usually C rather than C++ admittedly) but what's the ultimate aim of all this effort? - just so you can shuffle a port pin around the design at the last minute or something else? I often wonder if it's worth the effort that the 10 minutes of editing it might replace in the unlikely event of needing to make a pin assignment change would be?

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

1) Let me discuss about my newer discovers.

This snippet:

enum
{
	epcmsk0 = (int)&PCMSK0
};

gives the erros:

Quote:
Compiling: templ.cpp
templ.cpp:30: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
templ.cpp:30: error: `*' cannot appear in a constant-expression
templ.cpp:30: error: `&' cannot appear in a constant-expression

What means that the trouble is not around templates, but constant expression understanding.
Most of C++ people have already had their own nightmares about it.
It at least gives me a simpler way to investigate a solution, but it is not here right now...

2) About the last post.

I don't want to throw a long long long discussion about programming preferences, languages and so on.

I have reasons enough to believe that this development is worth enough. Having sufficient abstraction over the hardwrare have always been a dream, but it comes with no overhead. Usually it is a code+memory overhead, something impossible in reduced 8 bit MCUs.
Templating a lot of things and relying in the compiler to do its optimizations is something to investigate. If it give me a good result, I would have found a really amazing way to do the work without ugly #ifdefed code.

After the initial hard and dirty work, its utilization is relativelly simple, and will render benefits for a long time.

I hope to be able to show everyone what I am saying, but I need to make it work first...

In the last word, there's always the love for research. If after all I conclude that it wasn't worth, I will have sufficient arguments to speak about, instead of being another talker about everything I ever feared and barely know about.

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

Jaques wrote:

If I create something like:

struct UartRegs {
    char BAUDRATE;
} uart0regs = { 12 };

template
class Uart {
  public:
    Uart() {};

};

Uart uart0;

It will compile fine. No complains at all because uart0 is a reference to a statically allocated entity known at compile time.

More precisely, it's known at link time.
Likewise
extern volatile uint8_t portb;

should work if portb is given the correct address.
It can be given the correct address with a linker parameter.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

I just did what you said:

#include	

extern volatile uint8_t portb;

template< uint8_t ® >
class Template
{
public:
	Template( );
	virtual	~Template( );

    void bitOn( uint8_t bitNo )
    {	reg |= _BV( bitNo );	}
};

int	main( )
{
	Template< portb > t;

	return 0;
}

Quote:

templ.cpp: In function 'int main()':
templ.cpp:18: error: 'portb' is not a valid template argument for type 'uint8_t&' because of conflicts in cv-qualification
templ.cpp:18: error: invalid type in declaration before ';' token
t

I don't understand why should the "extern" qualifier make any difference.
This code is unable to give a "C++ defined constant-expression" to the template as before.

I think I must clarify that I plan to use the MCU register definitions that are already declared in the toolchain.
It will be quite useless if I have to write every single address for a myriad of registers in a dozen different MCUs.
My aim is to implement drivers that are (at least a bit) MCU independent. Its backend, though, must address MCU registers, so it must take its memory addresses. Having thos addresses as template parameters will let the compiler to generate code as if I was using direct register names. It won't be possible if I declare a pointer-that-takes-the-address-of-a-register.

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

Quote:

I don't understand why should the "extern" qualifier make any difference.

The term cv-qualification stands for const and volatile qualification. If you make your template parameter volatile those errors will go away. Other errors might pop up though...

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

I know 0.1 about C++, so this is just a stab at 'something'-

#include   

uint8_t test_8;
uint16_t test_16;
uint32_t test_32;

#define LED0 PORTB,3
#define LED1 PORTB,4

template< class R >
static inline void bitOn( R ®, uint8_t bitNo ) {
    reg |= (1UL<<bitNo );
}
template< class R >
static inline void bitOff( R ®, uint8_t bitNo ) {
    reg &= ~(1UL<<bitNo );
}

int main(){
    bitOn(PORTB,0);
    bitOn(test_8,1);
    bitOn(test_16,10);
    bitOn(test_32,20); 
    uint8_t temp=PINB;  
    bitOn(temp,4);
    PINB=temp;
    bitOn(LED0);
    bitOff(LED1);
}

(I just compiled it at the command line -c only, dumped the object file, and it 'looks' correct, but who knows).

edit- I was wrong, I know 0.01 about C++ (off a decimal point)

I'm probably missing the 'big picture', but it seems macros can do this just as easily.

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

I'm just guessing, but I suppose that the OP would like to have a more OO notation when manipulating port pins, eg the port is the "prime object". Apart from templates having a wee bit akward syntax, and can generate a good deaal of code bloat if not handled correctly this is something of an overkill IMHO.

You'd get just as much OO notation from defining an ordinary class for a port, have the constructor take the actual port as a parameter and then instantiate global objects for the ports you want to use. This could be done largely in a header file that you could bring from project to project.

Sketchy (leaving <<placeholders>> in this sketch for things that I'm too lazy to look up right now) ):

class IOPort
{
public:
   IOPort( <<1>> port) : m_port(port) {}
   void BitOn(int bitNumber) {port |= 1<<bitNumber;}
   void BitOff(int bitNumber) {port &= ~(1<<bitNumber);}
   bool operator( )( uint8_t bitNumber ) { return m_port & (1<<bitNo);} 
   .
   .
   .
private:
   <<1>> m_port;
}

Where <<1>> is the declaration of an IO port as described in the avr-libc documentation.

Then you'd

IOPort portb(PORTB);

in one place and then

portb.BitOn(4);

and similar.

I've deliberately left out all inlining, const-correctness etc as I'm sketching, and wouldn't get it right anywy. A real implememtation would of-course do all that by the book.

Could the OP shed some light on why he want's templates for this? Just playing around? Or?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Sorry Johan, but it don't.

Here, again, I post the complete test code:

#include	

volatile uint8_t portb;

template< volatile uint8_t ® >
class Template
{
public:
	Template( );
	virtual	~Template( );

    void bitOn( uint8_t bitNo )
    {	reg |= _BV( bitNo );	}
};

int	main( )
{
//	Template< portb > t;
	Template< DDRB > t;

	return 0;
}

And now, the same error again:

Quote:

templ.cpp:19: error: `*' cannot appear in a constant-expression
templ.cpp:19: error: template argument 1 is invalid
templ.cpp:19: error: invalid type in declaration before ';' token

Have you got to build a sample with a templated volatile arg and then instantiated it with a declared SFR? How?

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

Curt,

the example you give works, of course. And you are right, it has no much advantage over conventional C programming.

In the example, you pass a "pointer to register" to the member functions.
Your template is has a "type parameter". It means that R is an object type (a class or struct).
C++ templates are someting astonishingly powerful and full of tricks.
"Type parameters" are not the only kind allowed. C++ templates can also have "non-type parameters". It can be used in constructs like:

template< int N >
class CuriousArray
{
    char array[ N ];
}

If I proceed with the pointers like you did, I will have methods that take a pointer as parameter, and every time I call a "bitOn" method, the processor will do a register address load/store with intermediary steps to acomplish the act.
With my non-type parameter, I want to have a compiler generated code that replaces automatically the SFR references just as if we were coding #defines. No pointer indirections. The compiled code will (hopefully) be as good as the handcrafted one, but manutainability will be very improoved since I just need to keep a single definition for all the common stuff for the registers.

Annnnd... Johan...

It is somewhat of a play, but I think it will give very good results. It will be really useful if I can get it to work.

I have several situations were I have several different hardware running adapted versions of the same driver, exclusively taylored for each of those hardware.
I see no point in having to modify the driver code when the change is related not to the driver functioning but rather to the pin assignments a given engineer gave at that time.
If what I plan to do give me a good result, I will be able to completely isolate low and high level driver implementation.
Even more, up to a level, the "low level" would be reduced to a variable declaration!
The template parameters would care of fitting the register names and MCU pins for me!
It sounds pretty good to my ears to make the effort worth.

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

Jaques wrote:
And now, the same error again:

Quote:

templ.cpp:19: error: `*' cannot appear in a constant-expression
templ.cpp:19: error: template argument 1 is invalid
templ.cpp:19: error: invalid type in declaration before ';' token

Have you got to build a sample with a templated volatile arg and then instantiated it with a declared SFR? How?

No can do.
The problem is fundamental.
The SFR declarations are expressions that
C++ does not accept as template arguments.

What you are trying to do can be automated,
but you will need to write code-generation tools.

I suspect that it is not worth any effort.
I just tried something similar and avr-gcc
did not inline the member function.

You might be able to do something useful with
placement syntax and one-byte objects.
One-byte objects pretty much eliminate virtual methods.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

You are right...

I've been just doing some comparizons that led me to conclude what you're saying (I may post the results if anyone is interested in).

I created a tool that translates SFR declarations into something my templates accept, so I can now compile my test programs. Unfortunatelly, it is not right what I planed to do at first, but I guess I will not find any hidden magic, so I better keep going and don't loose more time.

In my hierarchy I will not suffer much because of virtual functions. The classes are mainly wrapper that will not have decendants.

And about those one-byte objects? I will thing about it and how could it be worth. Thanks!

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

Jaques wrote:
In my hierarchy I will not suffer much because of virtual functions. The classes are mainly wrapper that will not have decendants.
If they won't have descendants,
giving them virtual methods isn't useful.
The first virtual function will require at least 2 bytes per object.
Quote:
And about those one-byte objects? I will thing about it and how could it be worth. Thanks!

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

I guess I know a little about virtual methods to avoid using it where it is not needed, but thank you for reminding.

Now, the news...

In standard C, we would access bits like this:

DDRB &= ~_BV( 0 );
 110:	20 98       	cbi	0x04, 0	; 4
DDRB |= _BV( 0 );
 112:	20 9a       	sbi	0x04, 0	; 4

The compiler is smart enough to understand that the memory area being referenced is bit-mapped and uses cbi/sbi instructions.

Other memory areas make the compiler to generate the indirect read/modify/write sequence below:

( *( volatile uint8_t * )0x?? ) |= _BV( 0 );
       	ldd	r24, Y+1	; 0x01
       	ori	r24, 0x01	; 1
       	std	Y+1, r24	; 0x01

Consider now the templated bit handling class in the following code code:

#include	"iomxx0_1.hpp"
#include	

typedef	volatile uint8_t	Register;
typedef	Register *			Register_pointer;

template< uint8_t reg >
class RegBit
{
public:
    static inline void bitOn( uint8_t bitNo )
    {	( *( volatile uint8_t * )reg ) |= _BV( bitNo );	}

    static inline void bitOff( uint8_t bitNo )
    {	( *( volatile uint8_t * )reg ) &= ~_BV( bitNo );	}
};

int	main( )
{
	RegBit< ooregs::oo_DDRB >::bitOff( 0 );
	RegBit< ooregs::oo_DDRB >::bitOn( 0 );

	return 0;
}

where "iomxx0_1.hpp" contains the translated register addresses like "ooregs::oo_DDRB" generated by my very own tool from the original mcu include file.

The generated assembly code is:

    static inline void bitOff( uint8_t bitNo )
    {	( *( volatile uint8_t * )reg ) &= ~_BV( bitNo );	}
 114:	20 98       	cbi	0x04, 0	; 4
    static inline void bitOn( uint8_t bitNo )
    {	( *( volatile uint8_t * )reg ) |= _BV( bitNo );	}
 116:	20 9a       	sbi	0x04, 0	; 4

Just as tight as the C version!

Allright, you may wonder, but which advantages are in exchanging a simple C line of code by the class declaration and weird template sintax?

i. The weird sintax is easily cleaned up by convient typedefing. This is done throughly across all standard C++ libraries. After some time, in fact, you may even get used to it, whatever ;)

ii. As I stated before, I face a bunch of situations where the same thing cannot be the same because a given context made an engineer to take a given solution and other time another one (or the same) adopts another solution in a different board.

The classic solution to this problem is to "#define" tons of conditions and tons of macros to handle the peculiarities of each project.

This pratice is widespread known to be a harsh perspective to mantainers.
The template solution, at least for my wish, is an effort toward a more flexible solution.

Of course my simplified examples are not giving the complete scene for anyone trying to understand me, but I plan to share the evolution of my experiments so others will be able to help me too.

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

Jaques wrote:
The classic solution to this problem is to "#define" tons of conditions and tons of macros to handle the peculiarities of each project.

This pratice is widespread known to be a harsh perspective to mantainers.
The template solution, at least for my wish, is an effort toward a more flexible solution.

Show evidence.

Show evidence,

1. that your way provides more flexibility (might require to produce a definition of "flexibility" first)

2. that your kind of flexibility is needed and wanted in the majority of MCU code and projects

3. that defines lack the flexibility you want to provide

4. that defines are difficult / bad to maintain

5. that your solution is better to maintain, particular in the light of the ugly C++ template syntax and "tons of typedefs" to hide the template syntax

6. The same as 5. but taking the maintenance of your template library vs. garden-variety defines into account

7. that defines are more difficult to read and understand than your templates and typedefs

8. that flexibility is a fix for the alleged maintenance problem and the implied "defines are difficult to read" problem. Explain why orthogonal concepts like flexibility and maintenance are related in the general case and for the majority of MCU projects

9. that "C++ templates ... full of tricks" (your words) simplifies maintenance, especially in the light that it raises the competence bar for the maintainer (must be familiar with the particular tricks applied when hunting a bug which could well be because of the C++ template tricks applied)

Or in short, I think what you do is what many years ago one of my professors characterized as "academic w*nking" (yes, those were the times before political correctness). Nice to do on occasion, temporary entertaining, but otherwise doesn't serve the real purpose.

BTY: It is "syntax", not "sintax"

Stealing Proteus doesn't make you an engineer.

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

Jaques wrote:
ii. As I stated before, I face a bunch of situations where the same thing cannot be the same because a given context made an engineer to take a given solution and other time another one (or the same) adopts another solution in a different board.

The classic solution to this problem is to "#define" tons of conditions and tons of macros to handle the peculiarities of each project.

This pratice is widespread known to be a harsh perspective to mantainers.

My solution has been a text file in which such decisions are recorded:
FRED B5
GREG C4
EDDY D2

Python code would turn these into a header file with the desired #defines.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

I'd hire a hardware designer who did what he was told myself!

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

I still don't understand the advantage over macros, but I don't know anything about C++ either.

This is what I use in C-

#include 
//#include "my_hardware.h"

//--------------------------------------
//my_hardware.h
//--------------------------------------
//#ifndef __MY_HARDWARE_H_
//#define __MY_HARDWARE_H_

struct bits {
  uint8_t b0:1;
  uint8_t b1:1;
  uint8_t b2:1;
  uint8_t b3:1;
  uint8_t b4:1;
  uint8_t b5:1;
  uint8_t b6:1;
  uint8_t b7:1;
};

#define BIT(name,pin,reg) \
 ((*(volatile struct bits*)®##name).b##pin)
#define IN  PIN
#define OUT PORT
#define DIR DDR
//--------------------------------------
//all hardware described here
//define port letter and pin number
//                 port     pin
//                     \   /
//                      | |
#define LED2(reg)   BIT(D,5,reg)
#define LED1(reg)   BIT(D,1,reg)
#define SW2(reg)    BIT(D,2,reg)
//--------------------------------------
//#endif /* __MY_HARDWARE_H_ */


//--------------------------------------
//application
//--------------------------------------
int main(){
    SW2(DIR)=0;  //input
    SW2(OUT)=1; //pullup on
    LED2(DIR)=1; //output
    LED1(DIR)=1; //output  
    while(1){
        if(SW2(IN)==0){
            LED2(OUT)=1;
            LED1(OUT)=0;
        }
        else{
            LED2(OUT)=0;
            LED1(OUT)=1;
        }
    }
}

I have my fusebuster which needs to map hv programming pins on 40/28 pin sockets from a mega164 so I can hv program all dip avrs (I think there are about 7 variations). The above macros handle it all. No generating of special files, etc. I just define which port/pin is used for each signal on each avr variation. So I think I have hardware abstraction x7 on a single project.

edit- I compiled your latest version (-Os) with gcc 4.2.2 (I passed 'raw' numbers in the template- 0x23,instead of your header defined number). It decided to do a call to a function for the bitOff (bad), and used sbi for the bitOn (good). I think that is your biggest obstacle- you are (more) at the mercy of the compiler than the macro versions.

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

curtvm wrote:

...
//--------------------------------------
//all hardware described here
//define port letter and pin number
//                 port     pin
//                     \   /
//                      | |
#define LED2(reg)   BIT(D,5,reg)
#define LED1(reg)   BIT(D,1,reg)
#define SW2(reg)    BIT(D,2,reg)

For maintainability, this is the important part.
LED2 is defined once.
LED1 is defined once.
SW2 is defined once.
The other stuff, however messy, doesn't change much.
The nightmares happen when a single hardware change
entails multiple complicated changes in several files.
Even if done well, using templates will not keep people
from putting in .c files what should be in header files.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Hello!

I've been out for a while.
There are a number of things I might want to say, and I'm not sure of which of them is worth to say.

First, Skeeve is perfectly right in his las post:

skeeve wrote:

The other stuff, however messy, doesn't change much.
The nightmares happen when a single hardware change
entails multiple complicated changes in several files.
Even if done well, using templates will not keep people
from putting in .c files what should be in header files.

It is always possible to turn the entire world into a big mess if we're not working with responsability and competence.

The modern programming language features are often taylored to make good things easier and bad things more difficult.

I understand pretty well that there are "classic" ways of doing hundreds of things and that those ways have being used in embedded programming thoroughly. Most of people will never find any disadvantage in doing things this way, but it is not an excuse for not trying anything else.

It can be a better way for doing things right beside me. If I never turn my neck around, I will never know.

Now, I will take the freedom of making a comment.
In (almost) any human discussion, people tend to deviate from the main theme to emit their particular opinions with matters related to the topic, but it does not lead to the solution of the problem.

As so many people is interessed, I opened another topic were we can discuss pros and contras of each programming paradigm:

https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=73751&start=0&postdays=0&postorder=asc&highlight=

Finally, I have made progress in my efforts. Please let me organise my things and soon I will be sharing some of my thoughts... ;)

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

Jaques, it might be interesting to have a glimpse of another, similar, effort: AVR C++ Library.

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

Thanks Andreie.

I already knew this library.
Unfortunatelly I only came across it after doing a lot of work in my own code.

It is quite interesting and somewhat in the way I wanted to follow.
I also met uOS++:

http://sourceforge.net/projects/micro-os-plus/

As my brain and time allow me, I pretend test some systems based on both.

It also makes me think that I must find a time to talk to my bosses about contribuition to open source projects...

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

I've been looking for any idea on that problem. But there was no perfect idea, so i tried out this code, and it works correctly. Unfortunately it requires some variables to create(but I think compiler should optimize them, since they are used only in this template line. Maybe it could be done by macros, but I didn't check

typedef unsigned int uint;
typedef uint8_t * IOPointer;

template
class cBit{
public:
  static void on(){ *(IOPointer)T |= 1<<bit; }
  static void off(){ *(IOPointer)T &= ~(1<<bit); }
  static bool value(){ return (*(IOPointer)T) & (1<<bit); }
};

uint pTIMSK = (uint) ((IOPointer)&TIMSK);
typedef cBit timerOvf;

int main(){
	while(1){
		TIMSK = 99;
		timerOvf :: on();
		if(timerOvf::value()) timerOvf :: off();
	}	
	return 0;
}

Many people say, writing such thing for avr using c++ is pointless, but how could it be written shorter and more elegant in c:


class cInt0
{
public:
	typedef cBit state;
	typedef cBit flag;

	//*** Interrupt mode
	typedef enum {
		lowLevel =	0,
		change =	(1<<ISC00),
		fallMode =	(1<<ISC01),
		riseMode =	(1<<ISC01) | (1<<ISC00)
	} intType;

	static inline void setMode(intType mode){
		MCUCR = (MCUCR & (~((1<<ISC01)|(1<<ISC00))))  |  mode;
	}

};

typedef cInt0 myInt;

int main(){
  myInt :: setMode(myInt::lowLevel);
  myInt :: state::on();
}

And at the end ... sorry for my English ;]

I've discovered that this code isn't optimized while compiling. So I found maybe not 100% correct way but it works:
The variable eg. pTIMSK must be const, and must be declared in other file. There must be also an extern line to perform compiler 'external linkage' which it requires if we don't write it. So it would look like this:

// io_pointers.h
#include 
typedef uint8_t * IOPointer;

const unsigned int pTIMSK = (unsigned int) ((IOPointer)&TIMSK);
extern const unsigned int pTIMSK;

That code is optimized to typical bit operations which are done when we write eg. TIMSK |= 1<<3;

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

Hello people.

After a long time I have to say that my plans were minimally successful.
Please take a look at:

http://sourceforge.net/projects/...

It is really far from complete, but the ideas discussed here are already usable.

In the future I plan to "generalise" a lot of things and formalise classic structures for an OS, drivers, libraries and so.

I also plan to make things easier to others...

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

Hi Jaques,

I understand this thread may be dead, but I had the same problem and this is how I solved it...

 

// Create a map from physical pin numbers to registers for any MCU you use.
// These map may be available already to some extent so you can reuse and
// rename to your liking as necessary. Example here is for Arduino pin
// locations on an ATmega8.
#if defined __AVR_ATmega328P__
#define PORTR(P) \
((P) >= 0 && (P) <= 7 ? PORTD : (P) >= 8 && (P) <= 13 ? PORTB : PORTC)
#define DDRR(P) \
((P) >= 0 && (P) <= 7 ?  DDRD : (P) >= 8 && (P) <= 13 ?  DDRB : DDRC)
#define PINR(P) \
((P) >= 0 && (P) <= 7 ?  PIND : (P) >= 8 && (P) <= 13 ?  PINB : PINC)
#define BITN(P) \
((P) >= 0 && (P) <= 7 ?   (P) : (P) >= 8 && (P) <= 13 ? (P)-8 : (P)-14)
#elif defined __AVR_ATtiny85__
// Implement more here.
#else // UNSUPPORTED MCU.
#error "Unsupported mcu register mapping"
#endif // __AVR_ATmega328P__

// Then pass the pin numbers as template parameters.
template <uint8_t PIN>
struct PassRegInTemplate
{
  // This enum is superflous, you can use BITN(PIN) in place of BIT
  // but I like to define more specific names sometimes.
  enum PinBitNumbers
  {
    BIT = BITN(PIN)
  };

  PassRegInTemplate ()
  {
    // The following two are equivalent in produced ASM code.
    DDRR(PIN) |= 1<<BIT;
    //DDRB |= 1<<BIT;
  }
};

int main ()
{
  // Now this is the only place where you make interface changes.
  PassRegInTemplate<13> prit;
}
  

 

Last Edited: Sat. Aug 1, 2015 - 08:25 PM