Continue to Site

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.

Multitasking

Status
Not open for further replies.

mrfunkyjay

New Member
Hi all. I have several requirements in My Project. And it requires Multitasking, that I don't have any background knowledge about this.

My requirements are:

1. Reading PWM Signal from Pin RA1 and RA2. Two PWM Signals came from STEERING and THROTTLE Channels of Radio Control Receiver Module.

2. From these two different signals, I would like to have the outputs to LEDs while turning the steering left, Left LED blinks and turning the steering right, Right LED blinks. As well as the throttle trigger, when it is pressed, several LED also blink.

3. The problem is, my code below doesn't have any multitasking, that is why, only steering or throttle LED function is executed at a time, NOT BOTH. But I WANT BOTH to be able to execute.

Hereby I have my whole C Language program, I wrote it with MPLAB IDE C18 Compiler and PICKIT2. My MCU is PIC18F4520.

I need your help, thanks a lot!

Code:
#include <p18f4520.h>
#include <delays.h>

#pragma config OSC = HS
#pragma config WDT = OFF
#pragma config LVP = OFF

int i, iPulse1, iPulse2;
void vLeft(void);
void vRight(void);
void vFlashing1(void);
void vFlashing2(void);

/*Counts the length of a pulse on Pin RA1*/
int GetPulse1(){
    T1CON=0b00000000;			/*Init timer1*/
    TMR1L=0;
	TMR1H=0;
	TRISAbits.TRISA1=1;			/*Make this pin as an input*/
    while(PORTAbits.RA1==1); 	/*Make pin low*/
    while(PORTAbits.RA1==0);	/*While pin is high*/
	T1CONbits.TMR1ON=1;			/*Start timer0*/
	while(PORTAbits.RA1==1);	/*When pin is low*/
	T1CONbits.TMR1ON=0;			/*Stop timer0*/
	return TMR1H*256+TMR1L;		/*return 16bit timer value*/
}

/*Counts the length of a pulse on Pin RA2*/
int GetPulse2(){
	T3CON=0b00000000;			/*Init timer0*/
	TMR3L=0;
	TMR3H=0;
	TRISAbits.TRISA2=1;			/*Make this pin as an input*/
	while(PORTAbits.RA2==1);	/*Make pin low*/
	while(PORTAbits.RA2==0);	/*While pin is high*/
	T3CONbits.TMR3ON=1;			/*Start timer0*/
	while(PORTAbits.RA2==1);	/*When pin is low*/
	T3CONbits.TMR3ON=0;			/*Stop timer0*/
	return TMR3H*256+TMR3L;		/*return 16bit timer value*/
}

void main(void)
{
TRISA = 0b00000111; /*Initialize RA0 as Input Port to read PWM Signal*/
TRISB = 0b00000000; /*Initialize Port B as Output Port to flash LEDs*/
ADCON1 = 15; /*Digital Input*/
PORTB = 0;

while(1)
	{
		iPulse1=GetPulse1();
		if(iPulse1<7000){		/*If the Pulse Width is less than 1.4ms*/
			vLeft();			/*Blinks left LED*/
		}
			if(iPulse1>8000){	/*If the Pulse Width is more than 1.6ms*/
			vRight();			/*Blinks right LED*/
		}
		
		iPulse2=GetPulse2();
		if(iPulse2<7000){		/*If the Pulse Width is less than 1.4ms*/
			vFlashing1();		/*Flashing1*/
		}
			if(iPulse2>8000){	/*If the Pulse Width is less than 1.2ms*/
			vFlashing2();		/*Flashing2*/
		}

	}
}

