[CODE] [C] Simple Butterfly LCD driver

Go To Last Post
115 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Freaks,

Below is the current code I am using in my ButtLoad project to control the LCD. I rewrote the code from Atmel's Butterfly example for size and speed - the resulting code is nice enough with little bloat. The routines are designed for displaying strings out of flash or SRAM only (no direct character writing, etc). I thought it was worth posting here.

Use LCD_Init(); to initialize the driver (routine stolen from Atmel's code :D). To show a string from SRAM use LCD_puts, or for strings located in flash (see my PROGMEM tutorial) use the LCD_puts_f function. Scrolling is automatic if the string is more than six characters long.

The LCD contrast can be use set between 0x00 (lightest) and 0x0F (darkest) via the LCD_CONTRAST_LEVEL(level) macro.

Use as you see fit.

LCD_Driver.c

/*
               BUTTLCD -- Butterfly LCD Driver

               Copyright (C) Dean Camera, 2008

            dean [at] fourwalledcubicle [dot] com
                  www.fourwalledcubicle.com
*/

/*
	This is a basic driver for the Butterfly LCD. It offers the ability to
	change the contrast and display strings (scrolling or static) from flash
	or SRAM memory only.
	
	This has been completly rewritten from the Atmel code; in this version, as
	much processing as possible is performed by the string display routines
	rather than the interrupt so that the interrupt executes as fast as possible.
*/

#define INC_FROM_DRIVER
#include "LCD_Driver.h"

//                                  LCD Text            + Nulls for scrolling + Null Termination
static volatile char     TextBuffer[LCD_TEXTBUFFER_SIZE + LCD_DISPLAY_SIZE    + 1] = {};
static volatile uint8_t  StrStart        = 0;
static volatile uint8_t  StrEnd          = 0;
static volatile uint8_t  ScrollCount     = 0;
static volatile uint8_t  UpdateDisplay   = false;
static volatile uint8_t  ShowColons      = false;
       volatile uint8_t  ScrollFlags     = 0;

const           uint16_t LCD_SegTable[] PROGMEM =
{
    0xEAA8,     // '*'
    0x2A80,     // '+'
    0x4000,     // ','
    0x0A00,     // '-'
    0x0A51,     // '.' Degree sign
    0x4008,     // '/'
    0x5559,     // '0'
    0x0118,     // '1'
    0x1e11,     // '2
    0x1b11,     // '3
    0x0b50,     // '4
    0x1b41,     // '5
    0x1f41,     // '6
    0x0111,     // '7
    0x1f51,     // '8
    0x1b51,     // '9'
    0x0000,     // ':' (Not defined)
    0x0000,     // ';' (Not defined)
    0x8008,     // '<'
    0x1A00,     // '='
    0x4020,     // '>'
    0x0000,     // '?' (Not defined)
    0x0000,     // '@' (Not defined)
    0x0f51,     // 'A' (+ 'a')
    0x3991,     // 'B' (+ 'b')
    0x1441,     // 'C' (+ 'c')
    0x3191,     // 'D' (+ 'd')
    0x1e41,     // 'E' (+ 'e')
    0x0e41,     // 'F' (+ 'f')
    0x1d41,     // 'G' (+ 'g')
    0x0f50,     // 'H' (+ 'h')
    0x2080,     // 'I' (+ 'i')
    0x1510,     // 'J' (+ 'j')
    0x8648,     // 'K' (+ 'k')
    0x1440,     // 'L' (+ 'l')
    0x0578,     // 'M' (+ 'm')
    0x8570,     // 'N' (+ 'n')
    0x1551,     // 'O' (+ 'o')
    0x0e51,     // 'P' (+ 'p')
    0x9551,     // 'Q' (+ 'q')
    0x8e51,     // 'R' (+ 'r')
    0x9021,     // 'S' (+ 's')
    0x2081,     // 'T' (+ 't')
    0x1550,     // 'U' (+ 'u')
    0x4448,     // 'V' (+ 'v')
    0xc550,     // 'W' (+ 'w')
    0xc028,     // 'X' (+ 'x')
    0x2028,     // 'Y' (+ 'y')
    0x5009,     // 'Z' (+ 'z')
    0x1441,     // '['
    0x8020,     // '\'
    0x1111,     // ']'
    0x0000,     // '^' (Not defined)
    0x1000      // '_'
};

// ======================================================================================

/*
 NAME:      | LCD_Init
 PURPOSE:   | Initializes the Butterfly's LCD for correct operation, ready to display data
 ARGUMENTS: | None
 RETURNS:   | None
*/
void LCD_Init(void)
{
	// Set the initial contrast level to maximum:
	LCD_CONTRAST_LEVEL(0x0F);

    // Select asynchronous clock source, enable all COM pins and enable all segment pins:
    LCDCRB  = (1<<LCDCS) | (3<<LCDMUX0) | (7<<LCDPM0);

    // Set LCD prescaler to give a framerate of 64Hz:
    LCDFRR  = (0<<LCDPS0) | (3<<LCDCD0);    

	// Enable LCD and set low power waveform, enable start of frame interrupt:
    LCDCRA  = (1<<LCDEN) | (1<<LCDAB) | (1<<LCDIE);
}

/*
 NAME:      | LCD_puts
 PURPOSE:   | Displays a string from flash onto the Butterfly's LCD
 ARGUMENTS: | Pointer to the start of the flash string
 RETURNS:   | None
*/
void LCD_puts_f(const char *FlashData)
{
	/* Rather than create a new buffer here (wasting RAM), the TextBuffer global
	   is re-used as a temp buffer. Once the ASCII data is loaded in to TextBuffer,
	   LCD_puts is called with it to post-process it into the correct format for the
	   LCD interrupt.                                                                */

	strcpy_P((char*)&TextBuffer[0], FlashData);
	LCD_puts((char*)&TextBuffer[0]);
}

/*
 NAME:      | LCD_puts
 PURPOSE:   | Displays a string from SRAM onto the Butterfly's LCD
 ARGUMENTS: | Pointer to the start of the SRAM string
 RETURNS:   | None
*/
void LCD_puts(const char *Data)
{
	uint8_t LoadB       = 0;
	uint8_t CurrByte;

	do
	{
		CurrByte = *(Data++);
		
		switch (CurrByte)
		{
			case 'a'...'z':
				CurrByte &= ~(1 << 5);                   // Translate to upper-case character
			case '*'...'_':	                             // Valid character, load it into the array
				TextBuffer[LoadB++] = (CurrByte - '*');
				break;
			case 0x00:                                   // Null termination of the string - ignore for now so the nulls can be appended below
				break;
			default:                                     // Space or invalid character, use 0xFF to display a blank
				TextBuffer[LoadB++] = LCD_SPACE_OR_INVALID_CHAR;
		}
	}
	while (CurrByte && (LoadB < LCD_TEXTBUFFER_SIZE));

	ScrollFlags = ((LoadB > LCD_DISPLAY_SIZE)? LCD_FLAG_SCROLL : 0x00);

	for (uint8_t Nulls = 0; Nulls < 7; Nulls++)
	  TextBuffer[LoadB++] = LCD_SPACE_OR_INVALID_CHAR;  // Load in nulls to ensure that when scrolling, the display clears before wrapping
	
	TextBuffer[LoadB] = 0x00;                           // Null-terminate string
	
	StrStart      = 0;
	StrEnd        = LoadB;
	ScrollCount   = LCD_SCROLLCOUNT_DEFAULT + LCD_DELAYCOUNT_DEFAULT;
	UpdateDisplay = true;
}

/*
 NAME:      | LCD_vect (ISR, blocking)
 PURPOSE:   | ISR to handle the display and scrolling of the current display string onto the LCD
 ARGUMENTS: | None
 RETURNS:   | None
*/
ISR(LCD_vect, ISR_NOBLOCK)
{
	if (ScrollFlags & LCD_FLAG_SCROLL)
	{
		if (!(ScrollCount--))
		{
			UpdateDisplay = true;
			ScrollCount   = LCD_SCROLLCOUNT_DEFAULT;
		}
	}

	if (UpdateDisplay)
	{
		for (uint8_t Character = 0; Character < LCD_DISPLAY_SIZE; Character++)
		{
			uint8_t Byte = (StrStart + Character);

			if (Byte >= StrEnd)
			  Byte -= StrEnd;
			
			LCD_WriteChar(TextBuffer[Byte], Character);
		}
		
		if ((StrStart + LCD_DISPLAY_SIZE) == StrEnd)    // Done scrolling message on LCD once
		  ScrollFlags |= LCD_FLAG_SCROLL_DONE;
		
		if (StrStart++ == StrEnd)
		  StrStart     = 1;

       if (ShowColons)
            *((uint8_t*)(LCD_LCDREGS_START + 8)) = 0x01;
        else
            *((uint8_t*)(LCD_LCDREGS_START + 8)) = 0x00;

		UpdateDisplay  = false;                         // Clear LCD management flags, LCD update is complete
	}
}

/*
 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;
	}	
}

/*
 NAME:      | LCD_ShowColons
 PURPOSE:   | Routine to turn on or off the LCD's colons
 ARGUMENTS: | Boolean - true to turn on colons
 RETURNS:   | None
*/
void LCD_ShowColons(const uint8_t ColonsOn)
{
	ShowColons    = ColonsOn;
	UpdateDisplay = true;
}

LCD_Driver.h

/*
               BUTTLCD -- Butterfly LCD Driver

               Copyright (C) Dean Camera, 2008

            dean [at] fourwalledcubicle [dot] com
                  www.fourwalledcubicle.com
*/

#ifndef LCDDRIVER_H
#define LCDDRIVER_H

	// INCLUDES:
	#include 
	#include 
	#include 
	#include 
	
	// EXTERNAL VARIABLES:
	extern volatile uint8_t ScrollFlags;
	
	// DEFINES:
	#define LCD_LCDREGS_START          ((uint8_t*)&LCDDR0)
	#define LCD_SPACE_OR_INVALID_CHAR  0xFF
	
	#define LCD_CONTRAST_LEVEL(level)  do{ LCDCCR = (0x0F & level); }while(0)
	#define LCD_WAIT_FOR_SCROLL_DONE() do{ while (!(ScrollFlags & LCD_FLAG_SCROLL_DONE)) {} }while(0)
	
	#define LCD_SCROLLCOUNT_DEFAULT    6
	#define LCD_DELAYCOUNT_DEFAULT     20
	#define LCD_TEXTBUFFER_SIZE        20
	#define LCD_SEGBUFFER_SIZE         19
	#define LCD_DISPLAY_SIZE           6

	#define LCD_FLAG_SCROLL            (1 << 0)
	#define LCD_FLAG_SCROLL_DONE       (1 << 1)	

	// PROTOTYPES:
	void LCD_puts_f(const char *FlashData);
	void LCD_puts(const char *Data);
	void LCD_Init(void);
	void LCD_ShowColons(const uint8_t ColonsOn);
	
	#if defined(INC_FROM_DRIVER)
	  static inline void LCD_WriteChar(const uint8_t Byte, const uint8_t Digit);
	#endif

#endif

- Dean :twisted:

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

Last Edited: Wed. Dec 16, 2009 - 03:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I downloaded the whole package and gave it a try. I'm loading it to Butterfly using boot loader. With the build from your .zip file, I see a RAM ST displayed on the LCD. But when I built it with my WinAvr environment (no modification), and reloaded it to the Butterfly, I see nothing running (nothing on the screen).

Any idea?

Thanks

ps.
Here is what my WinAvr build looks like:

> "make.exe" all

-------- begin --------
avr-gcc (GCC) 3.3.2
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Compiling: Test.c
avr-gcc -c -mmcu=atmega169 -I. -gdwarf-2 -DF_CPU=8000000UL -Os -I"C:\WinAVR\avr\include" -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -mcall-prologues -Wall -Wstrict-prototypes -Wendif-labels -Winline -Wa,-adhlns=Test.lst -std=gnu99 -MD -MP -MF .dep/Test.o.d Test.c -o Test.o
cc1.exe: warning: `dwarf-2': unknown or unsupported -g option

Compiling: LCD_Driver.c
avr-gcc -c -mmcu=atmega169 -I. -gdwarf-2 -DF_CPU=8000000UL -Os -I"C:\WinAVR\avr\include" -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -mcall-prologues -Wall -Wstrict-prototypes -Wendif-labels -Winline -Wa,-adhlns=LCD_Driver.lst -std=gnu99 -MD -MP -MF .dep/LCD_Driver.o.d LCD_Driver.c -o LCD_Driver.o
cc1.exe: warning: `dwarf-2': unknown or unsupported -g option
LCD_Driver.c:171: warning: return type defaults to `int'
LCD_Driver.c:171: warning: function declaration isn't a prototype
LCD_Driver.c: In function `ISR':
LCD_Driver.c:171: warning: type of `LCD_vect' defaults to `int'
LCD_Driver.c:210: warning: control reaches end of non-void function

Linking: LCDDemo.elf
avr-gcc -mmcu=atmega169 -I. -gdwarf-2 -DF_CPU=8000000UL -Os -I"C:\WinAVR\avr\include" -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -mcall-prologues -Wall -Wstrict-prototypes -Wendif-labels -Winline -Wa,-adhlns=Test.o -std=gnu99 -MD -MP -MF .dep/LCDDemo.elf.d Test.o LCD_Driver.o --output LCDDemo.elf -Wl,-Map=LCDDemo.map,--cref -lm

Creating load file for Flash: LCDDemo.hex
avr-objcopy -O ihex -R .eeprom LCDDemo.elf LCDDemo.hex

Creating load file for EEPROM: LCDDemo.eep
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
--change-section-lma .eeprom=0 -O ihex LCDDemo.elf LCDDemo.eep

Creating Extended Listing: LCDDemo.lss
avr-objdump -h -S LCDDemo.elf > LCDDemo.lss

Creating Symbol Table: LCDDemo.sym
avr-nm -n LCDDemo.elf > LCDDemo.sym

Size after:
LCDDemo.elf :
section size addr
.data 12 8388864
.text 914 0
.bss 53 8388876
.noinit 0 8388929
.eeprom 0 8454144
Total 979

-------- end --------

> Process Exit Code: 0

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

You need to update your AVRLibC installation - you've got an older one where "ISR" is not defined. Since it is not defined, WinAVR just treats it as a standard int function, and so it does not handle the ISR interrupt vector.

Download the latest AVRLibC binaries from http://download.savannah.gnu.org... and extract over the top of the old ones (extract to WinAVR folder, overwrite existing files).

If you really do not wish to update for some reason, then either change "ISR" to "SIGNAL" or use my "Better GCC Interrupt Macro", available in another post in this tutorial forum.

- Dean :twisted:

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

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

Thanks very much for your help Dean. I download the 1.4.5 libc and it works fine now :)

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

