Doing 2 things at the same time

hay.
I want to make a robot that will follow a line and count junctions. So, I got 3 IR sensors. 2 Of them are used to follow the internal side of a line. The third is placed far from the other 2 sensors and it counts the junctions.
Here are my problems:

  1. when the left line following sensor is out of the line (then the robot should turn a little to the right) and at the same time the junction counting sensor is at a junction, the robot turns fast and sharply to the right (almost 180 deg).
  2. As you will see in the code, there is a delay function when the sensor counts the junctions. I think that that delay is interrupting with the line following routine and makes it slower when the orangutan counts the junction and following the line at the same time.

The code uses the orangutan lib.

//include the hedear files we will need
#include "device.h"
#include <avr/io.h> 
#include <util/delay.h> 
 
 #include "pwm.h" 
 #include "lcd.h"
#include "buzzer.h"
 
 #include "servo.h" 
 
// Past here the library has been loaded, and the rest is up to you. 
// Delay for N seconds 
void delay_sec(uint8_t sec) 
{ 
	unsigned int cycles; 
 
	// Delay 25ms at a time (38.4ms is the most we can delay with a 
	// 20MHz processor, unfortunately.  See the delay.h include file 
	// for more info.) 
        //BMK delay_sec
	for(cycles = 0; cycles < (sec * 8); cycles ++)
	{ 
		_delay_ms(25); 
	} 
} 
void sensors_init(void)
//this function defines and activates the sensors. there are 3 IR sensors and 3 IR LEDs
//I allso added a pullup resistor for each sensor.
{       //define 2 I/O pins for the leds
	DDRC |= (1<<PC1) | (1<<PC2);
	//define IR sensors as input
	DDRC &=~ (1<<PC5) | (1<<PC4) | (1<<PC0);
	//make pullup resistors to the IR sensors
	PORTC |= (1<<PC5) | (1<<PC4) | (1<<PC0);
}
//the main function
int main(void) 
{
//initiate the lcd's functions
lcd_init();
//initiate the motor pwm control
pwm_init();
//we will not use the servo just yet
//servo_init();
//initiate the IR sensors
sensors_init();

//variables that we will use to set the robot location
unsigned int x=0, xORy=1;
//xORy is a boolean, 1 = robot on x, 0 = robot on y
//set the crusor on the LCD to the first charectre in the first line
lcd_gotoxy(0,0);
//write X= on the lcd. this will let us know what is the current robot location on
//the X axell
lcd_string("X=");
//set the crusor of the LCD to the first charectre at the second line
lcd_gotoxy(0,1);
//same as the X but with Y.
lcd_string("Y=");
//endless loop
for(;;)
{

//positioning rutine
    //X
    //is the robot moving on X or on Y?
    //BMK moving on x
    if (xORy == 1){
	//the robot will count 14 lines on the X
           if (x < 14)
		{
		    //BMK count x
                    //the sensor that count the lines
		    if ((PINC & (1<<PC5)) > 0)
		    {
			x = x + 1;
			lcd_gotoxy(4,0);
                        lcd_int(x);
                        delay_sec(1);
                    }

		    //this ELSE belonges to the first IF in this rutine. it means that
		    //the robot finished counting 4 lines, and now he has to stop
                    //and turn to Y.
		}
		if (x < 14){
		       
		    //line following rutine
		    //for now we will use only 1 IR sensor to track the line. the
		    //robot will allways go to the right and whem he gets out
		    //of the line he will go back to the line by going left.
                    //so, is the sensor on the line IS on the line? or is it off of the line?
                    //BMK line traking rutine
		    pwm_a(50);
                    pwm_b(50);
		    if ((PINC & (1<<PC0)) == 0){
                        //left sensor
		       
				pwm_a(50);
				pwm_b(0);
				PORTC |= (1<<PC2);
		    }
		    else
		    {
			PORTC &=~ (1<<PC2);
		    }

		    if ((PINC & (1<<PC4)) == 0){
                        //right sensor
			pwm_a(0);
			pwm_b(50);
			PORTC |= (1<<PC1);

		    }
		    else
		    {
			PORTC &=~ (1<<PC1);
		    }
		   
		}
		else
		{
		    pwm_a(0);
		    pwm_b(0);
		}

	     

		   
}
                    else{

			pwm_a(0);
			pwm_b(0);
    
				}
		   
		}


}