void vLeft(void)
{
	for(i=0;i<1;i++)
	{
		LATBbits.LATB0 = 1;
		Delay10KTCYx(200);
		LATBbits.LATB0 = 0;
		Delay10KTCYx(150);
	}
}
void vRight(void)
{
	for(i=0;i<1;i++)
	{
		LATBbits.LATB1 = 1;
		Delay10KTCYx(200);
		LATBbits.LATB1 = 0;
		Delay10KTCYx(150);
	}
}
void vFlashing1(void)
{
	for(i=0;i<1;i++)
	{
		LATBbits.LATB2 = 1;
		Delay10KTCYx(60);
		LATBbits.LATB2 = 0;
		Delay10KTCYx(60);
		LATBbits.LATB3 = 1;
		Delay10KTCYx(60);
		LATBbits.LATB3 = 0;
		Delay10KTCYx(60);
		LATBbits.LATB4 = 1;
		Delay10KTCYx(60);
		LATBbits.LATB4 = 0;
		Delay10KTCYx(60);
		LATBbits.LATB5 = 1;
		Delay10KTCYx(60);
		LATBbits.LATB5 = 0;
		Delay10KTCYx(60);
	}
}
void vFlashing2(void)
{
	for(i=0;i<1;i++)
	{
		LATBbits.LATB2 = 1;
		Delay10KTCYx(30);
		LATBbits.LATB2 = 0;
		Delay10KTCYx(30);
		LATBbits.LATB3 = 1;
		Delay10KTCYx(30);
		LATBbits.LATB3 = 0;
		Delay10KTCYx(30);
		LATBbits.LATB4 = 1;
		Delay10KTCYx(30);
		LATBbits.LATB4 = 0;
		Delay10KTCYx(30);
		LATBbits.LATB5 = 1;
		Delay10KTCYx(30);
		LATBbits.LATB5 = 0;
		Delay10KTCYx(30);
	}
}
 
Unless you have more the one computer (or computer core) the computer can only do one thing at a time.

Multitasking provides the illusion that you are doing more the one thing at a time.
The computer switches between task so fast that you only think it is doing two or more things at once.

Thre are several ways to get your code to work.

A simple way is to add a free running timer. Have the blink routines check the value of the timer register. If it is less then half (128 for a 8 bit timer) turn the led off. If greater turn the led on. The trick is setting the prescaler to a value that makes you light blink at a reasonable rate.

My JPUG article from last month uses interrups to drive the junebugs charlieplexed display. If you read the articles you should be able to see how you could leverage that code to blink your leds.
 
Thre are several ways to get your code to work.

A simple way is to add a free running timer. Have the blink routines check the value of the timer register. If it is less then half (128 for a 8 bit timer) turn the led off. If greater turn the led on. The trick is setting the prescaler to a value that makes you light blink at a reasonable rate.

Okay, I have a question for you here. Which timer are you talking about here, hmm... The code I have above tells the blinking routines to check the amount of PULSE WIDTH from RA1 and RA2, both are separated and Timer 1 and Timer 3 was used. Blinking routines compare if it is above or less than several values.

I am sorry but I am newbie and I cannot digest what you are trying to say here. All I need is multitasking just like what I quoted. Hmm, I wonder how this multitasking initialized and work with my codes I have made before, thanks!!! :)
 
Okay, I have a question for you here. Which timer are you talking about here, hmm... The code I have above tells the blinking routines to check the amount of PULSE WIDTH from RA1 and RA2, both are separated and Timer 1 and Timer 3 was used. Blinking routines compare if it is above or less than several values.

I am sorry but I am newbie and I cannot digest what you are trying to say here. All I need is multitasking just like what I quoted. Hmm, I wonder how this multitasking initialized and work with my codes I have made before, thanks!!! :)
You do NOT need multitasking!

You are using timer1 and timer3 to measure pulses. I am talking about using another timer to tell you if the leds should be on or off. If you can not understand this your chance of getting good results with multitasking is very very very.... low. You have to walk first sort of thing.

Read the articles. Multiplexing Charlieplexed Displays by Daniel Johnson If they are over your head read the tutorials in my link.
 
hello mrfunkyjay,
This is not an easy answer, and it is early, before coffee, but I'll try to shed some light.

