Friday, March 6, 2009

avr-gcc read I/O on single port with AT90USB162

I want to repeatedly read from and write to a single port on a AT90USB162. This is to implement a key matrix scanner and pins will be repeated reconfigured as input or output. I ran in to trouble, reads seems to overlap as if they were being cached.

For example, I have pins 1 and 2 shorted.

pin 1 is set as an output. pin 2 an input.

pin 2 is set to pullup.

I output logic 0 on pin 1.

I read the value from pin 2 and store the result in a variable VAL1.

Now I set pin 1 as a input (and pullup).

I set another pin, say pin 3 to logic 0 (not shorted to anything).

I output logic 0 on pin 3.

I read the value from pin 2 in to a variable VAL2.

Now logically VAL1 should be 0 and VAL2 1. However that doesn't seem to be the case. If I do this I often read 1 for both VAL1 and VAL2. Removing the second read however makes VAL1 revert to it's correct value, EVEN THOUGH BOTH READS SHOULD BE INDEPENDENT!! I've not seen this with the AT90USBKey so it's a bit odd.

The solution I've found is to place a delay loop after the port configuration but before the read, in my code:

inline unsigned char get_scan(unsigned char pin) {

unsigned char p = 1 << pin;

DDR = 0x00 ^ p;
PORTD = 0xFF ^ p;

for(volatile int i=0;i<100;i++);

return ~p & PIND;

The volatile is important, if you omit that it doesn't work. I guess the compiler optimizes it out. (volatile tells the compiler some other external code could change this variable so explicitly check it where ever it's referenced).

No comments: