Microcontroller firmware that uses a simple, yet powerful scripting language to control the timing of input and output events with high temporal resolution. Written by Mattias Karlsson
behave.h
- Committer:
- mkarlsso
- Date:
- 2017-02-07
- Revision:
- 8:872b843a3053
- Parent:
- 7:5fe7329751d4
File content as of revision 8:872b843a3053:
//#include "mbed.h" #include <stdint.h> #include <string.h> #include <string> #include <vector> #include <list> #include <deque> #include <queue> #include "hardwareInterface.h" #define MBEDHARDWARE //here is where we define which platform we are compiling for //#define FPGAHARDWARE #define MAXVARNAMESIZE 30 //The maximum number of characters of a variable name #ifdef MBEDHARDWARE #include "mbedInterface.h" #endif #ifdef FPGAHARDWARE #include "fpgaInterface.h" #endif #define BLOCKMEMORYTYPES 1 #define VARIABLEMEMORYTYPES 2 #define ENVSETTINGSMEMORYTYPES 4 /* #define NUMEVENTS 50 #define NUMCONDITIONS 150 #define NUMINTCOMPARE 150 #define NUMACTIONS 150 #define NUMPORTMESSAGES 150 #define NUMINTOPERATIONS 150 #define NUMDISPLAYACTIONS 30 #define NUMTRIGGERACTIONS 30 #define NUMFUNCTIONS 50 #define INPUTCHARBUFFERSIZE 3072 */ #define ARITHMATIC_CONDITION 0 #define OR_CONDITION 1 #define AND_CONDITION 2 using namespace std; class event; //we foreward declare this because of class interdependencies //used in the digital port class to organize digital change events /* struct changeEvent { uint32_t timeStamp; bool triggered; };*/ struct VersionInfo { int major; int middle; int minor; int updateDay; int updateMonth; int updateYear; }; class AbstractPort { public: AbstractPort(); enum Direction{in,out,none}; enum DataType{digital, analog}; Direction portDir; DataType portType; virtual void write(int outVal); virtual int read() = 0; //Must be defined in inheriting class virtual void setThresh(int threshVal); uint32_t lastChangeTime; uint32_t lastOutChangeTime; bool update(); //called from the main loop void setTriggerUpEvent(event* eventInput); //attahces a routine to an upward change void setTriggerDownEvent(event* eventInput); //attahces a routine to a downward change void clearTriggerEvents(); void setReportUpdates(bool report); int getLastChangeState(); uint32_t getTimeSinceLastChange(); int state; bool outStateChanged; event* triggerUpEventPtr; event* triggerDownEventPtr; protected: virtual bool checkForChange(); bool hasTriggerFunction; bool reportUpdates; int lastInState; int thresh; uint32_t lastChangeInterval; private: }; class AnalogPort: public AbstractPort { public: AnalogPort(); void init(sAnalogIn* IP); void init(sAnalogOut* OP); void set(int outVal); int read(); void setThresh(int threshVal); void write(int outVal); private: sAnalogOut* outPin; sAnalogIn* inPin; }; class DigitalPort: public AbstractPort { public: DigitalPort(); void init(sDigitalIn* IP); void init(sDigitalOut* OP); void set(int outVal); int read(); void write(int outVal); protected: bool checkForChange(); private: sDigitalOut* outPin; sDigitalIn* inPin; }; //The digitalPort object directly controls and keeps data about the port. Each port has //one digital out and one digital in. class digitalPort { public: digitalPort(); //digitalPort(sDigitalOut* DOP, sDigitalIn* DIP); void init(sDigitalOut* DOP, sDigitalIn* DIP); void setDigitalOut(int outVal); //int getDigitalOut(); int getDigitalIn(); int getLastChangeState(); uint32_t getTimeSinceLastChange(); uint32_t lastChangeTime; uint32_t lastOutChangeTime; void setTriggerUpEvent(event* eventInput); //attahces a routine to an upward change void setTriggerDownEvent(event* eventInput); //attahces a routine to a downward change //void addStateChange(int newState, uint32_t timeStamp); bool update(); //called from the main loop int inState; int outState; bool outStateChanged; event* triggerUpEventPtr; event* triggerDownEventPtr; private: sDigitalOut* outPin; sDigitalIn* inPin; int lastInState; uint32_t lastChangeInterval; //changeEvent lastUpEvent; //changeEvent lastDownEvent; }; //an intVariable contains an integer value and the name of that variable within the script class intVariable { public: intVariable(); intVariable(std::string& tagInput, int initialValue); void set(std::string& tagInput, int initialValue); int value; //string tag; char tag[MAXVARNAMESIZE+1]; bool isUsed; }; //ACTION SECTION-- an 'action' is a command in the script. It can be a single command, //or a block containing a set of actions //------------------------------------------------------------------------------------ //display actions are used to output text messages via the serial port. The user can display //either a static text string or the value of a single variable. class displayAction { public: displayAction(); void set(int* variable, string varNameInput); void set(string text); bool isUsed; void execute(); void release(); private: int* dVariable; string dText; }; class triggerFunctionAction { public: triggerFunctionAction(); triggerFunctionAction(int functionNum); void set(int functionNum); bool isUsed; void execute(); void release(); private: int functionNum; }; //intOpertaion is an action that does addition or subtraction of integers and returns/stores the result //these operation are very limited so far (only + or - allowed, and only one operation per object, //for example a = b + b works but a = b + c + d does not. The output value can also be set to a random number. class intOperation { public: intOperation(); /* intOperation(int randParam, const char* cmpString, int cmpValInput); intOperation(int randParam, const char* cmpString, int* cmpIntVarInput); intOperation(int* intVarInput, const char* cmpString, int cmpValInput); intOperation(int* intVarInput, const char* cmpString, int* cmpIntVarInput); intOperation(int* intVarInput, intOperation* operationInput); */ ~intOperation(); //Supported operations with rand: //a = rand(x) //a = rand(x) + 3 //a = rand(x) + b void setRandOp(int randParam, const char* cmpString, int cmpValInput, bool flipped); void setRandOp(int randParam, const char* cmpString, int* cmpIntVarInput, bool flipped); //Supported regular operations //a = 5 //a = b //a = b + 6 //a = b + c void set(int* intVarInput, const char* cmpString, int cmpValInput); void set(int* intVarInput, const char* cmpString, int* cmpIntVarInput); void set(int* intVarInput, intOperation* operationInput); //Supported operations with clock() //a = clock() //a = clock() + 5 //a = clock() + b void setClockOp(int* intVarInput); void setClockOp(const char* cmpString, int cmpValInput, bool flip); void setClockOp(const char* cmpString, int* cmpIntVarInput, bool flip); void release(); bool isUsed; int execute(); private: int randHigh; int* cmpVal; int* intVal; intOperation* opPtr; bool cmpValGlobal; bool isClockAssign; //if the current clock value is part of the operation bool inputsFlipped; int (intOperation::*executePtr)(); int addAndStore(); int subtractAndStore(); int add(); int subtract(); int equals(); }; //portMessage is an action to change a port state. class portMessage { public: portMessage(); //portMessage(digitalPort* portIn, int whichToSet, int value); //whichToSet: 1 DigitalOut; 2 State //void setMessage(digitalPort* portIn, int whichToSet, int value); //whichToSet: 1 DigitalOut; 2 State //portMessage(int* portIn, int whichToSet, int value); //whichToSet: void setMessage(int* portIn, int whichToSet, int value, AbstractPort** portVector, int portVectorLength); //whichToSet: void execute(); void release(); bool isUsed; private: int whichToSet; //hard coded port number int* port; //alternative variable port number int value; int vectorLength; AbstractPort** portVector; }; //holder class for all possible actions. This include general system commands. class action { public: action(); ~action(); action(intOperation* opInput); action(portMessage* messageInput); action(event* eventInput); //action(event* eventInput, uint32_t delay); action(displayAction* displayInput); action(sSound* soundInput); action(triggerFunctionAction* triggerFuncInput); action(int8_t sysCommandInput); //for general system commands void set(intOperation* opInput); void set(portMessage* messageInput); void set(event* eventInput); //void set(event* eventInput, uint32_t delay); void set(displayAction* displayInput); void set(sSound* soundInput); void set(triggerFunctionAction* triggerFuncInput); void set(int8_t sysCommandInput); void execute(); void execute(uint32_t blockExecTime); void release(); bool isUsed; private: intOperation* op; portMessage* message; event* eventToCreate; displayAction* displayActionPtr; sSound* sound; triggerFunctionAction* triggerFunc; //uint32_t eventDelay; int8_t sysCommand; char actionType; }; //----------------------------------------------------- //CONDITION SECTION-- a 'condition' is used in the beginning of a block (if-else blocks or while blocks) //If the condition is true, the block is exectuted during a callback //------------------------------------------------------------------------------------ //intCompare is a condition class that compares the state value of a port or //an integer variable to another integer variable or operation output class intCompare { public: intCompare(); intCompare(AbstractPort* portInput, const char* cmpString, int cmpValInput, int whichToUse); intCompare(AbstractPort* portInput, const char* cmpString, int* cmpIntVarInput, int whichToUse); intCompare(int* intVarInput, const char* cmpString, int cmpValInput); intCompare(int* intVarInput, const char* cmpString, int* cmpIntVarInput); intCompare(int* intVarInput, const char* cmpString, intOperation* cmpIntOpInput); intCompare(AbstractPort* portInput, const char* cmpString, intOperation* cmpIntOpInput, int whichToUse); void set(AbstractPort* portInput, const char* cmpString, int cmpValInput, int whichToUse); void set(AbstractPort* portInput, const char* cmpString, int* cmpIntVarInput, int whichToUse); void set(int* intVarInput, const char* cmpString, int cmpValInput); void set(int* intVarInput, const char* cmpString, int* cmpIntVarInput); void set(int* intVarInput, const char* cmpString, intOperation* cmpIntOpInput); void set(AbstractPort* portInput, const char* cmpString, intOperation* cmpIntOpInput, int whichToUse); void release(); ~intCompare(); bool isTrue(); bool isUsed; private: AbstractPort* port; int* portValPtr; int* cmpVal; int* intVal; intOperation* intOp; void setPointer(const char* cmpString); void setPointer_operation(const char* cmpString); bool (intCompare::*isTruePtr)(); bool cmpValGlobal; bool greaterThan(); bool greaterOrEqual(); bool lessThan(); bool lessOrEqual(); bool equal(); bool notEqual(); bool greaterThan_op(); bool greaterOrEqual_op(); bool lessThan_op(); bool lessOrEqual_op(); bool equal_op(); bool notEqual_op(); }; //holder class for all possible conditions (so far only intCompare) class condition { public: condition(); condition(intCompare* compareInput); condition(condition* condition1, char condType, condition* condition2); ~condition(); void set(intCompare* compareInput); void set(condition* condition1, char condType, condition* condition2); bool isTrue(); bool isUsed; void release(); //called when the event is no longer being used; private: //char conditionType; //1 for intCompare intCompare* intCmp; condition* conditionPtrs[2]; char conditionType; }; //-------------------------------------------- //queueItem connects a pre-defined event with an exectution time. //They are placed in the eventQueue struct queueItem { uint32_t timeToExecute; event* eventPtr; }; //Organizes events in a temporal queue. check() is called from the main loop. //If the execution time of the event has passed, then the event is exectuted. class eventQueue { public: eventQueue(); void addEventToQueue(event* eventInput, uint32_t delay); void eraseQueue(); //clear all future events void check(void); private: std::vector<queueItem> events; int queueSize; }; //An 'event' is a block of 'actions' that can be gated with a boolean 'condition' set. All //conditions in the set must be true for the block of actions to be executed. Right now, //there is no OR logic (||), only AND (&&). //The entire event is placed on the event queue to be executed at a given delay. //At that future time, the condition is checked and if true, the block of actions //is exectuted. Note: an 'action' can be another event (or even the parent event), allowing //nested 'if' and 'while' statements. class event { public: event(); event(eventQueue* queueInput); ~event(); void setTimeLag(uint32_t timeLagInput); //the event will be exectuted at this time from now void setTimeLag(int* timeLagInput); //the event will be exectuted at this time from now void setWhileLoopPeriod(uint32_t period); void setWhileLoopPeriod(int* period); void addCondition(condition* conditionInput); //contains a set of conditions to check and a truth table bool isConditionTrue(void); //checks if the condition is true void addAction(action* actionInput); //called during script parsing, when the block is being defined void addToQueue(void); //places the event on the event queue with default time lag. When the time //lag has expired, the the block is executed void addToQueue(uint32_t delay); void execute(void); //Execute without checking the condition. Called only from the event queue void setNextElseEvent(event* eventInput); //allows for else event block uint32_t timeLag; //default time from now when the event will be executed (this is ignored once placed in event queue) int* timeLagVar; //exectution time lab defined by a variable eventQueue* queuePtr; void release(); //called when the event is no longer being used; char blockType; //0 callback //1 if ... do block (with conditions) //2 do block (no conditions) //3 else if ... do block //4 else do (no conditions) //5 while ... do every ... block //6 else while ... do every ... block //7 then if ... do block //8 then do (no conditions) uint32_t whileLoopPeriod; //if non-zero, the block is a while loop (executed at regular intervals) int* whileLoopPeriodVar; event* nextElseEventPtr; bool isUsed; bool timeLagIsConstant; bool whileLoopPeriodIsConstant; bool hasWhileLoop; private: int numConditions; int numActions; condition* conditionToCheck; action* actionArray[20]; //if statement (can be left empty, which is interpreted as 'true') //std::vector<condition*> conditionArray; //std::deque<action*> actionArray; }; //each functionItem help a poiter to an action, and the name of the function. Not currently in use. class functionItem { public: functionItem(action* actionInput, string tagInput); ~functionItem(); string tag; action* actionPtr; }; class blockBuffer { public: blockBuffer(); bool addLine(char* input, int numChars); string getNextLine(); int16_t linesAvailable(); bool empty(); void resetBuffer(); private: #ifdef MBEDHARDWARE //On the MBED, we need to put this on a different memory bank __attribute((section("AHBSRAM1"),aligned)) char charBuffer[INPUTCHARBUFFERSIZE]; #else char charBuffer[INPUTCHARBUFFERSIZE]; #endif int16_t bufferWritePos; int16_t bufferReadPos; int16_t _linesAvailable; }; //Parser for the incoming text. The parser is called when a line terminates with a semicolon (;). //Only the final line in a callback block should have a semicolon. class scriptStream { public: scriptStream(AbstractPort** portVectorInput, int numPortsInput, eventQueue* queueInput, sSystem* system); void parseBlock(); //Parses everything since the last semicolon was found void addLineToCurrentBlock(char* lineInput); // if the line did not end with a semicolon, add it to the current block private: int* findIntVariable(string nameInput); //used to retrieve the pointer to the designated variable if it exists int* findIntVariable(const char* nameInput); //used to retrieve the pointer to the designated variable if it exists int* findIntVariable(const char* nameInput, int start, int end); //used to retrieve the pointer to the designated variable if it exists bool createIntVariable(string nameInput); // creates a new interger variable action* evaluateAssignmentForAction(string expression); //parses a numerical assignment or operation (a = b - c) action* evaluateAssignmentForAction(const char* expression); //parses a numerical assignment or operation (a = b - c) bool evaluateConditions(string& expression, event* currentEvent); //parses a condition statement (a == b && c > d) condition* parseConditions(const char* expression,int start, int end); //parses a condition statement (a == b && c > d) int findFirstOrOutsideParenth(const char* expression, int start, int end); int findFirstAndOutsideParenth(const char* expression, int start, int end); bool isOutsideParenth(const char* expression,int foundItem, int start, int end); bool isNum(const char* expression, int start, int end); bool areStringsSame(const char* str1, const char* str2, int start, int end); int getRandomParam(string expression); int getRandomParam(const char* expression,int start,int end); int findStringLoc(const char* refString,const char* findString,int start, int end); void clearEnvironmentVariables(int mode); int currentTriggerPort; int currentTriggerDir; int currentPort; int currentFunction; string tmpLine; vector<string> tokens; bool lineError; int blockDepth; bool ifBlockInit; bool whileBlockInit; bool elseFlag; bool thenFlag; bool expectingDoStatement; int currentDelay; event* tmpEvent; string tmpString; vector<intVariable*> globalVariables; vector<event*> tmpEventPtrArray; //event* functionEventArray[NUMFUNCTIONS]; //bool functionSpotTaken[NUMFUNCTIONS]; //vector<functionItem*> functionArray; //any blocks declared outsite callback blocks are stored here //list<string> currentBlock; blockBuffer currentBlock; sSystem* system; AbstractPort** portVector; int numPorts; AbstractPort* digInPortVector[NUMDIGINPORTS]; uint8_t digInLookup[NUMDIGINPORTS]; int numDigInPorts; AbstractPort* digOutPortVector[NUMDIGOUTPORTS]; uint8_t digOutLookup[NUMDIGOUTPORTS]; int numDigOutPorts; AbstractPort* anInPortVector[NUMANINPORTS]; uint8_t anInLookup[NUMANINPORTS]; int numAnInPorts; AbstractPort* anOutPortVector[NUMANOUTPORTS]; uint8_t anOutLookup[NUMANOUTPORTS]; int numAnOutPorts; eventQueue* queuePtr; }; class mainLoop { public: mainLoop(); void init(); void exec(); private: void eraseBuffer(); uint32_t currentDIOstate[2]; bool digitalInChanged; bool digitalOutChanged; scriptStream *parser; sSystem *hardware; //hardware interface sSerialPort *pc; //communication to computer char buffer[256]; //digitalPort ports[NUMPORTS]; DigitalPort digInPorts[NUMDIGINPORTS]; DigitalPort digOutPorts[NUMDIGOUTPORTS]; AnalogPort anInPorts[NUMANINPORTS]; AnalogPort anOutPorts[NUMANOUTPORTS]; AbstractPort* ports[NUMDIGINPORTS+NUMDIGOUTPORTS+NUMANINPORTS+NUMANOUTPORTS]; };