The trouble is that your delay function is blocking; the processor is doing nothing while waiting for your delay to time out. If you just set a timer and watch for it to finish, you will have the same problem. The way most 'time-slice' routines work is by setting a timer to give a periodic interrupt. When this interrupt occurs, a variable -call it system_tick, or whatever - is incremented, and the timer is reloaded with a value to give the tick the right value. The value of this variable determines what 'state' your program is in. For example, reading the steering pulse width is one state, reading the throttle is another. Turning on an LED is yet another. Your tick value has to be small enough to attend to any of the time critical tasks as needed.

My brain isn't sharp enough yet to go any deeper, but I can point you in the general direction. You don't have your location filled in, but I'm guessing you are in Korea, because of the Hangul I see in your avatar. Are you? I got some good books with this information from Kyobo Books in Seoul. Embedded C by Michael J Pont, while focused on the 8051, has a very good discussion on an embedded OS in chapters 6 and 7.
Also, Programming Robot Controllers by Mike Predco has some ideas for state machines. Perhaps the best I have found is in The Firmware Handbook Edited by Jack Ganssle. Chapters 7, 8, 9 and 15, 16, 17 are relevant.

There is also a fair bit of discussion on the net, if you google state machine, real time OS (although this will give you hits on RTOS for desktop PC as well,)
There are some commercial offerings but these tend to be expensive (SALVO, uC/OS et al.) There are also some open source offerings:
**broken link removed**

Check the wiki: FreeRTOS - Wikipedia, the free encyclopedia
Wow, google has a Velasquez logo today! (one of my favorite painters)

Some compilers also offer a 'multi-tasking OS.' I know CCS, if you buy the IDE, includes one, as does Source Boost.
Source Boost SourceBoost Technologies - Home of BoostC Free PIC C Compiler and BoostBasic Free Pic Basic Compiler offers a size limited demo, and can be had with no limits, for under a hundred dollars!

I have not tried either of these RTOS's myself, though.

Hope this gets you going. Oh, and BTW, your should fill in your location, it will help people help you. :D
 
hi, I am coming from Indonesia. My location now is Munich, Germany.

Avatar I took somewhere from net, tho. Thanks for sharing.. I keep struggling myself now as this is a LONG JUMP for a newbie like me.

I have no idea really, up to now, how these two tasks run at the same time. I just want to have a blinking LEDs, for both steering and throttling task. So when I go left while pressing throttle, both LED functions are working.

I know there are thousands way to Rome, since I am looking for a newbie-way answer, I hope several guys here can get me out of this problem. THANKS FOR SHARING!!! I love this forum btw.
 
As 3V0 said, you don't need a RTOS for something so simple as flashing and LED while doing something else. A simple interrupt driven routine can flash the LED and with just a little more coding run your steering program. A cheap & cheerful 16F628A has more than enough processing power to do what you've described with interrupts, even the ancient 16F84 could do it.
PS the core of an RTOS is interrupts, it's really just task switching anyway.
 
I have written several multitasking applications in assembly. I don't use interrupts.

The method is to have every task as a subroutine.

Each subroutine returns as soon as the calculations or tests are done, and no subroutine ever contains a wait loop.

If a wait is required, use the timers and the subroutine returns if the wait period is not finished.

The main routine just calls all the task subroutines and loops back to the start.

That way every task subroutine is always called very often, so each task can respond very quickly if needed.

I've used this on tiny programs on a 12F629 right up to stuff on 16 bit processors.

I hope this helps.
 
So which solution is the best? I'm confused again, hmm considering a newbie here, I cannot do so much, but I will try to find the solution, thanks for sharing. Eh, btw, do u mind posting some codes here? Thanks.
 
I have written several multitasking applications in assembly. I don't use interrupts.

The method is to have every task as a subroutine.

Each subroutine returns as soon as the calculations or tests are done, and no subroutine ever contains a wait loop.

If a wait is required, use the timers and the subroutine returns if the wait period is not finished.

The main routine just calls all the task subroutines and loops back to the start.

That way every task subroutine is always called very often, so each task can respond very quickly if needed.

I've used this on tiny programs on a 12F629 right up to stuff on 16 bit processors.

I hope this helps.

btw, I want to ask you, how to return a subroutine here? and return to where, please guide me along since I am still a noob in programming and PIC programming also. thanks.
 
This is a simple as it gets. No additional timers, interrupts or multitasking.

This example will blink one led for left and a 2nd for right.
You should be able to expand on it to get what you need.
It may not compile or work exactly as is but it shows what you need to do. That is to say I did not test it.

Code:
#define L_LED LATBbits.LATB0
#define R_LED LATBbits.LATB1

unsigned char count;
int iPulse1, iPulse2;   // measured pulse width

do setup here...


count = 0;

while(1)
{
   iPulse1=GetPulse1();

   // turn both LED's off **1
   L_LED = 0;  
   R_LED = 0;
   if(iPulse1<7000)   // Pulse Width is less than 1.4ms
   {		
      L_LED = (count > (0xFF/2) );  // **2

   }
   if(iPulse1>8000)  // Pulse Width is more than 1.6ms*
   {	           
      R_LED = (count > (0xFF/2) )  // **3
   }
   delay(some_value_to_create_desired_flash_rate);	  
   count++;  // will roll over to zero after 0xFF	
}

If the a led was on at **1 and it is turned back on at **2 or **3 you will not be able to see that it was off.
 
One question here:

L_LED = (count > (0xFF/2) ) > what does this refer to? I have no Idea, thanks!!!
 
mrfunkyjay,

Can you describe how you would like the LEDs on RB2 through RB5 to behave based on the Throttle control values (when less than 1400 usecs or when greater than 1600 usecs)?

Mike
 
mrfunkyjay,

Back to your original query. As the guys have mentioned, there are plenty of ways to do what you want, though I'm not exactly sure what you're trying to do with the RB2..RB5 "throttle" LEDs.

You might consider using the CCPx module "capture" mode along with an interrupt service routine to capture the servo pulse width values as a "background" task. Then your main program can simply loop at the LED flash rate interval to flash your LEDs when appropriate.

Here's a crude untested pseudo code example (the Pulse1 and Pulse2 values have been multiplied by 2 in the interrupt service routine) which simply flashes the RB2 through RB5 LEDs in sequence when the throttle pulse is greater than 1.6 msecs;

Code:
;
;  unsigned char Mask = 0b00000100;     //
;
;  Init;                                // do your init stuff
;
;  While(1)
;  { DelayMS(250)                       // approx 1/4 second delay
;    if (Pulse1 < 14000)                // if Pulse1 < 1.4 msecs
;    { R_LED = 0;                       // turn off right LED
;      L_LED = ~L_LED;                  // toggle left LED @ 2 Hz
;    }
;    if (Pulse1 > 16000)                // if Pulse1 > 1.6 msecs
;    { L_LED = 0;                       // turn off left LED
;      R_LED = ~R_LED;                  // toggle right LED @ 2 Hz
;    }
;    if (Pulse2 > 16000)                // if Pulse2 > 1.6 msecs
;    { LATB &= ~Mask;                   // turn off previous LED
;      if (Mask == 0b00100000)          // if RB5 (end of sequence)
;        Mask = 0b00000100;             // reset mask for RB2
;      else                             // else
;        Mask <<= 1;                    // shift Mask bit left
;      LATB |= Mask;                    // turn on new LED
;    }
;    else                               // if Pulse2 < 1.6 msecs
;    { LATB &= 0b00000011;              // clear RB2..RB5 LEDs
;      Mask = 0b00000100;               // reset mask for RB2
;    }
;  }
;
I haven't used the CCPx module "capture" feature yet but I suspect that once you have set it up correctly in your "init" section that the interrupt service routine might look something like this (I'm assuming you're using a 20 MHz clock and TMR1 and TMR3 preselectors are 1:1);