Arbel

Hey Arbel,

Delays are the easiest way to deal with timing, but you quickly run into the problem you’re having now, what if you want the robot to be able to do something else during the delay? There are two basic approaches to dealing with this problem:

You can break long delays up by putting a shorter delay in a loop, and putting checks for various conditions and response actions in the loop. This approach is still pretty simple, but gets cumbersome really quickly!

The good news is that microcontrollers have hardware timers built into them. You can configure a timer at the beginning of your code with things like how fast to count and what to do when it reaches a certain number, start it when a particular event occurs (like when the sensors see a junction) then continue doing other things (like watching the line edge). When the counter reaches the magic number, it interrupts your program and executes some special instruction.

The datasheet on your AVR has a lot of information on timers and interrupts and the wide variety of things you can do with them, but I would recommend starting by reading Craig Limber’s (Climber) tutorial pages on timers and Interrupts.

Looking forward to seeing your robot!

-Adam

Hay
why is this line not working?

if (((PINC & (1<<PC4)) > 0) && ((PINC & (1<<PC0)) > 0)){
//do something
}

I want to check if 2 sensors are working at the same time. If this is the case, the robot will do some thing.
of course, It is not working…
Arbel

That line of code looks fine. In your earlier code I see you set pins C0 and C4 to be inputs, so the “do something” statement will be reached if pins C0 and C4 are both externally pulled high, is that what your sensor does? I/O pins are inputs by default, so actually as long as you’re not setting them as outputs somewhere else you should be fine. If your program isn’t behaving the way you expect it, I don’t think this particular line of code is the problem.

It could be an electro-mechanical problem, or a problem elsewhere in your code. If you want to post your current full code with that line in it I could have a look.

-Adam

P.S. The “>0” part of the statement is redundant. In C logic, zero is false and anything non-zero is true, so you could just write:

if ((PINC & (1<<PC4)) && (PINC & (1<<PC0)))

No big deal. Actually the compiler might be making that change for you if you have code optimization turned on.

here is my code:

//include the hedear files we will need
#include "device.h"
#include <avr/io.h> 
#include <util/delay.h> 
 
 #include "pwm.h" 
 #include "lcd.h"
#include "buzzer.h"
 
 #include "servo.h" 
 
