Problem Reading 3pi pin value

Hi,
I’m just simply trying to read whether PB1, PB4 and PD7 are high or low - I’m not using the LCD and after calling the pololu_3pi_init() I call my own init to correct the data lines. Code’s below - not sure where I’m going wrong here, AvoidBehaviour() never returns anything different no matter the sensor value. Help most appreciated.

Thanks

Mark

void initialise()
{
	pololu_3pi_init(2000);
	InitialiseADC();
	InitialiseAvoidSensors();
}

int main(void)
{
	initialise();

	while(1)
	{
              AvoidBehaviour(&MotorInfo, &IRAvoidResults);
	}

    return 0;
}

void InitialiseAvoidSensors(void)
{

	//DDRB = (DDRB & 0xED); // Ensure PB1 and PB4 are set as input
	//DDRD = (DDRD & 0x7F); // Ensure PD7 is set as input

	DDRB &= ~(1 << PORTB1);		// Set pin PB1 as an input  
	PORTB |= 1 << PORTB1;		// Enable pull-up on pin PB1 so that it isn't floating  

	DDRB &= ~(1 << PORTB4);		// Set pin PB4 as an input  
	PORTB |= 1 << PORTB4;	    // Enable pull-up on pin PB4 so that it isn't floating  

	DDRD &= ~(1 << PORTD7);	    // Set pin PD7 as an input  
	PORTD |= 1 << PORTD7;		// Enable pull-up on pin PD7 so that it isn't floating  

}

int AvoidBehaviour(struct MotorData* MotorInfo, struct SensorData* IRAvoidResults)
{

	char PortBData = PORTB;
	char PortDData = PORTD;

	if (PINB & ( 1 << PORTB1))
	{
		MotorInfo->MovementCommand = TurnRight;
		MotorInfo->LeftPowerPercent = MotorPower;
		MotorInfo->RightPowerPercent = MotorPower;

	}
	else if(PINB & ( 1 << PORTB4))
	{

		MotorInfo->MovementCommand = TurnRight;
		MotorInfo->LeftPowerPercent = MotorPower;
		MotorInfo->RightPowerPercent = MotorPower;
	}
	else if(PIND & ( 1 << PORTD7))
	{

		MotorInfo->MovementCommand = TurnLeft;
		MotorInfo->LeftPowerPercent = MotorPower;
		MotorInfo->RightPowerPercent = MotorPower;
		
	}
	else
	{
		MotorInfo->MovementCommand = TurnLeft;
		MotorInfo->LeftPowerPercent = MotorPower;
		MotorInfo->RightPowerPercent = MotorPower;
		return 0;
	}

	return 1;
}

Hello,
I don’t see anything obviously wrong, but it would help a lot if you could simplify your program to the simplest possible thing that doesn’t work the way you think it should. Something like:

int main()
{
  DDRD |= 1 << DDD1;
  DDRB &= ~(1 << DDB1);
  PORTB |= 1 << PORTB1;
  while(1)
  {
    if(PINB & (1 << PORTB1))
      PORTD |= 1 << PORTD1; // turn on LED
    else
      PORTD &= ~(1 << PORTD1); // turn off LED
  }
}

I didn’t test that, but I expect that you can get something like it to work, then add your other code in, one piece at a time, until you find the part that makes it fail. Then, if you still don’t understand why it doesn’t work, we can help you figure it out.

-Paul

Paul,
Cheers for that. Turns out the code was sound (But used you test bit anyway) - I had LEDs on the seonsir OP lines, which were somehow affecting the value the MPU saw. They’re not removed and all is happy - thought onto an ADC question - I want to read the ADC value, individually for each of the line sensors.

Using the ADC read method (PC5 jumper is removed to keep them on constantly) is doesn’t return any value. Using the read_line method I get values back - any suggestions:

int main()
{
	pololu_3pi_init(2000);

	unsigned int sensors[5];

	while(1)
	{
		// PC5 Jumper removed for both

		/* Works!
		calibrate_line_sensors(IR_EMITTERS_ON);
		read_line(sensors, IR_EMITTERS_ON);

		clear();
		print_long(sensors[1]);
		delay_ms(500);
		*/

                // Doesn't work - always returns zero
		unsigned int ADCVal = analog_read(1);  
		clear();
		print_long(ADCVal);
		delay_ms(500);

	}
}

Hello.

If your sensors cannot source/sink much current, LEDs on their outputs will act as pull-downs/pull-ups that keep the voltage from reaching the level needed to trigger a valid high/low. If you use a multimeter to look at the output voltage of the sensor, you will probably see the LED pulling it from 5V down to something like 2V due to its inability to provide as much current as the LED wants to draw.

To answer your second question, the reflectance sensors on the 3pi are not analog sensors. Rather, they are integrated QTR-RC sensors that are designed for use with digital I/O lines. You read these sensors by first driving the sensor’s output high for a few microseconds to charge a capacitor and then timing how long it takes for the line to go low (i.e. how long the capacitor takes to discharge through the phototransistor). This discharge time is dependent on the reflectance of the surface. If you never charge the capacitor, the voltage on the pin will just stay at ground, which is why your analog_read method doesn’t give you anything interesting. You can get more interesting results if you do something like:

DDRC |= 1 << PORTC1;  // make pin PC1 an output
PORTC |= 1 << PORTC1;  // drive pin PC1 high
delay_us(10);  // charge the sensor's capacitor for 10 us
DDRC &= ~(1 << PORTC1);  // make pin PC1 an input (do this before clearing the PORTC bit!)
PORTC &= ~(1 << PORTC1);  // disable internal pull-up on pin PC1 (make it a high-impedance input)
delay_us(500);  // wait for 500 us
unsigned int ADCVal = analog_read(1);  // read the analog voltage on pin PC1 after the capacitor has had 500 us to discharge
clear();
print_long(ADCVal);  // print the result to the LCD

Since the discharge rate of the capacitor will depend on the reflectance of the surface it is over, the voltage after 500 us should vary with the reflectance of the surface (it should be close to zero when you are over a very high-reflectance surface and it should be close to 5V when you are over a very low-reflectance surface).

Is there a reason why you don’t want to use the read_line() function? The (uncompiled and untested) code I wrote above might give you some fun results, but I’d definitely recommend using the Pololu AVR library functions for reading the reflectance sensors on pins PC0 - PC4.

- Ben

Thanks for that. I hadn’t realised that they were an RC setup, thought it was vanillia analogue. Cool, shall stick with the API stuff in that case, no point in reinventing the wheel :slight_smile:

Mark