Electronic – avr-gcc keypad interfacing code problem

avr-gccckeypadmicrocontroller

I have this function to read key press from a 4×3 key-board:

uint8_t GetKeyPressed()
{
  uint8_t r,c;

  KEYPAD_PORT|= 0X0F;

  for(c=0;c<3;c++)
  {
     KEYPAD_DDR&=~(0X7F);

     KEYPAD_DDR|=(0X40>>c);
     for(r=0;r<4;r++)
     {
        if(!(KEYPAD_PIN & (0X08>>r)))
        {
           return (r*3+c);
        }
     }
  }

return 0XFF;//Indicate No key pressed
}

Some macro, I missed:

    #define KEYPAD A  
    #define KEYPAD_PORT PORT(KEYPAD)
    #define KEYPAD_DDR   DDR(KEYPAD)
    #define KEYPAD_PIN   PIN(KEYPAD)

But I don't understand this code, pretty well, because of those bit shifting operations.

Can anyone help me with this code?

Compiler : avr-gcc

Micro-controller : ATmega328

Best Answer

In an abstract manner the code does this:

for each pin PA6 to PA4 (column)
    set pin as output, driving '0'
    for each pin PA3 to PA0 (row)
        if pin reads '0' then
            return key code calculated as row*3+column
return 0xFF as key code, meaning "no key"
  • KEYPAD_PORT|= 0X0F; should preset the output register of the port with '0's for PA6 to PA4. The other pins (bit 7 and bits 3 to 0) are not relevant. But here is an error because of the operator |=: If any of the bits 7 to 4 is already 1, it will keep this value. The correct statement is KEYPAD_PORT &= 0x8F;.

  • The KEYPAD_DDR register selects the direction of the pins of your keypad port. Each bit corresponds to a pin. Setting a bit to 1 make the pin an output, 0 an input.

  • The KEYPAD_PIN register is used to read the pins of your keypad port.

Now to the shifting operations:

KEYPAD_DDR|=(0X40>>c);: The hex value 0x40 is shifted to the right by the value of c. This results in values of 0x40 (0b01000000), 0x20 (0b00100000), and 0x10 (0b00010000). This value is then ORed to KEYPAD_DDR which was ANDed before with the complement of 0x7F = 0x80 (0b10000000). The results are 0xC0 (0b11000000), 0xA0 (0b10100000), and 0x90 (0b10010000), resp.

!(KEYPAD_PIN & (0X08>>r)): The hex value 0x08 is shifted to the right by the value of r. This results in values of 0x08 (0b00001000), 0x04 (0b00000100), 0x02 (0b00000010), and 0x01 (0b00000001). The value read from KEYPAD_PIN is ANDed with this value, giving zero if the "masked" pin is '0' and non-zero otherwise. By the unary operator ! a zero is converted to true and a non-zero to false. So the statement of the if will be executed if the masked pin is '0'.

Note: I like a lowercase 'X'/'B' better than the uppercase for hex and binary constants. But this is a bit of personal taste.

Only you can tell about the pin PA7. That's why I ignored it.