Code:
;
;  void isr_hi()
;  { if (PIR1bits.CCP1IF)               // if CCP1 interrupt
;    { PIR1bits.CCP1IF = 0;             // clear CCP1 interrupt flag
;      if (CCP1CONbits.CCP1M0)          // if rising edge interrupt
;      { TMR1 = 0;                      // clear TMR1 regs
;        T1CONbits.TMR1ON = 1;          // turn on TMR1
;        CCP1CONbits.CCP1M0 = 0;        // interrupt on falling edge
;      }
;      else                             // if falling edge interrupt
;      { T1CONbits.TMR1ON = 0;          // turn off TMR1
;        Pulse1 = CCP1;                 // pulse hi time, 0..10000
;        Pulse1 <<= 1;                  // multiply by 2, 0..20000
;        CCP1CONbits.CCP1MO = 1;        // interrupt on rising edge
;      }
;    }
;    if (PIR2bits.CCP2IF)               // if CCP2 interrupt
;    { PIR2bits.CCP2IF = 0;             // clear CCP2 interrupt flag
;      if (CCP2CONbits.CCP2MO)          // if rising edge interrupt
;      { TMR3 = 0;                      // clear TMR3 regs
;        T3CONbits.TMR3ON = 1;          // turn on TMR3
;        CCP2CONbits.CCP2MO = 0;        // interrupt on falling edge
;      }
;      else                             // if falling edge interrupt
;      { T3CONbits.TMR3ON = 0;          // turn off TMR3
;        Pulse2 = CCP2;                 // pulse hi time, 0..10000
;        Pulse2 <<= 1;                  // multiply by 2, 0..20000
;        CCP2CONbits.CCP2MO = 1;        // interrupt on rising edge
;      }
;    }
;  }
;
You might want to study the appropriate sections in the Data Sheet and study some of the good tutorials available online before coming back with more questions.

Kind regards, Mike
 
Last edited:
One question here:

L_LED = (count > (0xFF/2) ) > what does this refer to? I have no Idea, thanks!!!

I could have written it as

Code:
if (count > 0x7F)
{
         LATBbits.LATB0  = 1;
}
else
{
         LATBbits.LATB0  = 0;
}

With count ranging from 0 to 0xFF the expression (count>0x7F) will be zero half the time and 1 the other half. It will cause the left LED to blink.
 
Last edited:
One question here:

L_LED = (count > (0xFF/2) ) > what does this refer to? I have no Idea, thanks!!!

3v0 implemented this:

...
A simple way is to add a free running timer. Have the blink routines check the value of the timer register. If it is less then half (128 for a 8 bit timer) turn the led off. If greater turn the led on. The trick is setting the prescaler to a value that makes you light blink at a reasonable rate.
...

0xFF (math hexadecimal base) = 255 (math decimal base and also the max value of the one byte register), so if 3v0 tells you to count till 128, then it means that its half a byte lol right ? still catching this ? ok, then more than half a byte (FF / 2 = 127,5 or (int) 127 ) :D

0x(some value from 00 to FF) -> is the hexadecimal way of representing an number :)

Easy !

:D
Have fun !
 
Last edited:
one last thing, it consumes more asm instructions to use 0xFF / 2, since you need it like an constant, use the need constant value wich is 0x7F :) this way you make the execution faster and less probable to miss a pulse :)...
 
one last thing, it consumes more asm instructions to use 0xFF / 2, since you need it like an constant, use the need constant value wich is 0x7F :) this way you make the execution faster and less probable to miss a pulse :)...

A good compiler should realize that 0xFF/2 is a constant and evaluate it at compile time. C18 uses 2 instructions either way.

Code:
59:                   a = 0xFF/2;
  0106    0E7F     MOVLW 0x7f
  0108    6EDF     MOVWF 0xfdf, ACCESS
60:                   Nop();
  010A    0000     NOP
61:                   b = 0xFF;
  010C    0E01     MOVLW 0x1
  010E    68DB     SETF 0xfdb, ACCESS
62:                   Nop();
  0110    0000     NOP

EDIT: I used (0xFF/2) instead of 0x7F to show that I wanted half of the max possible value for a byte. I could have used #define MAX_COUNT 0xFF and (MAX_COUNT/2), maybe that would be clearer.
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top