Swordfish Code Snippet - Multi Tasking

Who ever said PIC's can't do more than one thing at the same time was right, that's because they have a single instruction pointer that executes one command at a time. But what if one function took 1mS, and another took 3mS (and so on). Instead of waiting forever servicing the one function/waiting for a condition, why not allocate a time interval for each task, eg

  • Perform Task 1 every 10mS
  • Perform Task 2 every 5mS
  • Perform Task 3 every 25mS

One thing to keep in mind is that each task takes time, and you should allocate enough time for the task to complete, and every other task (as sooner or later, all the tasks will be synchronized and will need to be serviced one after the other). This is only really important if you want perfect timing, but its definitely not needed for most (almost all) applications, as each task will be serviced no matter what with this method.


OK, lets move on. My first task is to scan for a button press. The button will control a counter on an LCD, but at the same time, I want to update the current time on the LCD. I cant check the button every mS, or else the counter  will almost instantly go to max, and there's no point updating the LCD display more than once a second to show current time... So lets multi-task!


To spice it up a little, I'm going to add two more tasks, frequency generators. (4 Tasks in total)

Task Frequency Interval
Check Button & Update Counter 500mS
Update Current Time 1000mS
Frequency Generator 1 100mS (10Hz)
Frequency Generator 2 5mS (200Hz)

 

Sounds a little tricky, the truth of the matter is that it is. Consider the following code and the video below;

Device = 18F452
Clock = 20
 
#option LCD_DATA = PORTD.4
#option LCD_RS = PORTE.0
#option LCD_EN = PORTE.1
 
Include "LCD.bas"
Include "utils.bas"
Include "convert.bas"
 
Dim
    TMR2_Int_Enable As PIE1.1,    // TMR2 interrupt enable
    TMR2_Overflow As PIR1.1,      // TMR2 overflow flag
    TMR2_On As T2CON.2,           // Enables TMR2 to begin incrementing
    mS As Word,                   // Time Registers
    S As Byte,                    //
    M As Byte,                    //
    H As Byte,                    //
    Task_1_Timer As Word,         // Task 1 = Check if button pressed
    Task_2_Timer As Word,         // Update Time on LCD
    Task_3_Timer As Word,         // Frequency output 1
    Task_4_Timer As Word,         // Frequency output 2           
    Task_1 As Boolean,            // Task service request bits
    Task_2 As Boolean,            //
    Task_3 As Boolean,            //
    Task_4 As Boolean,            //
    Example_Counter As Byte,      // Counter register for the button
    Button As PORTA.0             // Assing the button Pin
 
Const
    Task_1_Interval = 500,        // mS interval for button check
    Task_2_Interval = 1000,       // mS interval between LCD time display updates
    Task_3_Interval = 100,        // mS interval for frequency generator output 1
    Task_4_Interval = 5           // mS interval for frequency generator output 2
 
Interrupt TMR2_Interrupt()
    Save(0)
    If TMR2_Overflow = 1 And TMR2_Int_Enable = 1 Then
        TMR2_Overflow = 0                           // Reset TMR2 Overflow flag
        mS = mS + 1
        If mS = 1000 Then                           // Code for current time
            mS = 0
            S = S + 1
            If S = 60 Then
                S = 0
                M = M + 1
                If M = 60 Then
                    M = 0
                    H = H + 1
                    If H = 24 Then
                        H = 0
                    EndIf
                EndIf
            EndIf
        EndIf
        Task_1_Timer = Task_1_Timer + 1             // Increment all Task handlers
        Task_2_Timer = Task_2_Timer + 1
        Task_3_Timer = Task_3_Timer + 1
        Task_4_Timer = Task_4_Timer + 1               
        If Task_1_Timer = Task_1_Interval Then      // Check if a task interval is
            Task_1_Timer = 0                        //   ready to be flagged
            Task_1 = True
        EndIf
        If Task_2_Timer = Task_2_Interval Then
            Task_2_Timer = 0       
            Task_2 = True
        EndIf              
        If Task_3_Timer = Task_3_Interval Then
            Task_3_Timer = 0       
            Task_3 = True
        EndIf
        If Task_4_Timer = Task_4_Interval Then
            Task_4_Timer = 0       
            Task_4 = True
        EndIf                
    EndIf
    Restore
End Interrupt
 
Sub TMR2_Initialize()
    TMR2_On = 0               // Disbale TMR2
    TMR2_Int_Enable = 0       // Turn off TMR2 interrupts
 
    INTCON.6 = 1              // Peripheral Interrupts Enabled      
    T2CON.0 = 1               //  00 = Prescaler is 1
    T2CON.1 = 0               //  01 = Prescaler is 4
                              //  1x = Prescaler is 16
    PR2 = 249                 // TMR2 Period register PR2  
    T2CON.3 = 0               //  0000 = 1:1 postscale
    T2CON.4 = 0               //  0001 = 1:2 postscale
    T2CON.5 = 1               //  0010 = 1:3 postscale
    T2CON.6 = 0               //  1111 = 1:16 postscale   
    TMR2 = 0                  // Reset TMR2 Value   
    TMR2_Int_Enable = 1       // Enable TMR2 interrupts
    TMR2_On = 1               // Enable TMR2 to increment
    Enable(TMR2_Interrupt)
End Sub
 
Sub Check_Button()
    If Button = 1 Then
        Example_Counter = Example_Counter + 1
        WriteAt(1,11,DecToStr(Example_Counter,3))
    EndIf
End Sub
 
Sub Update_Time()
    WriteAt(2,7,DecToStr(H,2)":", DecToStr(M,2)":", DecToStr(S,2))
End Sub
 
Sub Freqency_Output_1()
    PORTB.0 = 1
    PORTB.0 = 0
End Sub
 
Sub Freqency_Output_2()
    PORTB.1 = 1
    PORTB.1 = 0
End Sub
 
 
DelayMS(200)                  // Allow LCD to warm up
SetAllDigital                 // Make all pins digital I/O's
Cls                           // Clear the LCD screen
 
WriteAt(1,1,"Counter = 000")  // Send text that only needs to be sent once
WriteAt(2,1,"Time:")          //   to the LCD
 
mS = 0                        // Reset all registers
= 0                         //
= 30                        //
= 12                        //
Example_Counter = 0           //
Task_1_Timer = 0              //
Task_2_Timer = 0              //
Task_3_Timer = 0              //
Task_4_Timer = 0              //
Task_1 = False                //
Task_2 = False                //
Task_3 = False                //
Task_4 = False                //
 
Input(Button)                 // Make the button an input
Low(PORTB.0)                  // Make frequency 1 signal an output and low
Low(PORTB.1)                  // Make frequency 2 signal an output and low
 
TMR2_Initialize               // Turn on TMR2 (with settings for 1mS interrupt)
 
 
While 1 = 1                   // Main program loop that is multi-tasked
    If Task_1 = True Then     // Waits for each service request bit to go high
        Task_1 = False        //  then services the task as required
        Check_Button
    EndIf
    If Task_2 = True Then
        Task_2 = False   
        Update_Time
    EndIf
    If Task_3 = True Then
        Task_3 = False   
        Freqency_Output_1
    EndIf
    If Task_4 = True Then
        Task_4 = False   
        Freqency_Output_2
    EndIf       
Wend


Notice that the interrupt contains only simple flag setting and variable increments. The actual tasks are performed outside the interrupt, so one task could be doing its thing, an interrupt occurs mid way through, "flagging" a task if its intervals elapses. This ensures that the task is serviced when the previous task is complete, easy!


The result, 4 tasks being performed at the same time at precise intervals;

multi-tasking1


Forum Activity

Member Access