Multi task on PIC16Fxx |Naming Symbols | Traffic Generation 1 | Traffic Generation 2 |

Multi Process code on a Pic

It may seem that running multiple processes on such a small chip is a bit of an ordeal. Why do it? Here is a simple way of doing co-operative multi tasking.

A way to simplify a problem is to write a state machine with carefully choosen events and states that best fit the problem. e.g. sports Timer.

Other processes or tasks are used to map the signals, conditions and inputs to the carefully choosen events.

If your code needs to respond to more than one input, the UNIX Client / Server model does not work very well. UNIX champions the remote procedure call. If these block, they need return quickly.

The client sends a request to the server, and the client blocks waiting for a response from the server. The server blocks waiting for connect requests and spawns a new process to handle the connect.

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.

If you need to repond to multiple sources, you need to poll each source and service any event or significant change of condition.

	Loop:
	Poll for a change in condition1, and if found send a message to a process.
	Poll for a change in condition2, and if found send a message to a process.

	For each process.
		Is there a message to process? if so process it.
	goto Loop.

Normally it is bad to loop and poll in a Windows console program as it hogs the processor e.g

  while( true ){
     // wait for character then echo it. 
     // this works well if there is only one input.
     
     putc( getc() );

  }  

On UNIX and other systems you can use select() to sleep the program waiting for an event on I/O or timeout.

	
  Loop:

        On Timer Event  
		Poll for a change in condition1, and if found send a message to a process.
		Poll for a change in condition2, and if found send a message to a process.


	For each process.
		Is there a message to process? if so process it.

        sleep, waiting for any event from a list. Awake on any event being waited for 


  goto Loop.

Here are examples:
rt.c I wrote a demonstrator in C for Dos in Borland TurboC back in 1992. It does not matter what it does, only the layout. Turbo C does not have a sleepAwaitingEvent() function.

iic.cpp Here is an attempt to use rs232, and stop spinning by using select() on a socket to sleep a console program complied with Bloodshed CPP.

bc001.asm A Template fot the PIC16F628 ( and PIC16F84 )

timer0.c Dabbles with HI-TECH Software\PICC\9.70\samples\TimerDemo modified for 16F690 and 16F886 demo boards.

The reason that I split my shipping forecast timer ( code: rt001.asm ), up into separate processes was due to the limited depth of the stack on the PIC. If a function wants another bit of code to run, instead of calling a function and using another stack level, there and then, it sets a bit in doTimeSlice, tidies up and returns. The main loop polls the bits in doTimeslice, and calls a routine. This routine resets the bit in doTimeSlice.

My shipping forecast timer splits the routines up so that I have a timer chain, a button read routine, and a routine that polls TMR0. I then have a multi-process application.

My processes are:

	
	Timer Chain	polls for TMR0 overflowing and generates events.
	IPtimeSlice	polls for button presses and generates events

	DTsS4event	run 4 times a second
	DTsSevent		run every second
	DTsMevent		run every minute
	DTsHevent		run every hour

	DTSRwrtEvent	scheduled for serial output

	DTSbeepPause	scheduled to pause beep machine in digit 5
	DTSbeepMachine	scheduled to run beep machine

Here is an example of a simple 5 minute timer:

;**************************************************************************
; 
;
; PROGRAM: 5 minute timer
;
; DESCRIPTION: turns on/off LED when buttons pressed. 
;			If timer running, times out and turns off LED.
;		one button starts for 5 minutes
;		another button starts for 15 seconds
;
;			16F84 clocked by 32.768 kHz
;
; AUTHOR:  Douglas Rice
; Copyright 2010
;
;
;
	INCLUDE "p16F84.inc"       
;
;**************************************************************************

        LIST P=16F84, R=DEC
	__idlocs  0x1234
	; __config  _LP_OSC&_PWRTE_ON & _WDT_OFF
	__config  _XT_OSC&_PWRTE_ON & _WDT_OFF

;--------------------------------------------------------------------------
; Sec 1. Equates and Constants
;--------------------------------------------------------------------------
; The General Purpose Registers start at the end of the 
; Special Purpose Registers.

