Thanks. The "task() == task1" or the "getId()" should both work. I would have to translate the number of "getID()", because there could be other tasks as well.
If I would have a number of tasks (unknown) and the order in which they are created is unknown, then the ID can be anything. Perhaps a translation is needed from 4,5,6 to 0,1,2.
That could happen if after a while I add a task, and have forgotten about the ID.
The condition "if ( task() == task1 ) " is always valid.
@dzives the time for context switch is very high and block interrupts on all context switch can mean the loss of one or more interrupts.
Are You sure that the method used to save the stack pointer also includes the third byte which is used for 2560? have You disassemble a code?
Warning inline for gcc/g++ is at the discretion of the compiler, you should add the attribute
The time ellasped is not as long as you said. I have measured 25µs in the simulator. This duration can be a little bit longer in certain cases. But you're right, I will fix that by letting interrupts enabled by default except for pushing and popping context from the stack.
Yes the third byte is saved. SP is always 16 bits. PC is 22bit and are automatically puhsed onto the stack when the ISR (naked) is called.
I use always attribute((always_inline) when I declare an inline function. Did you have got some warning during the compilation ?
In my previous post I said: "more than 20us"
Is very big time... You need disable interrupt only on restore StackPointer.
But when you call isr naked the compiler should not add anything. Have You checked?
GCC:
naked
Use this attribute on the ARM, AVR, C4x and IP2K ports to indicate that the specified function does not need prologue/epilogue sequences generated by the compiler. It is up to the programmer to provide these sequences.
post a disassebled code.
I not view in your code the attribute always_inline, example function call from isr is only inline.
this is correct way.
But when you call isr naked the compiler should not add anything. Have You checked?
A naked attribute means to not store any register. But the ISR is still called, so the PC is pushed on the stack like any function. The SP is stored just after registers (SPL and SPH).
From the datasheet:
During interrupts and subroutine calls, the return address Program Counter (PC) is stored on the Stack.
You need disable interrupt only on restore StackPointer.
True
Disassembled code:
0000071E PUSH R31 Push register on stack
0000071F IN R31,0x3F In from I/O location
00000720 ORI R31,0x80 Logical OR with immediate
00000721 PUSH R31 Push register on stack
00000722 PUSH R30 Push register on stack
00000723 PUSH R29 Push register on stack
00000724 PUSH R28 Push register on stack
00000725 PUSH R27 Push register on stack
00000726 PUSH R26 Push register on stack
00000727 PUSH R25 Push register on stack
00000728 PUSH R24 Push register on stack
00000729 PUSH R23 Push register on stack
0000072A PUSH R22 Push register on stack
0000072B PUSH R21 Push register on stack
0000072C PUSH R20 Push register on stack
0000072D PUSH R19 Push register on stack
0000072E PUSH R18 Push register on stack
0000072F PUSH R17 Push register on stack
00000730 PUSH R16 Push register on stack
00000731 PUSH R15 Push register on stack
00000732 PUSH R14 Push register on stack
00000733 PUSH R13 Push register on stack
00000734 PUSH R12 Push register on stack
00000735 PUSH R11 Push register on stack
00000736 PUSH R10 Push register on stack
00000737 PUSH R9 Push register on stack
00000738 PUSH R8 Push register on stack
00000739 PUSH R7 Push register on stack
0000073A PUSH R6 Push register on stack
0000073B PUSH R5 Push register on stack
0000073C PUSH R4 Push register on stack
0000073D PUSH R3 Push register on stack
0000073E PUSH R2 Push register on stack
0000073F PUSH R1 Push register on stack
00000740 CLR R1 Clear Register
00000741 PUSH R0 Push register on stack
00000742 IN R24,0x3D In from I/O location
00000743 IN R25,0x3E In from I/O location
00000744 STS 0x01C1,R25 Store direct to data space
00000746 STS 0x01C0,R24 Store direct to data space
In my code, all functions declared in header files have attribute((always_inline)), then I define them in another header file.
Is it possible that the bootloader will be overwritten ?
I have no problem with an Arduino Uno, but a Arduino Leonardo gets a corrupted bootloader if I upload my sketch. The leds don't even blink with a hard reset (power up).
I don't have the message queue for serial is working yet, but I show you my sketch as it is, which corrupts the Leonardo bootloader:
// 2016, april, testing the os48 with Arduino Uno en Leonardo.
//
// www.rtos48.com
//
// http://forum.arduino.cc/index.php?topic=347188.msg2705494#msg2705494
//
#include <os48.h> // The only file you have to include
using namespace os48; // This line is necessary
//Scheduler is a singleton, you don't have to create an instance because only one can exist.
Scheduler* scheduler = Scheduler::get(); //Get the instance
//Declare the task pointers as global vars to use them in the task functions.
Task* task1 = NULL;
Task* task2 = NULL;
Task* task3 = NULL;
Task* taskSerial = NULL;
Task* taskBlink = NULL;
const int buttons[] = { 8, 9, 10};
const int pinLed = 13; // use system led
// Override the Arduino weak yield (from inside the delay).
// Use either the os48 yield, or the os48 sleep.
void yield()
{
scheduler->yield();
}
void setup() {
Serial.begin(9600);
Serial.println(F( "Creating tasks..."));
// Create three tasks that have the same function.
task1 = scheduler->createTask( &funcButton, 100, PrNormal); //Creates a task associated to the 'func1' function with 60 bytes of stack memory.
task2 = scheduler->createTask( &funcButton, 100, PrNormal);
task3 = scheduler->createTask( &funcButton, 100, PrNormal);
taskSerial = scheduler->createTask( &funcSerial, 100, PrNormal);
taskBlink = scheduler->createTask( &funcBlink, 100, PrLow);
Serial.println(F("Starting..."));
scheduler->start(); //Starts the scheduler. At this point you enter in a multi tasking context.
//...
//Nothing will be executed here
}
void funcButton()
{
// Use the function pointer to test which task this is.
int index;
// This task will run twice
if( task() == task1)
index = 0;
else if( task() == task2)
index = 1;
else if( task() == task3)
index = 2;
int pinButton = buttons[index]; // pinButton can be any of the three buttons
pinMode( pinButton, INPUT_PULLUP);
boolean previousButton = false;
for(;;)
{
char buffer[50];
boolean currentButton = digitalRead( pinButton) == LOW ? true : false; // low is a active button
if( !previousButton && currentButton)
{
// The button has been pressed.
sprintf_P( buffer, PSTR( "Hello, button %d has been pressed."), index);
scheduler->sendMessage(taskSerial, new Message(0, buffer));
}
else if( previousButton && !currentButton)
{
// The button has been released.
sprintf_P( buffer, PSTR( "Hello, button %d was released"), index);
scheduler->sendMessage(taskSerial, new Message(0, buffer));
}
task()->sleep(20); // check button 50 times per second, so bouncing will not overflow the message queue.
}
}
void funcSerial()
{
int full = 0;
boolean error = false;
boolean transmitted = false;
for(;;)
{
// This is the only task that uses the Serial port. No need to OS48_NO_CS_BLOCK.
// This task tries to transmit the message to the serial port.
// If that is not possible during 1 second, an error is set.
Message* mess = task()->waitNextMessage(0); // look for message with id '0'
if( error)
{
delete mess;
}
while( !transmitted)
{
// The Serial could slow down if the transmit buffer is full, or if the connection with
// the computer is broken with a ATmega32U4.
// The Serial.availableForWrite() could be used.
int nAvailable = 0; // Serial.availableForWrite();
int nLen = strlen( mess->getBody().bStr);
if( nAvailable > 0) // is at least one byte free in the buffer ?
{
// The whole message can not be printed right now.
// That is not a big problem for an Arduino Uno, but the
// connection could be lost with an Arduino Leonardo.
full++;
if( full > 2)
{
error = true;
}
task()->sleep(100);
}
else
{
// Serial.println(mess->getBody().bStr);
delete mess;
}
}
}
}
void funcBlink()
{
pinMode( pinLed, OUTPUT);
for(;;)
{
digitalWrite( pinLed, LOW);
task()->sleep(400);
digitalWrite( pinLed, HIGH);
task()->sleep(100);
}
}
void loop() {} //Useless now but you have to let it defined.
Is it possible that the bootloader will be overwritten
The program itself cannot corrupt the bootloader during the execution. AVR is a harvard architecture (prgm space belongs in flash in this case) and since OS48 doesn't write any data in flash (in which the bootloader is sotred), I don't think that's possible to corrupt the bootloader.
You said it's during the upload ? Did you try to push the reset button when the upload starts ? I got some issues with the Leonardo in the past but I didn't make the connection with my lib. Every time I had to push the reset button during the upload process, then my prgm started normally. I cannot help you more because I'm not at home during 10 days, so I'm very limited. Keep me up. Try to test with some minimal codes.
Concerning your test code, you generate a lot of Message instances. Pay close attention to memory used and fragmentation. To avoid fragmentation you can reserve more blocks for the memory pool provided by changing a parameter in Advanced_parameters.h file.
Moreover, I don't yet provided safe new and delete function (these should be atomic functions). The memory pool is safe but not yet the malloc and free function. When you create a message, the lib try first to reserve a memory pool block (6 availables by default for messages) and if no block is available the malloc is called. I advice to use a semaphore which surrounds new and delete function. You can also use OS48_NO_CS_BLOCK depending of which type of behaviour you want. But the best pratice is to not send 3 messages every 20ms
My sketch is a test to try to keep it working while there are to many messages. I haven't got that far yet.
I will try pressing reset, but that should not make a difference. My Leonardo has already the new bootloader. I can upload any other sketch in linux to my Leonardo without problem.
Somehow the programming of the bootloader is initiated. At this moment I don't know how to find the cause.
I am evaluating libraries for Arduino multitasking. It seems that no one offers a library with a Thread/Task class where the "loop" function is a class member function. Instead these functions are static/global functions. Not being wizened in the way of C++/Arduino subtleties is there some fundamental underlying reason why no library allows use of a member function?
Have been playing around with the library a bit. This is a GREAT library! Thanks for your hard work.
BTW: I implemented a usec sleep function, and monitored the elapsed times. Seems to work quite well, with only a 30usec overhead using coop, and 60usec using round robin with two trivial tasks. Wouyld be nice if usec sleep were part of the library
=========================
/**
Sleeps for a given duration, with usec resolution. Not affected
by Task::resume(). Task overhead adds about 30usec for cooperative
and 60usec for preemptive scheduling.
@param sleepUsec Sleep duration (usec).
*/
void sleepUsec(unsigned long sleepUsec) {
unsigned long sleepStart = micros();
while (true) {
Scheduler::get()->yield();
unsigned long sleepElapsed = elapsedTime(sleepStart, micros());
if (sleepElapsed >= sleepUsec) {
break; // timeout expired
}
}
}
unsigned long elapsedTime(unsigned long startTime, unsigned long stopTime) {
if(startTime <= stopTime) {
// no rollover
return stopTime - startTime;
} else {
// rollover
return ((-1) - startTime) + stopTime + 1UL;
}
}
BTW: Assembly is not my strong suit. Is there some reason why the library could not be modified to use a class instance method as the "callback" function? Is it just a matter of time, know-how and energy, or is there some fundamental reason?
Does anybody know if the web page is broken for just me or everyone? Looks like name registration changed a few days back, now DNS returns unknown name for rtos48.come.