I said I was going to have a main() in this segment, which is going to happen, but there needs to be some additional elements first. Embedded systems have a general two step method for operation, initialize, and loop. The initialize portion is where all the hardware registers are configured and variables are pre-loaded. A good habit is to actually write a function init????() that performs the initialization for the particular device as part of the driver. The final code will then be able to have an initAll function that just calls all the init functions, before beginning the main loop.
One time
The earlier provided code for UART, A/D, and Timers, contains an init function for this purpose. Since the init function should only need to be called once, I like to call the code block oneTime(), with no parameters, and no return value.
/************************************************************************************************ onetime.c History: [JAC] Original Creation when the earth was cooling Copyright(c) 2016 Chaney Firmware ************************************************************************************************/ #include "types.h" void initTimers(void); void initUart(void); void initA2D(void); void oneTime(void) { initTimers(); // Timers initUart(); // Main channel comm initA2D(); // A/D conversion sei(); // Everybody ready, turn on the interrupts and go live } /* end of file */
This will be expanded as time moves on.
An additional linkage that comes in handy is to make the A/D continuous (but not too frequent), if the A/D runs continuously and operates on the interrupt, it is possible to starve the other routines, because the newer A/D converters are sufficiently fast enough that the management time is close to the time to make the conversion, and in many cases longer. Because of this point, it is a good idea to make sure the A/D interrupt routine is thread safe, and can enforce an exit. A simple semaphore is a means of to throttle the processing. The example code reads all the A/D channels in a continuous interrupt, then clears a hold off flag. On the other side, the refresh routine is called each millisecond, and if the flag is set will skip over the restart. Making a linkage of a real time update for the A/D as a function that is called from the millisecond timer tic routine.
Communication Interface
Now for the last element to make a usable measurement device, communication. I am a big proponent of using standards where they apply, because it keeps me from having to wear out brain cells coming up with a solution that has already been solved. Since this is an instrument, of sorts, I like to use the Standard Commands for Programmable Instruments (SCPI) for an interface protocol. The interface was developed with National Instruments quite a while ago, and has a hardware component that provides the first level of the interface. It is not necessary to implement the entire standard, but a small group of commands must be implemented in order to be compliant with the standard. It is also possible to use a simple serial interface and the messaging section to provide a robust interface for instrumentation. Any control interface requires some form of parsing activity in order to decipher the incoming messages, and if the messages are arriving at a rate of 9600 bpm, the parsing process can become a bit lethargic, with a periodic load when the buffered message is parsed. A solution would be to parse the message as it arrives, without waiting between characters for the next input. The solution presented here is a serial parsing process that works on a tree traversing state process.
Each node has a group of characters to compare, if a match is found that node becomes the new node. If the node has an associated process, that process is run on the next incoming character until the substring is completed. Upon completion, or if there is not an associated task, the node process repeats with the new group of characters at that node to compare. The system requires two tables, the first is the node tree with the command strings laid out in a linked list tree structure. The second table is a function table with the sub tasks that are called at the particular nodes.
The tree nodes are set up as a structure with the character for compare, the found link, the not found link, and a link to a node task to run.
struct pNode {
UByte c;
SWord found;
SWord notFound;
SWord callback;
};
To help describe the activity, I will use the required set of SCPI instructions:
*CLS Clear Status Command
*ESE Standard Event Status Enable Command
*ESE? Standard Event Status Enable Query
*ESR? Standard Event Status Register Query
*IDN? Identification Query
*OPC Operation Complete Command
*OPC? Operation Complete Query
*RST Reset Command
*SRE Service Request Enable Command
*SRE? Service Request Enable Query
*STB? Read Status Byte Query
*TST? Self-Test Query
*WAI Wait-to-Continue Command
Creating a set of enumerations for the search gives:
enum {
_,
_C, _CL, _CLS,
_E, _ES, _ESE, _ESEb,
_ESEq,
…
};
Since each of the commands in this set start with a ‘*’ character, the first node is:
{‘*’, _C, NULL, NULL} // for the first node (can’t have * as a character in an enum so _ is used)
There is no next character to compare, since all the strings start with a ‘*’, and no callback function, so those fields are null. Enumerations are used because the table can be indexed numerically to simplify the tree traversal. Moving on to the end of a search, the entry:
{‘?’, NULL, NULL, _ieeeESEq} // *ESE? Command identified so the call is made.
Again, an enumerated list of functions is used to reduce the table sizes. I understand that the explanation is a little sketchy, but the source is included, and if there are questions I will cover them individually. The SCPI extension used here is an extension of the MEASure component. Two sub components of tables, and monitors are created to manage calibration (tables) information and return (monitor) information.
Parser and tables
/************************************************************************************************ parser.c History: [JAC] Original Creation when the earth was cooling Copyright(c) 2016 Chaney Firmware ************************************************************************************************/ #include "types.h" void putComm(UByte c); void putStr(char* s); bool isSysEchoOn(void) { return true; } UByte ieeeConf(UByte c); UByte ieeeFetc(UByte c); UByte ieeeMeas(UByte c); UByte ieeeRead(UByte c); UByte ieeeMTabl(UByte c); UByte ieeeMMoni(UByte c); UByte ieeeMDlog(UByte c); UByte ieee_CLS(UByte c); UByte ieee_ESEs(UByte c); UByte ieee_ESEq(UByte c); UByte ieee_ESRq(UByte c); UByte ieee_IDNq(UByte c); UByte ieee_OPCs(UByte c); UByte ieee_OPCq(UByte c); UByte ieee_RST(UByte c); UByte ieee_SREs(UByte c); UByte ieee_SREq(UByte c); UByte ieee_STBq(UByte c); UByte ieee_TSTq(UByte c); UByte ieee_WAI(UByte c); void stCls(void); void stEseS(SWord d); void stEseQ(void); void stEsrQ(void); void stIdnQ(void); void stOpcS(SWord d); void stOpcQ(void); void stRst(void); void stSreS(SWord d); void stSreQ(void); void stStbQ(void); void stTstQ(void); void stWai(void); void m_Dlog(UByte t, UByte n, SWord* lst); void m_Moni(UByte t, UByte n, SWord* lst); void m_Tabl(UByte t, UByte n, SWord* lst); void reNum(SWord n); struct pN { UByte c; // Match character SWord nx; // not found try next SWord fn; // found next node SWord cb; // call back function number }; typedef UByte (*callBack)(UByte c); #define MAX_PARM 32 static UByte offs = 0; static UByte nest = 0; static UByte mode = 0; static UByte neg = 0; static UByte mes = 0; static UByte err = 0; char st[30]; static SWord parm[MAX_PARM]; UByte getScpiMode(void) { return mode; } enum { /* list of table offsets (spells out the command) */ __ ,__C,__CL,__CLS ,__E,__ES,__ESE,__ESEs ,__ESEq ,__ESR,__ESRq ,__I,__ID,__IDN,__IDNq ,__O,__OP,__OPC,__OPCs ,__OPCq ,__R,__RS,__RST ,__S,__SR,__SRE,__SREs ,__SREq ,__ST,__STB,__STBq ,__T,__TS,__TST,__TSTq ,__W,__WA,__WAI ,_C,_CO,_CON,_CONF ,_F,_FE,_FET,_FETC ,_R,_RE,_REA,_READ ,_M,_ME,_MEA,_MEAS ,_mT,_mTA,_mTAB,_mTABL ,_mM,_mMO,_mMON,_mMONI ,_mD,_mDL,_mDLO,_mDLOG }; enum { // list of function offsets (tasks to run) cb_CLS ,cb_ESEs ,cb_ESEq ,cb_ESRq ,cb_IDNq ,cb_OPCs ,cb_OPCq ,cb_RST ,cb_SREs ,cb_SREq ,cb_STBq ,cb_TSTq ,cb_WAI ,cbConf ,cbFetc ,cbRead ,cbMeas ,cbmTabl ,cbmMoni ,cbmDlog }; static callBack cTbl[] = { // function links (vector table) ieee_CLS ,ieee_ESEs ,ieee_ESEq ,ieee_ESRq ,ieee_IDNq ,ieee_OPCs ,ieee_OPCq ,ieee_RST ,ieee_SREs ,ieee_SREq ,ieee_STBq ,ieee_TSTq ,ieee_WAI ,ieeeConf ,ieeeFetc ,ieeeRead ,ieeeMeas ,ieeeMTabl ,ieeeMMoni ,ieeeMDlog }; static struct pN pTbl[] = { // search tree {'*', _C,__C,-1} ,{'C',__E,__CL,-1},{'L', -1,__CLS,-1},{'S', -1, -1,cb_CLS} ,{'E',__I,__ES,-1},{'S', -1,__ESE,-1},{'E',__ESR,__ESEs, -1},{' ',__ESEq, -1,cb_ESEs} ,{'?', -1, -1,cb_ESEq} ,{'R', -1,__ESRq, -1},{'?', -1, -1,cb_ESRq} ,{'I',__O,__ID,-1},{'D', -1,__IDN,-1},{'N', -1,__IDNq, -1},{'?', -1, -1,cb_IDNq} ,{'O',__R,__OP,-1},{'P', -1,__OPC,-1},{'C', -1,__OPCs, -1},{' ',__OPCq, -1,cb_OPCs} ,{'?', -1, -1,cb_OPCq} ,{'R',__S,__RS,-1},{'S', -1,__RST,-1},{'T', -1, -1,cb_RST} ,{'S',__T,__SR,-1},{'R',__ST,__SRE,-1},{'E', -1,__SREs, -1},{' ',__SREq, -1,cb_SREs} ,{'?', -1, -1,cb_SREq} ,{'T', -1,__STB,-1},{'B', -1,__STBq, -1},{'?', -1, -1,cb_STBq} ,{'T',__W,__TS,-1},{'S', -1,__TST,-1},{'T', -1,__TSTq, -1},{'?', -1, -1,cb_TSTq} ,{'W', -1,__WA,-1},{'A', -1,__WAI,-1},{'I', -1, -1,cb_WAI} /*************************************************************************************************/ ,{'C', _F, _CO,-1},{'O', -1, _CON,-1},{'N', -1, _CONF, -1},{'F', -1,_mT, cbConf} ,{'F', _R, _FE,-1},{'E', -1, _FET,-1},{'T', -1, _FETC, -1},{'C', -1,_mT, cbFetc} ,{'R', _M, _RE,-1},{'E', -1, _REA,-1},{'A', -1, _READ, -1},{'D', -1,_mT, cbRead} ,{'M', -1, _ME,-1},{'E', -1, _MEA,-1},{'A', -1, _MEAS, -1},{'S', -1,_mT, cbMeas} ,{'T',_mM,_mTA,-1},{'A', -1,_mTAB,-1},{'B', -1,_mTABL, -1},{'L', -1, -1,cbmTabl} ,{'M',_mD,_mMO,-1},{'O', -1,_mMON,-1},{'N', -1,_mMONI, -1},{'I', -1, -1,cbmMoni} ,{'D', -1,_mDL,-1},{'L', -1,_mDLO,-1},{'O', -1,_mDLOG, -1},{'G', -1, -1,cbmDlog} }; /************************************************************************************************ * Serial Parser - a creation by Jack Chaney, created long ago when the earth was cooling. * Instead of waiting for the entire string to be entered, before parsing the instructions, * a state tree is used to parse the string on a per character basis as the string comes in * Optionally, at each node (recognized character) it is possible to route the incoming data * to a procedure. If the procedure returns 0 the search continues along the "found" stream. * If the procedure returns a 1 the search restarts at the head of the tree. ************************************************************************************************/ void parse(UByte c) { UByte p; static SByte b = 0; static SWord t = 0; if (isSysEchoOn()) { putComm(c); } // Send the character back if echo is on if (t == -1) { t = 0; b = 0; } // Last pass was an end of branch, so reset to top of tree p = ('a' <= c) && (c <= 'z') ? c & ~bit5 : c; // (optional) upper case instructions if (b == 0) { // Clear flag = no more processing & last character found while ((t != -1) && (pTbl[t].c != p)) { t = pTbl[t].nx; } // Search the list for a match if (t != -1) { // not end of branch, so we have found a match if (pTbl[t].cb == -1) { t = pTbl[t].fn; } // if no process then point to next level search else { b = 1; } // if existing process, set flag so characters are sent there } } else { // b is non zero, send incomming charater to linked process b = cTbl[pTbl[t].cb](c); // b returns: 0=complete, 1=reset, other=continue to process t = b == 0 ? pTbl[t].fn : b == 1 ? -1 : t; // 0, point to next level search. 1, point to tree top } } /************************************************************************************************ * Parse a numeric string on a per character basis, for parameter lists ************************************************************************************************/ UByte parsNum(UByte c) { // string parser for numeric input (works for decimal, octal, and hex based on mode) switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': parm[offs] *= mode == 1 ? 8 : mode == 2 ? 16 : 10; if (neg == 0) { parm[offs] += c & 15; } else { parm[offs] -= c & 15; } break; case '8': case '9': if (mode == 1) { err = 1; offs = 0; nest = 0; } else { parm[offs] *= mode == 2 ? 16 : 10; if (neg == 0) { parm[offs] += c & 15; } else { parm[offs] -= c & 15; } } break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': if (mode != 2) { err = 1; offs = 0; nest = 0; } else { parm[offs] *= 16; if (neg == 0) { parm[offs] += (c + 9) & 15; } else { parm[offs] -= (c + 9) & 15; } } break; case ' ': nest = 0; offs = 0; parm[offs] = 0; err = 0; neg = 0; break; case ',': if (offs + 1 < MAX_PARM) { offs++; parm[offs] = 0; neg = 0; } else { err = 3; } break; case '(': nest++; break; case ')': if (nest > 0) { --nest; } else { err = 2; } break; case '-': neg = neg == 0 ? 1 : 0; break; default: break; } return c; } void reNum(SWord n) { switch(getScpiMode()) { case 1: /* octal */ sprintf(st,"%o",n); break; case 2: /* hex */ sprintf(st,"%04x",n); break; default: /* decimal */ sprintf(st,"%d",n); break; } putStr(st); } UByte ieeeConf(UByte c) { if ((c == 13)||(c == ';')) { c = 1; } if (c == ':') { c = 0; mes = 1; err = 0; nest = 0; offs = 0; neg = 0; } return c; } UByte ieeeFetc(UByte c) { if ((c == 13)||(c == ';')) { c = 1; } if (c == ':') { c = 0; mes = 2; err = 0; nest = 0; offs = 0; neg = 0; } return c; } UByte ieeeMeas(UByte c) { if ((c == 13)||(c == ';')) { c = 1; } if (c == ':') { c = 0; mes = 3; err = 0; nest = 0; offs = 0; neg = 0; } return c; } UByte ieeeRead(UByte c) { if ((c == 13)||(c == ';')) { c = 1; } if (c == ':') { c = 0; mes = 4; err = 0; nest = 0; offs = 0; neg = 0; } return c; } UByte ieeeMTabl(UByte c) { if ((c == 13)||(c == ';')) { if ((err == 0) && (nest == 0)) { m_Tabl(mes, offs, parm); c = 0; } else { c = 1; err = 0; nest = 0; neg = 0; } mes = 0; } else { c = parsNum(c); } return c; } UByte ieeeMMoni(UByte c) { if ((c == 13)||(c == ';')) { if ((err == 0) && (nest == 0)) { m_Moni(mes, offs, parm); c = 0; } else { c = 1; err = 0; nest = 0; } mes = 0; neg = 0; } else { c = parsNum(c); } return c; } UByte ieeeMDlog(UByte c) { if ((c == 13)||(c == ';')) { if ((err == 0) && (nest == 0)) { m_Dlog(mes, offs, parm); c = 0; } else { c = 1; err = 0; nest = 0; } mes = 0; neg = 0; } else { c = parsNum(c); } return c; } UByte ieee_CLS(UByte c) { if ((c == 13)||(c == ';')) { stCls(); } return 0; } UByte ieee_ESEs(UByte c) { if ((c == 13)||(c == ';')) { stEseS(parm[0]); c = 0; err = 0; nest = 0; offs = 0; neg = 0; } else { c = parsNum(c); } return c; } UByte ieee_ESEq(UByte c) { if ((c == 13)||(c == ';')) { stEseQ(); } return 0; } UByte ieee_ESRq(UByte c) { if ((c == 13)||(c == ';')) { stEsrQ(); } return 0; } UByte ieee_IDNq(UByte c) { if ((c == 13)||(c == ';')) { stIdnQ(); } return 0; } UByte ieee_OPCs(UByte c) { if ((c == 13)||(c == ';')) { stOpcS(parm[0]); c = 0; err = 0; nest = 0; offs = 0; neg = 0; } else { c = parsNum(c); } return c; } UByte ieee_OPCq(UByte c) { if ((c == 13)||(c == ';')) { stOpcQ(); } return 0; } UByte ieee_RST(UByte c) { if ((c == 13)||(c == ';')) { stRst(); } return 0; } UByte ieee_SREs(UByte c) { if ((c == 13)||(c == ';')) { stSreS(parm[0]); c = 0; err = 0; nest = 0; offs = 0; neg = 0; } else { c = parsNum(c); } return c; } UByte ieee_SREq(UByte c) { if ((c == 13)||(c == ';')) { stSreQ(); } return 0; } UByte ieee_STBq(UByte c) { if ((c == 13)||(c == ';')) { stStbQ(); } return 0; } UByte ieee_TSTq(UByte c) { if ((c == 13)||(c == ';')) { stTstQ(); } return 0; } UByte ieee_WAI(UByte c) { if ((c == 13)||(c == ';')) { stWai(); } return 0; } /* end of file */
Status (required elements)
/************************************************************************************************ scpiStatus.c History: [JAC] Original Creation when the earth was cooling Copyright(c) 2016 Chaney Firmware ************************************************************************************************/ #include "types.h" /************************************************************************************************ Status component for SCPI parser, implements the status registers and instructions to make the parser a legal SCPI implementation. Minimum instructions: *CLS *ESE *ESE? *ESR? *IDN? *OPC *OPC? *RST *SRE *SRE? *STB? *TST *WAI Refer to IEEE 488.2 documentation for more information. **************************************************************************************************/ #define MANUF_NAME "J.Chaney" #define MODEL_NUM "Test Mon" void putComm(UByte c); void putStr(char *s); UByte getScpiMode(void); void reNum(SWord n); SWord getSerialNo(UByte n); static UByte ese; static UByte esr; static UByte sre; static UByte stb; static char st[20]; void stCls(void) { esr = 0x80; stb = ((ese & esr) != 0) ? stb | bit5 : stb & ~bit5; stb = ((sre & stb) != 0) ? stb | bit6 : stb & ~bit6; } void stEseS(SWord d) { ese = (UByte)(d & 191); stb = ((ese & esr) != 0) ? stb | bit5 : stb & ~bit5; stb = ((sre & stb) != 0) ? stb | bit6 : stb & ~bit6; } void stEseQ(void) { reNum(ese); putComm(';'); } void stEsrQ(void) { reNum(esr); putComm(';'); } void stIdnQ(void) { UWord s; putStr(MANUF_NAME); putComm(','); putStr(MODEL_NUM); sprintf(st, ",%04X%04X%04X%04X",getSerialNo(0),getSerialNo(1),getSerialNo(2),getSerialNo(3)); putStr(st); s = VERS_NO; sprintf(st,",v%d.%d.%d;",(s >> 12),((s >> 5) & 127),(s & 31)); putStr(st); } void stOpcS(SWord d) { stb = d != 0 ? stb | bit0 : stb & ~bit0; stb = ((ese & esr) != 0) ? stb | bit5 : stb & ~bit5; stb = ((sre & stb) != 0) ? stb | bit6 : stb & ~bit6; } void stOpcQ(void) { } void stRst(void) { } void stSreS(SWord d) { sre = (UByte)(d & 255); stb = ((sre & stb) != 0) ? stb | bit6 : stb & ~bit6; } void stSreQ(void) { reNum(sre); putComm(';'); } void stStbQ(void) { reNum(stb); putComm(';'); } void stTstQ(void) { } void stWai(void) { stb &= ~bit0; stb = ((ese & esr) != 0) ? stb | bit5 : stb & ~bit5; stb = ((sre & stb) != 0) ? stb | bit6 : stb & ~bit6; } void initScpi(void) { stCls(); } /* end of file */
Measure (data exchange)
/************************************************************************************************ scpiMeasure.c History: [JAC] Original Creation when the earth was cooling Copyright(c) 2016 Chaney Firmware ************************************************************************************************/ #include "types.h" /************************************************************************************************ Measure component for SCPI paser implemented commands for MEAS: section - provides :MONI (monitor), :TABL (table), and :DLOG (data logging, stubbed) - output is through putStr(string) and putComm(single character) ************************************************************************************************/ void reNum(SWord n); void putComm(UByte c); void putStr(char* s); static UWord curTbl; static UByte monLen; static SWord monLst[16]; bool isTableAvailable(UWord n); UByte getTableTyp(UWord n); UByte getTableHit(UWord n); UByte getTableWid(UWord n); SWord getTableCel(UWord n, UByte x, UByte y); void setTableVal(UWord n, UByte x, UByte y, SWord d); SWord getMon(UWord n); void confMoni(UByte i, SWord *n); void fetcMoni(void); void confTabl(UByte i, SWord *n); void fetcTabl(void); void confDlog(UByte i, SWord *n); void fetcDlog(void); void m_Moni(UByte t, UByte n, SWord* lst) { switch (t) { case 1: /* CONF */ confMoni(n+1, lst); break; case 2: /* FETC */ fetcMoni(); break; case 3: /* MEAS */ confMoni(n+1, lst); fetcMoni(); break; case 4: /* READ */ fetcMoni(); break; default: break; } } void m_Tabl(UByte t, UByte n, SWord* lst) { switch (t) { case 1: /* CONF */ confTabl(n+1, lst); break; case 2: /* FETC */ fetcTabl(); break; case 3: /* MEAS */ confTabl(n+1, lst); fetcTabl(); break; case 4: /* READ */ fetcTabl(); break; default: break; } } void m_Dlog(UByte t, UByte n, SWord* lst) { switch (t) { case 1: /* CONF */ confDlog(n+1, lst); break; case 2: /* FETC */ fetcDlog(); break; case 3: /* MEAS */ confDlog(n+1, lst); fetcDlog(); break; case 4: /* READ */ fetcDlog(); break; default: break; } } void confTabl(UByte i, SWord *n) { SWord ct; SWord row; SWord col; SWord ht; SWord wd; // instruction parameters. // parm0 = table number // parm1 = function 0-cell,1-row,2-block // 0 - parm2 = x, parm3 = y, parm4 = data // 1 - parm2 = y, parm3-n = data // 2 - parm2 = x, parm3 = y, parm4 = wid, parm5 = hgt, parm 6-n = data if (i > 0) { curTbl = n[0]; if (isTableAvailable(curTbl)) { ht = getTableHit(curTbl); wd = getTableWid(curTbl); switch (n[1]) { case 1: // row if ((i > 3) && (n[2] < ht)) { for (col = 0, ct = 3; (ct < i) && (col < wd); col++, ct++) { setTableVal(n[0],col,n[2],n[ct]); } } break; case 2: // block if (i > 6) { ct = 0; for (row = 0, ct = 6; (row < n[5]) && (ct < i); row++) { for (col = 0; (col < n[4]) && (ct < i); col++, ct++) { if ((col+n[2] < wd) && (row+n[3] < ht)) { setTableVal(n[0],col+n[2],row+n[3],n[ct]); } } } } break; default: // cell if ((i > 4) && (n[2] < wd) && (n[3] < ht)) { setTableVal(n[0],n[2],n[3],n[4]); } break; } } } } void fetcTabl(void) { UByte i; UByte j; UByte x; UByte y; char st[8]; if ((curTbl > 0) && isTableAvailable(curTbl)) { sprintf(st,"%d,",curTbl); putStr(st); sprintf(st,"%d,",getTableTyp(curTbl)); putStr(st); x = getTableWid(curTbl); sprintf(st,"(%d",x); putStr(st); y = getTableHit(curTbl); sprintf(st,",%d),",y); putStr(st); if (y > 1) { putComm('('); } for (j = 0; j < y; j++) { if (j > 0) { putComm(','); } putComm('('); for (i = 0; i < x; i++) { if (i > 0) { putComm(','); } reNum(getTableCel(curTbl,i,j)); } putComm(')'); } if (y > 1) { putComm(')'); } } else { putStr("()"); } putComm(';'); } void confMoni(UByte i, SWord *n) { if (i > 0) { for (monLen = 0; monLen < i; monLen++) { monLst[monLen] = n[monLen]; } } } void fetcMoni(void) { UByte ct; char st[8]; sprintf(st,"%d,(",monLen); putStr(st); if (monLen > 0) { for (ct = 0; ct < monLen; ct++) { if (ct > 0) { putComm(','); } reNum(monLst[ct]); } putComm(')'); putComm(','); putComm('('); for (ct = 0; ct < monLen; ct++) { if (ct > 0) { putComm(','); } reNum(getMon(monLst[ct])); } } putComm(')'); putComm(';'); } void confDlog(UByte i, SWord *n) { } void fetcDlog(void) { } /* end of file */
Tables were discussed, and monitors are just a return of any SWord element from the controller. This includes all the A/D conversions, any table interpolation, and the version number.
Last is main() as promised. All that is needed for main is to first initialize using onetime() function, then in a forever loop, check the input stream, and parse the character. Yes, that is all, everything else operates on an event basis.
/************************************************************************************************ main.c History: [JAC] Original Creation when the earth was cooling Copyright(c) 2016 Chaney Firmware ************************************************************************************************/ #include "types.h" void oneTime(void); int main(void) { oneTime(); forever { if (isCommNotEmpty()) { parse(getComm()); } } return 0; } /* end of file */
Till next installment.