// Past here the library has been loaded, and the rest is up to you. 
// Delay for N seconds 
void delay_sec(uint8_t sec) 
{ 
	unsigned int cycles; 
 
	// Delay 25ms at a time (38.4ms is the most we can delay with a 
	// 20MHz processor, unfortunately.  See the delay.h include file 
	// for more info.) 
 
	for(cycles = 0; cycles < (sec * 3); cycles ++)
	{ 
		_delay_ms(25); 
	} 
} 
void sensors_init(void)
//this function defines and activates the sensors. there are 3 IR sensors and 3 IR LEDs
//I allso added a pullup resistor for each sensor.
{
	DDRC |= (1<<PC1) | (1<<PC2);
	//define IR sensors as input
	DDRC &=~ (1<<PC5) | (1<<PC4) | (1<<PC0);
	//make pullup resistors to the IR sensors
	PORTC |= (1<<PC5) | (1<<PC4) | (1<<PC0);
}
//the main function
int main(void) 
{
//initiate the lcd's functions
lcd_init();
//initiate the motor pwm control
pwm_init();
//we will not use the servo just yet
//servo_init();
//initiate the IR sensors
sensors_init();

//variables that we will use to set the robot location
unsigned int x=0, xORy=1, i;
//xORy is a boolean, 1 = robot on x, 0 = robot on y
//set the crusor on the LCD to the first charectre in the first line
lcd_gotoxy(0,0);
//write X= on the lcd. this will let us know what is the current robot location on
//the X axell
lcd_string("X=");
//set the crusor of the LCD to the first charectre at the second line
lcd_gotoxy(0,1);
//same as the X but with Y.
lcd_string("Y=");
//endless loop
for(;;)
{

//positioning rutine
    //X
    //is the robot moving on X or on Y?
    //BMK moving on x
    if (xORy == 1){
                //the robot will count 4 lines on the X
		if (x < 14){
		       
		    //line following rutine
		    //for now we will use only 1 IR sensor to track the line. the
		    //robot will allways go to the right and whem he gets out
		    //of the line he will go back to the line by going left.
                    //so, is the sensor on the line IS on the line? or is it off of the line?
                    //BMK line traking rutine
		    pwm_a(50);
                    pwm_b(50);
		    if ((PINC & (1<<PC0)) == 0)
		    {
			//left sensor
		        pwm_a(50);
		        pwm_b(0);
			PORTC |= (1<<PC2);
		    }
		    else
		    {
			PORTC &=~ (1<<PC2);
		    }

		    if ((PINC & (1<<PC4)) == 0){
                        //right sensor
			pwm_a(0);
			pwm_b(50);
			PORTC |= (1<<PC1);

		    }
		    else
		    {
			PORTC &=~ (1<<PC1);
		    }
		    if (((PINC & (1<<PC4)) == 0) && ((PINC & (1<<PC0)) == 0))
		    {
			pwm_a(0);
			pwm_b(0);
		    }
                    //BMK count grid x
		    if ((PINC & (1<<PC5)) > 0)
		    {
			x = x + 1;
			lcd_gotoxy(4,0);
			lcd_int(x);
                        //this loop brakes the delay function so the line following rouine will work
			for (i=0;i<15;i++)
			{
			    if ((PINC & (1<<PC0)) == 0)
			    {
				//left sensor
		       		pwm_a(50);
				pwm_b(0);
				PORTC |= (1<<PC2);
			    }


			    else
			    {
				PORTC &=~ (1<<PC2);
			    }

			    if ((PINC & (1<<PC4)) == 0)
			    {
				//right sensor
				pwm_a(0);
				pwm_b(50);
				PORTC |= (1<<PC1);

			    }
			    else
			    {
				PORTC &=~ (1<<PC1);
			    }

			    _delay_ms(10);
			}

                    }

		    //this ELSE belonges to the first IF in this rutine. it means that
		    //the robot finished counting 4 lines, and now he has to stop
                    //and turn to Y.


		   
}
                    else{

			pwm_a(0);
			pwm_b(0);
    
				}
		   
		}


}
}

Arbel

I think I see your problem, it’s not that one line, but the way the code is structured inside the “if(x<14)” loop.

Lets assume both the left and right sensors are outputting low. First, the code sets pwm_a and pwm_b to 50. Then you have a function that checks if the left sensor is low. It is, so the function sets pwm_a to 50 and pwm_b to 0. Then the next function checks if the right sensor is low. It is, so that function sets pwm_b to 50 and pwm_a to 0. Then you have your function which checks the state of both sensors, and since they’re both low, both motor pwms are set to zero. Lets assume the “if ((PINC & (1<<PC5)) > 0)” check is false, so your loop repeats.

The problem is that these checks loop very quickly, and all take roughly the same amount of time, so for 1/4 of the time you’re telling the left motor to move, for 1/4 of the time you’re telling the right motor to move, for 1/4 of the time you’re telling both motors to move, and the rest of the time you’re not telling either motor to move. The motors won’t respond that quickly, and the entire if(x<14) loop only takes 0.28ms in simulation (assuming you’re running your AVR at 8MHz), which is about two motor PWM cycles. Since you’re changing the PWM parameters four times over just two PWM cycles, it’s hard to predict how the motors are going to behave, except that it won’t be how you want them to!

The simplest fix would be just a little reordering of your if(x<14) loop so it only sets the pwm parameters one way in each loop, something like this:

