Search code examples
embeddedavrlcdkeypadatmega16

Interfacing 16*2 keypad and LCD with atmega32


I have a disappointing problem with a keypad module that when pressing on any of its keys, it normally shows up the key pressed on an LCD module. The problem is whenever I press the key, the rows stop being scanned and I have no ability to press any other key to be shown on my LCD if my application, for instance, a system to be recieving a password and shown on the LCD. Another problem facing me right now if I want to show a list on the LCD and I want to turn another page on the screen to resume showing my list. How can I manage to implement this ?! I'm attaching a screenshot for my schematic plus I'm providing the code for both the keypad and LCD to be checked. And thank you anyway for helping me. enter image description here

My application code:

#define F_CPU 8000000UL
#include <util/delay.h>
#include <avr/io.h>
#include "LCD.h"
#include "Keypad.h"

int main(void)
{
    /* Replace with your application code */
    uint8_t keypadPress = 0;
    Keypad_vInit();
    LCD_vInit();

    while( !Keypad_u8Scan() )
    {
        keypadPress = Keypad_u8Scan();
        if( keypadPress == '8' )
        {
            LCD_vPrintChar( '8' );
            while( keypadPress == '8' );
        }
    }
}

My LCD library:

#if defined MODE_4

static void sendFallingEdge( void ); /* This prototype is declared static       to avoid modifying it. */

static void sendFallingEdge( void )
{
    /* Initializing the EN pin of the LCD when detecting a falling edge. */
    /* The following code is the representation of falling edge using the   system clock. */
    PORTB |= ( 1 << EN );
    _delay_ms( 1 );
    PORTB &= ( ~ ( 1 << EN ) );
    _delay_ms( 1 );
}

void LCD_vSendCmd( char cmd )
{
    /* Transferring the first nibble. */
    PORTA &= 0x0F;
    PORTA |= ( cmd & 0xF0 );
    CLR_BIT( PORTB, RS ); /* Transferring instructions data. */
    sendFallingEdge( );

    /* Transferring the second nibble. */
    PORTA &= 0x0F;
    PORTA |= ( cmd << 4 );
    CLR_BIT( PORTB, RS ); /* Transferring instructions data. */
    sendFallingEdge( );
}

void LCD_vInit( void )
{
    DDRA |= 0xF0; /* DDRA |= 0b11110000; */
    DDRB |= 0x0E; /* DDRB |= 0b00001110; */ /* Those three HIGH bits are the RS, RW and EN respectively. */
    CLR_BIT( PORTB, RW ); /* Write mode enabled according to the LCD's datasheet. */

    LCD_vSendCmd( 0x33 );
    _delay_ms( 1 );

    LCD_vSendCmd( 0x32 );
    _delay_ms( 1 );

    LCD_vSendCmd( 0x28 );
    _delay_ms( 1 );

    LCD_vSendCmd( 0x01 );
    _delay_ms( 1 );

    LCD_vSendCmd( 0x0F );
    _delay_ms( 1 );
}

void LCD_vPrintChar( char data )
{
    PORTA &= 0x0F;
    PORTA |= ( data & 0xF0 );
    SET_BIT( PORTB, RS ); /* Transferring display data. */
    sendFallingEdge( );

    PORTA &= 0x0F;
    PORTA |= ( data << 4 ); 
    SET_BIT( PORTB, RS ); /* Transferring display data. */
    sendFallingEdge( );
}

void LCD_vPrintString( char * str )
{
    uint8_t counter;
    for( counter = 0; str[ counter ] != '\0'; counter ++ )
    {
        LCD_vPrintChar( str[ counter ] );
    }
}

void LCD_vPrintNumbers( uint8_t str[ ], uint8_t size )
{
    uint8_t counter;
    for( counter = 0; str[ counter ] < size; counter ++ )
    {
        LCD_vPrintChar( str[ counter ] );
    }
}

void LCD_vClrScreen( void )
{
    LCD_vSendCmd( CLR_SCRN );
}

void LCD_vMoveCursor( char row, char column )
{
    char cmd;
    if( row == 1 )
    {
        cmd = STARTROW0 + column - 1;
        LCD_vSendCmd( cmd );
    }
    else if( row == 2 )
    {
        cmd = STARTROW1 + column - 1;
        LCD_vSendCmd( cmd );
    }
}

#endif

My Keypad library:

#include <avr/io.h>
#include "std_macros.h"

void Keypad_vInit( void )
{
    DDRC = 0x0F; 
    CLR_BIT( SFIOR, PUD ); 
    PORTC = 0xFF;          
}
unsigned char Keypad_u8Scan( void )
{
    unsigned char row, column, scan, buttonPressed = 0;
    unsigned char KP[ 4 ][ 4 ] = { { '7', '8', '9', '/' },
                                   { '4', '5', '6', '*' },
                                   { '1', '2', '3', '-' },
                                   { ' ', '0', '=', '+' }           
                                 };
    for( row = 0; row < 4; row ++ )
    {
        PORTC |= 0x0F;
        CLR_BIT( PORTC, row );
        for( column = 4; column < 8; column ++ )
        {
            scan = READ_BIT( PINC, column );
            if( scan == 0 )
            {
                buttonPressed = KP[ row ][ column - 4 ];
            }
        }
    }
    return buttonPressed;
}

And at last my standard macros:

#define SET_BIT( REGISTER, BIT_NUM ) ( REGISTER = REGISTER | ( 1 << BIT_NUM ) )
#define CLR_BIT( REGISTER, BIT_NUM ) ( REGISTER = REGISTER & ( ~( 1 << BIT_NUM ) ) )

Solution

  • Your while loop terminated as soon as !Keypad_u8Scan() is false, and then main() terminates. This will happen when any key other than 8 is pressed, because you only wait for the key release if the key was 8.

    In any "big-loop" scheduled embedded system, main() should not normally terminate - the outer loop should be indefinite.

    The following scheduling loop will work (assuming the keypad and LCD functions work), and is more easily extensible - adding additional key-down event handlers is simply a matter of adding a new case block to the switch:

    for(;;) // forever
    {
        // Wait for key-down
        do
        {
            keypadPress = Keypad_u8Scan();
    
        } while( keypadPress == 0 ) ;
    
        // Process key
        switch( keypadPress )
        {
            case '8' :
            {
                LCD_vPrintChar( '8' );
            }
            break ;
    
            default :
            {
                // any other key not explicitly handled
            }
        }
    
        // Wait for key-up
        while( Keypad_u8Scan() != 0 )
        {
            // do nothing
        }
    }
    

    A better structure perhaps, is to have a separate function that waits for a key-down event as follows:

    uint8_t getKey()
    {
        uint8_t key = 0 ;
    
        // Wait for key release if pressed on entry
        while( Keypad_u8Scan() != 0 )
        {
            // do nothing          
        } 
    
        // Wait for new key press
        do
        {
            key = Keypad_u8Scan();
    
        } while( key == 0 ) ;
    
        return key ;
    }
    

    Then your main loop can become much simpler:

    for(;;) // forever
    {
        keypadPress = getKey() ;
    
        // Process key
        switch( keypadPress )
        {
            case '8' :
            {
                LCD_vPrintChar( '8' );
            }
            break ;
    
            default :
            {
                // any other key
            }
        }
    }