/*
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;
}
/*
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 <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <stdbool.h>
// 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