; DoTimeSlice bits
; these bits are set to schedule a timer chain event
;	DoTimeSlice Bits, to schedule, set bit
;	0	run on 1/8th Second
;	1	not used
;	2	not used
;	3	not used
;	4	not used
;	5	not used
;	6	not used
;	7	run	BeepMachine

DTsS8event	EQU	0	; run on 1/8th Second
DTsSevent	EQU	1	; run on Second
DTsMevent	EQU	2	; run on Minute
DTsHevent	EQU	3	; run on Hour
;
DTSrunning	EQU	7	; Timer running
  
;--------------------------------------------------------------------------
; Sec 1.1 Button and LED 
;--------------------------------------------------------------------------

; PORTA bits
BUTTON_SET_5MIN		EQU	4
BUTTON_SET_15SEC	EQU	5

; PORTB bits
LED			EQU	1


;--------------------------------------------------------------------------
; Sec 2.0 Variables
;--------------------------------------------------------------------------
;
; 
  ; cblock 0x20 ; on other chips
  cblock 0x0C ; on 16F84
;
; Variables start 0x0C

DoTimeSlice		; See bit usage above

CLtmr8sec		; counts 1/8  seconds
CLnbcdS 		; stores current time
CLnbcdM 	

IPlast			; Input variables
IPnew		
IPbuttonEvent	; stores if button pressed and button event awaiting service
  endc

;--------------------------------------------------------------------------
; Sec 3. Program Code
;--------------------------------------------------------------------------

	ORG     0       
    GOTO    Start

	ORG 	4
	retfie 

;--------------------------------------------------------------------------
; Sec 3.1	Main Program Init Code
;--------------------------------------------------------------------------

Start
	; Disable Interupts
	MOVLW	H'00'
	MOVWF	INTCON

	; Set up PortB:7 as output.
	banksel	TRISB
	MOVLW	0XFF
	MOVWF	TRISB
	MOVLW	0xF0	
	MOVWF	TRISA

	; Change Prescaler
	CLRWDT
	; Configure Tmr 0 to clock off Oscillator running at 32.768kHz
	; 32768 / 4 in
	; Set up prescaller for 8192 / 4 / 256 = 8 ticks perseconds.
	MOVLW	0x0 + 1 	; For Tmr0 0= /2, 1= /4, 2= /8, 3= /16
	MOVWF	OPTION_REG

	banksel PORTA
	bsf		PORTA,LED
	bcf		PORTA,LED

	CLRF	DoTimeSlice	
	; flush button events
	call	IPtimeslice
	call	IPtimeslice
	; clear any button events
	CLRF	IPbuttonEvent
	
	call 	CLinitTimeout
	GOTO	MainLoop
	
	
;--------------------------------------------------------------------------
; Sec 3.2	Main Program 
;--------------------------------------------------------------------------

MainLoop	
		; Assuming:
		; 	Crystal = 32.768 kHz
		;	Prescaller ticks at Crystal / 4 or 8192 ticks per second.
		;	Prescaller set to divide by 4 to give 8 timeouts per second 
		;	Tmr0 times out 8 times per second.
		;
		BTFSC	INTCON,T0IF		; test for TMR0 time out
		CALL	CLtimeslice

		; Any events that exist
		BTFSC	DoTimeSlice,DTsS8event	; On 1/8 Second Tick
		CALL	SS8tick					; Polls Switches 8 times a second

		BTFSC	IPbuttonEvent,BUTTON_SET_5MIN	; On BUTTON_SET Press
		CALL	IPsetPressed

		BTFSC	IPbuttonEvent,BUTTON_SET_15SEC	; On BUTTON_SET2 Press
		CALL	IPsetPressed15sec

		GOTO 	MainLoop

;--------------------------------------------------------------------------
; Sec 4.	Subroutines, procedures and functions
;--------------------------------------------------------------------------

;--------------------------------------------------------------------------
; Sec 4.1	Button Poll Routine
;--------------------------------------------------------------------------
		
IPtimeslice
		;	----___
		;	--_____
		;
		; This reads all Port B inputs and looks for Press
		banksel	IPlast
		MOVFW	IPnew
		MOVWF	IPlast
		MOVFW	PORTB
		MOVWF	IPnew
		; IP last contains new setting, IPlast contains previous
		COMF	IPnew,W
		ANDWF	IPlast,W
		; now force IPbuttonEvent bits to high for new pressed button
		; the service routine should set the bit to clear the event.
		IORWF	IPbuttonEvent,F
	
		RETURN


