Creating a task from C++

Discussion to talk about software related topics only.
Post Reply
v8dave
Posts: 333
Joined: Thu Dec 31, 2009 8:31 pm

Creating a task from C++

Post by v8dave »

Hi all,

Up until now I have always done plain C with UCOS but now I have a need to create a single class to encapsulate serial port handling for 8 serial ports. As each port will be doing exactly the same function I don't want to write it 8 times so I have decided to try and use C++ classes for this.

The use of classes I understand but I can't seem to get the OSTaskCreate to take the pointer to the task function. I have followed the Wiki at:

http://wiki.embeddedethernet.com/Developing_in_Cpp

This is what I have so far in the header file:

Code: Select all

class InDriver {
	int TXD, RXD;		// eTPU TX and RX
	int fd;				// File descriptor
	int BaudRate;		// Serial baud rate
	int Bits;			// Number of bits
	int TxPortEn;		// GPIO TX Port enable
	int ATSMode;		// Serial protocol mode
	int Port;			// Which port, 0 - 7
	DWORD TaskStack[USER_TASK_STK_SIZE] __attribute__( ( aligned( 4 ) ) );

public:
	InDriver(int Priority, char *Name, int InitTxd, int InitRxd, int InitBaudRate, int InitBits,
			 int InitTxPortEn, int InitATSMode, int InitPort);
	~InDriver();
private:
	void InDriverTask(void *ptr);
	void SetInTx();
	void SetInRx();
	void WaitDataOut();
	int SendData(char* Data, int DataLen);
};

Code: Select all

InDriver::InDriver(int Priority, char *Name, int InitTxd, int InitRxd, int InitBaudRate,
				   int InitBits, int InitTxPortEn, int InitATSMode, int InitPort)
{
	TXD = InitTxd;
	RXD = InitRxd;
	BaudRate = InitBaudRate;
	Bits = InitBits;
	TxPortEn = InitTxPortEn;
	Port = InitPort;

    if ( OSTaskCreatewName(InDriverTask,
						   NULL,
						   ( void * ) (TaskStack + USER_TASK_STK_SIZE),
						   ( void * ) TaskStack,
						   Priority,
						   Name) != OS_NO_ERR )
    {
       iprintf( "*** Error creating task InDriver\r\n" );
    }
}
I get the following error at the call to OSTaskCreatewName():

argument of type 'void (InDriver::)(void*)' does not match 'void (*)(void*)'

The task itself is written as follows (partial code onyl)

Code: Select all

