Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

XMEGA-B1XPLAINED: LCD TUTORIAL

XMEGA-B1XPLAINED: LCD TUTORIAL

In the last tutorial we saw system clock of XMEGA in detail. Now, we will see the tutorial on LCD.

ATXMEGAB1 has Segment LCD interface, which supports interface up to 40 segment terminals and 4 common terminals. It also supports ASCII character mapping. The XMEGA-B1 XPLAINED uses C24048A LCD, which has

  • Seven 14-segment alphanumeric digits
  • Five 7-segment numeric digits including negative sign and DPs
  • One bar graph of 9 segments (pixels)
  • 13 icons of one segment (pixel)

The more details of C24048A are available here.

We will use system clock configuration function from last tutorial which will be same for rest of the tutorials.

First of all let’s go through LCD initialization.

In XMEGA-B1 XPLAINED the backlight of LCD is connected to PE5. In order to turn on the backlight we will configure PE.5 as output. To do so,


PORTE_DIR|=0x20; //define PORTE.5 as output pin

PORTE_OUT|=0x20; //set PORTE.5 to turn on LCD backlight

CTRLA:

Setting ENABLE bit enables LCD module, and clearing this bit will turn OFF LCD module immediately. XBIAS bit decides whether LCD uses internal or external voltage source for operation. If this bit is set, then external voltage source is mandatory for LCD.

Setting DATALCK i.e. Data Register Lock bit, locks Shadow Display Memory. During this if display memory is updated, it remains unchanged. Once this bit is cleared, the changes in display memory are reflected when a new frame starts.

COMSWP & SEGSWP bits are used to swap the order of common terminals and segment terminals of LCD respectively.

Setting CLRDT bit clears data memory immediately (not control register) & it resets as soon as display memory is cleared.

Setting SEGON bit enables all segments of LCD.

When BLANK bit is set, display is blanked after completion of frame and all segment and common terminals are driven to ground. Setting or clearing this bit has no effect on display memory.


LCD_CTRLA |=LCD_ENABLE_bm|LCD_SEGON_bm; //enable LCD module and all segments of LCD

CTRLB:

CTRLB register is used to define frame rate of LCD.

PRESC bit defines the tap point from ripple counter. If this bit is set the value is 16 & if it is cleared then the value is 8. The CLKDIV[2:0] defines division ratio in clock divider. To check the frame rate use below given formula:

Frame Rate = F(clkLCD) / (KxNx(1+CLKDIV))

Where,

N = prescaler divider (8 or 16)

K = 8 for 1/4, 1/2 and static duty

K = 6 for 1/3 duty

In our case, duty is 1/4 so K will be 8 & N is 16.

So, the frame rate in our case is 62.5 Hz. The LPWAV i.e. Low Power Waveform bit can be set to reduce thepower consumption. DUTY[1:0] bits defines the duty cycle. The common pins which are not used will be driven to ground. As we are using all common terminals, both bits will be 0.


LCD_CTRLB = LCD_PRESC_bm|LCD_CLKDIV1_bm|LCD_CLKDIV0_bm|LCD_LPWAV_bm; //prescaler 16, clock divider 4 and low power consumption.

CTRLC:

This register defines the number of pins that are used as segment drivers. The rest of unused pins (except highest 16 pins) are driven to ground.

As here all pins are used as segment driver, the value of CTRLC register will be,


LCD_CTRLC = 0x3f; //as all 40 pins are used as segment pins

In this tutorial we won’t use interrupt.

CTRLD:

This register is used to configure blink rate of LCD. (This feature is not used in this tutorial.)

CTRLF:

This register defines the maximum voltage on segment & common pins. The formula is:

VLCD = 3.0 V + (FCONT[5:0] x 0.016 V)

The acceptable value of voltage for fine contrast is within range of 2.5V to 3.5V. In our case we will keep this value approximately 3.5V. So,


LCD_CTRLF = 0x1f; //i.e. 31 is the value of FCONT[5:0]

LCD initialization is finished over here.

Now in order to display different patterns on LCD, two methods are available. First is using CTRLG & CTRLH registers and second is, to use LCD DATA registers. Let’s check both the methods.

By using CTRLG & CTRLH registers:

CTRLG:

In this method we need to configure CTRLG register such a way that, it specifies the number of segment & common terminals used to display digit. It also specifies the start segment on LCD which is used to write decoded display. In our case it is 14 segments with 4 common terminals. Start segment is at position 12. Hence,


