|
Реализация конечного автомата на 8битном контроллере., Около 20 состояний. Хорошие исходники бы посмотреть. |
|
|
|
Jul 9 2017, 10:42
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Всем привет! Сейчас сделано как-то так. Прекрасно работает, расширяется. Всем доволен, но хотелось бы сравнить с тру 8ми битным подходом. Собственно какую-нибудь хорошую реализацию конечного автомата и хотелось бы где-то посмотреть. Код enum DeviceEvents { DEVEV_HEARTBEAT, //this event occures every ~100ms DEVEV_STARTBTN_PRESSED, //etc... DEVEV_NO_EVENT = 0xff, }
enum DeviceState_enum { DEVSTATE_OFF, DEVSTATE_LOCKED, //etc.... DEVSTATE_INVALID = 0xff, }
//state interface class struct DeviceStateImplementation { virtual void EnterThisStateFrom( const DeviceState_enum &prevState ) = 0; //Handles event and returns new device state in response of event virtual DeviceState_enum HandleEvent( const DeviceEvents &event ) = 0; };
//Implementation of DEVSTATE_OFF state class StateImpl_OFF: public DeviceStateImplementation { timestamp_t MillisecondsCounter; uint8_t MinutesCounter; bool ReactOnStartBTNRelease;
void ResetMillisecondsCounter(); public: virtual void EnterThisStateFrom( const DeviceState_enum &prevState ); virtual DeviceState_enum HandleEvent( const DeviceEvents &event ); };
//Implementation of DEVSTATE_OFF state void StateImpl_OFF::EnterThisStateFrom( const DeviceState_enum &prevState ) { TurnOffAllOutputs(); DisableRPMMeasure(); G_SettingsStateSelector.ResetPressCounter(); ReactOnStartBTNRelease = false; ResetMillisecondsCounter(); }
void StateImpl_OFF::ResetMillisecondsCounter() { MillisecondsCounter = GetTickCount(); MinutesCounter = 0; }
DeviceState_enum StateImpl_OFF::HandleEvent( const DeviceEvents &event ) { DeviceState_enum retval = DEVSTATE_OFF;
switch(event) { case DEVEV_HEARTBEAT: ACC_LEDBlinker.DoTheJob();
if( G_SettingsStateSelector.GetSelectedState() != DEVSTATE_INVALID ) { ACC_LEDBlinker.StartEndlessBlinkSequence(100, 100);//fast blinking indicating some settings mode was selected } break;
case DEVEV_STARTBTN_PRESSED: ReactOnStartBTNRelease = true; break;
case DEVEV_STARTBTN_RELEASED: //react on release only if press also was made in this state if( ReactOnStartBTNRelease ) { ResetMillisecondsCounter(); if( GlobalVarsRef.BrakePedal.GetButtonState() == GPIOBTNSTATE_PUSHED ) { DeviceState_enum SelectedSettingsModeState = G_SettingsStateSelector.GetSelectedState(); if( SelectedSettingsModeState != DEVSTATE_INVALID ) retval = SelectedSettingsModeState; else retval = DEVSTATE_CRANKING; } else { retval = DEVSTATE_IGNON; } } break;
case DEVEV_STARTBTN_LONGPRESS: if( GlobalVarsRef.BrakePedal.GetButtonState() == GPIOBTNSTATE_PUSHED ) retval = DEVSTATE_FORCEDCRANKING; else retval = DEVSTATE_ACCON; break;
case DEVEV_BREAKPEDAL_PRESSED: ResetMillisecondsCounter(); G_SettingsStateSelector.SelectorWasPressed();
ACC_LED::Clear(); ACC_LEDBlinker.StartBlinkSequence(2, 50, 100); break;
case DEVEV_BREAKPEDAL_RELEASED: ACC_LED::Clear(); ACC_LEDBlinker.AbortBlinkSequence(); break;
case DEVEV_ENGINE_STARTED: retval = DEVSTATE_RUNNING; break;
case DEVEV_ENGINE_STALLED: break; }
return retval; }
/////main state machine implementation class MainStateMachine { public: void EnterStateForced( const DeviceState_enum &state ); void ProcessEvent( const DeviceEvents &evnt );
private: DeviceStateImplementation *CurrentDevStateImplementation; //<----pointer to state interface class DeviceState_enum CurrentDevState;
void SetNewDeviceState( const DeviceState_enum &newDevState ); };
void MainStateMachine::ProcessEvent( const DeviceEvents &evnt ) { DeviceState_enum newDevState = CurrentDevStateImplementation->HandleEvent( evnt ); if( newDevState != CurrentDevState ) SetNewDeviceState( newDevState ); }
void MainStateMachine::SetNewDeviceState( const DeviceState_enum &newDevState ) { switch( newDevState ) {
case DEVSTATE_OFF: CurrentDevStateImplementation = &OFFState; break; case DEVSTATE_LOCKED: CurrentDevStateImplementation = &LockedState; break; } CurrentDevStateImplementation->EnterThisStateFrom( CurrentDevState ); CurrentDevState = newDevState; }
//main loop simplifiyed int main() { while(1) { event = DEVEV_HEARTBEAT; while( event != DEVEV_NO_EVENT ) { MainStateMachine.ProcessEvent( event ); event = DetectOneMoreEvents(); } } }
--------------------
The truth is out there...
|
|
|
|
|
Jul 10 2017, 07:37
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(sigmaN @ Jul 9 2017, 13:42)  Всем доволен, но хотелось бы сравнить с тру 8ми битным подходом. Собственно какую-нибудь хорошую реализацию конечного автомата и хотелось бы где-то посмотреть. Биты то при чем? Реализация автомата - дело интимное. При реализации UI как автомата параметры такие: - состояний - около сотни - событий - два десятка (кнопки и таймеры) - новое состояние определяется внутри обработки конкретного состояния (т.к. не все, что происходит и изменяет состояния - это события) - изменение состояния - тоже событие (удобно для единообразия) - Х-макросы для перечисления имен функций, названий состояний, текста в первой строке, особенностей поддержки каждого состояния (например, чистить или нет экран) - механизм возврата в предыдущее состояние Очень важная для меня цель была - прозрачность понимания, т.к. приходится корректировать проекты после большого перерыва.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Jul 11 2017, 06:33
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(sigmaN @ Jul 11 2017, 02:02)  . . . В связи с чем и становится интересно, а будет ли реальная экономия хоть чего-нибудь если все эти состояния положить в огромный switch, который потом IAR доблестно превратит в таблицу с адресами функций... Что в итоге будет снова ооочень напоминать vtable ) Смотря как компилятором реализуется работа vtable и какая "вложенность". Если использовать правильный switch (опятьже зависит от комилятора) который генерирует "вычисляемые" переходы. "правильный" - это в IAR конструкция Код switch ( __even_in_range(TBIV, 14) ) { ...
|
|
|
|
|
Jul 11 2017, 06:38
|
Частый гость
 
Группа: Участник
Сообщений: 91
Регистрация: 3-07-11
Пользователь №: 66 028

|
Dog Pawlowa, можно тоже исходники глянуть? m65535625@gmail.com А то как не крути вечно такой колхоз получается (Передача пакета данных по usart) CODE void Sendpackage(void){ static uint8_t mode; static uint16_t counter; uint8_t* Data; switch(mode){ case 0:{ //--------------------------------------------- //--------Ожидание команды на отправку--------- //--------------------------------------------- if(TransmitDataStart==Transmit_START){ TransmitDataStart=Transmit_STOP; Data=&Protocol; mode=1; } break; } //------------------------------------------------- case 1:{ if(counter<sizeof(ProtocolExchange_t)){ if(USART3->SR & USART_SR_TC){ USART3->DR =(uint8_t)Data[counter]; counter++; } }else{ mode=0; } break; } //------------------------------------------------- } }
|
|
|
|
|
Jul 11 2017, 10:02
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Цитата Смотря как компилятором реализуется работа vtable и какая "вложенность". Ну "вложенность" получается 1. Указатель на абстрактный класс указывает на реализацию класса. Про __even_in_range() не знал, протестируем Add: Последний IAR STM8 Error[Pe020]: identifier "__even_in_range" is undefined main.cpp 140
--------------------
The truth is out there...
|
|
|
|
|
Jul 11 2017, 10:46
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(sigmaN @ Jul 11 2017, 13:02)  Error[Pe020]: identifier "__even_in_range" is undefined main.cpp 140 __even_in_range() встречал только в IAR for MSP430. Сдаётся мне, что макрос этот характерен только для данного ядра. Насколько помню: там (в MSP430) значения некоторых регистров периферии, содержащие перечисления, сделаны с шагом ==2 (т.е. - значения enum-ов для них идут с шагом 2, а не 1 как в стандарте). Сделано это чтобы такое значение можно было просуммировать с адресом начала таблицы переходов (по switch) и сразу выполнить ветвление без лишних команд. even - потому что через 2, так как размер памяти - 16 бит. В этом макросе первый аргумент - это регистр, содержащий перечисление в таком формате, второй аргумент определяет размер таблицы переходов. В STM8 регистры периферии с перечислениями не выполнены в таком формате, соответственно и макрос этот там бесполезен.
|
|
|
|
|
Jul 11 2017, 10:52
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(sigmaN @ Jul 11 2017, 13:02)  Error[Pe020]: identifier "__even_in_range" is undefined main.cpp 140  есть в IAR/MSP430 - используется в switch которые в векторах прерываний. (для ускорения работы и для "одноразового" чтения параметра switch - если в кач-ве параметра исползован регистр с "автосбросом") Есть смысл посмотреть в DASM как отрабатывают вирт. таблицы в Вашем компиляторе. Скорее всего по томуже принципу.
|
|
|
|
|
Jul 11 2017, 12:40
|
Местный
  
Группа: Свой
Сообщений: 339
Регистрация: 5-05-11
Пользователь №: 64 797

|
Цитата(sigmaN @ Jul 11 2017, 14:47)  Честно говоря пока у меня складывается впечатление, что если полиморфизм и проиграет голым уродливым свитчам то не значительно. Но без замеров это всё конечно пустые слова. По поводу C++ vs C есть замечательное видео: https://www.youtube.com/watch?v=D7Sd8A6_fYU...eature=youtu.be
|
|
|
|
|
Jul 11 2017, 20:24
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(pokk @ Jul 11 2017, 09:38)  А то как не крути вечно такой колхоз получается Что касается такого колхоза, то этого хватает у каждого. И вообще это не колхоз, это классика - см ниже!  Если речь идет о автоматах, кроющих практически всю функциональность устройства, то тут все становится сложнее, потому что не есть проблема реализовать это все, а проблемой является выбор собственного стиля и подхода, чтобы было все понятно и прозрачно в первую очередь для самого себя. CODE #define TRANSPORT_TIMEOUT_SERIAL_SEC 10 #define TRANSPORT_TIMEOUT_TCPIP_SEC 5 #define LONG_RUN_TIMEOUT_SEC 60 #define AUTO_PICKUP_TIME_MS 500 #define NUM_RETRIES 3
// added by me timeouts: #define RECEIVE_TIMEOUT_MS 1000 #define ANSWER_TIMEOUT_MS 100
enum { errNO=0, errMESSAGE_SIZE, errOP_CODE, errRECORDS_QTY, errINDEX1, errINDEX2, errINDEX3, errNOT_SUPPORTED, errREAD_ONLY, errWRONG_SIZE, errWRITE_ONLY, errWRONG_OPCODE, errSIZE, };
#define RECEIVED_MESSAGE_LENGTH ((int)com1.rx_buf[1]+((int)com1.rx_buf[2])<<8) #define RECEIVED_TYPE (com1.rx_buf[3]) #define RECEIVED_SEQUENCE (com1.rx_buf[4]) #define ctrlRECEIVED (com1.rx_buf[5]) #define ctrlACK ('A') #define ctrlNAK ('N') #define ctrlEND ('E') #define ctrlWAIT ('W') #define ctrlPICKUP ('P') #define ctrlDROP ('D') #define XML_PTR_FIRST (char*)(com1.rx_buf+5) #define XML_PTR_LAST (com1.rx_buf+RECEIVED_MESSAGE_LENGTH-4) #define ETX_INDEX (RECEIVED_MESSAGE_LENGTH-3)
/* Control message 1 byte STX StartMessage 2 byte Unsigned Binary Length Length of the entire message; including StartMessage and EndMessage but excluding CRC bytes 1 byte ASCII Type 'C' 1 byte Unsigned Binary Sequence 0x00 is invalid value 1 byte ASCII ControlFunction 'A' ACK 'N' NAK 'E' End Sequence 'W' Wait 'P' Pickup 'D' Drop Link 1 byte ETX EndMessage 2 byte Binary CRC See CRC Calculation
Payload message 1 byte STX StartMessage 2 byte UnsignedBinary Length Length of the entire message; including StartMessage and EndMessage but excluding CRC bytes 1 byte ASCII Type 'P' 1 byte Unsigned Binary Sequence 0x00 is invalid value Variable Unsigned Binary Payload The transported application protocol message 1 byte ETX EndMessage 2 byte Unsigned Binary CRC See CRC Calculation
*/
#define XML_START_OFFSET 5 #define XML_END_OFFSET 2
char payload_sent_wait_control; char payload_sent_time; char payload_sent_qty;
void PrepareTransportPayloadHeader(void) { com1.tx_head=com1.tx_buf; *com1.tx_head++=STX; com1.tx_head+=2; // pass length *com1.tx_head++='P'; // payload type *com1.tx_head++=RECEIVED_SEQUENCE; }
void PrepareTransportControlMessage(char com) { int my_checksum;
com1.tx_head=com1.tx_buf; *com1.tx_head++=STX; *com1.tx_head++=7; // length = 7 *com1.tx_head++=0; *com1.tx_head++='C'; // control type *com1.tx_head++=RECEIVED_SEQUENCE; *com1.tx_head++=com; *com1.tx_head++=ETX; my_checksum=crc16_ccitt(com1.tx_buf, 7); *com1.tx_head++=(char)(my_checksum&0x00ff); *com1.tx_head++=(char)(my_checksum>>8); }
void PrepareTransportTrailer(void) { int my_size; int my_checksum;
*com1.tx_head++=ETX; my_size=com1.tx_head- com1.tx_buf; com1.tx_buf[1]=(char)(my_size&0x00ff); com1.tx_buf[2]=(char)(my_size>>8); my_checksum=crc16_ccitt(com1.tx_buf, my_size); *com1.tx_head++=(char)(my_checksum&0x00ff); *com1.tx_head++=(char)(my_checksum>>8);
}
enum { UNCONFIGURED, RESTART_RX, WAITING_STX, WAITING_ETX, WAITING_BCC1, WAITING_BCC2, ANALYSE_MESSAGE, PREPARE_MESSAGE, START_SEND_MESSAGE, WAITING_START_TRANSMIT, WAITING_END_TRANSMIT, FINAL, PREPARE_NAK, PREPARE_DROP, PREPARE_END };
char transport_error;
#define USE_TIMEOUT 1
u16 TimeoutRunning(void) { #ifdef USE_TIMEOUT return com1.timeout; #else return 1; #endif }
void fRnt(void) {
static u16 my_checksum; static u16 message_checksum;
switch (pstate) { case UNCONFIGURED: Com1Init(); pstate++; break;
case RESTART_RX: Com1SetReceive(COM1_WAIT_STX); prot_error=errNO; pstate++; OFF_LED5(); OFF_LED4(); break;
case WAITING_STX: if ( payload_sent_wait_control ) { if (payload_sent_qty<2) { if (event==evSec) { if (payload_sent_time<10) payload_sent_time++; else { pstate=START_SEND_MESSAGE; payload_sent_qty++; payload_sent_time=0; } } } else // all attempts done payload_sent_wait_control=0; } else { payload_sent_qty=0; payload_sent_time=0; }
switch (com1.rx_status) { case COM1_RECEIVED: pstate=RESTART_RX; // buffer is over break;
case COM1_RECEIVING_: pstate=WAITING_ETX; com1.timeout=RECEIVE_TIMEOUT_MS; break; } break;
case WAITING_ETX: ON_LED4(); // recieving starts if (TimeoutRunning()) { if (com1.rx_head>com1.rx_tail) { switch (*com1.rx_tail) { case ETX: // case EOT: com1.rx_tail++; if (com1.rx_tail>com1.rx_buf+5) pstate++; break;
default: com1.rx_tail++; break; } } } else pstate=RESTART_RX; break;
case WAITING_BCC1: if (TimeoutRunning()) { if (com1.rx_head>com1.rx_tail) { com1.rx_tail++; pstate++; } } else pstate=RESTART_RX; break;
case WAITING_BCC2:
if (TimeoutRunning()) { if (com1.rx_head>com1.rx_tail) { com1.rx_tail++; pstate++; Com1SetReceive(COM1_PASSIVE); } } else pstate=RESTART_RX; break;
case ANALYSE_MESSAGE: OFF_LED4(); if (com1.rx_head<com1.rx_buf+8) // too short message { pstate=RESTART_RX; break; } my_checksum=crc16_ccitt(com1.rx_buf, com1.rx_tail-com1.rx_buf-2); message_checksum= ( ((unsigned int)*(com1.rx_head-2)) ) | ( ((unsigned int)*(com1.rx_head-1))<<8 );
#if (defined IGNORE_CHECKSUM)
#elif (defined USE_CHECKSUM) if ( (my_checksum!=message_checksum) || // wrong checksum (*(com1.rx_head-3) !=ETX) //not ETX ) { pstate=RESTART_RX; break; } #else #error "What to do with checksum?" #endif
switch (RECEIVED_TYPE) { case 'C': // control message switch (ctrlRECEIVED) { case ctrlEND: pstate=PREPARE_END; break; case ctrlDROP: pstate=PREPARE_DROP; break;
default: pstate=RESTART_RX; break; } break;
case 'P': // if (!AnalyseXmlOk(XML_PTR_FIRST,(char*)com1.rx_head-4)) pstate=PREPARE_NAK; else pstate=PREPARE_MESSAGE; break; } break;
case PREPARE_MESSAGE: ON_LED5(); // answering started com1.tx_head=com1.tx_buf; PrepareTransportPayloadHeader(); PrepareXml(); PrepareTransportTrailer(); pstate++; payload_sent_wait_control=1; payload_sent_time=0; break;
case START_SEND_MESSAGE: SET_TRANSMIT(); // for driver pstate++; com1.timeout=ANSWER_TIMEOUT_MS; break;
case WAITING_START_TRANSMIT: if (!com1.timeout) { Com1SetTransmit(COM1_TRANSMITTING); pstate++; } break;
case WAITING_END_TRANSMIT: if (com1.tx_status==COM1_TRANSMITTED) if (TRANSMIT_OVER()) { com1.timeout=0; pstate++; } break;
case FINAL: OFF_LED5(); pstate=RESTART_RX; break;
case PREPARE_NAK: pstate = UNCONFIGURED; break;
case PREPARE_DROP: //0207004300440389AC ON_LED5(); PrepareTransportControlMessage('D'); com1.timeout=ANSWER_TIMEOUT_MS; SET_TRANSMIT(); pstate=WAITING_START_TRANSMIT; break;
case PREPARE_END: ON_LED5(); PrepareTransportControlMessage('E'); com1.timeout=ANSWER_TIMEOUT_MS; SET_TRANSMIT(); pstate=WAITING_START_TRANSMIT; payload_sent_wait_control=0; break;
default: pstate = UNCONFIGURED; break; } }
--------------------
Уходя, оставьте свет...
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|