Strange problem reading port pin in m8

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi everyone,

I am experiencing a strange problem with mega8. I had some working code for software implementation (needed!) of I2C protocol which didn't work when ported to AVR. Anyways I suspect the following to be the cause:

The SDA pin I have defined is to be used both as input as well output. So attaching 2 LEDs & running debug code confirms that both are OK for output (lit brightly).

But, when I try to read the SDA pin with:

#define I2C_PORT		PORTD
#define I2C_DIR			DDRD	//set DDR bits to configure corresp. port bits as I/P
#define I2C_PORT_READ	PIND
#define I2C_SCL 	(1<<2)	//P2^3;
#define I2C_SDA 	(1<<3)	//P2^2;
#define I2C_SCL_HI	I2C_PORT |= I2C_SCL
#define I2C_SCL_LO	I2C_PORT &= ~I2C_SCL
#define I2C_SDA_HI	I2C_PORT |= I2C_SDA
#define I2C_SDA_LO	I2C_PORT &= ~I2C_SDA
#define I2C_SDA_DIR_IN		I2C_DIR &= ~I2C_SDA //configure SDA pin as input to read from RTC
#define I2C_SDA_DIR_OUT	 I2C_DIR |= I2C_SDA
.
.
.
unsigned char readI2C(uint8_t ACK_Bit)
{
    volatile uint8_t Data=0,i;
    I2C_SDA_DIR_IN;		//SDA = 1; (for 8051)
    I2C_SDA_LO;	  //disable internal pullups, otherwise will always read pin as 0!
	for (i=0;i<8;i++)
	{
		I2C_SCL_HI;
		Data<<= 1;
		//Data = (Data | SDA);
		if(I2C_PORT_READ & I2C_SDA)		
		Data |= 0x01;
		else
		Data &= ~0x01;
		
		I2C_SCL_LO;
		_delay_us(50);
	}
    I2C_SDA_DIR_OUT;	//remember to configure SDA pin to O/P again !! 

 	if (ACK_Bit == 1)
	I2C_SDA_LO; // Send ACK		
	else		
	I2C_SDA_HI; // Send NO ACK				
	_delay_us(50);
	I2C_SCL_HI;		
	_delay_us(50);
	I2C_SCL_LO;
	
	return Data;
}

It seems that when I turn on the pullup resistors while configuring the SDA pin as input, & then read it (via PIND) it shows the pin to be 0!
but when I configure SDA to be hi-Z input pin, & then connect an external pullup of 4.7K, it reads correctly as 1.

So in effect, the internal 'pullup' is actually behaving as pulldown, and a rather strong one.
I used 3.9K external pullup against the internal supposed 'pulldown', but was still reading 0. even a 100ohm resistor wasnt able to make it read one.

I had used another mega8, but still the same problem. Anyone experienced this sort of behavior?
Or is there something wron with my

if(I2C_PORT_READ & I2C_SDA)
.
.

I strongly suspect there's something wrong with the code :roll:
I am also using 4 bits of PORTD for driving an LCD (only as O/P), but there I'm taking care to set only those pins to O/P in DDRD register.

-
Regards,
Munish

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It seems that when I turn on the pullup resistors while configuring the SDA pin as input, & then read it (via PIND) it shows the pin to be 0!

That sounds strange, doesn't it? And no, it isn't the pullup resistors. What voltage is actually at the pin?

Quote:

#define I2C_SCL_HI I2C_PORT |= I2C_SCL

AFAIK, this isn't the way to do I2C with an AVR. You make the PORTx value for both SCL and SDA 0 -- the meaning then depends on the corresponding DDRx bit. To signal a high, the DDRx bit is cleared making it an input and the external pullup pulls it high. To signal a low, the DDRx bit is set making it an output and the AVR pulls it low against the external pullup.

You should be able to find already-made bit-banged I2C for AVRs.

http://homepage.hispeed.ch/peter...
https://www.avrfreaks.net/index.p...
http://www.mil.ufl.edu/~chrisarn...
...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes! you are absolutely right. In fact, the original code for the 8051 did exactly that- it only pulled LOW the pin that needed to be zero. rest of the work was done by pullups. I'd indeed modify my AVR code.

And I also think it aint the pullups. The voltage is 5V at the pin regardless whether I use the 3.9K or 100 ohm.
But the confusion is still there: why the code is taking +5V as low? especially when internal pullups are enabled. Am I missing something important? :shock:

P.S.
Thanks for the links. very helpful :D

-
Regards,
Munish

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Write a test program that demonstrates >>onlya<< the strange behaviour. Post that, give the symptoms, and tell exactly what is connected to the AVR pin(s) and the levels at those pins. [I'm a hair skeptical of your +5 claim above--did you actually check it with a 'scope during the test run?]

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
And I also think it aint the pullups. The voltage is 5V at the pin regardless whether I use the 3.9K or 100 ohm.
But the confusion is still there: why the code is taking +5V as low? especially when internal pullups are enabled. Am I missing something important? Shocked

You can damage your AVR and your I2C slaves with 100R. 1k0 to 10k is normal.

As Lee has said, you must make the SDA and SCL pins behave as 'open collector' with external 4.7k pull-up resistors. i.e. like the 8051 output pins.

You do this by PORTx.y = 0 and toggling DDRx.y

Read the Fleury bit-banged code.
Avoid the Pascal Stang code.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay, I did it the proper way:
replaced all instances of I2C_SDA_HI;
with
I2C_SDA_DIR_IN; //make I/P
I2C_SDA_LO; //disable internal pullups, ext pullup used

and I2C_SDA_LO;
with
I2C_SDA_DIR_OUT; // make O/P
I2C_SDA_LO; // now AVR pulls it low

although there is only 1 master, still did the same with SCL. And kudos! Now I2C is working.

However to get to the roots of the 'problem' I breadboarded another circuit, with LED connected to SCL line for output & nothing else to AVR. Configured SDA as input & ran the following:

int main(void)
{
	I2C_SDA_DIR_IN;	//input
	I2C_SDA_LO;	//I2C_SDA_LO: disable internal pullups
	I2C_SCL_DIR_OUT;	//output for led
	while(1)
	{
	  if(I2C_PORT_READ & I2C_SDA)   //if SDA high
	  I2C_SCL_HI;   //LED on

  	  else
	  I2C_SCL_LO;   //LED off
	}//while ends

}//main ends

Now with this code, when I disable pullups for SDA, it shows correct "haywire" behavior, picking up all sort of noise & all that Hi-Z stuff... this is in contrast to enabling pullups when it stays stable.

Quote:
did you actually check it with a 'scope during the test run?
Yes I did.
Quote:
Write a test program that demonstrates >>only<< the strange behaviour.
Actually I didnt suspect the AVR was bad, & wanted to know if the LCD was messing with other pins of PORTD. And somehow I knew if i wrote test prog, 99.9% chances are there it would run okay. (what you call this- bad luck? :lol: )

Anyways my original intent was to use the internal pullups only, to simplify the design.
But since I wasn't doing the proper way, that's why there were logic contentions in Proteus too when I checked later.
Now everything is working fine even with the internal pullups enebled/disabled. & I think it's a good idea to let external pullups connected too.
Thanks freaks.

-
Regards,
Munish

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oops! Just to be safe, it should be the other way round:
I2C_SDA_LO;
I2C_SDA_DIR_OUT;

instead of:
I2C_SDA_DIR_OUT;
I2C_SDA_LO;

same with SCL.
As suggested by Jepael in the last post in
https://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=84054&start=0

-
Regards,
Munish

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Now with this code, when I disable pullups for SDA, it shows correct "haywire" behavior, picking up all sort of noise & all that Hi-Z stuff... this is in contrast to enabling pullups when it stays stable.

Well, yeah--a floating input can have any value. There is NOTING surprising there. In an I2C configuration it would never happen--there are external pullups.

Quote:

I2C_SDA_LO;

Will you PLEASE quit fussing with the PORT bit when doing I2C? Other than a standard/precautionary set to 0 at startup, DON'T TOUCH IT AGAIN when doing I2C.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Never ever set a I2C bus line output and high at the same time.

You can't use internal pull-ups with software bit-bashing I2C, because you need the PORT register bits zero to control pulling or floating with DDR register.

OK, there is a slight chance you could do it, but you would first have to make sure you first stop pulling low and then turn on the pull-up, and first turn off the pull-up before pulling low, but the pull-ups are too weak for any reasonable operation.

So basically, use external pull-ups, set PORT register for SDA and SCL as 0 and never ever change those bits and just control open-collector operation with DDR register bits.