2022-11-17 20:43:21 -05:00

740 lines
17 KiB
C

/**
* ---------------------------------------------------------------+
* @desc HD44780 LCD Driver
* ---------------------------------------------------------------+
* Copyright (C) 2020 Marian Hrinko.
* Written by Marian Hrinko (mato.hrinko@gmail.com)
*
* @author Marian Hrinko
* @datum 18.11.2020
* @file hd44780.c
* @tested AVR Atmega16a
*
* @depend hd44780.h
* ---------------------------------------------------------------+
* @usage default set 16x2 LCD
* 4-bit with 3 control wires (RW, RS, E)
*/
// include libraries
#include <stdio.h>
//#include <util/delay.h>
//#include <avr/io.h>
#include "define.h"
#include "BoardCfg.h"
#include "hd44780.h"
#include "timer.h"
// +---------------------------+
// | Power on |
// | Wait for more than 15 ms | // 15 ms wait
// | after VCC rises to 4.5 V |
// +---------------------------+
// |
// +---------------------------+
// | RS R/W DB7 DB6 DB5 DB4 |
// | 0 0 0 0 1 1 | // Initial sequence 0x30
// | Wait for more than 4.1 ms | // 4.1 ms us writing DATA into DDRAM or CGRAM
// +---------------------------+
// |
// +---------------------------+
// | RS R/W DB7 DB6 DB5 DB4 |
// | 0 0 0 0 1 1 | // Initial sequence 0x30
// | Wait for more than 0.1 ms | // 100 us writing DATA into DDRAM or CGRAM
// +---------------------------+
// |
// +---------------------------+
// | RS R/W DB7 DB6 DB5 DB4 | // Initial sequence 0x30
// | 0 0 0 0 1 1 | // 37 us writing DATA into DDRAM or CGRAM 4 us tadd - time after busy flag disapeared
// | Wait for more than 45 us | // 37 us + 4 us = 41 us * (270/250) = 45us
// +---------------------------+
// |
// +---------------------------+ // 4bit mode 0x20 !!! MUST BE SET TIME, BF CHECK DOESN'T WORK CORRECTLY !!!
// | RS R/W DB7 DB6 DB5 DB4 | //
// | 0 0 0 0 1 0 | // 37 us writing DATA into DDRAM or CGRAM 4 us tadd - time after busy flag disapeared
// | Wait for more than 45 us | // !!! MUST BE SET DELAY TIME, BUSY FLAG CHECK DOESN'T WORK CORRECTLY !!!
// +---------------------------+
// |
// +---------------------------+
// | RS R/W DB7 DB6 DB5 DB4 | // Display off 0x08
// | 0 0 0 0 1 0 | //
// | 0 0 1 0 0 0 | //
// | Wait for BF Cleared | // Wait for BF Cleared
// +---------------------------+
// |
// +---------------------------+
// | RS R/W DB7 DB6 DB5 DB4 | // Display clear 0x01
// | 0 0 0 0 0 0 | //
// | 0 0 0 0 0 1 | //
// | Wait for BF Cleared | // Wait for BF Cleared
// +---------------------------+
// |
// +---------------------------+
// | RS R/W DB7 DB6 DB5 DB4 | // Entry mode set 0x06
// | 0 0 0 0 0 0 | //
// | 0 0 0 1 1 0 | // shift cursor to the left, without text shifting
// | Wait for BF Cleared | // Wait for BF Cleared
// +---------------------------+
/**
* @desc LCD display clear
*
* @param void
*
* @return void
*/
void HD44780_DisplayClear (void)
{
// Diplay clear
HD44780_SendInstruction(HD44780_DISP_CLEAR);
}
/**
* @desc LCD display on
*
* @param void
*
* @return void
*/
void HD44780_DisplayOn (void)
{
// send instruction - display on
HD44780_SendInstruction(HD44780_DISP_ON);
}
/**
* @desc LCD cursor on, display on
*
* @param void
*
* @return void
*/
void HD44780_CursorOn (void)
{
// send instruction - cursor on
HD44780_SendInstruction(HD44780_CURSOR_ON);
}
/**
* @desc LCD cursor off
*
* @param void
*
* @return void
*/
void HD44780_CursorOff (void)
{
// send instruction - cursor on
HD44780_SendInstruction(HD44780_CURSOR_OFF);
}
/**
* @desc LCD cursor blink, cursor on, display on
*
* @param void
*
* @return void
*/
void HD44780_CursorBlink (void)
{
// send instruction - Cursor blink
HD44780_SendInstruction(HD44780_CURSOR_BLINK);
}
/**
* @desc LCD draw char
*
* @param char
*
* @return void
*/
void HD44780_DrawChar (char character)
{
// Diplay clear
HD44780_SendData(character);
}
/**
* @desc LCD draw string
*
* @param char *
*
* @return void
*/
void HD44780_DrawString (char *str)
{
unsigned char i = 0;
// loop through 5 bytes
while (str[i] != '\0') {
//read characters and increment index
HD44780_SendData(str[i++]);
}
}
/**
* @desc Got to position x,y
*
* @param char
* @param char
*
* @return char
*/
char HD44780_PositionXY (char x, char y)
{
if (x > HD44780_COLS || y > HD44780_ROWS) {
// error
return ERROR;
}
// check which row
if (y == 0) {
// send instruction 1st row
HD44780_SendInstruction(HD44780_POSITION | (HD44780_ROW1_START + x));
} else if (y == 1) {
// send instruction 2nd row
HD44780_SendInstruction(HD44780_POSITION | (HD44780_ROW2_START + x));
}
// success
return 0;
}
/**
* @desc Shift cursor / display to left / right
*
* @param char item {HD44780_CURSOR; HD44780_DISPLAY}
* @param char direction {HD44780_RIGHT; HD44780_LEFT}
*
* @return char
*/
char HD44780_Shift (char item, char direction)
{
// check if item is cursor or display or direction is left or right
if ((item != HD44780_DISPLAY) && (item != HD44780_CURSOR)) {
// error
return ERROR;
}
// check if direction is left or right
if ((direction != HD44780_RIGHT) && (direction != HD44780_LEFT)) {
// error
return ERROR;
}
// cursor shift
if (item == HD44780_CURSOR) {
// right shift
if (direction == HD44780_RIGHT) {
// shit cursor / display to right / left
HD44780_SendInstruction(HD44780_SHIFT | HD44780_CURSOR | HD44780_RIGHT);
} else {
// shit cursor / display to right / left
HD44780_SendInstruction(HD44780_SHIFT | HD44780_CURSOR | HD44780_LEFT);
}
// display shift
} else {
// right shift
if (direction == HD44780_RIGHT) {
// shit cursor / display to right / left
HD44780_SendInstruction(HD44780_SHIFT | HD44780_DISPLAY | HD44780_RIGHT);
} else {
// shit cursor / display to right / left
HD44780_SendInstruction(HD44780_SHIFT | HD44780_DISPLAY | HD44780_LEFT);
}
}
// success
return 0;
}
/**
* @desc LCD init - initialisation routine
*
* @param void
*
* @return void
*/
void HD44780_Init (void)
{
// set E as output
//SETBIT(HD44780_DDR_E, HD44780_E);
// set RS as output
//SETBIT(HD44780_DDR_RS, HD44780_RS);
// set RW as output
//SETBIT(HD44780_DDR_RW, HD44780_RW);
// set DB7-DB4 as output
// HD44780_SetDDR_DATA4to7();
// clear RS
// CLRBIT(HD44780_PORT_RS, HD44780_RS);
LCD_RS_PIN = 0;
// clear RW
// CLRBIT(HD44780_PORT_RW, HD44780_RW);
LCD_RW_PIN = 0;
// clear E
// CLRBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 0;
// delay > 15ms
_delay_ms(16);
// Busy Flag (BF) cannot be checked in these instructions
// ---------------------------------------------------------------------
// Initial sequence 0x30 - send 4 bits in 4 bit mode
// HD44780_SendInstruction(HD44780_INIT_SEQ);JFM
HD44780_Send4bitsIn4bitMode(HD44780_INIT_SEQ);
// delay > 4.1ms
_delay_ms(5);
// pulse E
//HD44780_PulseE();
// delay > 100us
_delay_us(110);
HD44780_Send4bitsIn4bitMode(HD44780_INIT_SEQ);
// pulse E
// HD44780_PulseE();
// delay > 45us (=37+4 * 270/250)
_delay_us(50);
HD44780_Send4bitsIn4bitMode(HD44780_INIT_SEQ);
// 4 bit mode 0x20 - send 4 bits in 4 bit mode
HD44780_Send4bitsIn4bitMode(HD44780_4BIT_MODE);
// pulse E
//HD44780_PulseE();
// delay > 45us (=37+4 * 270/250)
_delay_us(50);
// ----------------------------------------------------------------------
// 4-bit & 2-lines & 5x8-dots 0x28 - send 8 bits in 4 bit mode
HD44780_SendInstruction(HD44780_4BIT_MODE | HD44780_2_ROWS | HD44780_FONT_5x8);
// display off 0x08 - send 8 bits in 4 bit mode
HD44780_SendInstruction(HD44780_DISP_OFF);
// display clear 0x01 - send 8 bits in 4 bit mode
HD44780_SendInstruction(HD44780_DISP_CLEAR);
// entry mode set 0x06 - send 8 bits in 4 bit mode
HD44780_SendInstruction(HD44780_ENTRY_MODE);
}
/**
* @desc Check Busy Flag (BF) in 4 bit mode
*
* @param void
*
* @return void
*/
void HD44780_CheckBFin4bitMode (void)
{
unsigned char input = 0;
Sleep(5);
return;
// clear DB7-DB4 as input
HD44780_ClearDDR_DATA4to7();
// set pull-up resistors for DB7-DB4
// HD44780_SetPORT_DATA4to7();
// clear RS
// CLRBIT(HD44780_PORT_RS, HD44780_RS);
LCD_RS_PIN = 0;
// set RW - read instruction
// SETBIT(HD44780_PORT_RW, HD44780_RW);
LCD_RW_PIN = 1;
// test HIGH level on PIN DB7
// after clear PIN DB7 should continue
// -------------------------------------
// us: 0.5|0.5|0.5
// ___ ___
// E: ___/ \___/ \__
// ___ ___
// DB7: \___/ \___/ \__
//
while (1) {
// Read upper nibble
// --------------------------------
// Set E
// SETBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 1;
// PWeh > 0.5us
_delay_us(0.5);
// read upper nibble (tDDR > 360ns)
input = HD44780_PIN_DATA;
// Clear E
// CLRBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 0;
// TcycE > 1000ns -> delay depends on PWeh delay time
// delay = TcycE - PWeh = 1000 - 500 = 500ns
_delay_us(0.5);
// Read lower nibble
// --------------------------------
// Set E
// SETBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 1;
// PWeh > 0.5us
_delay_us(0.5);
// read lower nibble (tDDR > 360ns)
//input |= (unsigned char)(HD44780_PIN_DATA >> 4);
char IsBusy = (HD44780_DATA7 == 1);
// Clear E
// CLRBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 0;
// TcycE > 1000ns -> delay depends on PWeh delay time
// delay = TcycE - PWeh = 1000 - 500 = 500ns
_delay_us(0.5);
// // check if DB7 is cleared
// if (!(input & (1 << HD44780_DATA7)))
if (IsBusy == 0)
{
// if BF cleared -> end loop
break;
}
}
// clear RW
// CLRBIT(HD44780_PORT_RW, HD44780_RW);
LCD_RW_PIN = 0;
// set DB7-DB4 as output
HD44780_SetDDR_DATA4to7();
}
/**
* @desc Check Busy Flag (BF) in 8 bit mode
*
* @param void
* @return void
*/
void HD44780_CheckBFin8bitMode (void)
{
}
/**
* @desc LCD send instruction
*
* @param unsigned short int
*
* @return void
*/
void HD44780_SendInstruction (unsigned short int data)
{
// Clear RS
// HD44780_PORT_RS &= ~(1 << HD44780_RS);
LCD_RS_PIN = 0;
// 4bit mode
// ------------------------------------------
if (HD44780_MODE == HD44780_4BIT_MODE) {
// send required data in required mode
HD44780_Send8bitsIn4bitMode(data);
// check busy flag
HD44780_CheckBFin4bitMode();
// 8 bit mode
// ------------------------------------------
} else if (HD44780_MODE == HD44780_8BIT_MODE) {
// send required data in required mode
HD44780_Send8bitsIn8bitMode(data);
// check busy flag
HD44780_CheckBFin8bitMode();
}
}
/**
* @desc LCD send data
*
* @param unsigned short int
*
* @return void
*/
void HD44780_SendData (unsigned short int data)
{
// Set RS
// SETBIT(HD44780_PORT_RS, HD44780_RS);
LCD_RS_PIN = 1;
// 4bit mode
// ------------------------------------------
if (HD44780_MODE == HD44780_4BIT_MODE) {
// send required data in required mode
HD44780_Send8bitsIn4bitMode(data);
// check busy flag
HD44780_CheckBFin4bitMode();
// 8 bit mode
// ------------------------------------------
} else if (HD44780_MODE == HD44780_8BIT_MODE) {
// send required data in required mode
HD44780_Send8bitsIn8bitMode(data);
// check busy flag
HD44780_CheckBFin8bitMode();
}
// Clear RS
// CLRBIT(HD44780_PORT_RS, HD44780_RS);
LCD_RS_PIN = 0;
}
/**
* @desc LCD send 4bits instruction in 4 bit mode
*
* @param unsigned short int
*
* @return void
*/
void HD44780_Send4bitsIn4bitMode (unsigned short int data)
{
// Set E
// SETBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 1;
// send data to LCD
HD44780_SetUppNibble(data);
// PWeh delay time > 450ns
_delay_us(0.5);
// Clear E
// CLRBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 0;
// TcycE > 1000ns -> delay depends on PWeh delay time
// delay = TcycE - PWeh = 1000 - 500 = 500ns
_delay_us(0.5);
}
/**
* @desc LCD send 8bits instruction in 4 bit mode
*
* @param unsigned short int
*
* @return void
*/
void HD44780_Send8bitsIn4bitMode (unsigned short int data)
{
// Send upper nibble
// ----------------------------------
// Set E
// SETBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 1;
// send data to LCD
HD44780_SetUppNibble(data);
// PWeh delay time > 450ns
_delay_us(0.5);
// Clear E
// CLRBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 0;
// TcycE > 1000ns -> delay depends on PWeh delay time
// delay = TcycE - PWeh = 1000 - 500 = 500ns
_delay_us(0.5);
// Send lower nibble
// ----------------------------------
// Set E
// SETBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 1;
// send data to LCD
HD44780_SetUppNibble(data << 4);
// PWeh delay time > 450ns
_delay_us(0.5);
// Clear E
// CLRBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 0;
// TcycE > 1000ns -> delay depends on PWeh delay time
// delay = TcycE - PWeh = 1000 - 500 = 500ns
_delay_us(0.5);
}
/**
* @desc LCD send 8bits instruction in 8 bit mode
*
* @param unsigned short int
*
* @return void
*/
void HD44780_Send8bitsIn8bitMode (unsigned short int data)
{
// Set E
SETBIT(HD44780_PORT_E, HD44780_E);
// send data to LCD
HD44780_SetUppNibble(data);
// send data to LCD
HD44780_SetLowNibble(data);
// PWeh delay time > 450ns
_delay_us(0.5);
// Clear E
CLRBIT(HD44780_PORT_E, HD44780_E);
// TcycE > 1000ns -> delay depends on PWeh delay time
// delay = TcycE - PWeh = 1000 - 500 = 500ns
_delay_us(0.5);
}
/**
* @desc LCD send upper nibble
*
* @param unsigned short int
*
* @return void
*/
void HD44780_SetUppNibble (unsigned short int data)
{
// LCD_DB4_PIN = 0;
// LCD_DB5_PIN = 0;
// LCD_DB6_PIN = 0;
// LCD_DB7_PIN = 0;
unsigned short test = data;
if (data & 0x80)
{
LCD_DB7_PIN = 1;
}
else
{
LCD_DB7_PIN = 0;
}
if (data & 0x40)
{
LCD_DB6_PIN = 1;
}
else
{
LCD_DB6_PIN = 0;
}
if (data & 0x20)
{
LCD_DB5_PIN = 1;
}
else
{
LCD_DB5_PIN = 0;
}
if (data & 0x10)
{
LCD_DB4_PIN = 1;
}
else
{
LCD_DB4_PIN = 0;
}
// clear bits DB7-DB4
// CLRBIT(HD44780_PORT_DATA, HD44780_DATA7);
// CLRBIT(HD44780_PORT_DATA, HD44780_DATA6);
// CLRBIT(HD44780_PORT_DATA, HD44780_DATA5);
// CLRBIT(HD44780_PORT_DATA, HD44780_DATA4);
// // set DB7-DB4 if corresponding bit is set
// if (data & 0x80) { SETBIT(HD44780_PORT_DATA, HD44780_DATA7); }
// if (data & 0x40) { SETBIT(HD44780_PORT_DATA, HD44780_DATA6); }
// if (data & 0x20) { SETBIT(HD44780_PORT_DATA, HD44780_DATA5); }
// if (data & 0x10) { SETBIT(HD44780_PORT_DATA, HD44780_DATA4); }
}
/**
* @desc LCD send lower nibble
*
* @param unsigned short int
*
* @return void
*/
void HD44780_SetLowNibble (unsigned short int data)
{
// clear bits DB7-DB4
CLRBIT(HD44780_PORT_DATA, HD44780_DATA3);
CLRBIT(HD44780_PORT_DATA, HD44780_DATA2);
CLRBIT(HD44780_PORT_DATA, HD44780_DATA1);
CLRBIT(HD44780_PORT_DATA, HD44780_DATA0);
// set DB7-DB4 if corresponding bit is set
if (data & 0x08) { SETBIT(HD44780_PORT_DATA, HD44780_DATA3); }
if (data & 0x04) { SETBIT(HD44780_PORT_DATA, HD44780_DATA2); }
if (data & 0x02) { SETBIT(HD44780_PORT_DATA, HD44780_DATA1); }
if (data & 0x01) { SETBIT(HD44780_PORT_DATA, HD44780_DATA0); }
}
/**
* @desc LCD pulse E
*
* @param void
*
* @return void
*/
void HD44780_PulseE (void)
{
// Set E
// SETBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 1;
// PWeh delay time > 450ns
_delay_us(0.5);
// Clear E
// CLRBIT(HD44780_PORT_E, HD44780_E);
LCD_E_PIN = 0;
// TcycE > 1000ns -> delay depends on PWeh delay time
// delay = TcycE - PWeh = 1000 - 500 = 500ns
_delay_us(0.5);
}
/**
* @desc Set PORT DB4 to DB7
*
* @param void
*
* @return void
*/
void HD44780_SetPORT_DATA4to7 (void)
{
// set DB4-DB7
LCD_DB4_PIN = 1;
LCD_DB5_PIN = 1;
LCD_DB6_PIN = 1;
LCD_DB7_PIN = 1;
// SETBIT(HD44780_PORT_DATA, HD44780_DATA4);
// SETBIT(HD44780_PORT_DATA, HD44780_DATA5);
// SETBIT(HD44780_PORT_DATA, HD44780_DATA6);
// SETBIT(HD44780_PORT_DATA, HD44780_DATA7);
}
/**
* @desc Clear DDR DB4 to DB7
*
* @param void
*
* @return void
*/
void HD44780_SetDDR_DATA4to7 (void)
{
// set DB4-DB7
LCD_DB4_PIN_DIR = PIN_OUTPUT;
LCD_DB5_PIN_DIR = PIN_OUTPUT;
LCD_DB6_PIN_DIR = PIN_OUTPUT;
LCD_DB7_PIN_DIR = PIN_OUTPUT;
// CLRBIT(HD44780_DDR_DATA, HD44780_DATA4);
// CLRBIT(HD44780_DDR_DATA, HD44780_DATA5);
// CLRBIT(HD44780_DDR_DATA, HD44780_DATA6);
// CLRBIT(HD44780_DDR_DATA, HD44780_DATA7);
}
/**
* @desc Set DDR DB4 to DB7
*
* @param void
*
* @return void
*/
void HD44780_ClearDDR_DATA4to7 (void)
{
LCD_DB4_PIN_DIR = PIN_INPUT;
LCD_DB5_PIN_DIR = PIN_INPUT;
LCD_DB6_PIN_DIR = PIN_INPUT;
LCD_DB7_PIN_DIR = PIN_INPUT;
// SETBIT(HD44780_DDR_DATA, HD44780_DATA4);
// SETBIT(HD44780_DDR_DATA, HD44780_DATA5);
// SETBIT(HD44780_DDR_DATA, HD44780_DATA6);
// SETBIT(HD44780_DDR_DATA, HD44780_DATA7);
}