if (x < 14){
	//line following rutine
	//for now we will use only 1 IR sensor to track the line. the
	//robot will allways go to the right and whem he gets out
	//of the line he will go back to the line by going left.
	//so, is the sensor on the line IS on the line? or is it off of the line?
	//BMK line traking rutine

	if (((PINC & (1<<PC4)) == 0) && ((PINC & (1<<PC0)) == 0)){
		pwm_a(0);
		pwm_b(0);
		PORTC |= (1<<PC1);
		PORTC |= (1<<PC2);
	}else if ((PINC & (1<<PC0)) == 0){
		//left sensor
		pwm_a(50);
		pwm_b(0);
		PORTC |= (1<<PC2);
		PORTC &=~ (1<<PC1);
	}else if ((PINC & (1<<PC4)) == 0){
		//right sensor
		pwm_a(0);
		pwm_b(50);
		PORTC |= (1<<PC1);
		PORTC &=~ (1<<PC2);
	}else{
		pwm_a(50);
		pwm_b(50);
		PORTC &=~ ((1<<PC1)|(1<<PC2));
	}

	//BMK count grid x...

I didn’t even look at the code past that last line yet, one thing at a time.

With this structure, the pwm parameters are only set once per loop, and if the sensor state hasn’t changed, the motor pwm command will be the same and the motors should behave properly. You might also think about holding on to the previous sensor state, and if it hasn’t changed between loops, skipping the rest of the checks. After reading a little of the Orangutan-Lib pwm-configuration function code, I don’t think it’s a problem to keep updating the pwm parameters so rapidly with the same values, but I’m not 100% sure about this.

-Adam

hay ya

A strange thing, when the sensor in PC0 and the sensor in PC5 are set low, the robot goes hard to the right. there is no section in the code to make it behave that way. why O why?

//include the hedear files we will need
#include "device.h"
#include <avr/io.h> 
#include <util/delay.h> 
 
 #include "pwm.h" 
 #include "lcd.h"
#include "buzzer.h"
 
 #include "servo.h" 
 
// Past here the library has been loaded, and the rest is up to you. 
// Delay for N seconds 
void delay_sec(uint8_t sec) 
{ 
	unsigned int cycles; 
 
	// Delay 25ms at a time (38.4ms is the most we can delay with a 
	// 20MHz processor, unfortunately.  See the delay.h include file 
	// for more info.) 
 
	for(cycles = 0; cycles < (sec * 3); cycles ++)
	{ 
		_delay_ms(25); 
	} 
} 
void sensors_init(void)
//this function defines and activates the sensors. there are 3 IR sensors and 3 IR LEDs
//I allso added a pullup resistor for each sensor.
{
	DDRC |= (1<<PC1) | (1<<PC2);
	//define IR sensors as input
	DDRC &=~ (1<<PC5) | (1<<PC4) | (1<<PC0);
	//make pullup resistors to the IR sensors
	PORTC |= (1<<PC5) | (1<<PC4) | (1<<PC0);
}
//the main function
int main(void) 
{
//initiate the lcd's functions
lcd_init();
//initiate the motor pwm control
pwm_init();
//we will not use the servo just yet
//servo_init();
//initiate the IR sensors
sensors_init();

//variables that we will use to set the robot location
unsigned int x=0, xORy=1, i;
//xORy is a boolean, 1 = robot on x, 0 = robot on y
//set the crusor on the LCD to the first charectre in the first line
lcd_gotoxy(0,0);
//write X= on the lcd. this will let us know what is the current robot location on
//the X axell
lcd_string("X=");
//set the crusor of the LCD to the first charectre at the second line
lcd_gotoxy(0,1);
//same as the X but with Y.
lcd_string("Y=");
//endless loop
for(;;)
{

//positioning rutine
    //X
    //is the robot moving on X or on Y?
    //BMK moving on x
    if (xORy == 1){
                //the robot will count 4 lines on the X
		if (x < 6){
		       
		    //line following rutine
		    //for now we will use only 1 IR sensor to track the line. the
		    //robot will allways go to the right and whem he gets out
		    //of the line he will go back to the line by going left.
                    //so, is the sensor on the line IS on the line? or is it off of the line?
		    //BMK line traking rutine


		    if (((PINC & (1<<PC5)) > 0) && ((PINC & (1<<PC0)) == 0))
		    {

			pwm_a(50);
			pwm_b(0);
                        delay_sec(1);
		        PORTC |= (1<<PC1);
		        PORTC |= (1<<PC2);
		    }else if (((PINC & (1<<PC4)) == 0) && ((PINC & (1<<PC0)) == 0))
		    {
			pwm_a(-50);
			pwm_b(-50);
			PORTC |= (1<<PC1);
                        PORTC |= (1<<PC2);
		    }else if ((PINC & (1<<PC0)) == 0){
			//left sensor
			pwm_a(50);
			pwm_b(0);
			PORTC |= (1<<PC2);
			PORTC &=~ (1<<PC1);
		    }else if ((PINC & (1<<PC4)) == 0){
			//right sensor
			pwm_a(0);
			pwm_b(50);
			PORTC |= (1<<PC1);
			PORTC &=~ (1<<PC2);
		    }else{
			pwm_a(50);
			pwm_b(50);
			PORTC &=~ ((1<<PC1)|(1<<PC2));
		    }
                    //BMK count grid x
		    if ((PINC & (1<<PC5)) > 0)
		    {
			x = x + 1;
			lcd_gotoxy(4,0);
			lcd_int(x);
                        //this loop brakes the delay function so the line following rouine will work
			for (i=0;i<15;i++)
			{
                             if (((PINC & (1<<PC5)) > 0) && ((PINC & (1<<PC0)) == 0))
		    {

			pwm_a(50);
			pwm_b(0);
                        delay_sec(1);
		        PORTC |= (1<<PC1);
		        PORTC |= (1<<PC2);
		    }else if (((PINC & (1<<PC4)) == 0) && ((PINC & (1<<PC0)) == 0))
		    {
			pwm_a(-50);
			pwm_b(-50);
			PORTC |= (1<<PC1);
                        PORTC |= (1<<PC2);
		    }else if ((PINC & (1<<PC0)) == 0){
			//left sensor
			pwm_a(50);
			pwm_b(0);
			PORTC |= (1<<PC2);
			PORTC &=~ (1<<PC1);
		    }else if ((PINC & (1<<PC4)) == 0){
			//right sensor
			pwm_a(0);
			pwm_b(50);
			PORTC |= (1<<PC1);
			PORTC &=~ (1<<PC2);
		    }else{
			pwm_a(50);
			pwm_b(50);
			PORTC &=~ ((1<<PC1)|(1<<PC2));
                            }

			    _delay_ms(20);
			}

                    }

		    //this ELSE belonges to the first IF in this rutine. it means that
		    //the robot finished counting 4 lines, and now he has to stop
                    //and turn to Y.


		   
}
                    else{

			pwm_a(0);
			pwm_b(0);
    
				}
		   
		}


}
}

Arbel

What is the state of PC4 when this happens?

From your code it looks like it should just be turning as fast as pwm_a(50), pwm_b(0) would make it go. Is it turning much harder than that?

-Adam

PC4 is high. It turns much much faster then PWM(50).
I tried to add an IF statement to deal with both sensors together, but It wont work.
Arbel

I put your code on my Orangutan, and I think I’m seeing the behavior you’re describing, but for me it only happens when PC0 is low and PC5 is high (the state of PC4 doesn’t seem to matter). Can you verify that you’re seeing it when PC0 AND PC5 are held low? I’m not using sensors, I’m just using little wire jumpers to ground to test.

-Adam

Sorry, you are right, PC0 is low and PC5 is high.
Arbel

Well I don’t think you’re doing anything wrong, but it seems like there’s something seriously wrong with the Orangutan-lib PWM function.

If I make a super-simple program like this:

int main(void){
	pwm_init();

//MOTOR COMMAND HERE

	while(1);
}

I can get some really weird behavior. If I just say pwm_a(50) or pwm_b(50) I get the expected performance, but if I say:

	pwm_a(50);
	pwm_b(0);

I get no motion at all of either motor. I get the same lack of motion if I say:

	pwm_a(50);
	pwm_b(0);
	pwm_a(50);
	pwm_b(0);

And here’s the real kicker, if I add a large enough delay in between the two sets of motor commands (which is similar to how your code executes) like this:

	pwm_a(50);
	pwm_b(0);
	_delay_ms(10);
	pwm_a(50);
	pwm_b(0);

I get motor A going at full speed, just like you’re seeing! What the heck?

Maybe we can get one of the guys who wrote the Orangutan-lib libraries to take a look at it (Tom?), if not I’ll try to debug it at some point.

For now, you might want to have a look at Jim Remington’s example PWM code for the Orangutans here.

Sorry you’re having trouble, but at least now you know it’s not something you’re doing wrong!

-Adam