LCD_CTRLG|=LCD_TDG1_bm|LCD_STSEG3_bm|LCD_STSEG2_bm; //14 segment & 4 common terminals, start position at 12.

CTRLH:

In this register if bit DEC is set then STSEG bit field is decremented & if it is cleared then STSEG bit field is incremented.

DECODE[6:0]bit field is computed by digit decoder, converted to display codes & then automatically written into display memory according to STSEG value.

In our case we will keep DEC bit 0 so that it will be incremented.

In order display patterns on LCD (on both 14-segment alphanumeric digits and 7-segment numeric digits) & configure CTRLG, CTRLH registers, we need to consider following three parameters

  1. Width of segments (i.e. total number of segments)
  2. Direction (either from left to right or right to left)
  3. First segment (position of first segment)

In order to decide the above three parameters check image for 14-segment digits given below from document AVR1618.

 

  1. Width of segments:

As you can see the total number of segments available is 7 (i.e. from A6 to A0).

 

  1. Direction:

In AVR 1618 it is given that for 14-segment digits,

Digit A6 is driven by SEG[12:15]

Digit A5 is driven by SEG[16:19]

.

.

.

Digit A0 is driven by SEG[39:36]. Hence to display characters from left->right the digit needs to be incremented. Therefore, the direction bit will be 0 for left->right and 1 for right->left.

  1. First segment:

From the image above if you want to display pattern from left to right, then first segment associated with A6 i.e. A6-h comes at position 12 in the table. Hence third parameter will be 12.

If you want to display pattern from right to left, then first parameter associated with A0 i.e. A0-h comes at position 36 in the table. Hence in that case third parameter will be 36.

This is the case with 14-segment digits. For 7-segment digits also we need to decide values for above three parameters. For that again, consider the following image from AVR1618 for 7-segment digits.

 

  1. Width of segments:

As you can see the total number of segments available is 5 (i.e. from D4 to D0). If you see the segment D4 is half segment hence one can manage it manually and consider D3 to D0 for calculation of this parameter. If D4 is manually managed then width of segments becomes 4.

 

  1. Direction:

In AVR1618 it is given that for 7-segment digits,

Display D4 is driven by SEG[8:9]

Display D3 is driven by SEG[6:7]

.

.

.

Display D0 is driven by SEG[2:3]. Hence to display characters from left->right display needs to decremented. Therefore, direction bit will be 1 for left->right and 0 for right->left.

 

  1. First Segment:

From the image above if you want to display pattern from left to right, then first segment associated with D4, i.e. D4-a comes at position 10 in the table. Hence third parameter will be 10. Again if D4 is manually managed and if we consider displays from D3 to D0 then D3 will be the leftmost display then first segment associated with D3, i.e. D3-a comes at position 8 in the table. Hence third parameter will be 8.

If you want to display pattern from right to left, then first parameter associated with D0, i.e. D0-a comes at position 2 in the table. Hence in that case third parameter will be 2.

For this tutorial we won’t manage D4 manually. Therefore while passing parameters to D4 make sure that only supporting pattern is passed or else junk pattern will be displayed.

 

This is the way to use CTRLG & CTRLH register to display data on LCD. With this method we will display “ATXMEGA” on 14-segment digits and “128b1” on 7-segment digits. The entire code is given below.


#include <avr/io.h>

//The following set of macros is for Alphanumeric Segments

#define width_14seg 7    //as total segments are 7 from A6 to A0
#define first_14seg 12   //use 12 for left to right as A6-h has first position at 12
                         //use 36 for right to left as A0-h has first position at 36
#define direction_14seg 0 //as direction is incrementing from left to right
                          //use one for right to left i.e. decrementing

//The following set of macros is for Numeric Segments

#define width_7seg 5       //as total segments are 5 from D4 to D0
#define first_7seg 10      //use 2 for right to left as D0-a has first position at 2
                           //use 10 for left to right as D4-a has first position at 10
#define direction_7seg 1   //as direction is decrementing from left to right
                           //use zero for right to left i.e. incrementing


unsigned char text[]="ATXMEGA";
unsigned char number[]="128b1";

void clock_init(void);
void lcd_init(void);
void display_alphanumeric(unsigned char *pattern);
void display_numeric(unsigned char *pattern);


int main(void)
{
 clock_init();

 lcd_init();

 display_alphanumeric(text);

 display_numeric(number);

 while(1);
}


