Task acting strangley when used inside C++ classes on AVR Atmega2560 uC

simplethings07 wrote on Tuesday, August 01, 2017:

Hello guys,

I am using AVR-GCC on AVR Atmega2560 uC with FreeRTOS v9. So basically I want to implement a simple radar with ultrasonic sensor and a servo motor. When I run servo motor and ultrasonic sensor on seperate tasks in my main.cpp, everything works just fine. Then I tried to wrap these 2 threads inside Radar class:

Radar::Radar(UltrasonicSensor* ultrasonicSensor, Servo* servo)
{
	this->ultrasonicSensor = ultrasonicSensor;
	this->servo = servo;
	
	this->objectAngle = 0;
	this->objectDistance = 0;
	
	xTaskCreate(
	   Radar::ultrasonicThread
	,  NULL  // A name just for humans
	,  128  // Stack size
	,  this
	,  2  // priority
	,  NULL );
	
	xTaskCreate(
	   Radar::servoThread
	,  NULL  // A name just for humans
	,  128  // Stack size
	,  this
	,  2  // priority
	,  NULL );
}

void Radar::ultrasonicThread(void* pvParameters)
{
	Radar* radarInstance;
	
	radarInstance = static_cast<class Radar*>(pvParameters);
	
	while (true)
	{
		radarInstance->ultrasonicSensor->read( &(radarInstance->objectDistance) );
	}
}

void Radar::servoThread(void* pvParameters)
{
	int8_t angle;
	Radar* radarInstance;
	
	radarInstance = static_cast<class Radar*>(pvParameters);
	
	while (true)
	{
		for ( angle = 0; angle < 180; ++angle )
		{
		  radarInstance->servo->write(angle);
		  radarInstance->objectAngle = angle;
		  vTaskDelay(20 / portTICK_PERIOD_MS);
		}
		
		for ( angle = 180; angle > 0; --angle )
		{
		  radarInstance->servo->write(angle);
		  radarInstance->objectAngle = angle;
		  vTaskDelay(20 / portTICK_PERIOD_MS);
		}
	}
}

void Radar::open(void) {vTaskStartScheduler();}

int8_t Radar::read(uint8_t* distance, uint8_t* angleInDegres)
{	
	*angleInDegres = this->objectAngle;	
	*distance = this->objectDistance;
	
	return 0;
}

You can see that I have servoThread and ultrasonicThread, and I am doing exactly the same thing as I did in my main.cpp, where it worked just fine.


HardwareSerial& btSrialPort = Serial3;
HardwareSerial& pcSerialPort = Serial;
Servo servoMotor;
UltrasonicSensor ultrasonicSensor(trigPin, echoPin);
MessageProcessor messageProcessor(&btSrialPort);
Radar radar(&ultrasonicSensor, &servoMotor);

uint8_t distance;
uint8_t angle;

void radarThread(void* pvParameters)
{
  uint8_t angle, distance;
  
  while (true)
  {
    radar.read(&distance, &angle);
    pcSerialPort.println(distance);
  }
}

void setup()
{
  pcSerialPort.begin (9600);
  btSrialPort.begin(9600);
  
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  servoMotor.attach(servoPin);
   
  multiThreadingBootstrap();
}

void loop()
{

} 

void multiThreadingBootstrap(void)
{

  xTaskCreate(
     radarThread
  ,  (const portCHAR *)"Radar"   // A name just for humans
  ,  128  // Stack size
  ,  NULL
  ,  2  // priority
  ,  NULL );

  radar.open();
  vTaskStartScheduler();
}

You can see that in function multiThreadingBootstrap I create a new thread called radarThread, which then calls method ** Radar::read**, which returns 2 parameters that are read from two thread methods: Radar::servoThread and Radar::ultrasonicThread.

When I use it this way my ultrasonic sensor acts strangely, it detects objects with delay, servo motor goes only in one direction. Could a problem lay in the fact that I have two threads running inside C++ class and one thread running in my main.cpp ?

simplethings07 wrote on Wednesday, August 02, 2017:

Anyone?

rtel wrote on Wednesday, August 02, 2017:

Sorry, we only ever test with C rather than C++, but there are some C++ wrappers for FreeRTOS on the web and in the FreeRTOS Interactive site, for example: https://interactive.freertos.org/hc/en-us/community/posts/210028906-Using-FreeRTOS-with-C-

hs2sf wrote on Wednesday, August 02, 2017:

Seems that you starting the scheduler twice in multiThreadingBootstrap():
1st in radar.open() and 2nd explicitely right below. That could cause problems I guess :wink:

richard_damon wrote on Thursday, August 03, 2017:

You don’t show the class definition, but one thing that might be the issue is that the thread functions can’t be normal methods of the class, as that will have extra parameter (this) that is implied. Technically, even static member functions might not be compatibld, but normally are. To be absolutely sure, the base task function should be a free function that has been declared with extern “C” linkage (which I don’t think can be made a member of a class).

The two vTaskStartScheduler won’t be a problem, as the first call will never return.

simplethings07 wrote on Thursday, August 03, 2017:

Here is my class definition, for the previous post:

Radar.h

#ifndef _RADAR_H_ 
#define _RADAR_H_

#include <stdint.h>

class Servo;
class UltrasonicSensor;

class Radar
{
	private:
		UltrasonicSensor* ultrasonicSensor;
		Servo* servo;
		uint8_t objectDistance;
		uint8_t objectAngle;
	
	public:
		Radar(UltrasonicSensor* ultrasonicSensor, Servo* servo);
		int8_t read(uint8_t* distance, uint8_t* angleInDegres);
		Radar* getInstance() { return this; }
		void open(void);
	
	private:
		static void ultrasonicThread(void* parameter);
		static void servoThread(void* parameter);
};

you can see that both ultrasonicThread and servoThread are static. I couldn’t even compile my code with non-static class methods as task functions, since compiler would give me an error.
I tried to follow your suggestion, but it gives the same result:

Radar.h

extern "C" void ultrasonicSensorThread(void* pvParameters);
extern "C" void servoMotorThread(void* pvParameters);

class Radar
{
	private:
		UltrasonicSensor* ultrasonicSensor;
		Servo* servo;
		uint8_t objectDistance;
		uint8_t objectAngle;
	
	public:
		Radar(UltrasonicSensor* ultrasonicSensor, Servo* servo);
		int8_t read(uint8_t* distance, uint8_t* angleInDegres);
		Radar* getInstance() { return this; }
		void open(void);
		
	private:
		friend void ultrasonicSensorThread(void* pvParameters);
		friend void servoMotorThread(void* pvParameters);
	
	private:
		static void ultrasonicThread(void* parameter);
		static void servoThread(void* parameter);
};

Radar.cpp

void ultrasonicSensorThread(void* pvParameters)
{
	Radar* radarInstance;
	
	radarInstance = static_cast<class Radar*>(pvParameters);
	
	while (true)
	{
		radarInstance->ultrasonicSensor->read( &(radarInstance->objectDistance) );
	}
}

void servoMotorThread(void* pvParameters)
{
	int8_t angle;
	Radar* radarInstance;
	
	radarInstance = static_cast<class Radar*>(pvParameters);
	
	while (true)
	{
		for ( angle = 0; angle < 180; ++angle )
		{
		  radarInstance->servo->write(angle);
		  radarInstance->objectAngle = angle;
		  vTaskDelay(20 / portTICK_PERIOD_MS);
		}
		
		for ( angle = 180; angle > 0; --angle )
		{
		  radarInstance->servo->write(angle);
		  radarInstance->objectAngle = angle;
		  vTaskDelay(20 / portTICK_PERIOD_MS);
		}
	}
}

Radar::Radar(UltrasonicSensor* ultrasonicSensor, Servo* servo)
{
	this->ultrasonicSensor = ultrasonicSensor;
	this->servo = servo;
	
	this->objectAngle = 0;
	this->objectDistance = 0;
	
	xTaskCreate(
	   ultrasonicSensorThread
	,  NULL  // A name just for humans
	,  128  // Stack size
	,  this
	,  2  // priority
	,  NULL );
	
	xTaskCreate(
	   servoMotorThread
	,  NULL  // A name just for humans
	,  128  // Stack size
	,  this
	,  2  // priority
	,  NULL );
}

richard_damon wrote on Friday, August 04, 2017:

one thing I see that looks strange:
int8_t angle

