Как сопоставить концепцию класса C ++ с функциями osTimerNew () и osThreadNew () в C?
Как использовать функцию-член C ++ в качестве реализации обратного вызова osTimerNew () и osThreadNew () в Keil RTOS2.
Спасибо
Как сопоставить концепцию класса C ++ с функциями osTimerNew () и osThreadNew () в C?
Как использовать функцию-член C ++ в качестве реализации обратного вызова osTimerNew () и osThreadNew () в Keil RTOS2.
Спасибо
С объектно-ориентированной точки зрения я бы предположил, что вы обращаетесь к этому неверно. Нет прямой связи между потоком и таймером, которая указывает, что они должны быть в одном классе, и это не вопрос механического сопоставления функций с классами. Скорее вам нужно идентифицировать классы, то есть вещи, для которых вы хотите создать экземпляры объектов, а затем определить интерфейсы - методы, которые определяют функции и возможности этих объектов.
С этой целью я бы предложил, чтобы поток (или задача) и таймер были отдельными классами. Вы можете создать класс более высокого уровня для периодической задачи, который затем может быть составлен и / или получен из этих других классов. Например:
or
Давайте для начала рассмотрим класс cTask
. Было бы неправильно (или, по крайней мере, бессмысленно) просто обернуть функцию osThreadNew()
в оболочку класса; скорее вам нужно думать о задаче как о class
и учитывать все то, что может делать класс. С этой целью справочник по CMSIS RTOS может послужить источником вдохновения для организации документации. В нем есть разделы Управление потоками и Флаги потоков, которые можно использовать для разработки интерфейса cTask
.
У простого класса задачи может быть следующий пример интерфейса:
class cTask
{
public:
typedef uint32_t tEventFlags ;
cTask();
virtual ~cTask();
eOSStatus spawn( const char* taskname,
int taskpriority = DEFAULT_PRIORITY,
int stack_size = DEFAULT_STACK, void* stack_ptr = 0 );
void setEvent( tEventFlags flags ) const ;
static void delay(int period);
static void lock();
static void unlock();
int getPriority() const ;
int setPriority(int new_priority);
private :
virtual void threadMain() = 0 ;
tEventFlags eventWait( tEventFlags flags, int timeout ) ;
static void entry_point( void* arg )
{
cTask* instance = reinterpret_cast<cTask*>(argv) ;
instance->threadMain() ;
}
} ;
И тогда у вас может быть задача:
class cMyThread : cTask()
{
public :
cMyThread()
{
spawn( "mythread" ) ;
}
void someEvent()
{
setEvent( 0x0001 ) ;
}
void someOtherEvent()
{
setEvent( 0x0002 ) ;
}
private:
void threadMain()
{
for(;;)
{
tEventFlags event eventWait( 0x0003, WAIT_FOREVER ) ;
if( (event & 0x0001) != 0 )
{
// process some event
}
if( (event & 0x0002) != 0 )
{
// process some other event
}
}
}
} ;
Таким образом, вы можете создать экземпляр и взаимодействовать с экземпляром od cMyThread
таким образом:
cMyThread thread_a ;
cMyThread thread_b ;
thread_a.someEvent() ;
thread_b.someOtherEvent() ;
Очевидно, что интерфейс мог бы быть гораздо более обширным, и вы захотели бы добавить классы для семафоров, мьютексов, очередей сообщений, а также таймеров.
Вышеупомянутое является только иллюстративным; как видите, возможно, предстоит еще много работы, но чтобы ответить на ваш вопрос, osThreadNew () _ 9_cTask :: spawn () _ 10_cTask :: threadMain () _ 11_entry_point () _ 12_this` указатель.
Вы бы применили аналогичный подход к классу cTimer
с учетом определения интерфейса с точки зрения того, что может делать таймер. такие как запуск, отмена, ожидание, установка обработчика событий и т. д.
Нет необходимости рабски предоставлять интерфейс для каждой функции ОСРВ CMSIS; Слой C ++ дает возможность абстрагироваться от некоторых деталей во что-то более простое в использовании и более легкое для переноса на какой-либо другой API ОСРВ.
Вы передаете это osTimerNew () / osThreadNew () вместо параметра аргумента void *.
struct task
{
task ( uint32_t timer_period_ms )
{
// === casting (needs must)
using fp = void ( task::* ) ( void * );
using os_fp = void ( * ) ( void * );
auto cast =
[] ( fp in )
{
union {
fp in;
os_fp out;
} u { in };
return u.out;
};
auto timer_id = osTimerNew
(
cast ( &task::rtos_timer_callback ),
osTimerPeriodic,
this, // let RTOS know about the object
nullptr
);
m_id_thread = osThreadNew
(
cast ( &task::rtos_thread_callBack ),
this, // let RTOS know about the object
nullptr
);
osTimerStart ( timer_id, timer_period_ms );
}
virtual ~task() = default;
virtual void do_work () = 0;
private:
void rtos_timer_callback ( void * pvArg )
{
osThreadFlagsSet ( m_id_thread, 0x01 );
}
__NO_RETURN void rtos_thread_callBack ( void * pvArg )
{
while (1)
{
osThreadFlagsWait ( 0x01, osFlagsWaitAny, osWaitForever );
do_work ();
}
}
private:
osThreadId_t m_id_thread {};
};
Теперь используйте класс задачи:
struct d_task_0 : public task
{
d_task_0 ( uint32_t timer_period_ms ) : task { timer_period_ms } {}
void do_work () final
{
// called every d_task_0::timer_period_ms
}
};
и создадим еще одну задачу:
struct d_task_1 : public task
{
d_task_1 ( uint32_t timer_period_ms ) : task { timer_period_ms } {}
void do_work () final
{
// called every d_task_1::timer_period_ms
}
};
И напоследок создадим воркеров:
d_task_0 worker0 { 500 }; // d_task_0::do_work () called every 500ms
d_task_1 worker1 { 800 }; // d_task_1::do_work () called every 800ms
Документация RTOS2:
и реализация:
https://github.com/ARM-software/CMSIS_5/tree/develop/CMSIS/RTOS2
Моя цепочка инструментов: Keil MDK-ARM Plus 5.33.0.0; ArmClang / Ссылка v6.15
Решение для кастинга пришло отсюда: Приведение между void * и указатель на функцию-член
Другой способ кастинга:
using os_fp = void ( * ) ( void * );
void ( task::*pTimer ) ( void * ) = &task::rtos_timer_callback;
void * task_timer = ( void*& ) pTimer;
auto timer_id = osTimerNew
(
reinterpret_cast<os_fp>(task_timer),
osTimerPeriodic,
this, // let RTOS know about the object
nullptr
);
Источник: Получить адрес в памяти функции-члена?
reinterpret_cast<>
указатель на тип класса и вызвать функцию-член.
- person Frank; 04.06.2021