[CODE] [C] Simple Butterfly LCD driver

Go To Last Post
123 posts / 0 new

Pages

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Works very well! Thanks for posting it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hi,

i wanna ask about how to add LCD_UpdateRequired function to dean's code..

thanks..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hakagiri wrote:
hi,

i wanna ask about how to add LCD_UpdateRequired function to dean's code..

thanks..

Please start a new thread or at least continue with the thread you already started at:
https://www.avrfreaks.net/index.p...

The purpose of the tutorial threads is to discuss the tutorial, not solve unrelated problems.

Smiley

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

oh.. okay, i'm sorry... :D

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The purpose of the tutorial threads is to discuss the tutorial, not solve unrelated problems.


As such I'm going to lock this thread. If anyone has anything to add to the original article PM js, plons or clawson and ask us to temporarily unlock this.

Moderator

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

i wanna ask about how to add LCD_UpdateRequired function to dean's code..

It would just be something simple like:

void LCD_UpdateRequired(void)
{
    UpdateDisplay = true;
}

Added to the driver code.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for this LCD driver, it allowed me to get characters on the screen with next to zero nowledge of C 8)
I noticed that

LCD_WAIT_FOR_SCROLL_DONE();

Does not check if the text is long enough to scroll, and if it isnt, waits forever...

For the solar hotwater controller that I am building I need the numbers (and eventially will need the arrows). I was going to ask if you could do it for me, but I have come up with a brute force hack on your code, without really understanding it too well that gives the numbers 1-5 sequentially. i.e if 3 then you get 3,2,1. I'me sure theres a much nicer way of doing it. I havnt check the 3 as R200(?) is desoldered atm

diff /usr/src/AVR/include/LCD_Driver.c /usr/src/AVR/butterfly/LCD-test/LCD_Driver.c
29a30
> 		volatile uint8_t  LCDNumbers      = 5;
228,229c229,264
<     if (Byte != LCD_SPACE_OR_INVALID_CHAR)              // Null indicates invalid character or space 
<       SegData = pgm_read_word(&LCD_SegTable[Byte]);    
---
>     if (Byte != LCD_SPACE_OR_INVALID_CHAR) {             // Null indicates invalid character or space 
>     /* To adds pecial characters to scrolling display we have thre cases 
>      * for 1-5:
>      * 1 and 2, OR 0x4 to Digit 0 and 1 (2 and 3 on LCD) respectivly
>      * 4 and 5, OR 0x2 to Digit 2 and 3 (4 and 5 on LCD) respectivly
>      * 3, set LCDDR3=0x1, as I think this is not written to for normal CSS characters
>      * Ime sure theres a better way (and there is for just 1 and 2...
>      * But ime doing this with a long list of if statements...
>      */    
>       if (LCDNumbers >= 5 && Digit == 3)
> 	      SegData = pgm_read_word(&LCD_SegTable[Byte])|0x2;
> 		else if (LCDNumbers >= 4 && Digit == 2)
> 	      SegData = pgm_read_word(&LCD_SegTable[Byte])|0x2;
> 		else if (LCDNumbers >= 2 && Digit == 1)
> 		  SegData = pgm_read_word(&LCD_SegTable[Byte])|0x4;
> 		else if (LCDNumbers >= 1 && Digit == 0)
> 		  SegData = pgm_read_word(&LCD_SegTable[Byte])|0x4;
> 		else 
> 		  SegData = pgm_read_word(&LCD_SegTable[Byte]);	  	 
> 	 }
> 	 else
> 		 if (LCDNumbers >= 5 && Digit == 3)
> 	      SegData = 0x2;
> 		else if (LCDNumbers >= 4 && Digit == 2)
> 	      SegData = 0x2;
> 		else if (LCDNumbers >= 2 && Digit == 1)
> 		  SegData = 0x4;
> 		else if (LCDNumbers >= 1 && Digit == 0)
> 		  SegData = 0x4;
> 		else 
> 		  SegData = 0x0 ;	
> 		  
> 	if (LCDNumbers >= 3 && Digit == 0)
> 	   LCDDR3 = 0x1;
> 	 else 
> 	   LCDDR3 = 0x0;
253a289,300
>     UpdateDisplay = true; 
>  }
> 
>  /* 
>   NAME:      | LCD_ShowNumbers 
>   PURPOSE:   | Routine to sequentially turn on the LCD's numbers 
>   ARGUMENTS: | Highest number, 0 for none 
>   RETURNS:   | None 
>  */ 
>  void LCD_ShowNumbers( uint8_t LNumbers) 
>  { 
>     LNumbers    = LCDNumbers; 

the LCD_ShowNumbers() thing doesn't work, but setting the value LCDNumbers seems to, again due to my complete ignorance of C.

If you could improve on this and perhaps do it in a way that allowed for more general orring of bits to LCDDR* then I could try and fill in the rest to get arrows working. I can see myself doing it with another heap of if statements, which doesnt feel very eloquent.
I guess its more general to call LCD_Numbers() and have just this number appear, and as such LCD_Arrows() and have the corresponding arrow appear.

and... theres always one more thing... I would like to use portD, which means making sure that there is no way that anything gets put on segments 5,6,and 7 (All special segments except 9 are ok tho). How can I be sure of this by modifying your driver?

TIA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi there!
I've got a Butterfly, and I'm trying since fife weeks to write something on the display...
I'm new on AVR but I know the C-Language well. I've tried to make a new Project with AVRStudio 5 and copied the code from Dean (first comment) I've made a headerfile for the LDC_Driver.h but nothing works... :(
The only thing I can do, is to put the programm which was on it at first.
I'm becoming desperate of trying...
Can somebody help, even when it's a old thread??

THX in advence

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Will this code work with my Atmega169?

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Depends if it's attached to an LCD with the same segment layout as the Atmel Butterfly or not.

However even if the segment layout is different the general technique should be adaptable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Depends if it's attached to an LCD with the same segment layout as the Atmel Butterfly or not.

However even if the segment layout is different the general technique should be adaptable.

Well, it is an AVR Butterly board.

Please let me know what you mean by "LCd with the same segment layout as the Atmel Butteryfly or not."

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Well, it is an AVR Butterly board.

This is a joke right? It's a LCD driver for the Butterfly board, which has the ATMEGA169 on it. Therefore, if you have a Butterfly board with said MEGA169 on it there's a fairly good chance it will be compatible.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks buddy....it helped me a lot!

Success is optional, choose wisely!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you Dean for posting the LCD driver - it worked for me perfectly the first time.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

Sorry for bringing back this old thread. My main question is more about C/C++ programming.

 

Original AVR Butterfly LCD has 16 bits for one digit, but my LCD has 8 bits.

 

Original LCD_WriteChar method:

 

/*
 NAME:      | LCD_WriteChar (static, inline)
 PURPOSE:   | Routine to write a character to the correct LCD registers for display
 ARGUMENTS: | Character to display, LCD character number to display character on
 RETURNS:   | None
*/
static inline void LCD_WriteChar(const uint8_t Byte, const uint8_t Digit)
{
	uint8_t* BuffPtr = (uint8_t*)(LCD_LCDREGS_START + (Digit >> 1));
	uint16_t SegData = 0x0000;

	if (Byte != LCD_SPACE_OR_INVALID_CHAR)              // Null indicates invalid character or space
	  SegData = pgm_read_word(&LCD_SegTable[Byte]);	

	for (uint8_t BNib = 0; BNib < 4; BNib++)
	{
		uint8_t MaskedSegData = (SegData & 0x0000F);

		if (Digit & 0x01)
		  *BuffPtr = ((*BuffPtr & 0x0F) | (MaskedSegData << 4));
		else
		  *BuffPtr = ((*BuffPtr & 0xF0) | MaskedSegData);

		BuffPtr += 5;
		SegData >>= 4;
	}	
}

 

If I change this line:

*BuffPtr = ((*BuffPtr & 0x0F) | (MaskedSegData << 4));

to this line:

*BuffPtr = ((*BuffPtr & 0x0F) | (MaskedSegData << 2));

I can "move cursor" to the second digit fine, but it doesn't move to the third digit.

 

So my main questions are about this particular LCD_WriteChar method from C/C++ perspective:

1) what means SegData & 0x0000F in this line? Why 0x0000F instead of 0x0F?

uint8_t MaskedSegData = (SegData & 0x0000F);

2) what means Digit & 0x01 in this line?

if (Digit & 0x01)

3) what means this line?

*BuffPtr = ((*BuffPtr & 0x0F) | (MaskedSegData << 4));

 

Regards,

Furieux

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you really talking about a segment LCD driven by a 169 or a 329?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Clawson,

Yes, sorry, I’m actually talking about 329.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1) 0xF is exactly the same as 0x000F (or any number of leading 0s in fact) but I think the author is making the point that it's masking the bottom 4 from the full 16 bits.

 

2) a & 1 test is usually an even/odd test. Even numbers are 0 when & 1 while odd numbers return 1 which is interpreted as "true" in a conditional test.

 

3) the line is taking the bottom 4 bits of what is already in the location pointed to then putting MaskedSegData into the upper 4 bits.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, could you please help me to analyze this function deeper?

 

uint8_t* BuffPtr = (uint8_t*)(LCD_LCDREGS_START + (Digit >> 1));

Here BuffPtr is defined which corresponds to LCD registers starting with LCDDR0. Next would be LCDDR5, then LCDDR10, and then LCDDR15 (according to COM0, COM1, COM2, and COM3). This is clear for me. But again - what Digit >> 1 means here?

 

Then,

uint16_t SegData = 0x0000;

Here SegData is declared with initial value.

 

Then,

if (Byte != LCD_SPACE_OR_INVALID_CHAR)
    SegData = pgm_read_word(&LCD_SegTable[Byte]);

Here it's checking if Byte is not invalid character and if it is not, then avr function pgm_read_word() is parsing LCD_SegTable[] array for the Byte value and then store it to the SegData variable.

For example, let's take number 0 to be shown on my segment LCD which in my case in array LCD_SegTable[] is defined as 0x3132

 

Then,

for (uint8_t BNib = 0; BNib < 4; BNib++) {

Here it is looping through nibbles (4 bits). I think this is because AVR Butterfly LCD has 16 bits for the digit. But in my case I have 8 bits, so I thought I need to loop only through two nibbles? I tried to change here BNib < 2, but then wrong segments are lit.

 

Then,

uint8_t MaskedSegData = (SegData & 0x0000F);

Here MaskedSegData is defined and it takes SegData value which was read from LCD_SegTable[] array (in this example it is 0x3132) and then it is AND-ed with bottom 4 bits. Right?

If I convert 0x3132 from hex to bin, i get

0000 0000 0011 0001 0011 0010

AND-ed with 1111 it is then

0000 0000 0011 0001 0011 0010 right? The same!

Why it is AND-ed in the first place? You wrote that it is checking for even/odd numbers. Why it is needed here?

 

Then,

if (Digit & 0x01)
    *BuffPtr = ((*BuffPtr & 0x0F) | (MaskedSegData << 4));

This I cannot understand at all. It's AND-ing Digit with 1 (if the last bit is 1?) and if true, then BuffPtr is AND-ed with bottom 4 bits and then OR-ed... what that means? It's definitely related to LCDDRx, but I can't get the point.

 

Then,

else
    *BuffPtr = ((*BuffPtr & 0xF0) | MaskedSegData);

So if AND-ing Digit with 1 is false (the last bit is 0?), then BuffPtr is AND-ed with top 4 bits and then again OR-ed... what that means?

 

And finally,

BuffPtr += 5;
SegData >>= 4;

BuffPtr now is incremented by 5 (this probably is related to those LCDDR0, LCDDR5, LCDDR10 and so on, or LCDDR1, LCDDR6, LCDDR11 and so on). This probably is needed because next for loop iteration will be on the next register. Right?

And SegData? This I don't understand at all. What SegData = SegData >> 4 means in the first place? I know that shifting to the right is basically dividing, but I'm not sure if it's that case here.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As you already seem to be getting lost with some basic C code.

I would suggest setting a side the code you have at the moment and take your time to get to grips with the C operators.

All your questions seem to be coming from not understanding what the specific operators do and in what order they are executed.

 

Also you have to have a good look at the datasheet.

My first guess it that the pointer BuffPtr is initialized to point to the start of the RAM area in the chip that holds the actual display data. so what segments on the display are off or on

All that is done is to update the state of certain pixels that need to be updated to display the correct character on the display itself

 

All the programmer has done is instead of putting some calculations on separate lines they have combined them on a single line to make the code better readable ( in their eyes... )

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

meslomp wrote:
As you already seem to be getting lost with some basic C code. I would suggest setting a side the code you have at the moment and take your time to get to grips with the C operators.
+10

 

Exactly what I was going to say. You can't really hope to use a code like this until you understand it and to understand it you need a grasp of some of the basic concepts in C like shifting and masking.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I got it.

Until that my only option will be to just leave unconnected those two pins (unneeded 8 bits) and use the same code...

Anyway, thanks for the help!

Last Edited: Mon. Jul 9, 2018 - 10:52 AM

Pages