;--------------------------------------------------------------------------
; Sec 4.3	Clock Chain Routine
;--------------------------------------------------------------------------
		
CLtimeslice
		; TMR0 timeout
		BCF	INTCON,T0IF
		BSF	DoTimeSlice,DTsS8event

		; This ticks 8 per second, 
		; 
		DECFSZ	CLtmr8sec,f
		RETURN
		; divide by 8
		MOVLW	8
		MOVWF	CLtmr8sec
		
		btfss	DoTimeSlice,DTSrunning	
		return

		; Counter	- Seconds
		; This ticks 1 per second, 
		; 

CLbcdS
		BSF	DoTimeSlice,DTsSevent
		DECFSZ	CLnbcdS,f		
		RETURN
		movlw	.60
		movwf	CLnbcdS
	

CLbcdM
		BSF	DoTimeSlice,DTsMevent	
		DECFSZ	CLnbcdM,f		
		RETURN
		movlw	.60
		movwf	CLnbcdM

		;
		; 5 minute time out reset running flag and turn off LED
		;
		bcf		DoTimeSlice,DTSrunning	
		bsf		PORTA,LED
		return



CLinitTimeout	
	; using DECFSZ so add 1
	;
	movlw	.59 + 1
	movwf	CLnbcdS
	
	movlw	.4 + 1
	movwf	CLnbcdM

	movlw	8
	movwf	CLtmr8sec

	return


CLinitTimeout15sec
	MOVLW	.14 + 1
	MOVWF	CLnbcdS
	
	movlw	.0 + 1
	movwf	CLnbcdM

	MOVLW	8
	MOVWF	CLtmr8sec

	return


;--------------------------------------------------------------------------
; Sec 4.5	Event Handler Routines - Timer
;--------------------------------------------------------------------------
SS8tick		;  do this 8 times a second
		BCF		DoTimeSlice,DTsS8event
		; poll key every 8 seconds
		CALL	IPtimeslice		
		
		
		RETURN


;--------------------------------------------------------------------------
; Sec 4.6	Event Handler Routine - Buttons
;--------------------------------------------------------------------------
;

IPsetPressed	
		BCF	IPbuttonEvent,BUTTON_SET_5MIN
	
		;
		; test if running
		;
		btfss	DoTimeSlice,DTSrunning	
		goto	IPsetPressed1
		
		; running timer so stop it and turn off LED
		bcf		DoTimeSlice,DTSrunning	
		bsf		PORTA,LED
		return

IPsetPressed1	
		; running not timer so start it and turn on LED
		bsf		DoTimeSlice,DTSrunning		
		call	CLinitTimeout
		bcf		PORTA, LED
		return

IPsetPressed15sec	
		BCF	IPbuttonEvent,BUTTON_SET_15SEC
	
		;
		; test if running
		;
		btfss	DoTimeSlice,DTSrunning	
		goto	IPsetPressed15sec_1
		
		; running timer so stop it and turn off LED
		bcf		DoTimeSlice,DTSrunning	
		bsf		PORTA,LED
		return

IPsetPressed15sec_1	
		; running not timer so start it and turn on LED
		bsf		DoTimeSlice,DTSrunning		
		call	CLinitTimeout15sec
		bcf		PORTA, LED
		return

;--------------------------------------------------------------------------
; Program End
;--------------------------------------------------------------------------
		END

Multi Process Code on a PIC

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.

Testing for events:

The purpose of a process may be to test for a change of condition and on condition change, create an event. It may be run periodically, or as often as possible.

What is important, is that, when a process sends a message, the target process may not run it immediately. Another process may run to process another event. The message is stored until the receiving process can deal with it.

I do this by having a variable called doTimeSlice, The sending process sets a bit, and the receiving process resets the bit when it is run.

This diagram summarises it in logic, event and ack are pulsed high, and the out out is active low :

