I'm faced with a weird problem. I'm developing a program for AVR
.
- Microcontroller
ATtiny85
. - Frequency 1 MHz.
- Programmer
pickit2
. - Software for programming
avrdude
on Linux. - Compiler
avr-g++
. - Optimization
-Os
(I tested with-O0
, it doesn't work too).
This program must enable output pin PB3
. But for some reason, it doesn't.
#include <avr/io.h>
#include <avr/interrupt.h>
#define NOINLINE_ENABLED 1
#if NOINLINE_ENABLED == 1
#define NOINLINE __attribute__ ((noinline))
#else
#define NOINLINE
#endif
// Error occurs only if SetLedState method has noinline attribute or function implemented in other .cpp file (also noinline).
// NOINLINE_ENABLED macros enables noinline for this method
void NOINLINE SetLedState(bool state) {
if (state)
PORTB |= (1 << PB3);
else
PORTB &= ~(1 << PB3);
}
class Test {
public:
Test(unsigned int a) : a_(a) {}
void Update() {
// a_ for some reasons doesn't equal 5, but it's impossible.
if (this->a_ == 5)
SetLedState(true);
}
private:
// I don't know why, but error doesn't occur if I add first variable, which can never be used
// unsigned int padding = 0;
const unsigned int a_;
};
static Test *test;
ISR(TIMER1_OVF_vect) {
// error occurs when method called from timer interrupt method
test->Update();
}
[[noreturn]] int main() {
test = new Test(5);
DDRB = (1 << PB3);
PORTB = 0;
TIMSK = (1 << TOIE1);
TCCR1 = (1 << CS10);
sei();
// no error occurs when method called from this place
// test->Update();
while (true);
}
I wrote some comments to explain why it doesn't work. I think the problem is in the optimizer.
Output PB3
doesn't enable if (and):
Test::Update
method calls from timer overflow interrupt.a_
class member has offset 2 and more.SetLedState
is no inlined or implemented in other*.cpp
file.
Why? Thanks in advance!
Edit:
Also, I tried to change the implementation of new
C++ operator.
Earlier it called return alloca(size)
, but now I tried to call return malloc(size)
. And it worked. Also, this initialization works too:
static Test test_static;
test = &test_static;
Why it doesn't work with alloca
? What is the difference with malloc and alloca AVR functions?
-
1\$\begingroup\$ @user448: Can you post the code generated by the compiler? \$\endgroup\$doynax– doynax2021年01月01日 14:55:59 +00:00Commented Jan 1, 2021 at 14:55
-
1\$\begingroup\$ @user448 alloca allocates memory on the stack, rather then the heap, in the stack frame of the caller, so it's automatically freed when the caller returns. There's little use in that for allocating objects of known size like in this case, you could make it a plain automatic variable. \$\endgroup\$Unimportant– Unimportant2021年01月01日 15:01:43 +00:00Commented Jan 1, 2021 at 15:01
-
3\$\begingroup\$ @user448: Another sanity is to verify that the allocation actually succeeds. Given the 512 bytes of RAM of the target chip I wouldn't be surprised if no heap space had been set up, in which case we are touching whatever happened to be at address zero. \$\endgroup\$doynax– doynax2021年01月01日 15:11:33 +00:00Commented Jan 1, 2021 at 15:11
-
2\$\begingroup\$ The original question is a good one, but as an aside, wouldn't this kind of thing be better in C rather than C++? \$\endgroup\$Pete W– Pete W2021年01月01日 15:11:50 +00:00Commented Jan 1, 2021 at 15:11
-
2\$\begingroup\$ Thank you for reminding me why I write code for small machines in assembler and avoid second-guessing compilers. \$\endgroup\$glen_geek– glen_geek2021年01月01日 15:40:22 +00:00Commented Jan 1, 2021 at 15:40
1 Answer 1
The reason(s) why this doesn't work for you could be
- heap memory allocation doesn't work at all on your setup
- the interrupt doesn't see the updated pointer because the pointer isn't volatile.
That said, I'd advise not to use the heap at all on such a small target. And why would you? You will know at build time which pins you want to trigger, so you can create the object(s) globally or local to main().
Also, why use an interrupt? If it is just to try it, OK, go ahead, but for beginners I'd stick to other solutions. Check the FSM (Finite State Machine) style.
Coding for such a small system in C++ is IMO the good way, but be aware that it is not like coding for larger systems. You will have to know very well what you are doing, and you will find lots of C++ examples and advice that might be pefect for largere systems, but not for an AVR8.
-
\$\begingroup\$ Agree massively re: no heap. The big 4 rules for small real time firmware, IMHO -- no malloc, no recursion, no unbounded loops, no exceptions \$\endgroup\$Pete W– Pete W2021年01月01日 17:51:20 +00:00Commented Jan 1, 2021 at 17:51
-
1\$\begingroup\$ In my freferred C++ style I add: no indirections (no vurtuals, no function pointers). That makes it possible to calculate the stack size - one design nightmare less. \$\endgroup\$Wouter van Ooijen– Wouter van Ooijen2021年01月01日 17:54:11 +00:00Commented Jan 1, 2021 at 17:54
-
\$\begingroup\$ I would agree re: dynamic dispatch resulting from polymorphism -- I think that's what you mean? As a C rather than C++ programmer, I must allow one use of function pointers, for the dispatch table, if implementing a command interface. The table is static and gets generated at compile-time with some macro madness. It makes it convenient to combine different groups of commonly used "command sets". \$\endgroup\$Pete W– Pete W2021年01月01日 18:05:37 +00:00Commented Jan 1, 2021 at 18:05
-
\$\begingroup\$ I am a C++ programmer, so I would use templates to make those functions sufficiently "known" at compile time to avoid indirection. But I must use some 'magic' to amke the tooling find the mains of all threads. \$\endgroup\$Wouter van Ooijen– Wouter van Ooijen2021年01月01日 18:08:55 +00:00Commented Jan 1, 2021 at 18:08
Explore related questions
See similar questions with these tags.