Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
indianajones11
PostPosted: Jan 25, 2012 - 08:03 AM
Raving lunatic


Joined: Nov 28, 2004
Posts: 3896
Location: San Diego, Ca

Here's another tutorial for driver code of the HD44780 controller. R/W pin is grounded. Code offers :

4 / 8 bit I/F for Mega, Xmega and some Tiny series MCUs . I've interfaced a 5V LCD with 3.0V Xmegas directly .

Option to use the virtual ports feature for Xmega parts .

Compiler : WinAvr20100110
IDE : Studio 4.18 SP3


Code:
slow_clk()
fast_clk()

are functions to change CPU frequency when it's too fast for the LCD, but are specific to Xmegas . Change according to your system clock, and oscillator, if you use these . Add the asm file below as just another source file, as you would for a C-file within the Studio project tree .


Jerome


lcd.c :
Code:
#include <util/delay.h>
#include   <avr/io.h>
#include <avr/pgmspace.h>

#include   "lcd.h"

extern  void ccp_write( void *ccp_reg, uint8_t value );


// Clearing a row has similar code to lcd_xy, so I factored out the common parts and so this is a combo function . See CLR_LINE () macro in lcd.h .

void   set_lcd_xy( uint8_t row, uint8_t col ) 

{

static  uint8_t   xx[4] = { 0, 0x40, 0x14, 0x54 };  // Should be changed to switch construct  else it eats 4 RAM bytes.

uint8_t   x = 0;


   x = xx[row];

   if ( col < CLR_COLS )   {
      x += col;
   }

   x |= 0x80;
   lcd_cmd( x );
         
// This section provides a clr_row op. if needed .

   if ( col >=  CLR_COLS ) // Put col > CLR_COLS for 2nd parameter to clr a row.
    {   
         for ( uint8_t i = Max_col; i; i-- )
            lcd_putc( ' ' );
         //set_lcd_xy( row, 0 ); // Now set cursor to 1st column on same row.
    }

}// End set_lcd_xy


void   lcd_putc( char a )
{

uint8_t   temp;

   if ( a > 9)
      temp = a;
   else  temp = a + 0x30; // Convert  to hex.
   
   set_rs;

#if defined   MODE_4

uint8_t   hi, lo;

   #if   defined   ( PORTA_OUT )

      slow_clk();
   #endif

   hi = (temp & UPPER_MASK );
   lo = (temp & LOWER_MASK );

   set_e;
   lcd_data = hi;
   clr_e;
   
   set_e;
   lcd_data = ( lo << 4 );
   clr_e;

#elif  defined   MODE_8
   //slow_clk();

   set_e;
   lcd_data = temp;
   clr_e;
#endif

   _delay_us(40);
   clr_rs;

   #if   defined   ( PORTA_OUT )

      fast_clk();

   #endif

}// End lcd_putc


void   lcd_puts( char *s ) 
{

   while( *s != '\0' )
   {
       lcd_putc( *s++ );
   }

}// End lcd_puts


void   lcd_cmd( uint8_t c ) 
{
   clr_rs;

#if defined   MODE_4

uint8_t   hi, lo;

   #if   defined   ( PORTA_OUT )

      slow_clk();
   #endif

   hi = ( c & UPPER_MASK );
   lo = ( c & LOWER_MASK );

   set_e;
   lcd_data = hi;
   clr_e;

   set_e;
   lcd_data = ( lo << 4 );
   clr_e;
      
#elif  defined   MODE_8

   set_e;
   lcd_data = c;
   clr_e;

#endif

   if ( c < 4 ){
      
      _delay_ms(2);
   }

   else{

      _delay_us(40);
   }   

   #if   defined   ( PORTA_OUT )

      fast_clk();
   #endif

}// End lcd_cmd


void   lcd_putstr_P( PGM_P s )   
{

char   temp;
   

   while(  ( temp = pgm_read_byte( s++ ) ) != '\0' )   
   {   lcd_putc( temp );   
        }

}// End lcd_putstr_P

#if   defined  ( PORTA_OUT ) // Xmegas only .

void   slow_clk( void ){ 

   OSC_CTRL &= ~( 1<< 4 );    // disable PLL    

   ccp_write( (void * )&CLK_CTRL, 0x00 );  // Int. 2MHz as temp. system clk.
}


