Протопотоки в C++
Привет, я работаю над встроенной системой, которая управляет лифтом через последовательную шину. Каждый раз, когда я отправляю сообщение (пакет содержит адрес, длину данных, данные, crc), мне нужно ждать ответа лифта, который представлен пакетом подтверждения.
ACK устанавливается каждый раз, когда я получаю пакет от лифта. Получение сообщений осуществляется через прерывание. Пакет ACK выглядит так:
0xA0,0x00,0x00,0x00,0x00
и когда он приходит, я устанавливаю ACK true
Вот моя функция отправки:
bool TransferService::send(char recAddr,char dataLength, char *data){
pc.putc(startByte);
pc.putc(recAddr);
pc.putc(controllerAddress);
pc.putc(dataLength);
for (int i = 0; i < dataLength; i++) {
pc.putc(data[i]);
}
pc.putc(getCrc(recAddr,controllerAddress,data, dataLength));
_messageReceived = false;
timer.reset();
timer.start();
ACK = false;
do {
if (ACK) {
break;
}
} while(timer.read_ms()<=15);
timer.stop();
if (!ACK) {
send(recAddr,dataLength,data);
}
}
Это только попытка, это не работа.
bool TransferService::send(char recAddr,char dataLength, char *data){
PT_BEGIN();
timer.reset();
timer.start();
do {
pc.putc(startByte);
pc.putc(recAddr);
pc.putc(controllerAddress);
pc.putc(dataLength);
for (int i = 0; i < dataLength; i++) {
pc.putc(data[i]);
}
pc.putc(getCrc(recAddr,controllerAddress,data, dataLength));
_messageReceived = false;
PT_WAIT_UNTIL(!timer.read_ms() <=10 || ACK);
} while(timer.read_ms() <=10);
PT_END();
}
Мой вопрос заключается в том, как сделать так, чтобы первая функция работала корректно с использованием прототоков.
1 ответ
Структура функции потока в реализации Protothreads Адама Данкела довольно ясна, и ваша функция явно не следует ей.
Функция потока Protothread должна возвращать int
и обычно имеет подпись:
int threadfunction( struct pt* pt ) ;
и должен быть определен с использованием PT_THREAD
макрос таким образом:
PT_THREAD( threadfunction( struct pt* pt ) )
{
PT_BEGIN(pt) ;
// thread body here
PT_END(pt) ;
}
Из документации Protothread...
Функция protothread всегда должна возвращать целое число, но никогда не должна явно возвращать - возврат выполняется внутри операторов protothread.
Глядя на определение PT_THREAD
Я не вижу ничего, что могло бы помешать его использованию с функцией-членом C++ или использованием дополнительных аргументов, кроме pt
, в этом случае следующее ближе к правильному:
PT_THREAD( TransferService::send( struct pt* pt, char recAddr, char dataLength, char *data )
{
PT_BEGIN( pt );
timer.reset();
timer.start();
pc.putc( startByte );
pc.putc( recAddr );
pc.putc( controllerAddress );
pc.putc( dataLength );
for( int i = 0; i < dataLength; i++ )
{
pc.putc( data[i] );
}
pc.putc( getCrc( recAddr, controllerAddress, data, dataLength ) );
_messageReceived = false;
PT_WAIT_UNTIL( pt, timer.read_ms() > 10 || ACK );
PT_END();
}
В вашей попытке у вас был и цикл do-while, и PT_WAIT_UNTIL
, но ПОДОЖДИТЕ делает цикл ненужным. На самом деле нет необходимости передавать pt
аргумент, необходимый struct pt
может быть членом класса или даже глобальным (хотя это было бы опрометчиво).
Обратите внимание, что вышеизложенное следует шаблону, очевидному в вопросе, но это будет необычный шаблон дизайна; чаще всего поток выполняется бесконечно, а не для одной транзакции. Не зная всего вашего приложения, я бы предложил, чтобы поток работал на более высоком уровне, чем отдельная транзакция. send()
функция, такая, что у вас есть поток "отправителя", который может многократно выполнять операции отправки, не завершаясь, как показано в следующем наброске (т.е. не полный или "реальный" код и с большим количеством предположений):
// Constructor...
TransferService::TransferService()
{
PT_INIT( &m_pt ) ; // Where thread state m_pt
// is a member variable of
// type struct pt (or just pt
// since this is C++)
...
}
// Thread function
PT_THREAD(TransferService::senderThread() )
{
PT_BEGIN(&m_pt);
for(;;)
{
PT_WAIT_UNTIL( &m_pt, ready_to_send ) ;
timer.reset();
timer.start();
send( recAddr, dataLength, data ) ;
PT_WAIT_UNTIL( &m_pt, timer.read_ms() > 10 || ACK );
}
PT_END(pt);
}
// Single send transaction function
bool TransferService::send( char recAddr, char dataLength, char *data )
{
pc.putc( startByte );
pc.putc( recAddr );
pc.putc( controllerAddress );
pc.putc( dataLength );
for( int i = 0; i < dataLength; i++ )
{
pc.putc( data[i] );
}
pc.putc( getCrc( recAddr, controllerAddress, data, dataLength ) );
_messageReceived = false;
}
Обратите внимание, что из-за способа, которым PT_...
API работает, PT_WAIT_UNTIL
должен быть в той же функции, что и PT_BEGIN
а также PT_END
, следовательно, перемещение таймера ожидания в коде выше.