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:
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
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.
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: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 MainLoopThe 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 )
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 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
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
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.
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.
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 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!