void   fast_clk( void ){
   
   //OSC_PLLCTRL = ( 8<< 0 );  Set this in main() so it doesn’t get done multiple times. Fout = (2 MHz * PLL_of_8X ) = 16 MHz .

   OSC_CTRL |= ( 1<< 4 ); // Enable PLL .
   while( !( OSC_STATUS & 0x10 ) ); // Wait unitl PLL as sys_clk is rdy to go.

   ccp_write( (void * )&CLK_CTRL, 0x04 );    
}

#endif




lcd.h :
Code:
#ifndef    LCD_H

// Driver for HD44780 controller. R/W pin is gnd'ed.

   #define    LCD_H

   #include   <avr/io.h>
   #include <util/delay.h>
        #include <avr/pgmspace.h>
   

   #define   MODE_4    1  // Set to 4 bit mode
   //#define   MODE_8    1  // Set to 8 bit mode
   
   #define   VP_ON   1  // Use Virtual Port
   //#define   VP_OFF  1  // Don't use Virtual Port

   #define   Max_rows   4
   #define   Max_col    20

   #define   EN     2
   #define   RS     3

   #if defined DDRA  // To identify Mega or Tiny series being used.
   
        #define   lcd_ddr     DDRA
        #define   lcd_data    PORTA 

        #define   lcd_ctrl_ddr   DDRD
        #define   lcd_ctrl     PORTD

        #define   set_e     lcd_ctrl |= (1 << EN)  //;  sets a bit
        #define   clr_e     lcd_ctrl &= ~( 1 << EN )   //;  clr a bit

        #define   set_rs    lcd_ctrl |= (1 << RS)
       #define   clr_rs    lcd_ctrl &= ~( 1<< RS)
   
   #elif  defined ( PORTA_OUT )  // To identify Xmega series MCU being used.
      
      #ifdef   VP_ON

            #define   lcd_ddr   VPORT0.DIR 
         #define   lcd_data  VPORT0.OUT 
      
         #define   lcd_ctrl_ddr    VPORT1.DIR
         #define   lcd_ctrl   VPORT1.OUT 

         #define   set_e     VPORT1.OUT  |= ( 1<< EN )
         #define   clr_e     VPORT1.OUT  &= ~( 1<< EN )
         
         #define   set_rs     VPORT1.OUT  |= ( 1<< RS )
         #define   clr_rs     VPORT1.OUT  &= ~( 1<< RS )

      #elif   VP_OFF
         
         #define   CTRL_PORT  PORTE

            #define   lcd_ddr   PORTD.DIRSET 
         #define   lcd_data  PORTD.OUTSET 
      
         #define   lcd_ctrl_ddr    CTRL_PORT.DIRSET
         #define   lcd_ctrl   CTRL_PORT.OUTSET

         #define   set_e     CTRL_PORT.OUTSET = ( 1<< EN )
         #define   clr_e     CTRL_PORT.OUTCLR = ( 1<< EN )
         
         #define   set_rs     CTRL_PORT.OUTSET = ( 1<< RS )
         #define   clr_rs     CTRL_PORT.OUTCLR = ( 1<< RS )
      
      #endif  // End #ifdef   VP_ON

   #endif // End #if defined DDRD

   // LCD cmds

   #define   disp_on  2
   #define   cursor_on  1
   #define   blink_on  0
   #define   CLR_COLs   50  // Used as 2nd param. in set_lcd_xy to clr screen.

   #define   I_D   1
   #define   S     0
   #define   S_C   3
   #define   R_L   2
   #define   DL    4
   #define   N     3
   #define   F     2

   // 4 or 8 bit I/F

   #define   _8_bit    0x30
   #define   _4_bit   0x28

   #define   UPPER_MASK      0xF0
   #define   LOWER_MASK    0x0F

   #define   CLR_LCD       0x01
   #define   GO_HOME     0x02
   #define   ONE_LINER  0x30
   #define   TWO_LINER 0x38
   #define   disp_off  0x08   
   #define   FAST_DELAY   50 
   #define   CURSOR_ON   ( 1<< cursor_on )
   #define   BLINK_ON    ( 1<< blink_on )
   #define   CURSOR_OFF   0

   #define   CLR_LINE( row )    set_lcd_xy( row, CLR_COLS ) 


/******** Prototypes ***********************/

   void   set_lcd_xy( uint8_t , uint8_t );
   void   lcd_putc( char );
   void   lcd_puts( char * );
   void   lcd_cmd( uint8_t );
        void   lcd_putstr_P( PGM_P s );

   static  inline void   init_lcd( uint8_t );

   void   slow_clk ( void );   // Needed if CPU freq...
   void   fast_clk ( void );  //... too fast for LCD.


