Projet

Général

Profil

Publication de fichiers » lcd.c

Redmine Admin, 2013-02-16 17:23

 
/*
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
*/
(1-1/2)