ATmega48 & C++ & Interrupts

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

Hi,

I'm currently in the process of refactoring a C project to C++ code and I was curious if anyone knew how to use interrupt service routines in C++. I have a few ideas, but none are ideal.

Thanks.
-Migit

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

best to just leave them as they are.

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

Quote:
I was curious if anyone knew how to use interrupt service routines in C++.

The same way that you use them in C.

Regards,
Steve A.

The Board helps those that help themselves.

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

Ok.
I was hoping to avoid using global methods and have the interrupts in a class, but I suppose I can just use function pointers to call methods inside the corresponding class instead of having all the code in the global method.

If there is a better way though, I'm looking for ideas =)

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

Quote:
I suppose I can just use function pointers to call methods inside the corresponding class instead of having all the code in the global method.

This is not a good method, even in C. The ISR should simply register in a flag that the interrupt happened, then let the object check that flag and act when it is set.

Regards,
Steve A.

The Board helps those that help themselves.

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

A function call in an ISR kills any hope you have of making it fast. I support the consensus, it is best to do it the same way as C ( at least with the current ISR implementation ). What I have done, however, is to write static class methods that I can use in an interrupt. If the method is short and - being class static - does not depend on object values, it is typically inlined, avoiding the function call overhead, simplifying the ISR code, and keeping code factoring more modular. The static call is ugly, and you have to know the precise class, but that can be handled with an inline static function template ( global ) to wrap the call and allow the compiler to extract the class type.

It's only a partial solution, but it is something.

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

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

Time for my bi-yearly C++ rant. Note: I write this not to convince the die-hard C++ fanboyz, but to give the borderline C <-> C++ users some food for thought, if they care to think on their own, instead of blindly following the party line.

After all those wrappers and "typically inlining" (read: hoping for the compiler to get it right), ugly (why ugly?) invocation of static functions, and using templates as glorious macros, one ends up with ...

drumm rolls ...

just a fscking interrupt service handler, setting a flag or two.

Congratulations, one managed to name a method Interrupt::HandlerForVector(), instead of having a special function ISR(vector). No, wait, one has both Interrupt::HandlerForVector() and ISR(vector) now. Great achievement - not.

Significant increase in maintainability - not.
Significant increase in simplicity (KISS) - not.
Significant increase in toolchain simplicity - not.

This C++ stuff is all just wanking about being able to get an improper tool to almost work right - instead of just using an appropriate tool.

The nature of an interrupt happening is such that it is not associated with a particular software object (object = instance of a class), and desperately trying to associate it with one is a typical mistake in OOP. The interrupt happens outside the context of an object. It is like an act of $DEITY, hitting ones system unpredictably, not a nicely timed and arranged message to an object.

To do it right one would have to model the whole processor and its components as objects, and have a hardware adaptation layer dispatching hardware events, like interrupts and input values changing, to corresponding objects. And then one could do some serious OOP, e.g.

* letting a pin object message a handler object, that expressed interest in the information, that a pin change interrupt happened,

* which then e.g. messages a timer object that the timer needs to be reset,

* which e.g. then in turn messages the display proxy object that the display needs updating,

* which in turn fires off messages to port and pin objects to change output pins.

But good luck with squeezing that into a small MCU. The limited resources of a small MCU tyically don't allow for a thorough OO abstraction of the MCU.

So one ends up where one always ends up with C++ on small MCUs. With a half-arse OOA, where one just uses some syntactic C++ sugar to wrap existing non-OO architecture in pseudo objects, like USART or timer objects. But one still invokes the methods of these pseudo objects in a procedural manner. The typical main loop in a C++ MCU application is a typical example of failed OOD/OOA - a procedure to call procedures.

Despite all the claims one can not harvest the power of OOD/OOA, because the need to tightly couple an application in a small MCU to the MCU's non-OO hardware means the non-OO hardware architecture heavily blends trough into your software. This means you have to heavily compromise on the OOA. Call it an impedance mismatch of given hardware architecture and desired architecture.

Stealing Proteus doesn't make you an engineer.

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

Quote:

is all just wanking about

Don't be coy - tell us what you REALLY think :lol:

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

Na, then I'd have to suggest to perform physically impossible acts, and someone might get hurt trying to perform them.

Stealing Proteus doesn't make you an engineer.

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

Quote:
Despite all the claims[,] one can not harvest the power of OOD/OOA, because the need to tightly couple an application in a small MCU to the MCU's non-OO hardware means the non-OO hardware architecture heavily blends trough into your software.

First off, I did respond that in most cases I would do things in the traditional way... at the moment. I would, however, have to say that I disagree on some particular points. The problem with your argument is that you state that no ISR should be at the mercy of an object. Yes, exactly... but that is neither required, nor implicit in the desire to make it a part of a class. The whole point of class static members is that they are related to the class, but separate from the objects. Constructing a class that contained the ISRs for any particular peripheral as static methods is fully in keeping with the function and invocation pattern of an interrupt. There are some limitations on the method, of course: it cannot take any parameters, and it must not return a value... just like the function created by the ISR macro. Right now avr-gcc allows you to bind an ISR to a class static method ( I don't remember the syntax right now, I don't use it ) and it does allow for ISR definition within a class. It works just fine, but it does limit the usefulness of OOA because the function is created and bound regardless of object creation. That means that you can create your class and have your abstraction in place, but you are not able to inherit from that class and change the ISR code. It works fine if all you want to change is the object functionality but ISRs are created, as I said, regardless.

There would be ways of easing this issue, and perhaps it would be useful to do so, though I rarely find it so pressing that I would be inclined to work on it myself. It may have been one of my overly dramatic moments when I referred to static method calls as 'ugly'. Certainly they are a feature that C programmers will not simply understand on sight - some C++ programmers may not. They also, as I said, are tied to the class name and, thus, create another place that needs to be edited when the implementation name is changed, for instance by inheritance. A template function handles that quickly and easily and gets rid of the somewhat specialized static method call in one fell swoop. And yes, it is a glorified macro... with static type checking and type computation capabilities. What's wrong with glorified macros?

I feel that I have no reason to push the use of C++ or OO practices on microcontrollers, those that want them will find them. Nevertheless, I feel entirely comfortable arguing for their correctness. By design, C++ has the ability to represent both object bound and global data and functionality without losing the semantic connectedness of the module. In a weaker ( or perhaps less visible ) way, other standard OO languages ( like Java ) have the same capabilities. Does that mean that everyone should jump on the C++ bandwagon to gain the advantages of true enlightenment? of course not. OO is useful in certain places just as standard C is, and asm is. In the realm of embedded systems, it seems strange to constrain the designer to just one paradigm as flexibility is the ultimate tool when trying to optimize a design for maximum functionality in the smallest footprint.

Perhaps I fall into the borderline C <=> C++ class that you described as opposed to the C++ fanboyz class, as I always find myself questioning my use of C++ in any design. Do I need the abstraction? or am I just trying to be 'correct'. Is this going to add overhead to the code or the executable that is going to cause a problem? Questioning the implementation both before coding starts and as it progresses should be a part of any serious software project, especially one as tightly constrained as embedded systems. Blindly following the party line ( regardless of which party and what line ) is sure to lead to a non-optimal result in some way. If that is simply that it takes fifteen times as long to write and is impossible to debug than it hardly matters if the code is as fast as it can be. In the long run, it simply won't be viable code for an evolving product.

That said, back to the primary thrust of the OP. Do your ISRs the standard C way... unless you don't.

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

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

ArnoldB wrote:

To do it right one would have to model the whole processor and its components as objects, and have a hardware adaptation layer dispatching hardware events, like interrupts and input values changing, to corresponding objects. And then one could do some serious OOP, e.g.

* letting a pin object message a handler object, that expressed interest in the information, that a pin change interrupt happened,

* which then e.g. messages a timer object that the timer needs to be reset,

* which e.g. then in turn messages the display proxy object that the display needs updating,

* which in turn fires off messages to port and pin objects to change output pins.

But good luck with squeezing that into a small MCU. The limited resources of a small MCU tyically don't allow for a thorough OO abstraction of the MCU.

So one ends up where one always ends up with C++ on small MCUs. With a half-arse OOA, where one just uses some syntactic C++ sugar to wrap existing non-OO architecture in pseudo objects, like USART or timer objects. But one still invokes the methods of these pseudo objects in a procedural manner. The typical main loop in a C++ MCU application is a typical example of failed OOD/OOA - a procedure to call procedures.

Despite all the claims one can not harvest the power of OOD/OOA, because the need to tightly couple an application in a small MCU to the MCU's non-OO hardware means the non-OO hardware architecture heavily blends trough into your software. This means you have to heavily compromise on the OOA. Call it an impedance mismatch of given hardware architecture and desired architecture.

Did I mention this is for a research project to increase portability of microprocessor code? I'll admit that the majority of my uC coding has been done in assembly so this is most definitely a learning experience for myself and my teammate.