static  inline  void init_lcd( uint8_t cursor )
{

#if defined   MODE_4  // Init. Code for 4 bit I/F.


uint8_t   lo_nibble, hi_nibble;   
   

   #if defined  PORTA_OUT  // Setup for Xmegas .
       
      #if defined   VP_ON

         PORTCFG.VPCTRLA = PORTCFG_VP1MAP_PORTE_gc | PORTCFG_VP0MAP_PORTD_gc; // VP0 for PORTD, VP1 for PORTE

          VPORT0.DIR = 0xF0; // Set Upper nibble as output .
          VPORT1.DIR = ( 1<< EN | 1<< RS );  //Set lcd control pins as output .
      
      #elif   defined  VP_OFF
         
         lcd_ddr |= 0xF0;
         lcd_ctrl_ddr |= (1<<EN) | (1<<RS);
      #endif
   
   #elif   defined  DDRA  // Setup for all other MCU types .

      lcd_ddr |= 0xF0; // Set Upper nibble as output .
      lcd_ctrl_ddr |= (1<<EN) | (1<<RS); // Set lcd control pins as output .

   #endif // End #if defined  PORTA_OUT

   // Must send MSN ONLY and 3X 1st. and Enable line must toggle for each nibble.

   lcd_data = 0;  // Data and ...
   lcd_ctrl &= ~( (1<<EN) | (1<<RS) );  //... control lines init. to zero .

   _delay_ms(60);  // PORst. delay, needed.

   hi_nibble =  _8_bit;

   set_e;               
   lcd_data = hi_nibble;   // Send 1st function set, must be ( 0x3x ) to lcd... first time.
   clr_e;
   _delay_ms(6);      // Must be > 4.1 ms

   set_e;             //  2nd send of function set
   lcd_data = hi_nibble;
   clr_e;
   _delay_us(105);   // Must be > 100 uS      

   set_e;           // 3rd @@!@!!!! send of function set.
   lcd_data = hi_nibble;
   clr_e;
   _delay_us(105);

//************ Code above this line cannot be changed, except TWO_LINER.
      
   hi_nibble = _4_bit;
   set_e;                  // This section sets desired data bit size I/F to 4 bits.
   lcd_data = hi_nibble;  //TWO_LINER;
   clr_e;
   _delay_us(FAST_DELAY);

   hi_nibble = ( ( _4_bit | 0x08 ) & ( UPPER_MASK ) );
   lo_nibble = ( ( _4_bit | 0x08 ) & ( LOWER_MASK ) );

   set_e;                  // This part preserves data bit size I/F, and sets font and # of lines.
   lcd_data = hi_nibble; 
   clr_e;
//_delay_us(FAST_DELAY); NO !! Delay after a byte, NOT nibble !
      
   set_e;                 
   lcd_data = ( lo_nibble << 4 ); 
   clr_e;
   _delay_us(FAST_DELAY);      
      // 1 byte sent.

   hi_nibble = ( disp_off & UPPER_MASK );
   lo_nibble = ( disp_off & LOWER_MASK );

   set_e;                   
   lcd_data = hi_nibble;
   clr_e;
   
   set_e;                   
   lcd_data = ( lo_nibble << 4 );
   clr_e;
   _delay_us(FAST_DELAY);

   hi_nibble = ( CLR_LCD & UPPER_MASK );
   lo_nibble = ( CLR_LCD & LOWER_MASK );

   set_e;                   
   lcd_data = hi_nibble;
   clr_e;

   set_e;                   
   lcd_data = ( lo_nibble << 4 );
   clr_e;
   _delay_us(FAST_DELAY);

   hi_nibble = ( ( 0x04 | 1 << I_D ) & UPPER_MASK );
   lo_nibble = ( ( 0x04 | 1 << I_D ) & LOWER_MASK );

   set_e;           // Set entry mode -- 0x06 is cursor shift right
   lcd_data = hi_nibble;
   clr_e;

   set_e;                 
   lcd_data = ( lo_nibble << 4 );
   clr_e;
   _delay_us(FAST_DELAY);

/********** end of 4 bit init. **********************/
      
   hi_nibble = ( 0x14 & UPPER_MASK );
   lo_nibble = ( 0x14 & LOWER_MASK );

   set_e;                  // 0x14 is enable cursor right shift
   lcd_data = hi_nibble;
   clr_e;

   set_e;                 
   lcd_data = ( lo_nibble << 4 );
   clr_e;
   _delay_us(FAST_DELAY);

   hi_nibble = ( CLR_LCD & UPPER_MASK );
   lo_nibble = ( CLR_LCD & LOWER_MASK );

   set_e;                 
   lcd_data = hi_nibble;
   clr_e;

   set_e;                 
   lcd_data = ( lo_nibble << 4 );
   clr_e;
   _delay_ms(2);

   hi_nibble = ( GO_HOME & UPPER_MASK );
   lo_nibble = ( GO_HOME & LOWER_MASK );

   set_e;                 
   lcd_data = hi_nibble;
   clr_e;

   set_e;                 
   lcd_data = ( lo_nibble << 4 );
   clr_e;
   _delay_ms(2);

   hi_nibble = ( ( 0x08 |  1<< disp_on  |  cursor  ) & UPPER_MASK );
   lo_nibble = ( ( 0x08 |  1<< disp_on  |  cursor  ) & LOWER_MASK );

   set_e;
   lcd_data = hi_nibble;  //( 1<< cursor_on)  | ( 1<< blink_on );
   clr_e;
      //_delay_us(FAST_DELAY);

   set_e;
   lcd_data = ( lo_nibble << 4 ); 
   clr_e;
   _delay_us(FAST_DELAY);
   // 1 byte sent.

#elif  defined   MODE_8  // Init. Code for 8 bit I/F.

   #if defined  PORTA_OUT
   
     PORTCFG.VPCTRLA = PORTCFG_VP1MAP_PORTE_gc | PORTCFG_VP0MAP_PORTD_gc; // VP0 for PORTD, VP1 for PORTE

          VPORT0.DIR = 0xF0; // Set Upper nibble as output .
          VPORT1.DIR = ( 1<< EN | 1<< RS );  //Set lcd control pins as output .

   #elif   defined  DDRA // Setup for all other MCU types .

      lcd_ddr = 0xFF;
      lcd_ctrl_ddr |= (1<<EN) | (1<<RS);

   #endif

      lcd_data = 0;  // Clr data and...      
      lcd_ctrl &= ~( (1<<EN) | (1<<RS) );  //... control lines .

      _delay_ms(60);  // PORst. delay needed.

      set_e; // Send 1st function set, 0x3x to lcd... first time.
      lcd_data = TWO_LINER;    // 0x38
      clr_e;
      _delay_ms(6);  // Must be > 4.1 Ms

      set_e; //  2nd send of function set
      lcd_data = TWO_LINER;
      clr_e;
      _delay_us(105);  // Must be > 100 uS      

      set_e; // 3rd send of function set.
      lcd_data = TWO_LINER;
      clr_e;
      _delay_us(105);

/********** Code above this line cannot be changed *********************/

      set_e;  // This line actually sets desired data bit size I/F, font and # of lines.
      lcd_data = TWO_LINER;
      clr_e;
      _delay_us(FAST_DELAY);

      set_e;
      lcd_data = disp_off;
      clr_e;
      _delay_us(FAST_DELAY);

      set_e; // 0x01
      lcd_data = CLR_LCD;
      clr_e;
      _delay_us(FAST_DELAY);

      set_e; // Set entry mode -- 0x06 is cursor shift right
      lcd_data = ( 0x04 | 1 << I_D ); // 0x01
      clr_e;
      _delay_us(FAST_DELAY);

/***************** end of 8 bit init. *****************************/

      set_e; // 0x14 is enable cursor right shift
      lcd_data = 0x14;
      clr_e;
      _delay_us(FAST_DELAY);

      set_e; // 0x01
      lcd_data = CLR_LCD;
      clr_e;
      _delay_ms(2);

      set_e; // 0x02
      lcd_data = GO_HOME;
      clr_e;
      _delay_ms(2);

      set_e;
      lcd_data = ( 0x08) | ( 1<< disp_on) | ( cursor );  //( 1<< cursor_on)  | ( 1<< blink_on );
      clr_e;
      _delay_us(FAST_DELAY);

#endif // #if defined   MODE_4

   }// End init_LCD

#endif  // End  #ifndef    LCD_H


ccp.S :
Code:
#define   _SFR_ASM_COMPAT   1
#define   _SFR_OFFSET  0

#include  <avr/io.h>


#define   CCP_SIGN   0xD8


.global  ccp_write

.func  ccp_write

   ccp_write:

    movw   r26, r24  // Copy register address into X ptr.
   ldi  r24, CCP_SIGN
   out  CCP, r24
    st   X, r22  // Write value to protected register X_ptr.
ret
.endfunc

// Must use registers shown, BUT you can use Y ( must preserve these registers ) or Z ptr. instead of X .

_________________
1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1
 
 View user's profile Send private message  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits