MIKR/lectures/LCD_lib/HD44780.c

590 lines
12 KiB
C
Raw Permalink Normal View History

2024-04-16 20:18:18 +02:00
#include "HD44780.h"
//----- Auxiliary data --------------------------//
#define _Byte2Ascii(Value) (Value = Value + '0')
#if (LCD_Size == 801)
#define __LCD_Rows 1
#define __LCD_Columns 8
#define __LCD_LineStart_1 0x00
#elif (LCD_Size == 802)
#define __LCD_Rows 2
#define __LCD_Columns 8
#define __LCD_LineStart_1 0x00
#define __LCD_LineStart_2 0x40
#elif (LCD_Size == 1601)
#define __LCD_Rows 1
#define __LCD_Columns 16
#define __LCD_LineStart_1 0x00
#if (LCD_Type == B)
#define __LCD_LINESTART_1B 0x40
#endif
#elif (LCD_Size == 1602)
#define __LCD_Rows 2
#define __LCD_Columns 16
#define __LCD_LineStart_1 0x00
#define __LCD_LineStart_2 0x40
#elif (LCD_Size == 1604)
#define __LCD_Rows 4
#define __LCD_Columns 16
#define __LCD_LineStart_1 0x00
#define __LCD_LineStart_2 0x40
#define __LCD_LineStart_3 0x10
#define __LCD_LineStart_4 0x50
#elif (LCD_Size == 2001)
#define __LCD_Rows 1
#define __LCD_Columns 20
#define __LCD_LineStart_1 0x00
#elif (LCD_Size == 2002)
#define __LCD_Rows 2
#define __LCD_Columns 20
#define __LCD_LineStart_1 0x00
#define __LCD_LineStart_2 0x40
#elif (LCD_Size == 2004)
#define __LCD_Rows 4
#define __LCD_Columns 20
#define __LCD_LineStart_1 0x00
#define __LCD_LineStart_2 0x40
#define __LCD_LineStart_3 0x14
#define __LCD_LineStart_4 0x54
#elif (LCD_Size == 4001)
#define __LCD_Rows 1
#define __LCD_Columns 40
#define __LCD_LineStart_1 0x00
#elif (LCD_Size == 4002)
#define __LCD_Rows 2
#define __LCD_Columns 40
#define __LCD_LineStart_1 0x00
#define __LCD_LineStart_2 0x40
#endif
//-----------------------------------------------//
//----- Prototypes ----------------------------//
static void LCD_SendCommandHigh(uint8_t Command);
static void LCD_Send(uint8_t Data);
static uint8_t LCD_Read(void);
static inline void Pulse_En(void);
static void Int2bcd(int32_t Value, char *BCD);
//---------------------------------------------//
//----- Functions -------------//
//Setup LCD.
void LCD_Setup(void)
{
//LCD pins = Outputs
PinMode(LCD_D4, Output);
PinMode(LCD_D5, Output);
PinMode(LCD_D6, Output);
PinMode(LCD_D7, Output);
PinMode(LCD_RS, Output);
PinMode(LCD_RW, Output);
PinMode(LCD_EN, Output);
//LCD pins = 0
DigitalWrite(LCD_D4, Low);
DigitalWrite(LCD_D5, Low);
DigitalWrite(LCD_D6, Low);
DigitalWrite(LCD_D7, Low);
DigitalWrite(LCD_RS, Low);
DigitalWrite(LCD_RW, Low);
DigitalWrite(LCD_EN, Low);
//----- Soft reset -----
//1. Wait for more than 15ms
_delay_ms(__LCD_Delay_1);
//2. Command 32: LCD 8-bit mode
LCD_SendCommandHigh(__LCD_CMD_FunctionSet | __LCD_CMD_8BitMode);
//3. Wait for more than 4.1ms
_delay_ms(__LCD_Delay_2);
//4. Command 32: LCD 8-bit mode
LCD_SendCommandHigh(__LCD_CMD_FunctionSet | __LCD_CMD_8BitMode);
//5. Wait for more than 100us
_delay_ms(__LCD_Delay_3);
//6. Command 32: LCD 8-bit mode, for the 3rd time
LCD_SendCommandHigh(__LCD_CMD_FunctionSet | __LCD_CMD_8BitMode);
//7. Wait for more than 100us
_delay_ms(__LCD_Delay_4);
//----- Initialization -----
//1. Command 32: LCD mode
LCD_SendCommandHigh(__LCD_CMD_FunctionSet | __LCD_CMD_4BitMode);
//2. Command 32: LCD mode and size
LCD_SendCommand(__LCD_CMD_FunctionSet | __LCD_CMD_4BitMode | __LCD_CMD_2Line | __LCD_CMD_5x8Dots);
//3. Command 8: Display On, Cursor off, Blinking Off
LCD_SendCommand(__LCD_CMD_DisplayControl | __LCD_CMD_DisplayOn | __LCD_CMD_CursorOff | __LCD_CMD_BlinkOff);
//4. Command 4: Auto increment, No shifting
LCD_SendCommand(__LCD_CMD_EntryModeSet | __LCD_CMD_EntryIncrement | __LCD_CMD_EntryNoShift);
//5. Command 1: Clear display, cursor at home
LCD_SendCommand(__LCD_CMD_ClearDisplay);
}
//Send command to LCD.
void LCD_SendCommand(uint8_t Command)
{
LCD_WaitBusy();
DigitalWrite(LCD_RS, Low);
LCD_Send(Command);
}
//Send data to LCD.
void LCD_SendData(char c)
{
LCD_WaitBusy();
DigitalWrite(LCD_RS, High);
LCD_Send((uint8_t)(c));
}
//Wait until busy flag is cleared.
void LCD_WaitBusy(void)
{
uint8_t busy = 0;
PinMode(LCD_D4, Input); //D7:D4 = Inputs
PinMode(LCD_D5, Input);
PinMode(LCD_D6, Input);
PinMode(LCD_D7, Input);
DigitalWrite(LCD_RS, Low); //RS=0
DigitalWrite(LCD_RW, High); //RW=1
do
{
//High nibble comes first
DigitalWrite(LCD_EN, High);
_delay_us(__LCD_Pulse_us);
busy &= ~(1<<__LCD_BusyFlag);
busy |= (DigitalRead(LCD_D7)<<__LCD_BusyFlag);
DigitalWrite(LCD_EN, Low);
//Low nibble follows
Pulse_En();
}
while(BitCheck(busy, __LCD_BusyFlag));
PinMode(LCD_D4, Output); //D7:D4 = Outputs
PinMode(LCD_D5, Output);
PinMode(LCD_D6, Output);
PinMode(LCD_D7, Output);
DigitalWrite(LCD_RW, Low); //RW = 0
}
//Build character in LCD CGRAM from data in SRAM.
void LCD_BuildChar(char *Data, uint8_t Position)
{
if (Position < 0)
return;
if (Position >= 8)
return;
Point_t p = LCD_GetP();
uint8_t i;
//Every character in CGRAM needs 8bytes
LCD_SendCommand(__LCD_CMD_SetCGRAMAddress | (Position<<3));
//Save the character byte-by-byte
for (i = 0 ; i < 8 ; i++)
LCD_SendData(Data[i]);
//Return to the DDRAM position
LCD_GotoXY(p.X, p.Y);
}
//Build character in LCD CGRAM from data in Flash memory.
void LCD_BuildChar_P(const char *Data, uint8_t Position)
{
if (Position < 0)
return;
if (Position >= 8)
return;
Point_t p = LCD_GetP();
uint8_t i;
//Every character in CGRAM needs 8bytes
LCD_SendCommand(__LCD_CMD_SetCGRAMAddress | (Position<<3));
//Save the character byte-by-byte
for (i = 0 ; i < 8 ; i++)
LCD_SendData(pgm_read_byte(Data[i]));
//Return to the DDRAM position
LCD_GotoXY(p.X, p.Y);
}
//Clear display.
void LCD_Clear(void)
{
LCD_SendCommand(__LCD_CMD_ClearDisplay);
}
//Clear line.
void LCD_ClearLine(uint8_t Line)
{
uint8_t i = 0;
LCD_GotoXY(0, Line);
while(i <= __LCD_Columns)
{
LCD_SendData(' ');
i++;
}
}
//Go to specified position.
void LCD_GotoXY(uint8_t X, uint8_t Y)
{
if ((X < __LCD_Columns) && (Y < __LCD_Rows))
{
uint8_t addr = 0;
switch (Y)
{
#if ((defined(__LCD_LineStart_4)) || (defined(__LCD_LineStart_3)) || (defined(__LCD_LineStart_2)) || (defined(__LCD_LineStart_1)))
case (0):
addr = __LCD_LineStart_1;
#if ((LCD_Size == 1601) && (LCD_Type == B))
if (X >= (__LCD_Columns>>1))
{
X -= __LCD_Columns>>1;
addr = __LCD_LINESTART_1B;
}
#endif
break;
#endif
#if ((defined(__LCD_LineStart_4)) || (defined(__LCD_LineStart_3)) || (defined(__LCD_LineStart_2)))
case (1):
addr = __LCD_LineStart_2;
break;
#endif
#if ((defined(__LCD_LineStart_4)) || (defined(__LCD_LineStart_3)))
case (2):
addr = __LCD_LineStart_3;
break;
#endif
#if (defined(__LCD_LineStart_4))
case (3):
addr = __LCD_LineStart_4;
break;
#endif
}
addr = __LCD_CMD_SetDDRAMAddress | (addr | X);
LCD_SendCommand(addr);
}
}
//Get current position.
Point_t LCD_GetP(void)
{
Point_t p;
p.X = LCD_Read();
p.Y = 0;
#if (__LCD_Rows == 1)
#if ((LCD_Size == 1601) && (LCD_Type == B))
if (p.X >= __LCD_LINESTART_1B)
p.X = p.X - __LCD_LINESTART_1B + (__LCD_Columns>>1);
#endif
#elif (__LCD_Rows == 2)
if (p.X >= __LCD_LineStart_2)
{
p.X -= __LCD_LineStart_2;
p.Y = 1;
}
#elif (__LCD_Rows == 3)
if (p.X >= __LCD_LineStart_2)
{
p.X -= __LCD_LineStart_2;
p.Y = 1;
}
else if (p.X >= __LCD_LineStart_3)
{
p.X -= __LCD_LineStart_3;
p.Y = 2;
}
#elif (__LCD_Rows == 4)
if (p.X >= __LCD_LineStart_4)
{
p.X -= __LCD_LineStart_4;
p.Y = 3;
}
else if (p.X >= __LCD_LineStart_2)
{
p.X -= __LCD_LineStart_2;
p.Y = 1;
}
else if (p.X >= __LCD_LineStart_3)
{
p.X -= __LCD_LineStart_3;
p.Y = 2;
}
#endif
return p;
}
//Get X position.
uint8_t LCD_GetX(void)
{
return LCD_GetP().X;
}
//Get Y position.
uint8_t LCD_GetY(void)
{
return LCD_GetP().Y;
}
//Print character.
void LCD_PrintChar(char Character)
{
LCD_SendData(Character);
}
//Print string from SRAM.
void LCD_PrintString(char *Text)
{
while(*Text)
LCD_SendData(*Text++);
}
//Print string from Flash memory.
void LCD_PrintString_P(const char *Text)
{
char r = pgm_read_byte(Text++);
while(r)
{
LCD_SendData(r);
r = pgm_read_byte(Text++);
}
}
//Print integer.
void LCD_PrintInteger(int32_t Value)
{
if (Value == 0 )
{
LCD_PrintChar('0');
}
else if ((Value > INT32_MIN ) && (Value <= INT32_MAX))
{
//int32_max + sign + null = 12 bytes
char arr[12] = { '\0' };
//Convert integer to array (returns in reversed order)
Int2bcd(Value, arr);
//Print
LCD_PrintString(arr);
}
}
//Print double.
void LCD_PrintDouble(double Value, uint32_t Tens)
{
if (Value == 0)
{
//Print characters individually so no string is stored into RAM.
LCD_PrintChar('0');
LCD_PrintChar('.');
LCD_PrintChar('0');
}
else if ((Value >= (-2147483647)) && (Value < 2147483648))
{
//Print sign
if (Value < 0)
{
Value = -Value;
LCD_PrintChar('-');
}
//Print integer part
LCD_PrintInteger(Value);
//Print dot
LCD_PrintChar('.');
//Print decimal part
LCD_PrintInteger((Value - (uint32_t)(Value)) * Tens);
}
}
//Send only high nibble to LCD.
static void LCD_SendCommandHigh(uint8_t Data)
{
DigitalWrite(LCD_RS, Low);
//Send the high nibble
DigitalWrite(LCD_D4, BitCheck(Data, 4));
DigitalWrite(LCD_D5, BitCheck(Data, 5));
DigitalWrite(LCD_D6, BitCheck(Data, 6));
DigitalWrite(LCD_D7, BitCheck(Data, 7));
Pulse_En();
}
//Send data to LCD.
static void LCD_Send(uint8_t Data)
{
//Send the high nibble
DigitalWrite(LCD_D4, BitCheck(Data, 4));
DigitalWrite(LCD_D5, BitCheck(Data, 5));
DigitalWrite(LCD_D6, BitCheck(Data, 6));
DigitalWrite(LCD_D7, BitCheck(Data, 7));
Pulse_En();
//Low nibble comes after
DigitalWrite(LCD_D4, BitCheck(Data, 0));
DigitalWrite(LCD_D5, BitCheck(Data, 1));
DigitalWrite(LCD_D6, BitCheck(Data, 2));
DigitalWrite(LCD_D7, BitCheck(Data, 3));
Pulse_En();
}
//Read status from LCD.
static uint8_t LCD_Read(void)
{
uint8_t status = 0;
LCD_WaitBusy();
PinMode(LCD_D4, Input); //D7:D4 = Inputs
PinMode(LCD_D5, Input);
PinMode(LCD_D6, Input);
PinMode(LCD_D7, Input);
DigitalWrite(LCD_RS, Low); //RS = 0
DigitalWrite(LCD_RW, High); //RW = 1
//High nibble comes first
DigitalWrite(LCD_EN, High);
_delay_us(__LCD_Pulse_us);
status |= DigitalRead(LCD_D4)<<4;
status |= DigitalRead(LCD_D5)<<5;
status |= DigitalRead(LCD_D6)<<6;
DigitalWrite(LCD_EN, Low);
//Low nibble follows
DigitalWrite(LCD_EN, High);
_delay_us(__LCD_Pulse_us);
status |= DigitalRead(LCD_D4);
status |= DigitalRead(LCD_D5)<<1;
status |= DigitalRead(LCD_D6)<<2;
status |= DigitalRead(LCD_D7)<<3;
DigitalWrite(LCD_EN, Low);
PinMode(LCD_D4, Output); //D7:D4 = Outputs
PinMode(LCD_D5, Output);
PinMode(LCD_D6, Output);
PinMode(LCD_D7, Output);
DigitalWrite(LCD_RW, Low); //RW = 0
return status;
}
//Sends pulse to PIN_EN of LCD.
static inline void Pulse_En(void)
{
DigitalWrite(LCD_EN, High);
_delay_us(__LCD_Pulse_us);
DigitalWrite(LCD_EN, Low);
}
//Converts integer value to BCD.
static void Int2bcd(int32_t Value, char BCD[])
{
uint8_t isNegative = 0;
BCD[0] = BCD[1] = BCD[2] =
BCD[3] = BCD[4] = BCD[5] =
BCD[6] = BCD[7] = BCD[8] =
BCD[9] = BCD[10] = '0';
if (Value < 0)
{
isNegative = 1;
Value = -Value;
}
while (Value > 1000000000)
{
Value -= 1000000000;
BCD[1]++;
}
while (Value >= 100000000)
{
Value -= 100000000;
BCD[2]++;
}
while (Value >= 10000000)
{
Value -= 10000000;
BCD[3]++;
}
while (Value >= 1000000)
{
Value -= 1000000;
BCD[4]++;
}
while (Value >= 100000)
{
Value -= 100000;
BCD[5]++;
}
while (Value >= 10000)
{
Value -= 10000;
BCD[6]++;
}
while (Value >= 1000)
{
Value -= 1000;
BCD[7]++;
}
while (Value >= 100)
{
Value -= 100;
BCD[8]++;
}
while (Value >= 10)
{
Value -= 10;
BCD[9]++;
}
while (Value >= 1)
{
Value -= 1;
BCD[10]++;
}
uint8_t i = 0;
//Find first non zero digit
while (BCD[i] == '0')
i++;
//Add sign
if (isNegative)
{
i--;
BCD[i] = '-';
}
//Shift array
uint8_t end = 10 - i;
uint8_t offset = i;
i = 0;
while (i <= end)
{
BCD[i] = BCD[i + offset];
i++;
}
BCD[i] = '\0';
}
//-----------------------------//