I've just updated my first post with my latest code.

I've reduced the code footprint quite a lot, and eliminated the segment buffering (unneeded since the update time is under a few milliseconds). Total ISR time has been reduced, and now the LCD routine can be interrupted by other ISRs. I've coded the interrupt routine in such a way that it cannot interrupt itself, preventing stack overflows.

Eliminating the segment buffering has greatly reduced the RAM requirements of the driver.

Feedback welcome!

EDIT: I've created a simple test file, and linked it against both Atmel and my own driver:

int main(void)
{
    LCD_Init();
	
	LCD_puts_f(PSTR("AVR BUTTERFLY GCC"));
	LCD_puts("AVR BUTTERFLY GCC");

	for (;;) {}
	
	return 0;
}

My Driver:

Program:     810 bytes (4.9% Full)
(.text + .data + .bootloader)

Data:         50 bytes (4.9% Full)
(.data + .bss + .noinit)

Atmel's Driver:

Program:    1106 bytes (6.8% Full)
(.text + .data + .bootloader)

Data:         71 bytes (6.9% Full)
(.data + .bss + .noinit)

Note that the RAM above includes in both cases the 18 byte SRAM string in the test program as well as the RAM used by the drivers.

I'm yet to come up with a good way to measure the speed of the two drivers, so if anyone has any ideas in that respect please put them here.

- Dean :twisted:

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

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

Hello!

I'm newbei and I just uploadted this LCD driver to my Butterfly.
And tried to print the "LCD_puts("RAM STRING");" function,
instead of getting "RAM STRING" I've got "RA*+,W" on my LCD.
Any ideas why this driver doesn't work?
I'v tried few 'homemade' drivers and they doesn't work on my Butterfly,
but they work on my friends old butterfly.
The time that my LCD works is when I put the original software in it.

Any ideas??

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

That's odd henge - all Butterflies should have identical displays. I've heard of the latest boards having contrast issues, but not incorrect display like you're getting.

1) When did you purchase your Butterfly?
2) Have you made any modifications to the board?
3) What revision of the official firmware did you board use?

- Dean :twisted:

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

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

Jeah, it's odd and even odder is that I accedently fixed it.
I changed the LCD framerate from 32 Hz to 64 Hz:
LCDFRR = (3<<LCDCD0); Now text is right on all segments and now
you can see the text from all the angles. Before you couldn't see the
text directly from front.

I tought that my soldering had damaged the board but when I uploaded the original software (rev07
it worked fine. In the rev07 the frame rate is 32 Hz and it works. I hadn't hat the time to figure this out yet.

If someone has an explantion for this, I'd like to hear it.

-henge :shock:

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

Work on my butterfly, but it won't scroll for some reason. All I see on the screen is "FLASH"..but it won't scroll the whole string. Any ideas why? I compiled with winavr

Chief Tinkerer

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

EDIT: Nevermind, I'm tired.

What is the exact string you're trying to scroll? If the string is less than or equal to 6 characters in length it will stay static, and will only scroll if it is larger than the number of connected character cells on the Butterfly's LCD.

- Dean :twisted:

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

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

That's why I am confused. It's set to print the default string in your test.c program which is "FLASH STRING 2". All the screen reads is "FLASH" and if I put in "FLASHSTRING", the screen will read "FLASHS" but won't scroll. Any ideas?

abcminiuser wrote:
EDIT: Nevermind, I'm tired.

What is the exact string you're trying to scroll? If the string is less than or equal to 6 characters in length it will stay static, and will only scroll if it is larger than the number of connected character cells on the Butterfly's LCD.

- Dean :twisted:

Chief Tinkerer

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

Well, if the string is displaying, the ISR is firing correctly - so I assume you've executed a sei(); in your main code. Odd that it isn't scrolling, perhaps I made a copy-paste error somewhere. Can I PM you the updated and known-working driver so you can test it out with your board?

- Dean :twisted:

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

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

You bet, anything helps. I'm a little lost as to why the text won't scroll. I would love to get this driver working.

abcminiuser wrote:
Well, if the string is displaying, the ISR is firing correctly - so I assume you've executed a sei(); in your main code. Odd that it isn't scrolling, perhaps I made a copy-paste error somewhere. Can I PM you the updated and known-working driver so you can test it out with your board?

- Dean :twisted:

Chief Tinkerer

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

I've just updated my original and second posts to new versions of the code. The new code is faster, adds the LCD_WAIT_FOR_SCROLL_DONE() macro - which, as its name implies, causes program execution to halt until the message has scrolled across the LCD - and works with lowercase characters. Obviously the LCD still displays all characters in uppercase, however I've added code to automatically translate the lower case characters into uppercase characters.

New driver size is slightly larger:

Program:     834 bytes (5.1% Full)
(.text + .data + .bootloader)

Data:         50 bytes (4.9% Full)
(.data + .bss + .noinit)

However should be very slightly faster and fuller-featured. Still beats the Atmel driver by 272 bytes - and should be much faster to boot!

- Dean :twisted:

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

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

Just tried to compile your test file with latest AVR Studio and WinAVR, here the result:

avr-gcc.exe -mmcu=atmega169p -Wall -gdwarf-2 -O0 -MD -MP -MT LCD_Test.o -MF dep/LCD_Test.o.d -c ../LCD_Test.c
In file included from ../LCD_Test.c:3:
../LCD_Driver.c: In function 'LCD_puts':
../LCD_Driver.c:161: error: 'for' loop initial declaration used outside C99 mode
../LCD_Driver.c: In function '__vector_22':
../LCD_Driver.c:191: error: 'for' loop initial declaration used outside C99 mode
../LCD_Driver.c: In function 'LCD_WriteChar':
../LCD_Driver.c:225: error: 'for' loop initial declaration used outside C99 mode
make: *** [LCD_Test.o] Error 1
Build failed with 3 errors and 0 warnings...

Whats my mistake?

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

You need to turn on the "C99" C standards mode. By default the compiler compiles in a variation of the old "C89" standard which does not allow for variables to be declared inside of a for loop.

If you're using a makefile, change your -std line to:

CSTANDARD = -std=gnu99

If using AVRStudio as a frontend:

Project Menu -> Configuration Options. Select the "Custom Options" tab, in the text box type "-std=gnu99" and press the "Add" button. Click "Ok" to close the window and it should work fine.

- Dean :twisted:

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

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

Thanks
Its a very usefull driver. :D

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

hmm... doesn't seem to work on this Butterfly here.

The LCD shows nothing... and if examined closely, one can see a slight flickering at one end of the LCD, but nothing else.

Could the frequency of the Butterfly specified in the Makefile be the cause of the problem? :?

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

The makefile frequency shouldn't make any difference, but by all means give it a go. Did you initialize the driver via the Init routine before writing to it? Are you enabling interrupts?

- Dean :twisted:

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

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

thanks! :lol:
adding a sei(); before the init solved the problem.
From the previous comments I somehow got the notion that I am NOT supposed to enable interrupts for the driver to work... I guess it was pretty much the other way.

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

May you help ? I search program that help to create table LCD_SegTable for nation chars.

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

I've got a program I found on the net a while ago to design custom Butterfly LCD characters, which I can post if you like. However, the Butterfly LCD is very limited in what it can display, and you will not be able to add in accents or non-English characters.

- Dean :twisted:

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

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

I loaded your program on to butterfly...But i am not getting any text on my lcd...what might be the problem.

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

I can post if you like.
Dean, please send the program.

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

sairahul321 wrote:
I loaded your program on to butterfly...But i am not getting any text on my lcd...what might be the problem.

There could be a variety of reasons - first up, are you sure you've loaded the program correctly, and started it by cycling power and pushing the joystick upwards?

I've attached the Butterfly LCD program. I didn't make it and found it on 'Freaks a while ago, so I've no claims to authorship nor any ability to support it.

- Dean :twisted:

Attachment(s): 

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

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

I tried to compile this driver and here is my error:

C:/Programme/Atmel/WinAVR/avr/include/avr/signal.h:36:2: warning: #warning "This header file is obsolete. Use ."
../Lcd_driver.c: In function `LCD_Init':
../Lcd_driver.c:113: error: `LCDCCR' undeclared (first use in this function)
../Lcd_driver.c:113: error: (Each undeclared identifier is reported only once
../Lcd_driver.c:113: error: for each function it appears in.)
../Lcd_driver.c:116: error: `LCDCRB' undeclared (first use in this function)
../Lcd_driver.c:116: error: `LCDCS' undeclared (first use in this function)
../Lcd_driver.c:116: error: `LCDMUX0' undeclared (first use in this function)
../Lcd_driver.c:116: error: `LCDPM0' undeclared (first use in this function)
../Lcd_driver.c:119: error: `LCDFRR' undeclared (first use in this function)
../Lcd_driver.c:119: error: `LCDPS0' undeclared (first use in this function)
../Lcd_driver.c:119: error: `LCDCD0' undeclared (first use in this function)
../Lcd_driver.c:122: error: `LCDCRA' undeclared (first use in this function)
../Lcd_driver.c:122: error: `LCDEN' undeclared (first use in this function)
../Lcd_driver.c:122: error: `LCDAB' undeclared (first use in this function)
../Lcd_driver.c:122: error: `LCDIE' undeclared (first use in this function)
../Lcd_driver.c: At top level:
../Lcd_driver.c:191: warning: `LCD_vect' appears to be a misspelled signal handler
../Lcd_driver.c: In function `LCD_WriteChar':
../Lcd_driver.c:232: error: `LCDDR0' undeclared (first use in this function)
make: *** [Lcd_driver.o] Error 1
Build failed with 15 errors and 2 warnings...

can someone help me to fix it?

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

Remove the #include - it's no longer needed as the file includes all its previous functionality.

As for the other errors, it looks like you're not compiling for the MEGA169. Have you set up your environment to compile with the mmcu set to the MEGA169?

- Dean :twisted:

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

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

Thanks!
I forgot to set to mega169. My mistake. Now successfully compiled. :P

But after I flashed it to the board, my LCD (STK502) shows nothing :cry:

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

Did you load in the test application?

I've not tested the STK502 with the driver, but if the connections are the same as the Butterfly it should all work just fine. Does anyone here have a STK502 which they've successfully used with my code?

- Dean :twisted:

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

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

Did you snable the interrupts?
The first time I started playing with the butterfly I forgot to call 'sti' to enable interrupts... and nothing on the LCD...

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

Thanks. It works! Like Pedro said, i didnt enable the interrupts.

How i want to put like this:

Hello
(lcd clear)
Good Morning!
(lcd clear)

then the screen repeat itself

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

You can use the following snippet to do what you want. It performs an infinite loop, displaying a string from flash memory, waiting for it to scroll across the display, then displaying the next string and waits for that one to scroll, and repeats.

for (;;)
{
LCD_puts_f(PSTR("HELLO"));
LCD_WAIT_FOR_SCROLL_DONE();
LCD_puts_f(PSTR("GOOD MORNING"));
LCD_WAIT_FOR_SCROLL_DONE();
}

- Dean :twisted:

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

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

Anytime I try and compile this driver using AVR Studio 4, it locks my machine up. Does anybody else have this problem? Am I doing something wrong?

EDIT: I have solved the lockup problem by upgrading AVR Studio and changing to C99 mode.

Thanks all.

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

Does this driver support the use of the LCD's colons at all? If not, how would I go about enabling them?

Thanks

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

Quote:

Does this driver support the use of the LCD's colons at all? If not, how would I go about enabling them?

It does now, just updated the original post. Now use LCD_ShowColons(0x01) to turn them on, and LCD_ShowColons(0x00) to turn them off. Can't test at the moment, but it should work just fine.

- Dean :twisted:

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

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

Quote:
It does now, just updated the original post. Now use LCD_ShowColons(0x01) to turn them on, and LCD_ShowColons(0x00) to turn them off. Can't test at the moment, but it should work just fine.

- Dean :twisted:

The code works great.

Thank you very much!!!

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

Dean-

Please excuse my ignorance here, but I am trying to understand a bit about how you turn the colons on and off, but I can't seem to figure it out. The code functions great, i would just like to know a bit more about how it works. I am trying to turn them on and off individually but would like to understand a bit more about character mapping. Feel free to PM me or email me if you would prefer.

Thanks,

Zach

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

Zach,

I had trouble with it also. It's been a long while since I wrote the bulk of the driver, so I can't remember all the specifics. I do remember that each character is 16 bits (each mapping to one of the character LCD segments) which is split into four 4-bit nibbles. Each nibble is loaded into four LCD digit registers spaced five bytes apart, in the low-nibbles for the even digit numbers, and high-nibbles for the odd numbered digits.

All seemed like pure voodoo at the time, but seems to work well so I left it at that. For the "special" characters such as the little arrows along the top of the display and the colons I have no idea, I resorted to using my JTAG to flip the LCD register bits in turn until I found the appropriate bit/register.

Character mapping is as follows from what I can remember:

  HIGH NIBBLE       LOW NIBBLE
[DIGIT1_NIBBLE1 | DIGIT0_NIBBLE1] LCDDR0
[DIGIT3_NIBBLE1 | DIGIT2_NIBBLE1] LCDDR1
[DIGIT5_NIBBLE1 | DIGIT4_NIBBLE1] LCDDR2
[DIGIT1_NIBBLE2 | DIGIT0_NIBBLE2] LCDDR3
[DIGIT3_NIBBLE2 | DIGIT2_NIBBLE2] LCDDR4
[DIGIT5_NIBBLE2 | DIGIT4_NIBBLE2] LCDDR5
[DIGIT1_NIBBLE3 | DIGIT0_NIBBLE3] LCDDR6
[DIGIT3_NIBBLE3 | DIGIT2_NIBBLE3] LCDDR7
[DIGIT5_NIBBLE3 | DIGIT4_NIBBLE3] LCDDR8
[DIGIT1_NIBBLE4 | DIGIT0_NIBBLE4] LCDDR9
[DIGIT3_NIBBLE4 | DIGIT2_NIBBLE4] LCDDR10
[DIGIT5_NIBBLE4 | DIGIT4_NIBBLE4] LCDDR11

- Dean :twisted:

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

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

Hello Dean,

i found your LCD-ibrary really useful, but I have a little problem with it.

I just cant't write a character to a specific position on the display, like with LCD_putc().

I tried to use LCD_WriteChar for this and pass it an ASCII-character, but it doesn't print what I was expecting.
I dont really understand what LCD_WriteChar is doing. What do you pass to it?

Could you please help me figure out how to do that?

Thanks :)

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

Finally, a success:
I turned "static inline LCD_WriteChar()" into a plain "LCD_WriteChar()" and defined the following macro:

#define LCD_putc(digit,character) LCD_WriteChar((char)character  - '*', digit)

Not a beauty but it works sufficiently. Maybe somebody finds a more elegant and generic solution :)

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

Hi, Folks:

I found Dean's elegant code for the Butterfly LCD to be very clean and useful, so I've made a few minor modifications in order to add flashing digits and timers to support joystick input.

I've included general-purpose, easy to use routines to initialize and read the joystick, with proper debounce and autorepeat functions (using the LCD frame interrupt as a time base).

I also wrote a new Real Time Clock with a global 6-byte BCD buffer. The buffer provides a time base for dataloggers supporting 1 second, 10 seconds, 1 minute, 10 minutes, 1 hour and 10 hour data sampling intervals. Everything is demonstrated with a stand-alone application that turns the Butterfly into a real-time clock, settable using the joystick.

You might argue that the Butterfly as delivered already provides an RTC, but take a look at that code! Besides, what's the fun in using something out of the box? I've posted the source code in the Projects section and attached the zipped code to this message. Let me know if you have suggestions or problems with any of it!

Cheers, Jim

Attachment(s): 

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

Good daytime!
I'm using LCD driver not for a butterfly, but for another lcd-display (TN Glass, 1/4 duty, 1/3 bias, Operation voltage: 3.0V, 4com*24seg) + mega329 uC. When i try to compile next code, i'm having "LCDDR0 undeclared" problem and can't find why.