You must ensure that the events are processed before the next one is generated. The process must not hog the processing. A process may need to use a state machine to achieve this. A process should never loop waiting for a condition. The main loop should run a polling routine that tests for a condition, polling for each condition in turn.

This diagram summarises a latched state machine. To execute each process have a routine called XXtimeSlice. It can be made to run the logic, and each time XXtimeSlice is called is the software equivalent of clocking the latch:

The inputs are sampled, new outputs and states are calculated. ( It may be necessary to double buffer the latch. )

This is the code for the main loop.

;--------------------------------------------------------------------------
; Sec 3.2	Main Program 
;--------------------------------------------------------------------------

MainLoop	
	; Enable Interupts
	; Disable Interupts
MLdisableInt	

		; Poll timers and buttons for changes.
		CALL	CLtimeslice

		; Any events that exist
		BTFSC	DoTimeSlice,DTsS4event	; On 1/4 Second Tick
		CALL	SS4tick

		; Any events that exist
		BTFSC	DoTimeSlice,DTsSevent	; On Second Tick
		CALL	SStick

		BTFSC	DoTimeSlice,DTsMevent	; On Minute Tick
		CALL	SMtick

		BTFSC	DoTimeSlice,DTsHevent	; On Hour Tick
		CALL	SHtick

		BTFSC	DoTimeSlice,DTSRwrtEvent	; Serial Display
		CALL	SRwrtTime


		BTFSS	IPbuttonEvent,BUTTON_SET	; On BUTTON_SET Press
		CALL	IPsetPressed

		BTFSS	IPbuttonEvent,BUTTON_NEXT	; On BUTTON_NEXT Press
		CALL	IPnextPressed

		BTFSS	IPbuttonEvent,BUTTON_ONOFF	; On BUTTON_ONOFF Press
		CALL	IPonoffPressed

		BTFSS	IPbuttonEvent,BUTTON_OPENED	; On BUTTON_OPENED Press
		CALL	IPopenedPressed

		GOTO 	MainLoop
		

The event code could look like:
IPnextPressed	
		; clear event
		BSF	IPbuttonEvent,BUTTON_NEXT

		; guts of the routine.
		CALL	LSblink

		; run a state machine
		MOVLW	0	; event IPnextPressed	
		CALL 	IPsetJump
		MOVWF	IPsetState

		;output the state on port A, that has LEDs connected
		SWAPF	IPsetState,W
		MOVWF	PORTA

		; return quickly
		RETURN

This is how Microchip implement their peripherals. They each set a status bit and the service code is expected to reset it.

Micro chip have a page on their Wiki that illustrates this: Event Driven Modelling example .

To summarise:

	Loop:


	Poll for a change in condition1, and if found send a message to a process.
	Poll for a change in condition2, and if found send a message to a process.
	Poll for a timer to mature  and if so, send a message to a process.

	For each process.
		Is there a message to process? if so process it.


	; optionally sleep awaiting interupt. 
	; Use a timer to slow down polling of buttons to allow for switch contact bounce.
	; This saves energy.

	sleep


	goto Loop.

Micro chip have a page on their Wiki that illustrates this: Event Driven Modelling example .

bc001.asm A Template fot the PIC16F628 ( and PIC16F84 )

Testing for Button presses:

Sometimes you want to do something when a button is pressed or released.

However, often you only want to do something when a button mechanically moved i.e. when the person pushes or releases the button.

The PIC has a weak pull up which on a simple bread board can be used with a switch. Switches bounce, that is, for each press or release, the electrical contact may make / break many times.

This means that the onPress or onRelease event occurs too often, so some debouncing is required.

This diagram summarises it in logic:

This diagram summarises it in logic, using d-type flip flops:

On A Pic I use a simple parallel technique of reading multiple buttons. I call a routine I call IPtimeslice in a timer timeslice routine. This diagram summarises it in timing diagrams:

This diagram summarises it in timing diagrams, with the responding eventHandler cancelling the event:

This is the code I use to detect pressed.

