Main program Code setup
This document describes the main sections that a program should have to work properly.
See also Handling "Button"s , Using Leds, Buttons and Relays , Creating a "Timebase" and Bit handling. for some additional information.
A. The program layout:
program XXX; // 0. Name of the program
uses aaa, bbb, ccc; // 1. "Uses" clauses: Units that are used by the main program (if needed)
var A, B, C, State: byte; // 2. Global variable declarations (if needed)
procedure interrupt; // 3. The interrupt procedure (if needed)
procedure xxx; // 4. Your own procedures and functions (if needed)
begin // 5. Executable code begin
CmCon := 7; // 5.1. Initialisation of the PIC (registers)
Option_Reg := 15;
TrisB := %00110001;
Intcon.PEIE := 1;
A := 0; // 5.2. Initialisation of your main program
B := 20;
C := 255;
State := 0; // 5.2.1 "State" variable, represents the "state" the system is in.
IntCon. GIE := 1; // 5.2.2 Enable Interrupts (if needed)
while true do // 5.3. The program's main loop
if Button(PortC, 0, 100, 1) then // 5.3.1. Capture events
State := 5; // Code to be executed when an event occurs
// e.g. the "state" of the system depends on events detected
A := 4; // code to be executed on entering a new "state"
if TestBit(PortC, 1) = 1 then
case State of // 5.3.2. Processing to do depending on the "state" of the system
if A = 255 then State := 3;
C := ReadUart(...);
if C = '#' then State := 0;
end; // end of main loop
end. // 9. End of the executable code
B. The details
1. "Uses" clauses
This is obvious: all units used by this program have to be declared here.
2. Global variables
Also obvious I think.
3. The interrupt procedure
Only there when working with interrupts of course.
There can be only one interrupt procedure (always called "interrupt").
The routine must check the interrupt flag of each enabled interrupt, and, if set, do the interrupt processing.
The interrupt flag should also be reset before leaving the interrupt routine.
Pascal does automatic context saving on entering and context restore on exit of the interrupt procedure.
There is also no need to disable interrupts inside the interrupt procedure because interrupts are disabled on entering the routine and enabled again on exit.
Keep however in mind that some PICs have "interrup priorities", so here not all interrupting is disabled inside the interrupt service routine.
if PIR1.TMR1IF then // Check interrupt flag for timer 1 overflow (xxx ms elapsed)
... // do your interrupt processing here
... // ...
PIR1.TMR1IF := 0; // clear interrupt flag
if xxx.yyy then // check interrupt flag xxx.yyy
... // do processing
xxx.yyy := 0; // clear interrupt flag
4. Own procedures and functions
This is again obvious I think.
5. Executable Code begin
Here the declaration part end and the executable statements begins.
5.1. Initialisation of the PIC
Here the PIC registers are initialized. This initialisation assures that the PIC is configured correctly to perform its task.
PIC initialization includes settings for : comparators, options, ports, interrupts, timers, etc...
It does however NOT include the actual enabling if the interrupts. The latter is only done after everything is initialized.
5.2. Initialisation of your main program
In this section your own global variables are initialized and initialization routines are called (I2c, Uart, ...).
5.2.1. The "State" of the system
One of the most important variables that you (probably) need is a socalled "State" variable.
As its name suggests, it represents the "state" of the system. The several "States" differ from each other in the sense that the system behaves differently in each state.
It is therefore important to know at every moment in which "State" the system is in and act accordingly.
States should not have values like 0,1,2,..., but like 'Idle', 'Heating', 'Cooling', ... So, constant names should be defined to make the code more readable.
"States" sets can e.g. be
(Up, down, Stop)
(Jan, Feb, ...)
Only at the end of the entire initialization the interrupts (if any used) should be enabled.
5.3. The program's main loop
Here is the part where the actual work is done.
This part is designed as an endless loop and is always needed (unless all processing is done in the interrupt procedure, which is possible in some cases).
5.3.1 Capture events
One of the main tasks of the main loop is capturing events in the most broad sense: keypresses, statusses of Ports, ...
Next to this "event catching" in the main loop also the interrupt procedure catches some events like timer overflow, external interrupts, etc.
All these events can cause e.g. "state" changes and actions to be performed (including the actions to be done when the state of the system changes).
5.3.2 State dependant Processing
Also without events occurring some processing has (usually) to be done: e.g. reading values from a temperature sensor, displaying its value, driving leds and relays, etc...
In this part, depending on the "State" of the system, the appropriate procedures/functions are called to execute the actions to do in a particular state.
Make sure none of these called procedures or functions is blocking (= has its own endless loop), otherwise the main loop will not be continued.
The processing time in the called procedures slows down the main loop repetition frequency, slowing down system responsiveness. Make the called procedures as less time consuming as possible.