Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC's multi-tasking

Status
Not open for further replies.

gramo

New Member
Well I'm still playing with Swordfish, and am simply loving it, my latest little app 'multi-tasks' a bunch of tasks together, but servicing each at certain timed intervals.

The 4 tasks in this example are (chosen at random as examples);

* Check if a Button is pressed, and increment a counter on an LCD
* Update the current time on the LCD
* Frequency Generator 1
* Frequency Generator 2

You wouldn’t want to check if the button was pressed every mS, or else it would increment the counter at phenomenal speeds, but you would want to sample it faster than each update of the current time on the LCD.

The two frequency generators were just addons to display how more tasks can be added, and yet maintain excellent timing. With all this in mind, the following task intervals were chosen;


Check Button & Update Counter: 500mS
Update Current Time: 1000mS
Frequency Generator 1: 100mS
Frequency Generator 2: 5mS

Code:
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_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 time updates
    Task_3_Interval = 100,        // mS interval for frequency output 1
    Task_4_Interval = 5           // mS interval for frequency 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

Private 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

Private Sub Check_Button()
    If Button = 1 Then
        Example_Counter = Example_Counter + 1
        WriteAt(1,11,DecToStr(Example_Counter,3))
    EndIf
End Sub

Private Sub Update_Time()
    WriteAt(2,7,DecToStr(H,2), ":", DecToStr(M,2), ":", DecToStr(S,2))
End Sub

Private Sub Freqency_Output_1()
    PORTB.0 = 1
    PORTB.0 = 0
End Sub

Private 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
S = 0                         //
M = 30                        //
H = 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
        Task_1 = False
        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

The result;
**broken link removed**
Click here to watch the circuit in action

With the use of task intervals, you can get your PIC doing heaps at the same time, well, appear as if it’s at the same time ;)
 

jbarnaby2000

New Member
I understand you are using Swordfish as your compiler...but what program are you using to simulate you circuit??

Thx
 

Mike - K8LH

Well-Known Member
Thank you for the example. Do you think the 'overhead' associated with handling the variables as arrays make it less efficient?

Code:
dim RTC(3)                    ' hrs, mins, secs
dim TMR(3)                    ' hrs, mins, secs
Code:
' within your ISR
'
mS = mS + 1
if mS = 1000 then             ' if 1 second interval
  mS = 0                      ' perform once-per-second operations
  '
  ' increment real-time-clock
  '
  n = 2                       ' index 'seconds' element
  while RTC(n) = 59           ' while element (secs or mins) = 59
    RTC(n) = 0                ' set to 00 and
    n = n - 1                 ' bump index
  wend                        '
  RTC(n) = RTC(n) + 1         ' post increment (secs, mins, or hrs)
  RTC(0) = RTC(0) mod 24      ' change 24:00:00 to 00:00:00
  '
  ' decrement count down timer (photo timer, appliance timer, etc.)
  '
  if TMRON
    n = 2                     ' index 'seconds' element
    while TMR(n) = 0          ' while element (secs or mins) = 00
      TMR(n) = 59             ' set to 59 and
      n = n - 1               ' bump index
    wend                      '
    TMR(n) = TMR(n) - 1       ' post decrement (secs, mins, or hrs)
    if not (TMR(0) | TMR(1) | TMR(2))
      TMRON = 0               ' turn off timer and
                              ' perform timed out function here
    endif
  endif
endif
 
Last edited:

gramo

New Member
The free version of Swordfish limits you too 200 Variables. A quick way to tell if the code was optimized is to compile it, although not completely accurate, but close enough.

A much more accurate way would be to simulate the program with a simple signal like the following at the start and end of the interrupt routine.

Code:
PORTB.7 = 1
PORTB.7 = 0

Run this with each program variation with an external Timer device similar to the frequency counters shown in the first post, and you could time how long your ISR is
 

Mike - K8LH

Well-Known Member
Oh, you've got to be kidding?

Isn't there a way to look at the intermediate assembly code like I do with C18?
 

gramo

New Member
Download it and find out. Of course you can, but that is not going to give you a very accurate overall time scale of the ISR.

I have found the timing method to be the best as some conditions are not met on each interrupt, and in return some code will not run (If statements for example). Now you can form a cumulative average of the time required for your ISR as it goes through different conditions
 

Mike - K8LH

Well-Known Member
I did download it and install it last week after reading your comments (and Bill's) and after taking a quick look through the manual. It does look like an incredibly nice BASIC implementation. Bravo on a nice find....

If it has intermediate assembly files then I'll look at the generated code in there. That's really simpler for me and should show how many instruction cycles 'overhead' associated for accessing the array compared to direct variables.

Thanks, Mike
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Top