IPtimeslice
	;	----___
	;	--_____
	;
	; This reads all Port B inputs and looks for Presses on all 8 bits at once.


	MOVFW	IPnew
	MOVWF	IPlast
	MOVFW	PORT_BUTTONS ; PORT_BUTTONS is equ on port ( or timer ) of interest.
	MOVWF	IPnew
		
	; IP last contains new setting, IPlast contains previous

	; IPnew ----___
	; IPlast	--_____
	;

	; If you want to trigger event on press		
	COMF	IPnew,W		; ____------
	ANDWF	IPlast,W		; -----_____ 
				; ____-_____

	; now force IPbuttonEvent bits to high for new pressed button
	; the service routine should clear the bit to clear the event.
	IORWF	IPbuttonEvent,F
	RETURN

This is the code I use to detect a press or release.

IPtimeslice
	;	----___
	;	--_____
	;
	; This reads all Port B inputs and looks for Presses on all 8 bits at once.


	MOVFW	IPnew
	MOVWF	IPlast
	MOVFW	PORT_BUTTONS ; PORT_BUTTONS is equ on port ( or timer ) of interest.
	MOVWF	IPnew
		
	; IP last contains new setting, IPlast contains previous

	; IPnew ----___
	; IPlast	--_____
	;

	; If you want to trigger event on down and up 
	MOVFW	IPnew		; ----______
	XORWF	IPlast,W		; -----_____ 
				; ____-_____


	; now force IPbuttonEvent bits to high for new pressed button
	; the service routine should clear the bit to clear the event.
	IORWF	IPbuttonEvent,F
	RETURN

State Machines:

A process should react to an event and process it. A process may want to do a different response for each time the event is called, and a state machine is used to track the state.

This diagram summarises a latched state machine. In code if you have a function called XXtimeSlice, It can be made to run the logic as if the latch has been clocked once:

State machines are easy to impliment.

You combine the state and event to identify the routine to run.

Here is a PIC Jump table:

;----------------------------------------------------------------
; Sec 3.3 	Jump Table for running or time setting mode
;---------------------------------------------------------------- 

IPsetJump
	; Its not a CLICK or HOLD
	ADDWF	IPsetState,W
	CLRF	PCLATH ;  Assume that the tabel is in the bottom 256 
			  byte, and restrict to 4 states.
	ANDLW	0x03   ; restrict to 4 choices
		
  IF  0 !=  ( high ( IPjmpTable4End ) - high ( IPjmpTable4Start ) 
)
    Error "Table straddles Page Boundary "
  ENDIF
IPjmpTable4Start
	ADDWF	PCL,f
	; set choice State Table - limited to 8 states
	GOTO	STtoggle		; SET:OFF,NEXT
	GOTO 	SoundInit		; SET:OFF,set
	GOTO	SoundOneDigit		; SET:YELLOW,NEXT
	GOTO	SoundNextDigit		; SET:YELLOW,set
IPjmpTable4End

Naming of Functions and variables.

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.

Using Section Headers

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.

Apend the structure type - A tautology in context, a reminder out of context

I once worked on a project that had 100,000 symbols. They say that most educated people have a vocabulary of 10,000 words. Being expected to understand the purpose of 100,000 symbols is obviously beyond the educated person. Some structure to the names is required so that you can work out that you can ignore the symbol as irrelevant to what you are working on.

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 leakyBucketDataT 

or
 
    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 leakyBucketData

or
 
    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 leakyBucketData

This 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 leakyBucketData

This is much easier to read and maintain.
  
    leakyBucketDataT             //- type 
    leakyBucketDataT lb;         //- instance
    leakyBucketDataT lbA[100];   //- array  
    leakyBucketDataT *lbP;       //- pointer  

You 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;

Example C program