The entire point of this project is to completely abstract the processor/board logic out of the guts of the program so if any single part on the board changes, there is only a small, defined section that will be effected. Also, if all the processor specific code is in it's own defined section and the processor itself is discontinued, we won't have to completely re-write the software. We know this is going to increase the overhead (though we aren't sure by how much yet), but we aren't 100% set on using C++ yet. Using C++ objects just seems like the ideal solution for the job. The fastest? No, but it will help us achieve our goals to make the code more portable.

We aren't going to abstract every aspect of the uC into a class, however, we are going to extract all parts that our program requires (which is relatively small). The current implementation of the code is in C and takes up about half the flash memory on the processor so we have some room to work with.

I'm always open to ideas, and that's mainly why I posted here.

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

I may be putting words into his mouth ( if so I apologize ) but I believe that ArnoldB would argue that interrupt handling would already have portability 'issues' due to their hardware tie. My take on this is that the defined requirements are not well enough defined ( at least as far as what has been stated ). You mention that not everything will be abstracted, only what is important to the application: what does that consist of? how abstract need things be? What is it that is expected to be non-portable. Most pure C applications that are well designed have only a few, modular, non-portable sections. Is it possible to simply pull all these functions into a single implementation file and write new implementations to create a new port of the code if it does need to change.

Because there is no standard way to handle interrupts in C ( and thus C++ ) it will be a point of non-portability regardless. Perhaps pure C is your best bet, maybe the abstractive power of C++ will come in handy, maybe some sort of preprocessor is the best solution. We would really need to know more about the extent of what you are trying to accomplish to help more.

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

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

Quote:

Did I mention this is for a research project to increase portability of microprocessor code?

Like Java? But the problem with MCUs rather than MPUs is that 90% of what's done is tied to specific hardware blocks that differ from one MCU to another. It'd be very difficult to abstract those without losing a lot of MCU specific functionality.

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

If an old dog screams bloody murder at the mention of C++ on a microcontroller, it's probably just his way of justifying his lazy unwillingness to learn it.

And all this parroting of Keep Your ISR Short is rather newbie-esque and therefore simpleminded. In reality, you can do everything you want to in an ISR, take as long as you want, provided that what you want really meets the requirements.

Clearly this assignment is an exercise in abstraction and portability. These are admirable goals and I would be interested in how successful the effort proves out.

C: i = "told you so";

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

Quote:

If an old dog screams bloody murder at the mention of C++ on a microcontroller, it's probably just his way of justifying his lazy unwillingness to learn it.
Wrong, young grasshopper, wrong. I learned C++ two decades ago, and use it ever since - when it makes sense.

I remember the times when the compiler was still called Cfront, and was in fact a transpiler C++ -> C, and so slow you almost could have lunch while it was compiling a Hello World C++ program. I remember the times when Glockenspiel was the first Cfront port to a PC, and later when the first companies managed to deliver nearly working C++ compilers no longer based on Cfront.

I lived through the times when one had to take side in the OO methodologist war, or one wasn't considered a real OO programmer (Booch, Rumbaugh, or Jacobson, being the main choices, what later became The Three Amigos at Rational). I mentioned it because while I was actually a Booch guy, I got myself some OO training from Jacobsen himself, just for diversity. I didn't know then that Jacobson came from the hardware area. Part of the training was he explained how he sliced up the hardware of some telecommunication system into object-like stuff to get a handle on the complexity. And I remember I was actually grateful for this point of view, because he didn't use yet another fscking ATM design example.

Granted, I missed out on the times when C++ was still called C with Classes, but I have enough experience with C++ to avoid it where it doesn't make sense. And the current project I am paid for happens to be written in C++. Not on an AVR, but it is nevertheless horrible C++, with such wonderful single classes containing almost hundred methods, most of them static. Well, I get money to mop it up.

Stealing Proteus doesn't make you an engineer.

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

I meant applying C++ and OOP to embedded systems. Just because you haven't figured out how to do it well doesn't mean it can't be done.

Your statement implies you've decided it isn't worth your time to investigate. Nothing wrong with that, but the young'uns may well come along and eat your lunch.

C: i = "told you so";

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

cpluscon wrote:
Just because you haven't figured out how to do it well doesn't mean it can't be done.
It doesn't matter if I haven't figured out how to do it. In my experience those claiming they figured it out can't deliver.

Stealing Proteus doesn't make you an engineer.

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

I am not OOA/OOD/OOP guru but ...

"particular software object" receives (and reacts on) messages from other objects.
Can't any of these objects be "hardware object"? Why?
Moreover - look on whole firmware - all external messages are hardware generated.
"high level" objects inside firmware receive only "software" messages from "low level" objects and "low level" objects communicate with hardware. But all of them are objects.

I use following style

// from i2c.h
ISR(TWI_vect);

class i2c_t {
    friend void TWI_vect();
public:
    // ...
private:
    // ...
    uint8_t *bufptr;
    uint8_t left;
    uint8_t sla;
    uint8_t suba[2];
    uint8_t suba_len;

    inline void isr();
};

extern i2c_t i2c;
// from i2c.cpp
i2c_t i2c;
//...

ISR(TWI_vect)
{
    i2c.isr();
}

wbr, ReAl

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
ISR(TWI_vect) 
{ 
    i2c.isr(); 
}

Can you post the .lss for that? Does it not PUSH/POP the entire machine context for a hugely inefficient MCU software design? Or does the presence of the handler in the same file as the ISR() lead to the PUSH/POPs being limited to only the registers used?

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

clawson wrote:
Does it not PUSH/POP the entire machine context for a hugely inefficient MCU software design? Or does the presence of the handler in the same file as the ISR() lead to the PUSH/POPs being limited to only the registers used?
Yes, ISR(TWI_vect) is placed in file i2c.cpp together with all i2c_t member functions. Although i2c_t::isr() is inline function it is not needed for class clients and also placed in i2c.cpp file.

So, compiler can inline i2c_t::isr() into ISR() body and only really used registers are saved/restored.

void i2c_t::isr()
{
    bool done = false;
    const uint8_t twcr_default = (1 << TWEN) | (1 << TWIE) | (1 << TWINT);
    uint8_t twi_sr = TWSR & TW_STATUS_MASK;

    switch (twi_sr) {

ATmega168 code

00002d7e <__vector_24>:
    2d7e:       1f 92           push    r1
    2d80:       0f 92           push    r0
    2d82:       0f b6           in      r0, 0x3f
    2d84:       0f 92           push    r0
    2d86:       11 24           eor     r1, r1
    2d88:       8f 93           push    r24
    2d8a:       ef 93           push    r30
    2d8c:       ff 93           push    r31
    2d8e:       80 91 b9 00     lds     r24, 0x00B9  ; read and mask TWI status
    2d92:       88 7f           andi    r24, 0xF8
    2d94:       88 32           cpi     r24, 0x28
    2d96:       b9 f0           breq    .+46

wbr, ReAl

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

Oh then that's fine - it's just whenever one sees what is apparently a function call in an ISR() then it always looks a bit suspicious. But as long as the compiler can inline it there's no problem.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
// from i2c.h 
ISR(TWI_vect); 

I see no reason to put this in the .h file. The only thing that will be calling this function is the AVR itself, so no other module needs to see that it exists.

    friend void TWI_vect(); 

Does this do anything? There is no function called "TWI_vect()", ISR(TWI_vect) resolves to "__vector_XX".

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
There is no function called "TWI_vect()"
But you can write it this way, because ...
Koshchi wrote:
ISR(TWI_vect) resolves to "__vector_XX".
... the same macros resolve "TWI_vect()" to "__vector_XX()".

Stefan Ernst

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

Koshchi wrote:

// from i2c.h 
ISR(TWI_vect); 

I see no reason to put this in the .h file. The only thing that will be calling this function is the AVR itself, so no other module needs to see that it exists.

It's mandatory forward declaration for extern "C" linkage specifier. Without the declaration compilation will fail:

==== Compiling i2c.cpp
i2c.h:56: error: previous declaration of ‘void __vector_24()’ with ‘C++’ linkage
i2c.cpp:156: error: conflicts with new declaration with ‘C’ linkage

The simplest way to do right forward declaration is usage of the same ISR() macro.

sternst wrote:
But you can write it this way, because ...
Koshchi wrote:
ISR(TWI_vect) resolves to "__vector_XX".
... the same macros resolve "TWI_vect()" to "__vector_XX()".
Exactly.
And I prefer to use descriptive and mcu-independent

friend void TWI_vect();

instead of

friend void __vector_24();

wbr, ReAl

Last Edited: Tue. Apr 13, 2010 - 07:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Oh then that's fine - it's just whenever one sees what is apparently a function call in an ISR() then it always looks a bit suspicious.
I already got used to this style, to me not suspiciously.
In real code i2c_t::isr() is
void

i2c_t::isr()
{
    if (handler()) {
        event = true;
    }
}

for project with home-brew protothread-like switcher and

void
i2c_t::isr()
{
    if (handler()) {
        OS::TISRW ss;
        twi_event.SignalISR();
    }
}

for scmRTOS-based project
i2c_t::handler() is another inline function (the same for both projects)

p.s. disassembled code in my post above is borrowed from protothread-based project with void i2c_t::isr() and bool i2c_t::handler() functions, both functions are inlined.

wbr, ReAl

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

If Arduino had been written in C++ we wouldn't be having this discussion. It would be a different discussion titled "Reinventing Arduino."

Arduino is hardware. I think I mean Processing.

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

jonsoons wrote:
If Arduino had been written in C++ we wouldn't be having this discussion. It would be a different discussion titled "Reinventing Arduino."
Is any "C++ coding style for AVR GCC" discussion is "reinventing Arduino"?

Well, just see on Arduino code
HardwareSerial.cpp

#define RX_BUFFER_SIZE 128

struct ring_buffer {
  unsigned char buffer[RX_BUFFER_SIZE];
  int head;
  int tail;
};

ring_buffer rx_buffer = { { 0 }, 0, 0 };

#if defined(__AVR_ATmega1280__)
ring_buffer rx_buffer1 = { { 0 }, 0, 0 };
ring_buffer rx_buffer2 = { { 0 }, 0, 0 };
ring_buffer rx_buffer3 = { { 0 }, 0, 0 };
#endif

inline void store_char(unsigned char c, ring_buffer *rx_buffer)
{
  int i = (rx_buffer->head + 1) % RX_BUFFER_SIZE;

  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if (i != rx_buffer->tail) {
    rx_buffer->buffer[rx_buffer->head] = c;
    rx_buffer->head = i;
  }
}

#if defined(__AVR_ATmega1280__)

SIGNAL(SIG_USART0_RECV)
{
  unsigned char c = UDR0;
  store_char(c, &rx_buffer);
}
...

In this piece I can see just C-like type, global variables and function - typical C code (well, C99 because inline keyword).
These global variables shared between "free functions" and classes, ring buffer does not encapsulated in serial interface class.
Current discussion tell how to encapsulate data in hardware manager class and how to make ISR actually class non-static function.

wbr, ReAl

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

Quote:

If Arduino had been written in C++ we wouldn't be having this discussion.

I thought it was?

http://arduino.cc/en/Serial/Begin

provides:

Serial.begin(9600);

which is implemented as:

// Public Methods //////////////////////////////////////////////////////////////

void HardwareSerial::begin(long speed)
{
	*_ubrrh = ((F_CPU / 16 + speed / 2) / speed - 1) >> 8;
	*_ubrrl = ((F_CPU / 16 + speed / 2) / speed - 1);
  sbi(*_ucsrb, _rxen);
  sbi(*_ucsrb, _txen);
  sbi(*_ucsrb, _rxcie);
}

in a file called HardwareSerial.cpp

That sure looks like C++, not C to me?

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

@clawson This thread is about the wrapper not the implementation. The OP wants to use the C++ wrapper regardless of it having the same implementation as the C ISR code.
When they put an xmega chip in an Arduino they will not have to change the wrapper, just the underlying back-end (implementation) which the user doesn't see.
If the motivation for using C++ is that it gives you a wrapper with "bool" and "string" and "cout" etc. then Arduino does the same but with far less complexity.
The OP is motivated to encapsulate a function (ISR) so that irresponsible programmers will not call it willy-nilly. Is that really a danger? I am the only irresponsible programmer around here and even I don't call ISRs.
If the motivation is to break out of the limitations of C and create the "killer app" then I am eager to see it.
If the motivation is to "get fancy" then I just don't share that feeling.

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

Quote:
... the same macros resolve "TWI_vect()" to "__vector_XX()".

Then why not use the same macro in both places rather than writing obtuse code?

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:

Then why not use the same macro in both places rather than writing obtuse code?


Because to be politically-correct "these people" can't just do microcontroller work directly, but need to add at least one layer of obtuseness for job security.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Koshchi wrote:
Quote:
... the same macros resolve "TWI_vect()" to "__vector_XX()".

Then why not use the same macro in both places rather than writing obtuse code?
What on earth are you talking about?
What is obtuse in using "TWI_vect()"?
You are using the same macro when writing "TWI_vect()".
Or do you mean the macro ISR? That doesn't make sense at that point.
ISR(TWI_vect) -> defining the ISR
TWI_vect() -> referencing that ISR

Stefan Ernst

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

Quote:
What on earth are you talking about?

I'm talking about using one macro for the declaration and another for referencing it. That is obtuse, especially since it involves analyzing multiple layers of macros to determine that the two are actually equivalent.

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
I'm talking about using one macro for the declaration and another for referencing it. That is obtuse, especially since it involves analyzing multiple layers of macros to determine that the two are actually equivalent.
Utter nonsense. What two macros do you mean? There is only one relevant macro:

#define TWI_vect _VECTOR(xx)

And ISR() is a second (additional) macro which is needed for the declaration, but not usable for the reference.

    friend void ISR(TWI_vect);

would simply result in wrong, not compilable code.

So please enlighten me, what is your less obtuse alternative to "TWI_vect()"?

Stefan Ernst

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

Koshchi wrote:
I'm talking about using one macro for the declaration and another for referencing it.
There are two macros.
ISR(NAME) macro defines (when used with function body) or declares (when finished by semicolon) "C"-linkage interrupt function named as NAME.
TWI_vect macro #defines interrupt vector label for TWI interrupt for given mcu.

In

ISR(TWI_wect);

both macros are used - ISR() used for isr-declaration of function referenced by TWI_vect.

In

friend void TWI_vect();

only the second macro is used - for friend-declaration of function referenced by TWI_vect.

The same macro (TWI_vect) is used for the same purpose (referencing).

wbr, ReAl

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

Steve,
perhaps the problem is/was that you think/thought that "TWI_vect()" is a user defined macro? (whilst it is in fact the system macro from the io headers with function brackets behind it)

Stefan Ernst

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

Quote:
Steve,
perhaps the problem is/was that you think/thought that "TWI_vect()" is a user defined macro? (whilst it is in fact the system macro from the io headers with function brackets behind it)

No, I am simply saying that it is not immediately evident that this:

friend void TWI_vect();

refers to this:

ISR(TWI_vect);

The code is, therefore, obtuse.

Regards,
Steve A.

The Board helps those that help themselves.

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

sternst wrote:
So please enlighten me, what is your less obtuse alternative to "TWI_vect()"?
For avr-gcc port of scmRTOS I chosen following style (the port is my part of Harry's project)

scmRTOS/AVR/OS_Target.h

#ifdef __INTR_ATTRS	/* default ISR attributes from avr/interrupt.h */
# define	OS_INTERRUPT	extern "C" __attribute__((__signal__,__INTR_ATTRS))
#else
# define	OS_INTERRUPT	extern "C" __attribute__((__signal__))
#endif

i2c.h

OS_INTERRUPT void TWI_vect(); // forward declaration

class i2c_t
{
    friend void TWI_vect();

i2c.cpp

// all attributes are inherited from forward declaration in i2c.h
void TWI_vect()
{
    i2c.isr();
}

More readable but we need special macro OS_INTERRUPT. The macro has common name and usage for all scmRTOS ports so it is "native" for scmRTOS. For non-scmRTOS projects I prefer ISR()-based style as shown above because of only standard includes/defines are used.

The only usage of *_vect names in all my projects is interrupt vector referencing so this style is enough readable for me.

wbr, ReAl

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

Hey guys - The project is coming along and I've got another question for ya'll.

I'm trying to create a generic port class in which you pass in a parameter when you initialize it that specifies which port it's supposed to use. For this given project, I only need one port so this really isn't necessary, but I'm trying to be a good software engineer and plan for the future. My problem is that I cannot figure out how to simply save a value to the port's data register without using the PORTB macro.

The ideal situation is that you'd pass a unsigned char containing a value of 1-4 into the port class when you initialize it. Based on this value it would set a variable inside the class to the address of that specific port. Now that we have the address of the port in a variable, we need to save a value to that register.

What I'm trying to avoid is the following:
void Port::setDr(unsigned char value){
switch (port) //port is an unsigned char containing a value of 1-4 depending on what port it is
{
case 1:
PORTA |= value;
case 2:
PORTB |= value;
case 3:
PORTC |= value;
default:
PORTD |= value;
}
}

I want it to be the following:
//private variable in the h file contains the address of the port - this variable will be called port.

void Port::setDr(unsigned char value){
port |= value;
}
that's how i'd prefer it, but if it was inline assembly i'd be fine with it too. i've tried to do this with inline accessibly, but no matter what I do it just won't compile. I don't know if it's because I'm using the C++ compiler instead of the GCC compiler or if I'm simply doing it wrong...

suggestions?

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

Quote:

My problem is that I cannot figure out how to simply save a value to the port's data register without using the PORTB macro.

http://www.nongnu.org/avr-libc/u...

The FAQ in that manual is WELL worth reading - there's lots of "goodies" contained therein.

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

clawson wrote:
Quote:

My problem is that I cannot figure out how to simply save a value to the port's data register without using the PORTB macro.

http://www.nongnu.org/avr-libc/u...

The FAQ in that manual is WELL worth reading - there's lots of "goodies" contained therein.

That example makes perfect sense, but it doesnt seem to work for me when I save &PORTB to a variable and then output to it.

here's the code that matters:

Port.h:

#include 
#include 
class Port {
public:
	Port(volatile uint8_t *port);
	void setReg(uint8_t byte);
private:
	volatile uint8_t *portNum;

Port.cpp:

     Port::Port(volatile uint8_t *port) {
portNum = port;
}
void Port::setReg(uint8_t byte){

     *portNum |= byte;

}

The MCU class contains a Port *pb object in it.
The MCU's constructor initializes the port object with:

        Port ptB(&PORTB);
	pb=&ptB;

The setReg function is called by:
mcu.pb->setReg(0xAA);

The port's register never changes, but when i use the function straight out of the example it works without issue. Unfortunately I need to have it act upon a variable instead of the straight up &PORTB.

thanks in advance!

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

You're speaking a language I don't understand but can you not either:

a) statically analyse the .lss and see why the generated code does not do what you hope or

b) load the code into the simulator and again see what happens at runtime that you didn't expect.

(actually I think I understand enough of C++ to see that what you are trying should have worked)

As an old bit basher I'm not entirely sure why any of this is actually better than:

PORTB = 0xAA;

for one thing I bet your code uses STS not OUT. But I guess there is some merit in it?

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

Do you need to use a pointer or will using a reference work?
Using references is a little simpler and produces smaller code compared to a pointer.

Example

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

clawson wrote:
You're speaking a language I don't understand but can you not either:

a) statically analyse the .lss and see why the generated code does not do what you hope or

b) load the code into the simulator and again see what happens at runtime that you didn't expect.

(actually I think I understand enough of C++ to see that what you are trying should have worked)

As an old bit basher I'm not entirely sure why any of this is actually better than:

PORTB = 0xAA;

for one thing I bet your code uses STS not OUT. But I guess there is some merit in it?

The lss file shows it is using STS instead of OUT.

Yes, there is some merit to it. For this specific project we re-engineering an existing implementation written in C to use C++ and be more portable (this is so if we switch out the processor or a chip on the board, the core "business" logic will not need to change). We are creating a 3 layer'ed architecture to completely abstract out all hardware specific code from the business logic and see how much overhead it adds(it's for a research project so this might be a very bad way to do it...). One thing is for sure though. The code will be a LOT more readable if we can manage to fit it on our little ATmega48, but with only 4k of ram it's going to be rough since the class structure took up almost half the space already...

I did manage to get the port logic working. I turned optimization off and walked through it line by line and noticed that after

	Port ptB(&PORTB);
	pb=&ptB;

executed in the MCU's constructor it was calling the destructor for the port. The compiler though we were done with the variable and so the memory location saving the address of portb in the port class was clobbered. With c++ on QNX OS's, this wouldnt have happened, and that's really the only place I've used C++ on in the past.

I changed the code to not contain a pointer to the port object and it now works with the following:

MCU.h

class MCU {
public:
	MCU();
   ~MCU();
	Port *pb;//(&PORTB);  ---DOES NOT WORK
	Port tPB;//(volatile uint8_t &PORTA);

MCU.cpp:

MCU::MCU() {
	tPB.setPort(&PORTB);
}

Port.h:

class Port {
public:
	Port(volatile uint8_t *port);
		Port();
		void setPort(volatile uint8_t *port);
	void setReg(uint8_t byte);
private:
volatile uint8_t *portNum;

Port.cpp:

void Port::setReg(uint8_t byte){
*portNum |= byte;

}

Port::Port() {
	// TODO Auto-generated constructor stub

}
void Port::setPort(volatile uint8_t *port){
Port::portNum = port;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is no reason for a destructor to be called unless a variable leaves scope unless an explicit delete call is made. That makes me wonder where the port object is initially defined. If it is in the MCU constructor then yes, the destructor should be called, because the object will leave scope. Otherwise, I would be inclined to look much deeper, because something strange is happening. You haven't shown enough code to be sure either way however.

As another point, I would be amazed if the full 2 kB is really necessary for just wrapping hardware. There are a number of optimizations that work wonders in class abstractions. In fact, I have been able to create a port / pin abstraction that has zero overhead ( when compared to pure C ); though it is large and complicated code wise, using templates and macros and so forth. But it allows for the use of pin ( or port ) objects with no knowledge of how they are implemented at a lower level across most of the AVR range and could be expanded further with only minimal modification to header files.

Remember to implement anything possible with compile time constructs, as that is a sure way to reduce overhead and, of course, minimize indirections. Pointers are nice but they can be expensive code wise.

The trick, I've found, with this sort of abstractive layering, is to get it functioning, and then look for boilerplate that is causing bloat. Often one or two changes will greatly decrease the size, and increase the performance.

Good luck.

Martin Jay McKee

P.S. One final point... I have found optimization absolutely necessary for work in C++. There is even more initial bloat in the non-optimized version as compared to C, and it can make even the most innocuous looking code huge.

As with most things in engineering, the answer is an unabashed, "It depends."

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

mckeemj wrote:
There is no reason for a destructor to be called unless a variable leaves scope unless an explicit delete call is made. That makes me wonder where the port object is initially defined. If it is in the MCU constructor then yes, the destructor should be called, because the object will leave scope. Otherwise, I would be inclined to look much deeper, because something strange is happening. You haven't shown enough code to be sure either way however.

As another point, I would be amazed if the full 2 kB is really necessary for just wrapping hardware. There are a number of optimizations that work wonders in class abstractions. In fact, I have been able to create a port / pin abstraction that has zero overhead ( when compared to pure C ); though it is large and complicated code wise, using templates and macros and so forth. But it allows for the use of pin ( or port ) objects with no knowledge of how they are implemented at a lower level across most of the AVR range and could be expanded further with only minimal modification to header files.

Remember to implement anything possible with compile time constructs, as that is a sure way to reduce overhead and, of course, minimize indirections. Pointers are nice but they can be expensive code wise.

The trick, I've found, with this sort of abstractive layering, is to get it functioning, and then look for boilerplate that is causing bloat. Often one or two changes will greatly decrease the size, and increase the performance.

Good luck.

Martin Jay McKee

P.S. One final point... I have found optimization absolutely necessary for work in C++. There is even more initial bloat in the non-optimized version as compared to C, and it can make even the most innocuous looking code huge.

Yea, I do not have optimization turned on at the moment (With it turned on the entire chip layer is only 644 bytes at the moment). Since I'm brand new to the AVR platform I figured getting it working without optimization on would be enough of a challenge. When I finish the chip layer I'm going to enable the optimization and see if it still works =P

With the code posted in the previous section, the port object was originally defined in the MCU's constructor but a reference to it was saved in the MCU's h file. It went out of scope and deleted the object.

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

Quote:

I figured getting it working without optimization on would be enough of a challenge.

GCC's -O0 is only there for testing code in the unoptimsed case - it's not expected anyone use the unthinkably awful code it produces. When you turn the optimiser on your program may behave quite differently so I'd start with it on at the start or you may end debugging things twice.

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

ReAlAl wrote:
I am not OOA/OOD/OOP guru but ...

"particular software object" receives (and reacts on) messages from other objects.
Can't any of these objects be "hardware object"? Why?
Moreover - look on whole firmware - all external messages are hardware generated.
"high level" objects inside firmware receive only "software" messages from "low level" objects and "low level" objects communicate with hardware. But all of them are objects.

I use following style

// from i2c.h
ISR(TWI_vect);

class i2c_t {
    friend void TWI_vect();
public:
    // ...
private:
    // ...
    uint8_t *bufptr;
    uint8_t left;
    uint8_t sla;
    uint8_t suba[2];
    uint8_t suba_len;

    inline void isr();
};

extern i2c_t i2c;
// from i2c.cpp
i2c_t i2c;
//...

ISR(TWI_vect)
{
    i2c.isr();
}

I'm using this template for my watchdog interrupts, and I've got a question about how it works.

I'm not sure if I'm correct about this or not, but it seems to me that it's creating a second copy of the object.

For me, I've got my watchdog class as an object in my MCU class, but it seems to be defining a second watchdog object in the cpp file for the interrupt to use. I have my ISR set a flag in the watchdog class, and the main program loop polling this flag to check for interrupts.

So in short, my FWU class contains the main program loop (It has a pointer to an MCU object). The MCU object contains a watchdog object where a public flag is set. If I check the flag using MCU->watchdog.mFlag, will it return the proper value?

In case i left out key info, here is some of the code:

Watchdog.h:

#ifndef WATCHDOG_H_
#define WATCHDOG_H_
#include 				
#include 
#include 
//INTERRUPT
ISR(WDT_vect);

class WatchDog {
friend void WDT_vect();
public:
	WatchDog();
	~WatchDog();
	void reset(void);
	void enable(void);
	void disable(void);
	
volatile bool mFlag;
private:
inline void isr();
};
extern WatchDog wdt;

#endif /* WATCHDOG_H_ */

Part of MCU.h:

class MCU {
public:
	MCU();
   ~MCU();
   	void initMCU(void);
	Port pb;//(volatile uint8_t &PORTA);
	I2C  i2c;
	WatchDog wd;
	GenericTimer timer1;
	void enableGlobalInterupts(void);
	void sleepMCU(void);

watchdog.cpp (by no means complete, but the interrupts are implemented)

#include "WatchDog.h"
WatchDog wdt;


WatchDog::WatchDog() {
	// TODO Auto-generated constructor stub
mFlag=false;
}

WatchDog::~WatchDog() {
	// TODO Auto-generated destructor stub
}
void WatchDog::reset(void){ wdt_reset();}
void WatchDog::enable(void){
	wdt_enable(WDTO_8S); //enable watchdog for 8 second intervals
	WDTCSR |= (1<<WDIE); //enable watchdog interrupt
}
void WatchDog::disable(void){wdt_disable();}



void WatchDog::isr(){
mFlag=true;

}


ISR(WDT_vect){
	wdt.isr();
}

So the ISR is calling "wdt"'s isr method, not the MCU's wd object (at least that is what I think it's doing). Is there any way to fix this? I've tried getting a reference to the MCU object in the watchdog class, but no matter what I do it just won't compile. If I include the MCU.h file it errors out because the MCU is including the watchdog and the watchdog is including the MCU... when i try using a forward declaration, I get forward declaration errors... Is the only way to do this to create getters/setters inside the watchdog class to return the wdt's mFlag object instead of the MCU's copy? Is it really needed to have two separate copies in the first place?

Am i missing something here?

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

Do not include "MCU.h" inside WatchDog.h - it is not needed for class WatchDog declaration
This code compiled without errors

// WatchDog.h
#ifndef WATCHDOG_H
#define WATCHDOG_H

#include 
#include 
#include 

ISR(WDT_vect);

class WatchDog {
	friend void WDT_vect();
public:
	WatchDog() : mFlag(false) { }

	void reset() { wdt_reset(); }
	void enable(void) {
		wdt_enable(WDTO_8S);
		WDTCSR |= (1<<WDIE);
	}
	void disable(void){ wdt_disable(); } 
	volatile bool mFlag;
private:
	inline void isr();
};#endif
// WatchDog.cpp
#include "WatchDog.h" // not needed because of included in MCU.h - just for clarity
#include "MCU.h"

void WatchDog::isr() {
	mFlag=true;
}

ISR(WDT_vect) {
	mcu.wd.isr();
}
// MCU.h
#include 
#include "WatchDog.h"

class MCU {
public:
   MCU() {} // empty stub, just placeholder
   ~MCU() {}
   void initMCU(void) {}
   WatchDog wd;
};

extern MCU mcu;
// MCU.cpp
#include "MCU.h"

MCU mcu;

wbr, ReAl