void clock_init()
{
 OSC_PLLCTRL=OSC_PLLFAC3_bm; //select internal 2MHz oscillator as PLL clock source, PLL multiplication factor as 8

 OSC_CTRL=OSC_PLLEN_bm; //enable PLL

 while(!(OSC_STATUS & OSC_PLLRDY_bm)); //wait until PLL is locked to desired frequency and ready to use

 CCP=0xd8; //write Configuration Change Protection register

 CLK_CTRL=CLK_SCLKSEL2_bm; //select PLL as system clock source

 CCP=0xd8; //write Configuration Change Protection register

 CLK_PSCTRL=CLK_PSADIV0_bm; //select Prescaler A as 2, Prescaler B and Prescaler C as 1, Clksys=16MHz,   Clkper4=Clkper2=Clkper=Clkcpu=8MHz

 CLK_RTCCTRL=CLK_RTCEN_bm; //enable RTC clock source as 1KHz from 32KHz ULP internal oscillator
}


void lcd_init()
{
 PORTE_DIR|=0x20; //define PORTE.5 as output pin

 PORTE_OUT|=0x20; //set PORTE.5 to turn on LCD backlight

 LCD_CTRLA|=LCD_ENABLE_bm|LCD_SEGON_bm; //enable LCD module and all segments of LCD

 LCD_CTRLB=LCD_PRESC_bm|LCD_CLKDIV1_bm|LCD_CLKDIV0_bm|LCD_LPWAV_bm; // prescaler 16, clock divider 4 and low power consumption.

 LCD_CTRLC=0x3f; //as all 40 pins are used as segment pins

 LCD_CTRLF=0x1f; //i.e. 31 is the value of FCONT[5:0]
}


void display_alphanumeric(unsigned char *pattern)
{
 unsigned char direction,width;

 direction=direction_14seg;

 width=width_14seg;

 LCD_CTRLG=LCD_TDG1_bm|first_14seg;    //14 segments & 4 COM terminals, first_14seg is starting segment

 if(direction)

       direction=LCD_DEC_bm; //i.e. if set decrement, else increment

 for(;width!=0;width--)
 {
       if(*pattern=='\0')

                 break;

       LCD_CTRLH=direction|(*pattern++);
 }
}


void display_numeric(unsigned char *pattern)
{
 unsigned char direction,width;

 direction=direction_7seg;

 width=width_7seg;

 LCD_CTRLG=LCD_TDG0_bm|first_7seg;    //7 segments & 4 COM terminals, first_7seg is starting segment

 if(direction)

       direction=LCD_DEC_bm; //i.e. if set decrement, else increment

 for(;width!=0;width--)
 {
       if(*pattern=='\0')

                 break;

       LCD_CTRLH=direction|(*pattern++);
 }
}

 

By using LCD_DATA register:

For this method LCD initialization will be same as above only the display function will change.

The LCD_DATA register of LCD provides access to display memory so that one can directly control ON & OFF state of segments of LCD. In order to use LCD_DATA register to display particular pattern, please use the formulae given in the datasheet.

Where first formula decides which of LCD_ DATA register to use for displaying pattern while, the second one decides which bit to set from selected LCD_DATA register. E.g. consider pattern of “XMEGAB1” to display on 14-segment digits.

Before moving to actual calculations first let’s see how to decide parameters mentioned in above formulae.

pixel_COM: This parameter indicates value of the COM terminal related with particular segment i.e. from 0 to 3.

MAX_SEG: This parameter indicates total number of segments on LCD i.e. 40.

pixel_SEG: This parameter indicates value of segment related with particular pixel.

For first character i.e. ‘X’, let’s see how to calculate the values of above parameters, the calculation for remaining characters will be same.

In order to display ‘X’ on A6 we need to turn on following pixels.

A6->g, A6->i, A6->n, A6->l

Now, with reference to image1 (given above), calculate the corresponding common & segment terminals for above pixels respectively. And those values are,

(0,15), (1,12), (3,12), (2,15)

Now put these values in above two formulae & check which LCD_DATA registers are necessary to display ‘X’ on LCD.

If we put (0,15) in the above two formulae, it’ll give us answers as 0x11 & 0x07 respectively. It means for (0,15) we need to write,


LCD_DATA1=0x80; //0x11 is offset of register LCD_DATA1 & bit 7 hence 0x80

Same applies to rest of the three pixels,


LCD_DATA6=0x10;

LCD_DATA16=0x10;