/* --- 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;
}


Thoughs on Distributed state machines as used in Object Oriented Programming

http://java.sun.com/docs/books/tutorial/java/concepts/ What Is an Object?

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.

Thoughs on Distributed state machines as used in Object Oriented Programming, and web sites.

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.

Note: In software, it is probably impossible to not have a latch. If the state machine is written in a function called ProcessTimeSlice(), then every time the function is called, it is like clocking the state machine latch. In further examples, they will all be latched. There may even be more than one latch, each clocked by their own bit of software.

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.

In the diagram above, any available processor can be given the code for the combinationatory logic and run it. It will need to be given access to the state variables, and to avoid corruption, these will be locked for sole use by the processor. It will need a way of storing the state variables back into any central data store. so:
 
localCopyOfStateVariables =  StateVariables

get 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.

Thoughs on a traffic generators.

A traffic generator can be modelled by a process called once per timer tick. The process sends a traffic message.

You can start the timer with the required interArrival time.
You can start the timer with a fixed time and use probability to choose to send a call.
You can start the timer with a fixed time and use a catch up calculation to accumulate the interArrival Time until you have reached the timer tick. Each accumulation, you send a call.
Here is a traffic generator written in perl. Run these three Perl programs:
udp1.pl First Cuts in PERL - Traffic generator to 127.0.0.1 ports 2346 & 2347
udp2.pl First Cuts in PERL - Traffic sink on port 2346
udp3.pl First Cuts in PERL - Traffic sink on port 2347

udp4.pl First Cuts in PERL - Traffic generator to 127.0.0.1 ports 2346 & 2347 using Catch Up Calculations

A traffic generator can be modelled by a process called once per timer tick. The process sends a traffic message.

You can start the timer with the required interArrival time.
You can start the timer with a fixed time and use probability to choose to send a call.
You can start the timer with a fixed time and use a catch up calculation to accumulate the interArrival Time until you have reached the timer tick. Each accumulation, you send a call.

Thoughs on a traffic generators, and Norton / Thevenin equivalent circuits.

Electronic equivalent circuits come in two forms:
the voltage-source oriented Thévenin equivalent and the current-source oriented Norton equivalent (see figure). Norton @ wikipedia Thevenin @ wikipedia

I have defined terms for call generators:

In a call generator you have three parameters, which can be controlled with the others left to their own devices.

For a constant Interarrival time - calls are started at constant inter arrival times.

For a constant Erlang generator - the circuit occupancy is kept constant. Either by short calls or long calls.

For a constant Circuits generator - the number of circuits carrying calls is kept constant, when call ends another must start.

Where I use the word constant above, you can replace this by modulated. Modulated here means use a distribution like Negative exponential.

So for the "constant" Interarrival times, instead of using interArrivalTime = 10, use something like:

	# use rand() to generate a random number
        $interArrivalTime = -log( 1 - rand() ) / $CallsPerSecond;
Here is a bit of perl code from: udp4.pl: - Traffic generator using Catch Up Calculations.

This modulates the interarrival time and although it uses a constant tick, calls are sent to catch up the number sent.

This code quantises the times to the timer tick, but could be modified to impliment the other two equivalent call generators. Every timer tick you would check if the wanted Erlangs or circuits in use are matched by what is happening.

    # Catch Up calculation method. 
    # A fixed timer is used, and on each tick, 
    # calls are sent to catch up simulated time.

    my $nowTime = $timerTickTime;
 
    while( 1 ) {

	if ( $nowTime > 0.0 ) { 

           # start timer for a fixed period 
           $count = select( undef, undef, undef, $timerTickTime ) ;
           $nowTime = $nowTime - $timerTickTime;

	} else {        
        # Catch up with calls
        # generate a time interval modulated by Neg Exp distribution
        $interArrivalTime = -log( 1 - rand() ) / $CallsPerSecond;
        $sum         = $sum + $interArrivalTime;
        $running     = time - $StartedAt;


	# Now pick a call type
	$callType    = int( rand()*10 );

	# Now pick a destination
	$destination = int( rand()*10 );


        #print "Call Generator: $running,$sum,$callType,$destination,$interArrivalTime \r\n";
 
        #$ip = ;

	# Prepare Message
	# TS,Protocol,OrigID,DestID,Operation,P1,,,CRLF

        $message =  "$sum,0,0,$destination,MAKECAll,,,END\r\n";

        if ( rand() < 0.5 ) { 
          $hispaddr = sockaddr_in(2346, inet_aton("127.0.0.1") );
        } else {
          $hispaddr = sockaddr_in(2347, inet_aton("127.0.0.1") );
        }
        send(SOCKET, "CLG:,$message", 0, $hispaddr) ;

        print "Call Generator: $running,$sum,  $message";

	$nowTime = $nowTime + $interArrivalTime;
	}
    }


Thats enough for now!