Please, help.

main.c

/*
uC: ATmega329
avr-gcc (WinAVR 20081205) 4.3.2
*/

#define F_CPU 4000000UL //FCPU=4MHz
#define MCU atmega329

  
#include 
#include 
#include 

#include 
#include 

int main(void)
{ 
	
	LCD_Init(); 
    
	LCD_puts_f(PSTR("AVR BUTTERFLY GCC")); 
	LCD_puts("AVR BUTTERFLY GCC"); 

	for (;;) {} 
    
	return 0; 
}

My error-list:
Compiling C: main.c
avr-gcc -c -mmcu=atmega329 -I. -gdwarf-2 -DF_CPU=4000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=./main.lst -std=gnu99 -Wundef -MMD -MP -MF .dep/main.o.d main.c -o main.o
In file included from main.c:19:
./lcd_driver.c: In function '__vector_22':
./lcd_driver.c:209: error: 'LCDDR0' undeclared (first use in this function)
./lcd_driver.c:209: error: (Each undeclared identifier is reported only once
./lcd_driver.c:209: error: for each function it appears in.)
./lcd_driver.c: In function 'LCD_WriteChar':
./lcd_driver.c:225: error: 'LCDDR0' undeclared (first use in this function)
make.exe: *** [main.o] Error 1

