'------------------------------------------------------------------------------- ' (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