It may seem that running multiple processes or tasks on such a small chip as a PIC16F628 is a bit of an ordeal.
Why split up the problem into separate processes? What is a process?
Process B has to listen to Process A, Process C, the Hardware and respond and report to management.
To aid documentation of the code, prefix each symbol, name, variable, function associated with each process with a two letter code. You can then quickly understand which process it belongs to.
while(true){ // loop around each process in turn. // These must not block and should return as fast as possible // They may run code for any pending events and progress a state machine. PAtimeslice() PBtimeslice() PCtimeslice() MMtimeslice() }
Processes run concurrently, at the same time. They send messages between each other.
They react to incoming messages and process them and maybe send a message to another process.
The processes essentially run concurrently, but on a PIC they cannot. However, the processing required on each event should not take too long and each process can be run in turn and then only if they have a message to process.
If a process executes a delay loop, then it cannot the yield processing power of the chip to another process.
My shipping forecast timer beeps out the time when reviewing the time. This uses a process with a state machine as it would take too long to run and would affect the main loop timing.
It is possible to have the same code that compiles to identical .hex file to be loaded into a PIC chip. However one source code can be impossible to read, and the other easy to understand.
How you name functions variables and lay out the code, is vitally import.
You could write your code and name your variables and functions as you think up the names, but if you have set up processes, give each process or block of functionality a prefix. What I found was that the prefix means that the function or variable belongs to that bit of functionality, and prevents somebody trying to understand your code spending time reading and trying to understanding the wrong bit of code.
I split my shipping forecast timer up into the the following modular blocks:
CL - Clock chain functions. ST - State machine temporary variables LS - Loud Speaker driver IP - Input Polling SR - Serial Out OP - Button Open Logger Sound - Sound process
So I could group the variables as below:
;---------------------------------------------------------------- ; Sec 2.0 Variables ;---------------------------------------------------------------- ; Variables start 0x0C ; ; Variables for FlashLED ( FL ) process ; ; DoTimeSlice Bits, to schedule, set bit ; 0 run onQtrSecondTick ; 1 run onSecondTick ; 2 run onMinuteTick ; 3 run onHourTick ; 4 not used ; 5 run Serial Port readout ; 6 BeepPause ; 7 run BeepMachine DoTimeSlice EQU 0x0C STstate EQU 0x0E CLtmr4sec EQU 0x0F ; counts quarter seconds CLnbcdS EQU 0x10 ; stores current time CLnbcdM EQU 0x11 CLnbcdH EQU 0x12 CLdbcdM EQU 0x15 ; stores the time being input CLdbcdH EQU 0x16 CLbbcdM EQU 0x18 ; stores the time being beeped out CLbbcdH EQU 0x19 CLTNum EQU 0x1A ; Index into EEPROM when updating an ON/OFF time CLTNumChk EQU 0x1C ; EEPROM index when testing for ON/OFF time LScnt EQU 0x1F ; For Loudspeaker beep generation LScnt2 EQU 0x20 IPlast EQU 0x21 ; Input variables IPnew EQU 0x22 IPbuttonEvent EQU 0x23 IPsetState EQU 0x24 SRout EQU 0x25 ; for RS232 routines SRcnt EQU 0x27 SoundCnt EQU 0x28 ; Counts beeps Sound5Cnt EQU 0x2A ; count for 5th beep DispState EQU 0x2B ; Display State OPcnt EQU 0x2C ; Count OPopcnt EQU 0x2D ; Count OPbufferStart EQU 0x30 ; 32 bytes of ram that can store Dates
The function names are also grouped by and prefixed with the two letter variable names.
I also tried splitting the source code into sections like any good document. I tried putting a comment like:
;----------------------------------------------------------------
; Sec 2.0 Variables
;----------------------------------------------------------------
You could even include a contents comment block, but a grep of "Sec" would produce a contents page.
Adding a type coding to the symbol name also tells you how to treat it.
e.g.
Suppose I have a structure to the leakyBucketData.
I could have an array of leakyBucketData, and in my code, I could have a pointer to an instance of leakyBucketData.
If you add postfixs to define the type you end up with symbols:
leakyBucketDataT //- type definition leakyBucketData //- Instance of leakyBucketDataT leakyBucketDataA //- Array of instances of leakyBucketDataT leakyBucketDataP //- Pointer to instance of leakyBucketDataTor
struct leakyBucketDataS { int fill; int leak; }; //- structure definition typedef struct leakyBucketDataS leakyBucketDataT; - type definition leakyBucketDataT lb; //- instance of leakyBucketData leakyBucketDataT lbArray[100]; //- Array of instances of leakyBucketData leakyBucketDataT *lbPointer; //- Pointer to instance of leakyBucketDataor
struct leakyBucketDataS { int fill; int leak; }; //- structure definition typedef struct leakyBucketDataS leakyBucketDataT; - type definition leakyBucketDataT lb; //- instance of leakyBucketData leakyBucketDataT lbA[100]; //- Array of instances of leakyBucketData leakyBucketDataT *lbP; //- Pointer to instance of leakyBucketDataThis is much better than just adding 1,2 to the end of the symbol to make it unique:
struct leakyBucketData { int fill; int leak; }; struct leakyBucketData lb; //- instance of leakyBucketData struct leakyBucketData lb1[100]; //- Array of instances of leakyBucketData struct leakyBucketData *lb2; //- Pointer to instance of leakyBucketDataThis is much easier to read and maintain.
leakyBucketDataT //- type leakyBucketDataT lb; //- instance leakyBucketDataT lbA[100]; //- array leakyBucketDataT *lbP; //- pointerYou could argue that this seems to be tautological as the postfix duplicates the context. lbA is a an array as it has [100] after the definition, lbP is a pointer as it has a * in front of it. However, this means you need to constantly refer to the variable declarations to know the type if you do not postfix the symbol names and use them out of context. This wastes time and adds complexity. What is easier to read and understand
lb = ... lb1 = ... lb2 = ...or
lb = ... lbArray = ... lbPointer = ...or
lb = ... lbA = ... lbP = ...This is not a novel idea, but does not seem to be a subject taught on computer science courses. I have seen _ and __ prefix symbolds to do the same thing.
Here is the program with pointers dhr.c
Here is some Dephi ( note Delphi puts the type after the variable name )
type POFNotifyA = ^TOFNotifyA; POFNotifyW = ^TOFNotifyW; POFNotify = POFNotifyA; TOFNotifyA = packed record hdr: TNMHdr; lpOFN: POpenFilenameA; pszFile: PAnsiChar; end; TOFNotifyW = packed record hdr: TNMHdr; lpOFN: POpenFilenameW; pszFile: PWideChar; end; TOFNotify = TOFNotifyA;
/* --- The following code comes from c:\lcc\lib\wizard\textmode.tpl. */ #include <stdio.h> #include <stdlib.h> #include <string.h> // ============================ struct leakyBucketData { int fill; int leak; }; /* * old format variable names lb0,lb1,lb2 * use no postfix. I have run out of names so use lb1,lb2 */ struct leakyBucketData lb0; //- instance of leakyBucketData struct leakyBucketData lb1[100]; //- Array of instances of leakyBucketData struct leakyBucketData *lb2; //- Pointer to instance of leakyBucketData /* * Use Type Defs to simplify * use a postfix T for Typedef */ typedef struct leakyBucketData leakyBucketDataT; //- type definition leakyBucketDataT lb; //- instance of leakyBucketData /* * new format variable names lb,lbArray,lbPtr * use a postfix Array for arrays and Ptr for Pointers * This may be a bit verbose */ leakyBucketDataT lbArray[100]; //- Array of instances of leakyBucketData leakyBucketDataT *lbPtr; //- Pointer to instance of leakyBucketData /* * new format variable names lb,lbA,lbP * use a postfix A for Array and P for Pointer */ leakyBucketDataT lbA[100]; //- Array of instances of leakyBucketData leakyBucketDataT *lbP; //- Pointer to instance of leakyBucketData // ============================ void init( void ){ int cnt; for ( cnt = 0 ; cnt < 100 ; cnt++ ){ lb1[ cnt ].fill = cnt; lb1[ cnt ].leak = 50-cnt; } for ( cnt = 0 ; cnt < 100 ; cnt++ ){ lbArray[ cnt ].fill = cnt; lbArray[ cnt ].leak = 50-cnt; } for ( cnt = 0 ; cnt < 100 ; cnt++ ){ lbA[ cnt ].fill = cnt; lbA[ cnt ].leak = 50-cnt; } } int main(int argc,char *argv[]){ printf("\n=================!"); printf("\nHello World!"); printf("\n=================!"); printf("\n"); init(); printf("\n=================!"); /* * old format variable names lb0,lb1,lb2 * use no postfix. I have run out of names so use lb1,lb2 */ printf("\n %d %d ",lb1[3].fill,lb1[50].fill ) ; printf("\n %d %d ",lb1[3].leak,lb1[50].leak ); lb2 = lb1; printf("\n %d %d ",(*lb2).fill,(*lb2).leak ); lb2 = &lb1[89]; printf("\n %d %d ",lb2->fill,lb2->leak ); lb2 = &lb1[35]; printf("\n %d %d ",lb2->fill,lb2->leak ); lb2 = lb1; // lb2 +=10; // increment point by 10 elements of the array printf("\n %d %d ",lb2->fill,lb2->leak ); lb2 +=10; // increment point by 10 elements of the array printf("\n %d %d ",lb2->fill,lb2->leak ); lb2 +=10; // increment point by 10 elements of the array printf("\n %d %d ",lb2->fill,lb2->leak ); lb2 +=10; // increment point by 10 elements of the array printf("\n %d %d ",lb2->fill,lb2->leak ); printf("\n=================!"); /* * new format variable names lb,lbArray,lbPtr * use a postfix Array for arrays and Ptr for Pointers * This may be a bit verbose */ printf("\n %d %d ",lbArray[3].fill,lbArray[50].fill ) ; printf("\n %d %d ",lbArray[3].leak,lbArray[50].leak ); lbPrt = &lbArray[89]; printf("\n %d %d ",(*lbPrt).fill,(*lbPrt).leak ); lbPrt = &lbArray[35]; printf("\n %d %d ",lbPrt->fill,lbPrt->leak ); lbPrt = lbA; // lbPrt +=10; // increment point by 10 elements of the array printf("\n %d %d ",lbPrt->fill,lbPrt->leak ); lbPrt +=10; printf("\n %d %d ",lbPrt->fill,lbPrt->leak ); lbPrt +=10; printf("\n %d %d ",lbPrt->fill,lbPrt->leak ); lbPrt +=10; printf("\n %d %d ",lbPrt->fill,lbPrt->leak ); printf("\n=================!"); /* * new format variable names lb,lbA,lbP * use a postfix A for Array and P for Pointer */ printf("\n %d %d ",lbA[3].fill,lbA[50].fill ) ; printf("\n %d %d ",lbA[3].leak,lbA[50].leak ); lbP = &lbA[89]; printf("\n %d %d ",(*lbP).fill,(*lbP).leak ); lbP = &lbA[35]; printf("\n %d %d ",lbP->fill,lbP->leak ); lbP = lbA; // lbP +=10; // increment point by 10 elements of the array printf("\n %d %d ",lbP->fill,lbP->leak ); lbP +=10; printf("\n %d %d ",lbP->fill,lbP->leak ); lbP +=10; printf("\n %d %d ",lbP->fill,lbP->leak ); lbP +=10; printf("\n %d %d ",lbP->fill,lbP->leak ); printf("\n=================!"); return 0; }
An object is a software bundle of related state and behavior. Software objects are often used to model the real-world objects that you find in everyday life. This lesson explains how state and behavior are represented within an object, introduces the concept of data encapsulation, and explains the benefits of designing your software in this manner.
Here is combinatory simple logic system.
Here is a simple state machine system, as outputs are fed back to provide history.
Here is a simple state machine system, as outputs are fed back to provide history. A latch is added to clock the state machine.
Here is a state machine where the state information is stored in more than one place. There is an assumption that the state machine is not run continously. Often it is only run when the inputs change or on a regular time tick. Getting the state from a database may require a data base query. Maybe the state is held in a local copy, and either the database is responsible for pushing down updates or the statemachine is responsible for polling for updates.
localCopyOfStateVariables = StateVariablesget from array:
localCopyOfStateVariables = StateVariables[ instanceWanted ]get from any where:
localCopyOfStateVariables = getStateVariables( instanceWanted ) ... putStateVariables( instanceWanted )Here is an outline of running with locks:
lockStateVariables( instanceWanted ) localCopyOfStateVariables = getStateVariables( instanceWanted ) localCopyOfStateVariables = runCombinatoryLogic( localCopyOfStateVariables ) putStateVariables( instanceWanted ) unlockStateVariables( instanceWanted )
Here is a state machine used to model analogue electronics.
Thats enough for now!