Thanks a lot!
And sorry for my english, if it's bad

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

Sorry, i found problem:
file should be

   // DEFINES: 
   #define LCD_LCDREGS_START          ((uint8_t*)&LCDDR00) 

instead of:

   // DEFINES: 
   #define LCD_LCDREGS_START          ((uint8_t*)&LCDDR0) 

also i enabled interrupts, added sei() after lcd_init(). Is it right? LCD is still off - i thought there should be something abnormal, because of incorrect segment table, but there is nothing :( why?

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

Hello!

I'm newbei and i'm using keypad as input to my butterfly.
i tried to display on my butterfly lcd based on what i've key in from the keypad.

any ideas how this could be works??

This is my keypad 4x4 AVR-GCC C language:

#include

int main()

{

//high nibble for output(columns) low for input(rows);

DDRB=0xF0;

//enable internal pullups for PB0-PB3

PORTB=0x0F;

//Port D for indication only

DDRD=0xFF;

while (1) //loop key check forever

{

//first column

PORTB =0b01111111;

//check for rows and send key number to portD

//instead sending key number to PORTD you can use

// any function that serves pressed button

if (bit_is_set(PINB, 3)) PORTD=1;

if (bit_is_set(PINB, 2)) PORTD=2;

if (bit_is_set(PINB, 1)) PORTD=3;

if (bit_is_set(PINB, 0)) PORTD=4;

//second column

PORTB =0b10111111;

if (bit_is_set(PINB, 3)) PORTD=5;

if (bit_is_set(PINB, 2)) PORTD=6;

if (bit_is_set(PINB, 1)) PORTD=7;

if (bit_is_set(PINB, 0)) PORTD=8;

//third column

PORTB =0b11011111;

if (bit_is_set(PINB, 3)) PORTD=9;

if (bit_is_set(PINB, 2)) PORTD=10;

if (bit_is_set(PINB, 1)) PORTD=11;

if (bit_is_set(PINB, 0)) PORTD=12;

//fourth column

PORTB =0b11101111;

if (bit_is_set(PINB, 3)) PORTD=13;

if (bit_is_set(PINB, 2)) PORTD=14;

if (bit_is_set(PINB, 1)) PORTD=15;

if (bit_is_set(PINB, 0)) PORTD=16;

}

}

Please help me to display any character on my lcd. Crying or Very sad

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

Use my driver. You're trying to drive the LCD segments with normal DC output, which will quickly damage them if you end up getting it working. You need to use my driver, which uses the MEGA169's internal LCD driver circuitry that supplies the correct AC signals to the LCD segments to turn them on correctly.

- Dean :twisted:

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

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

what the name of your driver and where can i get it?

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

Quote:

what the name of your driver and where can i get it?

This thread perhaps?

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

what the name of your driver and where can i get it?

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

Use my driver

Quote:

what driver?

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

Use my driver
Quote:

what driver?

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

Have you actually READ this thread?!?! As I say the driver you seek is in the very first post of this thread.

Pages