|
/*
|
|
Copyright (c) 209, 2010, Patrice Nadeau
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of Patrice Nadeau nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL Patrice Nadeau BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* @file lcd.c
|
|
* @brief LCD on AVR
|
|
* @author Patrice Nadeau
|
|
* @see lcd.h
|
|
*/
|
|
|
|
#include "lcd/lcd.h"
|
|
|
|
static void _Clock (void){
|
|
/* Take the Enable pin high to low (send data) */
|
|
/* clock high */
|
|
PORT(lcd_E_PORT) |= (1<<lcd_E_PIN);
|
|
/* delay here ? */
|
|
/* clock low */
|
|
PORT(lcd_E_PORT) &= ~(1<<lcd_E_PIN);
|
|
}
|
|
|
|
static uint8_t _Send(uint8_t command){
|
|
/* Send a byte to the LCD (data or command) */
|
|
switch (lcd_DATA_LENGHT){
|
|
case 4:
|
|
/* high nibble output mode */
|
|
DDR(lcd_DATA_PORT) |= 0xf0;
|
|
/* send high nibble */
|
|
/* clear high nibble */
|
|
PORT(lcd_DATA_PORT) &= 0x0f;
|
|
PORT(lcd_DATA_PORT) |= (command & 0xf0);
|
|
_Clock();
|
|
/* send low nibble */
|
|
/* clear high nibble */
|
|
PORT(lcd_DATA_PORT) &= 0x0f;
|
|
PORT(lcd_DATA_PORT) |= (0xf0 & (command << 4));
|
|
_Clock();
|
|
break;
|
|
case 8:
|
|
DDR(lcd_DATA_PORT) = 0xff;
|
|
PORT(lcd_DATA_PORT) = command;
|
|
_Clock();
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
return 0;
|
|
}
|
|
|
|
static void _SendData(uint8_t data){
|
|
/* Send a byte of data to the LCD */
|
|
PORT(lcd_RW_PORT) &= ~(1<<lcd_RW_PIN);
|
|
PORT(lcd_RS_PORT) |= (1<<lcd_RS_PIN);
|
|
_Send (data);
|
|
_delay_ms(1);
|
|
}
|
|
|
|
static void _SendCommand(uint8_t command){
|
|
/* Send a byte of command to the LCD */
|
|
PORT(lcd_RW_PORT) &= ~(1<<lcd_RW_PIN);
|
|
PORT(lcd_RS_PORT) &= ~(1<<lcd_RS_PIN);
|
|
_Send (command);
|
|
/* delay for display or return home functions */
|
|
_delay_ms(3);
|
|
|
|
}
|
|
|
|
void lcd_InitPort(void){
|
|
DDR(lcd_RW_PORT) |= (1<<lcd_RW_PIN);
|
|
DDR(lcd_RS_PORT) |= (1<<lcd_RS_PIN);
|
|
/* enable output mode */
|
|
DDR(lcd_E_PORT) |= (1<<lcd_E_PIN);
|
|
}
|
|
|
|
/**
|
|
@note Only support 2 lines
|
|
*/
|
|
void lcd_FunctionSet(uint8_t line, uint8_t font){
|
|
uint8_t temp = 0;
|
|
uint8_t x;
|
|
/* check data length */
|
|
if (lcd_DATA_LENGHT == 4) {
|
|
lcd_InitPort();
|
|
/* high nibble in output mode */
|
|
DDR(lcd_DATA_PORT) |= 0xf0;
|
|
/* clear high nibble */
|
|
PORT(lcd_DATA_PORT) &= 0x0f;
|
|
/* set only bit 4-7 */
|
|
PORT(lcd_DATA_PORT) |= 0x30;
|
|
/* this loop need to be done 3 times, in case the LCD hardware reset
|
|
did not worked */
|
|
for (x = 0; x < 3; x++){
|
|
_Clock();
|
|
_delay_ms(5);
|
|
}
|
|
/* clear high nibble */
|
|
PORT(lcd_DATA_PORT) &= 0x0f;
|
|
/* set bit 4-7 and don't modify bit 0-3 */
|
|
PORT(lcd_DATA_PORT) |= 0x20;
|
|
_Clock();
|
|
_delay_ms(1);
|
|
/* 4 bits mode */
|
|
temp &= (0 << 4);
|
|
}
|
|
else {
|
|
lcd_InitPort();
|
|
/* whole byte in output mode */
|
|
DDR(lcd_DATA_PORT) = 0xff;
|
|
/* command 03, set bit 4-7 and don't modify bit 0-3 */
|
|
PORT(lcd_DATA_PORT) = 0x30;
|
|
for (x = 0; x < 3; x++){
|
|
/* this loop need to be done 3 times, in case the LCD hardware
|
|
reset did not work */
|
|
_Clock();
|
|
};
|
|
/* 8 bits mode */
|
|
temp |= (1 << 4);
|
|
};
|
|
/**
|
|
* @todo use of a case to suport 4 lines
|
|
*/
|
|
if (line == lcd_LINES_1)
|
|
temp &= ~(1 << 3);
|
|
if (line == lcd_LINES_2)
|
|
temp |= (1 << 3);
|
|
/**
|
|
* @todo use of a case to suport other fonts
|
|
*/
|
|
if (font == lcd_FONT_8)
|
|
temp &= ~(1 << 2);
|
|
if (font == lcd_FONT_11)
|
|
temp |= (1 << 2);
|
|
/* bit for the function set */
|
|
temp |= (1 << 5);
|
|
_SendCommand (temp);
|
|
}
|
|
|
|
void lcd_ClearDisplay(void) {
|
|
_SendCommand(1);
|
|
}
|
|
|
|
/**
|
|
* @todo Optimise the code
|
|
*/
|
|
void lcd_EnterMode(uint8_t rightleft, uint8_t shift){
|
|
int8_t temp;
|
|
temp = (1 << 2);
|
|
if (rightleft == lcd_SHIFT_DISPLAY_RIGHTLEFT) {
|
|
/* display will shift to the left by default */
|
|
}
|
|
else
|
|
temp |= (1 << 1);
|
|
if (shift == lcd_SHIFT_DISPLAY_OFF) {
|
|
/* display will not shift */
|
|
}
|
|
else
|
|
temp |= (1 << 0);
|
|
_SendCommand(temp);
|
|
_delay_ms(40);
|
|
}
|
|
|
|
/**
|
|
* @todo Optimise the code
|
|
*/
|
|
void lcd_DisplayOnOff(uint8_t display, uint8_t cursor, uint8_t blink){
|
|
int8_t temp;
|
|
temp = (1 << 3);
|
|
if (display == lcd_DISPLAY_OFF) {
|
|
/* already the default */
|
|
}
|
|
else
|
|
temp |= (1 <<2);
|
|
if (cursor == lcd_CURSOR_OFF) {
|
|
/* already the default */
|
|
}
|
|
else
|
|
temp |= (1 << 1);
|
|
if (blink == lcd_CURSOR_BLINK_OFF) {
|
|
/*already the default */
|
|
}
|
|
else
|
|
temp |= (1 << 0);
|
|
_SendCommand(temp);
|
|
}
|
|
|
|
void lcd_DDRAM(uint8_t position){
|
|
/* bit 7 is the command */
|
|
_SendCommand(position | (1 << 7));
|
|
}
|
|
|
|
void lcd_CGRAM(uint8_t address){
|
|
int8_t temp;
|
|
temp = address;
|
|
/* set bit 6 */
|
|
temp |= (1 << 6);
|
|
/* clear bit 7 */
|
|
temp &= ~(1 << 7);
|
|
_SendCommand(temp);
|
|
}
|
|
|
|
void lcd_WriteData (uint8_t character){
|
|
_SendData(character);
|
|
}
|
|
|
|
uint8_t lcdShift (uint8_t cursor_display, uint8_t direction){
|
|
int8_t error;
|
|
int8_t temp;
|
|
error = 0;
|
|
temp = (1 << 4);
|
|
switch (cursor_display){
|
|
case 0 :
|
|
/* cursor (default) */
|
|
break;
|
|
case 1 :
|
|
/* display */
|
|
temp |= (1 << 3);
|
|
break;
|
|
default :
|
|
error = 1;
|
|
};
|
|
switch (direction){
|
|
case 0 :
|
|
/* shift left (default) */
|
|
break;
|
|
case 1 :
|
|
/* shift right */
|
|
temp |= (1 << 2);
|
|
break;
|
|
default :
|
|
error = 2;
|
|
};
|
|
_SendCommand(temp);
|
|
return error;
|
|
}
|
|
|
|
void lcd_WriteString (char *string){
|
|
char c;
|
|
while ((c = *string++)){
|
|
lcd_WriteData(c);
|
|
};
|
|
}
|
|
|
|
void lcdReturnHome(void){
|
|
_SendCommand(1 << 1);
|
|
}
|
|
|
|
uint8_t lcd_BusyFlag(void){
|
|
PORT(lcd_RS_PORT) &= ~(1<<lcd_RS_PIN);
|
|
PORT(lcd_RW_PORT) |= (1<<lcd_RW_PIN);
|
|
/* input mode */
|
|
DDR(lcd_DATA_PORT) = 0x00;
|
|
_Send(0);
|
|
return (_Send(0));
|
|
}
|
|
|
|
uint8_t lcd_ReadData(void){
|
|
lcd_InitPort();
|
|
PORT(lcd_RW_PORT) |= (1<<lcd_RW_PIN);
|
|
PORT(lcd_RS_PORT) |= (1<<lcd_RS_PIN);
|
|
//* need to be modified for 4 bits */
|
|
/* input mode */
|
|
DDR(lcd_DATA_PORT) = 0x00;
|
|
return (_Send(0));
|
|
}
|
|
|
|
void lcd_ClearLine (uint8_t line){
|
|
uint8_t start, x;
|
|
if (line == lcd_LINES_1)
|
|
start = 0x00;
|
|
if (line == lcd_LINES_2)
|
|
start = 0x40;
|
|
/* clear all the characters *display and buffer */
|
|
for (x = 0; x <= 0x27; x++){
|
|
/* will work even if the direction is right to left */
|
|
lcd_DDRAM(start + x);
|
|
/* clear the character, 20H is the same as ClearDisplay */
|
|
lcd_WriteData(0x20);
|
|
};
|
|
/* put the cursor at the beginning of the line */
|
|
lcd_DDRAM(start);
|
|
}
|
|
|
|
void lcd_Byte(uint8_t data){
|
|
int8_t z;
|
|
for (z = 7; z >= 0; z--){
|
|
if (data & (1 << z))
|
|
lcd_WriteData('1');
|
|
else
|
|
lcd_WriteData('0');
|
|
};
|
|
}
|
|
|
|
void lcd_DoubleByte(uint16_t data){
|
|
int16_t z;
|
|
for (z = 15; z >= 0; z--){
|
|
if (data & (1 << z))
|
|
lcd_WriteData('1');
|
|
else
|
|
lcd_WriteData('0');
|
|
};
|
|
}
|
|
|
|
|
|
/* Change log
|
|
* 2009-07-18 Patrice Nadeau
|
|
* Initial release
|
|
* 2010-09-18 by Patrice Nadeau
|
|
* Moved the define for the port from the .c file
|
|
* Renamed functions to standardize the names
|
|
* lcd_FunctionSet now use lcd_InitPort
|
|
* lcd_functionSet now return nothing, changed to check #define
|
|
* lcd_ClearLine is now simplified
|
|
* lcd_DisplayOnOff is now simplified
|
|
* Added comments
|
|
* 2010-10-02 Patrice Nadeau
|
|
* Now use only the letter of the port for DDRx, PORTx, PINx with the new
|
|
utils.h included in lcd.h
|
|
* Added the _Clock() function to replace all the Enable high/low
|
|
* Renamed to lcd.c since lcd.h check if the current AVR is supported
|
|
*/
|