LCD_DATA11=0x80;

So above four statements will display letter ‘X’ on LCD. Similarly calculate values for rest of the characters. The method will be same in case on 7-segment digits. Only in case of 7-segment digits, we can display numbers from 0 to 9 and only few characters (alphabets) as it is numeric display.

The entire code for the same is given below. Only instead of calculating value of LCD_DATA register and bit position manually for every pixel,I have put one formula in code (from AVR 1618) so that you just need to pass pixel COM & pixel segment value as argument to function.


#include <avr/io.h>


#define MAX_SEG 40
#define LCD_MAX_NR_COM 4
#define LCD_MAX_NR_SEG 40


unsigned char text[37][2]={{0,15},
                           {1,12},
                           {3,12},
                           {2,15},
                           {2,17},
                           {1,17},
                           {0,19},
                           {1,16},
                           {1,18},
                           {2,18},
                           {2,21},
                           {1,21},
                           {0,22},
                           {1,23},
                           {3,21},
                           {0,26},
                           {1,25},
                           {2,25},
                           {3,25},
                           {2,26},
                           {2,24},
                           {2,29},
                           {1,29},
                           {0,30},
                           {1,30},
                           {1,31},
                           {2,28},
                           {2,30},
                           {0,34},
                           {1,34},
                           {2,34},
                           {3,33},
                           {2,32},
                           {0,32},
                           {3,35},
                           {1,38},
                           {2,38}};


void clock_init(void);
void lcd_init(void);
void display(unsigned char pix_com,unsigned char pix_seg);


int main(void)
{
 clock_init();

 lcd_init();

 for(unsigned char i=0;i<37;i++)

         display(text[i][0],text[i][1]);

 while(1);
}


void clock_init()
{
 OSC_PLLCTRL=OSC_PLLFAC3_bm; //select internal 2MHz oscillator as PLL clock source, PLL multiplication factor as 8

 OSC_CTRL=OSC_PLLEN_bm; //enable PLL

 while(!(OSC_STATUS & OSC_PLLRDY_bm)); //wait until PLL is locked to desired frequency and ready to use

 CCP=0xd8; //write Configuration Change Protection register

 CLK_CTRL=CLK_SCLKSEL2_bm; //select PLL as system clock source

 CCP=0xd8; //write Configuration Change Protection register

 CLK_PSCTRL=CLK_PSADIV0_bm; //select Prescaler A as 2, Prescaler B and Prescaler C as 1, Clksys=16MHz, Clkper4=Clkper2=Clkper=Clkcpu=8MHz

 CLK_RTCCTRL=CLK_RTCEN_bm; //enable RTC clock source as 1KHz from 32KHz ULP internal oscillator
}


void lcd_init()
{
 PORTE_DIR|=0x20; //define PORTE.5 as output pin

 PORTE_OUT|=0x20; //set PORTE.5 to turn on LCD backlight

 LCD_CTRLA |= (1<<LCD_ENABLE_bp)|(1<<LCD_SEGON_bp); //enable LCD module and all segments of LCD

 LCD_CTRLB = (1<<LCD_PRESC_bp)|(1<<LCD_CLKDIV1_bp)|(1<<LCD_CLKDIV0_bp)|(1<<LCD_LPWAV_bp); // prescaler 16, clock divider 4 and low power consumption.

 LCD_CTRLC = 0x3f; //as all 40 pins are used as segment pins

 LCD_CTRLF = 0x1f; //i.e. 31 is the value of FCONT[5:0]
}

void display(unsigned char pix_com,unsigned char pix_seg)
{
 if((pix_com<LCD_MAX_NR_COM)&&(pix_seg<LCD_MAX_NR_SEG))
 {
         unsigned char *reg=(unsigned char*)(0x0d10+(pix_com*((MAX_SEG+7)/8))+(pix_seg/8));

         *reg|=1<<(pix_seg%8);
 }
}

Conclusion: If we compare above two methods then,the first method is bit easy as CTRLH register automatically decodes the ASCII value of character. On the other hand, in second method we need to manually calculate the value of LCD_DATA register & bit position in that register. Second method is more useful when we want to display constant string on LCD.

Thanks for reading.

 

 




This post first appeared on Embedded Tutorials, please read the originial post: here

Share the post

XMEGA-B1XPLAINED: LCD TUTORIAL

×

Subscribe to Embedded Tutorials

Get updates delivered right to your inbox!

Thank you for your subscription

×