Multitasking with BASCOM-8051
The following two application notes are provided by David
H Lawrence one of our US resellers. As you will find out at http://www.rhombusinc.com
David is specialized in Industrial Embedded Processing.
The application note code and another example can be downloaded to
Download file #1 Download file #2
'-------------------------------------------------------------------------------
' (c) Copyright 2000 Rhombus Greenville SC
'Code can be used by others providing this header is
included in the source.
'===============================================================================
'Getting started in Real-Time Control using BASCOM Part 1
'===============================================================================
'INTRO
'For those who have not yet applied themselves to
real-time control using 8-bit
' MCU/MPUs, then these notes and segments of code may help
you get started.
'Or if you normally avoid Interrupts, then this first part
may change that by
' showing how Timer0 can be expanded into as many timers
as you wish, and with
' very little code.
'I am experienced with the 51 at assembler level but new
to the Bascom compiler.
' What surprises me the most is that despite the
convenience of using a
' compiler, it does not appear to be restricting in any
way - hence I hope to
' avoid the use of any assembler code.
'
'Whilst most of what will be described was written a good
15-25 years ago, I
' have checked and it does appear to fit in neatly with
present day theory for
' simple real-time control and is classified as
'Co-operative Multi-tasking'.
' There is no forced switching of tasks as with a RTOS,
and hence all CPU time
' is devoted to 'getting the job done' and no storage is
needed to hold all the
' intermediate states of pre-empted tasks.
'OVERVIEW OF MACHINE CONTROL
'All but the most simple machines require multiple tasks
operating in parallel.
' Each task consist of a series of states(steps) with
clearly defined logic that
' determines the transition from one state to the next.For
example a tank drain
' valve is open, the low level float switch indicates
empty but it is known that
' an additional 40 secs needs to be timed for a complete
drain and to finally
' close the valve. In parallel a safety locking sequence
must be performed on
' another part of the machine before a steam valve is
opened.
'
'It is that sort of parallel control that is easily
handled by PLCs where there
' is a continuous loop scanning all Inputs, then based on
pre-defined logic so
' each task progresses from one stage(step) to the next
and resulting in a new
' set of Outputs. With fast loops of Inputing, Processing
and Outputing the
' tasks are effectively being controlled in parallel.
'
'When I/O is added that cannot be included in that main
I/O loop, such as
' operator interfaces and host serial communication, then
the software must use
' interrupts to take whatever time is needed from the main
I/O loop to service
' the needs of those asynchronous events. It was at this
stage of the PLC's
' history that the limitations of relay ladder logic
became more than apparent.
' The PLC solutions to handle these new demands were far
from elegant,whereas
' for MPU/MPCs the solutions are a natural and can be
achieved very simply
' using the Bascom compiler.
'The methods to be described here have handled slow
process control as mentioned
' above, and together with both serial & operator
interfaces, They have also
' allowed an 11Mhz 8052 to precisely control a knitting
machine's 100+ pneumatic
' outputs in perfect sync with needles flying by at 800
per second, and as the
' operator keyed in a new batch and the host collected
production data.
'
'The sharing of CPU time is based on a priority ordered
list of tasks and with
' all asyncronous or time critical events being interrupt
driven. The main I/O
' tasks are prompted by timer set flags (only one in
example) which can also
' serve to distribute CPU demands. Analog inputs can also
benefit from using a
' timer rate related to the supply frequency. When each
task is completed it
' returns execution to the top of the list.
'Interrupt routines are kept at an absolute minimum and
any excessive processing
' needs are off-loaded to the main loop. This holds
interrupt service latencies
' to a minimum and avoids the need for either hardware or
software priority
' handling to satisfy critical timing issues.
'An example of off-loading interrupt servicing time would
be to merely flag the
' timer event shown below, avoid all needs for stacking
(it would not change the
' value of any registers, nor even the status flags), and
carry out the updates
' in the main loop. For simplicity, the timer ISR below
does its processing
' during the interrupt and that will normally be OK but
for the most demanding
' applications.
'
'Timer0 ISR code is very small and should be self
explicit. In order to show it
' working there is a main loop using those new timers as
inputs, a little logic
' for reloading them, and their run status is output to a
set of pins to allow
' scoping. For a steady trace the timers are running very
fast at a resolution
' of 2mS. If using the Timer0 expansion code in your own
applications then it is
' only a matter of defining a new constant for loading the
timer.
'Using P3.5 as a scope trigger will show P3.2.3.4
effectively operating in
' parallel and from the same Timer0.
'Part 2 will add transparent keypad input to change the
timer values whilst they
' are running & without affecting their operation
until the Enter key finalises
' their new value and they snap to the new timing.
'----- INITIALISATION
Dim Timers(4) As Byte , Tic_cnt0 As Byte , Isr_temp As Byte , Io_flag As Bit
Const 2ms = -1793 '
(2/1000)*(11059200/12)-50reload
Config Timer0 = Timer , Gate = Internal , Mode = 1 '16 bit,own code reloads
On Timer0 Timer_0_int
Enable Interrupts 'enable the use of interrupts
Enable Timer0
Priority Set Timer0 'highest priority
Counter0 = 2ms
Start Timer0
'----- MAIN LOOP
'Dumb code to show a set of timers (Timer0 expanded) each
running with their own
' individual values, and controlling their own output(a port
pin) and effectively
' in parallel.
Main_loop:
Do
'--Test for I/O prompt
If Io_flag = 1 Then Goto Io_control 'Dummy machine control
'--Test for waiting Host messages 'Nothing currently
implemented
'If Msg_flag Then
Goto Msg_rtn
'--Test for keypad activity
'If Key_flag Then
GoTo Key_rtn
Loop
'----- MACHINE CONTROL
Io_control:
'The 3 stages of
Read_Inputs/Process/Write_Outputs
'All inputs here
are Timer values and hence readily available internally.
'The processing
here is merely to watch for Timers(1) to count down to zero
' and then reset
its associated P3.5. On the next pass (when Io_flag is found
' set in the
Main_loop) we reload all 4 timers with their individual values &
' set their individual
flags.
'All subsequent
passes with Timer(1)<>0 has pins P3.2.3.4 updated to reflect
' when their
counts have reached zero.
'That is not a
separate Output stage as such, but if we had remote I/O then
' the equivalents
of P3.2.. would be internal bits and once all processing
' was complete,
we would output them as a distinct & separate operation as in
' a PLC.
Io_flag = 0
If Timers(1) = 0 Then
If P3.5 = 1 Then 'Dont restart till 1 pass later
Reset P3.5
Else
Timers(1) = 20 : Set P3.5
Timers(2) = 15 : Set P3.2
Timers(3) = 10 : Set P3.3
Timers(4) = 5 : Set P3.4
End If
Else
If Timers(2) = 0 Then Reset P3.2 'Could
avoid multiple resets
If Timers(3) = 0 Then Reset P3.3 ' but no
real savings
If Timers(4) = 0 Then Reset P3.4
End If
Goto Main_loop
'We return at the
highest priority level ignoring the tasks below this one
' but know that
they will be reached on the next pass, now that the Io_flag
' is reset.
*Except* if the interval set between scans is not realistic: eg
' if the Control
routines take 50mS and the Io_flag is set every 40mS then
' all other
functions will be blocked out. Timing the different functions in
' the Main_loop
and allocating realistic intervals is not a difficult task.
'----- INTERRUPT SERVICE ROUTINE
'On-chip Timer0 over-flow interrupts steal insignificant
slices of CPU time in
' order to update any number of independent timers. The
timer values halt at zero
' thereby doubling as 'Done' flags. Where timing ranges
cannot be covered by
' single byte values it may be more economical to group
them with multiple
' Tic_cnts in preference to expanding all timers to use
multiple bytes.
Timer_0_int:
Counter0 = 2ms
Start Timer0
Set Io_flag
Inc Tic_cnt0
If Tic_cnt0 => 1 Then '1=2mS for easy scoping
Tic_cnt0 = 0 '5/50 =10mS/100mS more typical
For Isr_temp = 1 To 4
If Timers(isr_temp) <> 0 Then Decr Timers(isr_temp)
Next
'If Timers(0)
<> 0 Then Decr Timers(0) 'Alternatively trade code size
'If Timers(1)
<> 0 Then Decr Timers(1) '
for speed
'If Timers(2)
<> 0 Then Decr Timers(2)
'If Timers(3)
<> 0 Then Decr Timers(3)
End If
Return
|