for(angle = 0; angle < 180; angle++) {

since the range of an int8_t is -128 to 127, this loop will never end, as angle will NEVER be above 180.

simplethings07 wrote on Friday, August 04, 2017:

Richard Damon you really have a good eye, I didn’t notice that. Somehow I wrote int8_t instead of uint8_t there, you can see that my uint8_t Radar::objectAngle member field is type of uint8_t already. Thank you, now it works like a charm! Now atleast I am sure that I can use FreeRTOS in C++!
One more question guys, is there way to use sleep inside a thread given in milliseconds. Something like:

vTaskDelay(10 / portTICK_PERIOD_US); 

hs2sf wrote on Friday, August 04, 2017:

Yes, of course. I’m using this (for non C++11 compiler replace auto w/ the appropriate type):

//! BSD style (non-POSIX) msleep
void    msleep( uint32_t TimeMS )
{
    auto TimeTicks = (TimeMS + portTICK_PERIOD_MS -1) / portTICK_PERIOD_MS; //!< round up to mult. of portTICK_PERIOD_MS
    vTaskDelay( TimeTicks );
}

richard_damon wrote on Friday, August 04, 2017:

I will admit that I make the same mistake at times myself. soo something I get used to catching. Small comment, you are perhaps prematurly over optimizing things by making a local angle variable an 8 bit type. I am much more apt to use int for something like this. Fixed size types are mostly used in storage structures, computations tend to be in natual sized types. Angles in degress will normally be at least an int16_t as the base domain for an angle is 0…360 (or -180 … 180). Jusgt because a particular angle is in a more limited range (-90 … 90 or 0 …180) isn’t normally a big enough of a reason to reduce the range to an 8 bit type. (Big tables might be a possible exception).

davidbrown wrote on Friday, August 04, 2017:

If that bug was not caught by your static error checking, you need to bump up your warnings! With gcc, this is -Wtype-limits, or -Wextra.

Making good use of your tools can save a lot of time and effort.

simplethings07 wrote on Friday, August 04, 2017:

I am using Atmel Studio 7, I have just checked Warnings http://imgur.com/fVFJnmj . seems like All warnings box has been already checked.

simplethings07 wrote on Friday, August 04, 2017:

The servomotor can only move half of circle (0 - +180 or -90 - +90 as you mentioned). Since this is 8bit microcontroller I try to squeeze everything.

simplethings07 wrote on Friday, August 04, 2017:

Sorry I wrote miliseconds instead of microseconds. I wanted to ask whether we can specify delay in microseconds to vTaskDelay function.

davidbrown wrote on Friday, August 04, 2017:

Like all IDE’s, Atmel Studio gives you a very simiplified view of the compiler options. It’s not too bad for getting started, but much of the power of the compiler is hidden. I haven’t used that IDE for many years (it is Windows only), but somewhere it will have a place for adding your own compiler options. Add “-Wextra” to that.

I would also recommend spending some time reading the compiler documentation for the particular tool version you have. There are many warning options that can help you write better code, and spot mistakes. Not everyone thinks compiler manuals are fun to read (I do, and have read several pretty much cover-to-cover). But it is important to know your tools - especially because many of the nice bits are off by default.

richard_damon wrote on Saturday, August 05, 2017:

Delaying in microseconds, Delays to things like vTaskDelay will; be in units of ticks, you could always define something like portTICK_PERIOD_MICROSEC, but you still won’t get a finer resolution.

One big issue is that the cost to run the scheduler can easily be big enough on many processors that such a delay is impractical. If you really need a delay of a few microseconds, it is generally reasonable to just spin wait, maybe looking at a timer (like the systick counter) to control the period.

As to the 8 bit local values, if you really need to squeeze the design that much, the overhead of C++ may be worse.

hs2sf wrote on Saturday, August 05, 2017:

C++ ‘overhead’ strongly depends on the features used. Using classes ie. OO style programming doesn’t cost anything you don’t want. When using e.g. exceptions or RTTI you’ve to pay for, of course.

simplethings07 wrote on Saturday, August 05, 2017:

With all due Respect Richard, C++ has no overhead when compared to C when used properly. That is why it was invented by Stroustrup in the first place. It gives you the same speed as C and level abstraction in your code. Yes it has overhead when using excpetions as HS2 said, but AVR-GCC doesn’t support exceptions anyways. You may have overhead also when using polymorphism, but it’s ONE extra PUSH operation , since compiler has to take a look in v_table, or for example when you use inhertance. But I always stick to rule from C# & Java by using interfaces withouth multiple inheritance. My code sizes in C++ compared to ones in C are the exact same size. And C++ is a strong tool, it gives you a level of abstraction, which enables you to write more readable code, more maintanable and most important resuable code with encapsulation of data, which makes your code less prone to errors. So I do not see why people alywas come with overhad of C++ thing. I would always choose OOP language compared to sequencial languages like C.

richard_damon wrote on Saturday, August 05, 2017:

Generally, I agree with that, C++ can be a very low cost additioon (which is why I wrote my C++ wrappers). In this case though, he was so worried about size that he was automatically making the bug threatening size reduction of an angle variable to save 1 BYTE of memory. Declaring an out-of-line constructor can cost that much by forcing a function call.

My point is more that if he feels it is ok to use C++, he shouldn’t be starting at that level of hand optimizing memory usage in other places.