void InDriver::InDriverTask(void *ptr)
{
    //
    // Open the serial port to input
    //

	fd = eTPUFDUartOpen(RXD, TXD, Bits, Bits, FS_ETPU_UART_NO_PARITY, FS_ETPU_PRIORITY_MIDDLE);
Any clues to what I am doing wrong...?

By the way, declaring the task as static void in the header files create even more errors:

cannot call member function 'void InDriver::SetInRx()' without object
cannot call member function 'void InDriver::SetInTx()' without object
from this location ats_indriver1.cpp
from this location ats_indriver1.cpp
from this location ats_indriver1.cpp
invalid use of member 'InDriver::ATSMode' in static member function
invalid use of member 'InDriver::Bits' in static member function
invalid use of member 'InDriver::fd' in static member function
invalid use of member 'InDriver::Port' in static member function
Ridgeglider
Posts: 513
Joined: Sat Apr 26, 2008 7:14 am

Re: Creating a task from C++

Post by Ridgeglider »

V8: If you are declaring the class as a static global object, the class constructor will run very early in the boot process before the OS is set up. (If you put a printf in the constructor, you will see it appear as one of the first messages after boot.) When I've built classes like this that use the OS, I've had to make the constructor bland by avoiding any OS calls. Within the class, I write another public function InitMyClass that can be called to do things like Initing OS stacks, semaphores, critical sections, etpu inits, etc, once the OS is up and running. This Init basically populates fields that were instantiated but empty after the constructor runs. I know there must be a better way to do this than by essentially disabling the constructor so it only can do a partial job, but so far I have not found it. The catch is that you MUST remember to call the InitMyClass method early within UserMain before any other calls to the class methods happen. I know some of Tod's examples address this issue, but I'm sorry to say that I didn't follow them. I'm not sure this is what's going on in your case, but perhaps it's a clue? I am interested in what you learn.
v8dave
Posts: 333
Joined: Thu Dec 31, 2009 8:31 pm

Re: Creating a task from C++

Post by v8dave »

Hi RG,

The class objects are only declared as pointers and then I new them when I am ready to create them. I only create them if needed, ie, the serial port is enabled.

I understand what you are saying so I could try this and see if I get rid of the error. Looking at Tod's Wiki entry it appears similar to what you describe. I would have thought that as I am declaring them on the fly (which might be the reason for the failure) I could put the TaskCreate in the constructor.

I'll report back my findings.

Dave...
User avatar
tod
Posts: 587
Joined: Sat Apr 26, 2008 8:27 am
Location: Southern California
Contact:

Re: Creating a task from C++

Post by tod »

Dave,
Sorry I don't have a lot of time to look at this in detail, but I'm going to guess that you are trying to pass a pointer to a member function to an OS method and the OS methods all want a pointer to a function (not a pointer to a member function). They don't have the same signature. In other words you can't pass "InDriverTask" directly. You have to do it indirectly.

The page you were reading is the correct one but make sure you see the entry on Using Class Member Functions in calls to the OS This directly addresses the issue you are seeing.

A static class member function or a member function of a normal class that is static do have the same signature but I think they are not good workarounds. In general static classes can get you in trouble (see the section on NSLOs). The section I referenced is the method I use, it seems overly complicated at first but like most things once you use the technique a few times, it just becomes an idiom.

If that is not the problem let me know and when I get back in the office later today I'll take a more detailed look.
rsg
Posts: 54
Joined: Thu May 15, 2008 5:36 am

Re: Creating a task from C++

Post by rsg »

I think Tod is on the right track. To be more concrete, this is what I do:

MyTaskClass.h:

Code: Select all

class MyTaskClass
{
// ...
private:
  static void myTaskWrapper(void *pd);
  void myTask();
// ...
};
MyTaskClass.cpp:

Code: Select all

void MyTaskClass::myTaskWrapper(void* pd)
{
  // Here, because you are in a static function, you can't access members
  MyTaskClass* pMe = (MyTaskClass*)pd;
  pMe->commandTask();
}

void MyTaskClass::myTask()
{
  while (1) {
    // In here, you can freely access any members, as you are now in a regular
    // class method.
  }
}
Here then is how you start your task:

Code: Select all

  // ...
  static uint32_t myStack[2*USER_TASK_STK_SIZE];
  OSTaskCreate(MyTaskClass::myTaskWrapper, this, &myStack[2*USER_TASK_STK_SIZE], myStack, 50);
  // ...
Hope this helps!
-Bob
User avatar
tod
Posts: 587
Joined: Sat Apr 26, 2008 8:27 am
Location: Southern California
Contact:

Re: Creating a task from C++

Post by tod »

Dave and anyone else that uses Tasks. I use a base class that does almost all the work for you. I posted the code on gist. Rather than write a lengthy response here, I wrote up details on this approach as a blog post. I would think you would find it worth your time to read it, and do some copying and pasting of code.
v8dave
Posts: 333
Joined: Thu Dec 31, 2009 8:31 pm

Re: Creating a task from C++

Post by v8dave »

Hi Tod,

You are a gentleman.. I used your TaskBase code and now it is working!

If you where closer by I would buy you a drink or few!! :D

Best regards
Dave...
User avatar
tod
Posts: 587
Joined: Sat Apr 26, 2008 8:27 am
Location: Southern California
Contact:

Re: Creating a task from C++

Post by tod »

Dave,
I'm just happy you took the time to download it and try it. Although I am thirsty! Maybe at the first World Wide NetBurner Developer Conference I can collect?

Tod
Post Reply