Created
February 16, 2019 08:32
-
-
Save OrsoEric/486385295cd1c3e9c0b1514892ad2eda to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**************************************************************************** | |
** ORANGEBOT PROJECT | |
***************************************************************************** | |
** Library to make use of lcd display in 4bit or 8bit mode | |
** The library is interrupt based to reduce the MPU consumption | |
***************************************************************************** | |
** Author: Orso Eric | |
** Creation Date: | |
** Last Edit Date: | |
** Revision: | |
** Version: 3.0 | |
****************************************************************************/ | |
/**************************************************************************** | |
** HYSTORY VERSION | |
***************************************************************************** | |
** V3.0 | |
** >Added detailed description for the LCD DISPLAY hardware, timing and instructions | |
** >Cleaned function names from library V2.0 | |
** >Now the lcd_init has bit description of the initialization commands for the display | |
** To edit the settings you just need to uncomment or comment the right SET_BIT option | |
** Much like all my others init function for ATMEL works | |
** >Tested the init function in 4BIT_MSB mode (my bodule) | |
** pin and timing defines, lcd_send_cmd, lcd_init | |
** >Added lcd_error var, keep track of library error status. Default state is LCD_OK | |
** >Tested the lcd_print_char. The function now write into lcd_show, and rise a lcd_updt flag | |
** >Updated and tested LCD_BUF macros. Now i store bot and top at the end of the data vector | |
** >Rewritten and tested lcd_update(), the core function. It works like a charm! The refresh rate is majestic! | |
** A char take 12 lcd_update() calls for random access or 6 calls for sequential access | |
** >CleanUp: I was able to remove the command queque altogether | |
****************************************************************************/ | |
/**************************************************************************** | |
** USED PIN | |
** TIPS: compile this field before writing any code: it really helps! | |
***************************************************************************** | |
** >RS (Register Select) | |
** >RW (Read/#Write) | |
** >E (Enable) | |
** >D4 - D7 | |
****************************************************************************/ | |
/**************************************************************************** | |
** LCD DISPLAY | |
***************************************************************************** | |
** DATASHEET | |
** DISPLAY : CDM1602K | |
** CONTROLLER : KS0066U | |
** PINS | |
** >RS (Register Select) | |
** '0' | I'm sending an instruction | |
** '1' | I'm sending a data | |
** >RW (Read/#Write) | |
** '0' | I'm writing to the LCD | |
** '1' | I'm reading from the LCD (This driver will not perform reads) | |
** >E (Enable) | |
** Usually '0' | |
** Strobe '0'->'1'->'0' will execute an operation | |
** >D0 - D7 | |
** Bidirectionall data bus | |
** The LCD can operate in 4 bit mode, using only D4-D7 (This driver will only handle 4bit mode) | |
***************************************************************************** | |
** WRITE INSTRUCTIONS (R/#W = '0') | |
** Most instruction are fast, they take about 40uS. Slow instruction 1.5mS are marked | |
** RS | D0 D1 D2 D3 D4 D5 D6 D7 | Instruction Name and Details | |
** -------------------------------------------------------------------------- | |
** 0 | 1 0 0 0 0 0 0 0 | 0) Clean LCD (slow) - reset the content | |
** 0 | / 1 0 0 0 0 0 0 | 1) Reset cursor position, Reset display shift (Slow) | |
** 0 | S D 1 0 0 0 0 0 | 2) Set shift, Set cursor direction | |
** | S = '1' -> Shift | |
** | D = '1' -> Increment | |
** 0 | B C E 1 0 0 0 0 | 3) Enable Show Display, Show Cursor, Cursor Blink | |
** | B = '1' -> Cursor will blink | |
** | C = '1' -> Show Cursor | |
** | E = '1' -> Display ON (content will be shown | |
** 0 | / / F D 1 0 0 0 | 4) Cursor working (NOTE: not sure about this command) | |
** | S = '1' -> cursor will shift display | '0' normal operation | |
** | D = '1' -> advance backward | |
** 0 | / / F N W 1 0 0 | 5) Font type, Bus Width | |
** | F = '1' -> Font 5x10 | '0' -> Font 5x7 | |
** | N = '1' -> 2 lines | '0' -> 1 line | |
** | W = '1' -> 8bit mode | '0' -> 4bit mode D4-D7 | |
** 0 | A0 A1 A2 A3 A4 A5 1 0 | 6) Set CGRAM address (Charater Generator RAM) (Font) | |
** 0 | A0 A1 A2 A3 A4 A5 A6 1 | 7) Set DDRAM address (Data Display RAM) | |
** 1 | D0 D1 D2 D3 D4 D5 D6 D7 | Write data to CGRAM or DDRAM | |
** | Decided by the most recent Set Address operation | |
** | Entry mode will define the cursor address after the operation | |
***************************************************************************** | |
** 4BIT OPERATION | |
** the display will only use its 4 to 7 bit of the data port | |
** First send bit 4 to 7 (high), strobe EN, then send bit 0 to 3 (low) | |
** Send Command 5 first. Special care has to be taken, since the default mode is 8bit | |
** Special care has to be | |
****************************************************************************/ | |
/**************************************************************************** | |
** DESCRIPTION | |
***************************************************************************** | |
** >Print functions update lcd_show and lcd_update vectors | |
** >The driver will take care of syncing vectors with the LCD DISPLAY itself | |
** >The user must call the display_init function to setup the display | |
** >The user must call the display_update function at a fixed rate | |
** that is the function sync the vectors with the content | |
** >I only use 4bit mode | |
****************************************************************************/ | |
/**************************************************************************** | |
** KNOWN BUG | |
***************************************************************************** | |
** >BUG Architecture (SOLVED) | |
** The version V2 of the library had quequeing problems, since print calls | |
** directly quequed command for the display, if I called too many prints, | |
** the buffer would overflow. | |
** SOLUTION: Architecture Shift V2 -> V3 | |
** Print calls update the lcd_show vector which contains what the user wants to display | |
** The driver will read from there and send commands to the display at his | |
** defined refresh rate. This way user won't have to worry about calling | |
** too many prints. The function is also a lot more efficient | |
** >BUG Cursor (SOLVED) | |
** When I write in 0,15, the cursor moves to 0,16 (out of screen) NOT 1,0 | |
** SOLUTION: I disable address optimization for position 1,0 only in lcd_update() | |
****************************************************************************/ | |
/**************************************************************************** | |
** USED PIN | |
** TIPS: compile this field before writing any code: it really helps! | |
***************************************************************************** | |
** | |
****************************************************************************/ | |
/**************************************************************************** | |
** INCLUDE | |
****************************************************************************/ | |
#define F_CPU 20000000 | |
#include <stdint.h> | |
#include <avr/io.h> | |
#include <util/delay.h> | |
#include "at_utils.h" | |
#include "at_lcd.h" | |
#include "at_string.h" | |
/**************************************************************************** | |
** GLOBAL VARIABILE | |
****************************************************************************/ | |
//LCD SHOW vector | |
// Print calls will update this vector. Driver will sync this vector with the display itself | |
U8 lcd_show[ LCD_ROW * LCD_COL ]; | |
//LCD CELLS UPDATE | |
// Print will rise the cell bit to '1', signaling the driver that content must be synced | |
U8 lcd_updt[ LCD_UPDT ]; | |
//Configuration flags for the print functions | |
U8 lcd_cfg_flags; | |
//current error code of the library, default state is LCD_OK | |
U8 lcd_error; | |
/**************************************************************************** | |
** FUNCTIONS | |
****************************************************************************/ | |
/**************************************************************************** | |
***************************************************************************** | |
** DRIVER GROUP | |
***************************************************************************** | |
****************************************************************************/ | |
/**************************************************************************** | |
** LCD SEND CMD | |
***************************************************************************** | |
** This function send a command to the LCD display. Use hard wired delay | |
** Use 4 bit mode only | |
****************************************************************************/ | |
void lcd_send_cmd( U8 cmd ) | |
{ | |
//*********************************************************************** | |
// STATIC VARIABILE | |
//*********************************************************************** | |
//*********************************************************************** | |
// LOCAL VARIABILE | |
//*********************************************************************** | |
//*********************************************************************** | |
// BODY | |
//*********************************************************************** | |
// This routine is used to send a command. use hard wired delay. Used only during init | |
//write 4 bit of command to the LCD data port, define decide if low or high bits are written | |
//macro will always use the 4 LSB of the input | |
//Send the 4MSB of cmd first, I move them to LSB for the macro to send, the macro will move them to the appropriate position | |
LCD_WRITE( cmd >> 4 ); | |
//Clear RS: I'm sending an'instruction | |
CLEAR_BIT( RS_PORT, RS_PIN ); | |
//Strobe the enable pin, time specify the delay after each edge | |
STROBE_EN( EN_STROBE_TIME ); | |
//Now send the 4LSB of cmd | |
LCD_WRITE( cmd ); | |
//Strobe the enable pin, time specify the delay after each edge | |
STROBE_EN( EN_STROBE_TIME ); | |
//the slowest command take 1.5ms | |
_delay_ms( LCD_LONG_WAIT ); | |
//*********************************************************************** | |
// RETURN | |
//*********************************************************************** | |
return; | |
} //End function: lcd_send_cmd | |
/**************************************************************************** | |
** LCD INIT | |
***************************************************************************** | |
** This function will initialize the LCD display | |
** SETTINGS | |
** | |
****************************************************************************/ | |
void lcd_init( void ) | |
{ | |
//*********************************************************************** | |
// STATIC VARIABILE | |
//*********************************************************************** | |
//*********************************************************************** | |
// LOCAL VARIABILE | |
//*********************************************************************** | |
U8 u8t; | |
//*********************************************************************** | |
// BODY | |
//*********************************************************************** | |
///---------------------------------------------------------------------- | |
/// COMMAND 5: Bus Width, Number of lines, Font Type | |
///---------------------------------------------------------------------- | |
//Clear the temp var | |
u8t = 0x00; | |
//Command 5 | |
SET_BIT( u8t, 5 ); | |
//Bus Width | |
// 0 | 4bit | |
// 1 | 8bit | |
//SET_BIT( u8t, 4 ); | |
//Number of lines | |
// 0 | 1 line | |
// 1 | 2 lines | |
SET_BIT( u8t, 3 ); | |
//Font type | |
// 0 | 5x10 | |
// 1 | 5x7 | |
//SET_BIT( u8t, 2 ); | |
//Don't Care | |
//SET_BIT( u8t, 1 ); | |
//Don't Care | |
//SET_BIT( u8t, 0 ); | |
//Send the command a special way (this time only) | |
//BUG: The default mode for the LCD is 8bit, the first command must be sent twice, and this way | |
lcd_send_cmd( u8t >> 4 ); | |
//Send the command | |
lcd_send_cmd( u8t ); | |
///---------------------------------------------------------------------- | |
/// COMMAND 0: Clear the display | |
///---------------------------------------------------------------------- | |
//Clear the temp var | |
u8t = 0x00; | |
//Command 0 | |
SET_BIT( u8t, 0 ); | |
//Send the command | |
lcd_send_cmd( u8t ); | |
///---------------------------------------------------------------------- | |
/// COMMAND 3: Show Display, Show Cursor, Blink Cursor | |
///---------------------------------------------------------------------- | |
//Clear the temp var | |
u8t = 0x00; | |
//Command 3 | |
SET_BIT( u8t, 3 ); | |
//Show Display Content | |
// 0 | Hidden | |
// 1 | Show | |
SET_BIT( u8t, 2 ); | |
//Show Cursor | |
// 0 | Hide Cursor | |
// 1 | Show Cursor | |
//SET_BIT( u8t, 1 ); | |
//Blink Cursor | |
// 0 | Don't Blink | |
// 1 | Blink | |
//SET_BIT( u8t, 0 ); | |
//Send the command | |
lcd_send_cmd( u8t ); | |
///---------------------------------------------------------------------- | |
/// INITIALIZE VARIABILES | |
///---------------------------------------------------------------------- | |
//Here, I initialize the lcd variabiles | |
//For every digit | |
for (u8t = 0;u8t < LCD_ROW*LCD_COL; u8t++) | |
{ | |
//initialize the show vector with spaces | |
lcd_show[ u8t ] = ' '; | |
} | |
//For every 8 digit bit | |
for (u8t = 0;u8t < LCD_UPDT; u8t++) | |
{ | |
//initialize the 8 update bit to 0 (0 = up to date) | |
lcd_updt[ u8t ] = 0; | |
} | |
//Clear configuration flags (defaults) | |
lcd_cfg_flags = 0x00; | |
//Error code | |
lcd_error = LCD_OK; | |
//*********************************************************************** | |
// RETURN | |
//*********************************************************************** | |
return; | |
} //End function: lcd_init | |
/**************************************************************************** | |
** LCD UPDATE | |
***************************************************************************** | |
** Main driver function. User should call this at a fixed frequency (10KHz) | |
** This function will automatically sync the content of lcd_show with the LCD display | |
** DESCRIPTION: | |
** I have two states of operations: | |
** >Send commands to display | |
** >Generate commands | |
** I only generate commands if the comand queque is empty and there are update flags rised | |
** If the command queque is empty, and there are no update flag rised, the function will do nothing (very efficient) | |
** A huge advantage of the command queque dont this way, is that now I can stop adding commands to the queque if the queque is full | |
** A bug of the version 2.0 of the library was that prints directly added command to the queque, so the user could simply overflow the buffer | |
** There was no way to solve i, because i could only ignore commands at best | |
** Now the update function is the one that decide what to upload to the queque | |
***************************************************************************** | |
** ALGORITHM | |
***************************************************************************** | |
** S0 is the IDLE state, when I'm S0 i search for a '1' in lcd_updt scanning forward | |
** If I find it, I'll have to send data to the LCD to sync it with the buffer | |
** I use two flags, ADR and H to signal S3 what to do | |
** If the lcd cursor is already in the right position I can skip sending the address, i wont' rise ADR flag | |
** S1), and S2)= are strightforward, they strobe EN | |
** S3) is the important final state. In here I use the ADR and H flag to send the LSB of the byte and to | |
** know if i have to send data or address information | |
** I follow it with S1 as long as I need to continue | |
** Once I'm done, i return to status S0, and I'm ready for the next flag search | |
** The last operation of S3 is to check if the sent data is still coherent with the buffer | |
** since the user might have already written something new. if it's coherent, i clear the update flag | |
** | |
** STATUS VAR | |
** exe(2) : i only needs 2 bits to encode the status | |
** adr(1) : signal S3 if i'm sending address informations | |
** h(1) : signal S3 if i have to send the LSB | |
** scan(5) : current scan position | |
** cursor(5) : current cursor position on the LCD display | |
** | |
** S0) - IDLE | |
** Search for a flag, did I find it? | |
** N: RET S0 //nothing to do, the LCD is synced with the buffer | |
** is the cursor in the right position? | |
** N: CLEAR RS, SET H, SET ADR, Send ADR(H) | |
** Y: SET RS, SET H, Send DATA(H) | |
** RET S1 | |
** S1) - SET EN | |
** SET EN | |
** RET S2 | |
** S2) - CLEAR EN | |
** CLEAR EN | |
** if ADR is clear && H is clear? | |
** I'm done, RET S0 | |
** RET S3 | |
** S3) | |
** if ADR is set && H is set? | |
** CLEAR H, Send ADR(L), UPDATE status.cursor, RET S1 | |
** if ADR is set && H is clear? | |
** CLEAR ADR, SET H, Send DATA(H), RET S1 | |
** if ADR is clear && H is set? | |
** CLEAR H, Send DATA(L), UPDATE lcd_updt flag, RET S1 | |
****************************************************************************/ | |
void lcd_update( void ) | |
{ | |
///************************************************************************** | |
/// STATIC VARIABILE | |
///************************************************************************** | |
//Status variabile for the LCD FSM | |
static Lcd_fsm_status status; | |
//Store the char being sent, it prevent glitches when the user update a char currently being scanned | |
static U8 data_buf; | |
///************************************************************************** | |
/// LOCAL VARIABILE | |
///************************************************************************** | |
U8 exe_temp; | |
U8 scan_temp; | |
U8 u8t, u8t1, u8t2; | |
///************************************************************************** | |
/// PARAMETER CHECK | |
///************************************************************************** | |
///************************************************************************** | |
/// BODY | |
///************************************************************************** | |
///Fetch | |
//Move status var to temp vars | |
exe_temp = status.exe; | |
scan_temp = status.scan; | |
///FSM | |
//If: S0 - IDLE | |
if (exe_temp == 0) | |
{ | |
///---------------------------------------------------------------------- | |
/// S0 - IDLE | |
///---------------------------------------------------------------------- | |
// If: I'm idle, I'm searching for the next rised flag using scan | |
// the flag are stored in 4 byte, every bit is a flag for one of 32 digit of the screen | |
// >First I scan the current byte pointed by scan, and see if there are rised update flag there | |
// >Then I scan the following bytes for at least one flag | |
// Y> I scan for the first bit, i will find it | |
// N> Return S0), there is nothing to do | |
// >Do I have to send the address? | |
// Y> rise address flag, send_data=address | |
// N> send_data = data | |
// | |
///First byte flag detection | |
//The byte I'm currently scanning, has a rised flag that I have yet to scan? | |
//ex1. 0 1 1 0 0 0 1 0 | ex2. 0 1 1 0 0 0 1 0 | |
//scan ^ | scan ^ | |
//mask 0 0 0 0 0 1 1 1 | mask 0 0 0 0 0 0 0 1 | |
//In the ex1. there is a 1 after the scan index, in ex2 no. | |
//what I do is to generate a mask with 1 after the scan index | |
//The & with the flag byte will return 1 if I have at least one rised flag to scan | |
//otherwise I will scan the next bytes | |
/// | |
//generate the mask, the position inside the byte is the 3LSB of scan | |
u8t = (0xff << (scan_temp & 0x07)); | |
//fetch the current flag byte, , the index of the vector is the 2MSB of scan | |
u8t1 = lcd_updt[ ((scan_temp & 0x18) >> 3) ]; | |
//If: The byte currently pointed by scan, still has unreaded update flag | |
if ((u8t & u8t1) != 0x00) | |
{ | |
//Here: I know there is at least one rised flag after scan position. I search for it. | |
//Create a mask with a 1 in scan | |
u8t = MASK( scan_temp & 0x07 ); | |
//While: mobile mask scan. shift up the bit and & with lcd_updt until I find the flag I'm searching for | |
while (u8t != 0x00) | |
{ | |
//If: I found the flag | |
if ((u8t & u8t1) != 0x00) | |
{ | |
//DEBUG PRINTF | |
DPRINTF("S0 - Found rised flag in first status.scan byte: %d\n", scan_temp); | |
//DPRINTF("S0 - >%2x< >%2x< >%2x<\n", u8t, u8t1, u8t&u8t1); | |
//break while | |
u8t = 0x00; | |
} // End If: I found the flag | |
//If: I did not find the flag | |
else | |
{ | |
//increase scan while I'm at it | |
scan_temp++; | |
//Scan the next bit. u8t will overflow to 0x00 once I'm done | |
u8t = u8t << 1; | |
//If: the mask overflowed | |
if (u8t == 0x00) | |
{ | |
//ERROR: The first check told me that there was a rised bit, but I didn't find it! Algorithmic error | |
LCD_ERROR( LCD_WTF ); | |
} | |
} //End If: I did not find the flag | |
} // End While: mobile mask scan. shift up the bit and & with lcd_updt until I find the flag I'm searching for | |
} //End If: The byte currently pointed by scan, still has unreaded update flag | |
//If: I have to search for an update flag in the following four bytes | |
else | |
{ | |
//I have to circularly scan the bytes of lcd_updt and scan four of them | |
//The fourth is the starting byte, but I have not scanned all bits of it | |
// 0 1 2 3 | |
// -> | -> scan | |
///Setup var | |
//clear the 3LSB of scan index, i will start bit search from the first bit of the byte | |
scan_temp = scan_temp & 0x18; | |
//Get the byte index | |
u8t1 = ((scan_temp & 0x18) >> 3); | |
//Setup increment | |
u8t = 1; | |
///Search | |
//While: Scan (LCD_UPDT) byte after the one pointed by scan for a rised flag | |
while (u8t <= LCD_UPDT) | |
{ | |
//Fetch the content of the byte I'm scanning. | |
u8t2 = lcd_updt[ u8t1 ]; | |
//If: the byte I'm scanning contain at least one rised flag | |
if (u8t2 != 0x00) | |
{ | |
//DEBUG PRINTF | |
//DPRINTF("S0 - Found a byte with risen flag: byte %d, scan %d\n", u8t1, scan_temp); | |
//break cycle | |
u8t = LCD_UPDT +1; | |
} | |
//If: the byte I'm scanning contain no rised flags | |
else | |
{ | |
//Advance relative increment | |
u8t++; | |
//Advance the absolute index | |
u8t1 = ((u8t1 +1) >= (LCD_UPDT))?(0):(u8t1+1); | |
//I update scan index while I'm at it, i advance by 8 bits | |
scan_temp = AT_CIRCULAR_SUM( scan_temp, 8, LCD_ROW *LCD_COL ); | |
//If: the search failed, it means that there are NO risen flags | |
if (u8t > LCD_UPDT) | |
{ | |
//It means I have nothing to do, I stay in S0-IDLE | |
DPRINTF("S0 - No flags found, nothing to do here: scan_temp: %d\n", scan_temp); | |
//I'm done | |
return; | |
} | |
} | |
} //End While: Scan (LCD_UPDT) byte after the one pointed by scan for a rised flag | |
///HERE: | |
//I have found a byte with a risen flag, I have to find the bit now | |
///Setup | |
//mask with LSB rised | |
u8t = 0x01; | |
//u8t1 has the index, I fetch the content instead. Scan temp already point to the first bit of that byte | |
u8t1 = lcd_updt[ u8t1 ]; | |
///Search | |
//While: I'm searching for a rised flag inside a byte | |
while (u8t != 0x00) | |
{ | |
//If: I find the flag I'm looking for | |
if ((u8t & u8t1) != 0x00) | |
{ | |
//break the while | |
u8t = 0x00; | |
} | |
//If: flag is clear | |
else | |
{ | |
//advance mask | |
u8t = u8t << 1; | |
//while I'm at it, I update the scan index as well | |
scan_temp++; | |
//If: I completed the scan and didn't find the flag | |
if (u8t == 0x00) | |
{ | |
//ERROR: The first check told me that there was a rised bit, but I didn't find it! Algorithmic error | |
LCD_ERROR( LCD_WTF ); | |
} | |
} | |
} //End While: I'm searching for a rised flag inside a byte | |
// | |
DPRINTF("S0 - Flag after first byte found: %d\n", scan_temp); | |
} //End If: I have to search for an update flag in the following four bytes | |
///HERE: | |
//scan_temp hold the position of a char to be processed | |
///LOAD DATA BUFFER, UPDATE STATUS VARS, JUMP | |
//BUG the cursor from 0,15 move out of screen to 0,16 NOT 1,0 For this position only, I always have to specify the address | |
//If: the cursor is already in the correct position | |
if (((scan_temp & 0x0f) != 0) && (scan_temp == status.cursor)) | |
{ | |
//I will skip sending the address | |
status.adr = 0; | |
//Load data into the buffer | |
u8t = lcd_show[ scan_temp ]; | |
data_buf = u8t; | |
//SET RS line, i'm sending data | |
SET_BIT( RS_PORT, RS_PIN ); | |
DPRINTF("S0 - Cursor is Up To Date, sending digit: %x\n", u8t); | |
} | |
//If: I have to move the cursor | |
else | |
{ | |
//I'm sending the address | |
status.adr = 1; | |
//Load data into the buffer | |
// B7='1' is signature for DDRAM write instruction | |
// B6='1' is address for the second row, i have to move B4 to B6 | |
// B3...B0 is the LSB of the address | |
u8t = MASK(7) | ((scan_temp & 0x10) << 2) | (scan_temp & 0x0f); | |
data_buf = u8t; | |
//CLEAR RS line, i'm sending an address | |
CLEAR_BIT( RS_PORT, RS_PIN ); | |
DPRINTF("S0 - Sending cursor position: scan: %d cursor: %d adr: %x\n", scan_temp, status.cursor, u8t); | |
} | |
//I'm sending the H part | |
status.h = 1; | |
//send the H part of the buffer (still stored in u8t) | |
LCD_WRITE( u8t >> 4 ); | |
//Jump to S1) | |
exe_temp = 1; | |
} //End If: S0 - IDLE | |
//If: S1 - SET EN | |
else if (exe_temp == 1) | |
{ | |
DPRINTF("S1 - SET_EN\n"); | |
SET_BIT( EN_PORT, EN_PIN ); | |
//Jump S2) | |
exe_temp = 2; | |
} //End If: S1 - SET EN | |
//If: S2 - CLEAR EN | |
else if (exe_temp == 2) | |
{ | |
// | |
CLEAR_BIT( EN_PORT, EN_PIN ); | |
//If I have sent DATA(L) | |
if ((status.adr == 0) && (status.h == 0)) | |
{ | |
DPRINTF("S2 - CLEAR_EN, DATA LSB was sent, I'm done\n"); | |
//I'm done, I don't have to execute the next state (no pins needs to be toggled | |
exe_temp = 0; | |
} | |
//default case | |
else | |
{ | |
DPRINTF("S2 - CLEAR_EN\n"); | |
//Jump S3) | |
exe_temp = 3; | |
} | |
} //End If: S2 - CLEAR EN | |
//If: S3 - | |
else if (exe_temp == 3) | |
{ | |
///---------------------------------------------------------------------- | |
/// S3 - | |
///---------------------------------------------------------------------- | |
// The final state, i use status.h, status.adr and data_buf | |
// Here i setup DATA and RS line, and jump to S1) to strobe EN line | |
//If: I have sent ADR(H) | |
if ((status.adr == 1) && (status.h == 1)) | |
{ | |
///Send ADR(L) | |
//Clear H flag | |
status.h = 0; | |
//load 4LSB of data buffer | |
u8t = data_buf; | |
//Send ADR(L) | |
LCD_WRITE( u8t & 0x0f ); | |
///Update cursor | |
//I have written ADR, I need to update the cursor | |
status.cursor = scan_temp; | |
DPRINTF("S3 - Send ADR LSB\n"); | |
} | |
//If: I have sent ADR(L) | |
else if ((status.adr == 1) && (status.h == 0)) | |
{ | |
///Send DATA(H) | |
//Set RS line (I'm sending data, not instructuins) | |
SET_BIT( RS_PORT, RS_PIN ); | |
//Clear ADR flag | |
status.adr = 0; | |
//Set H flag | |
status.h = 1; | |
//load digit into buffer | |
//Remember that user can async write into it, that's why I need a buffer in the first place | |
u8t = lcd_show[ scan_temp ]; | |
data_buf = u8t; | |
//Send DATA(H) | |
LCD_WRITE( u8t >> 4 ); | |
DPRINTF("S3 - Send DATA MSB\n"); | |
} | |
//If: I have sent DATA(H) | |
else if ((status.adr == 0) && (status.h == 1)) | |
{ | |
///Send DATA(L) | |
//Clear H flag | |
status.h = 0; | |
//load 4LSB of data buffer | |
u8t = data_buf; | |
//Send ADR(L) | |
LCD_WRITE( u8t & 0x0f ); | |
///Update lcd_updt | |
//If: the LCD is synced with lcd_show | |
if (u8t == lcd_show[ scan_temp ]) | |
{ | |
//I can clear the flag, B3 B4 address the byte inside the vector, B0-B2 address the bit inside the byte | |
CLEAR_BIT( lcd_updt[ ((scan_temp & 0x18) >> 3) ], (scan_temp & 0x07) ); | |
} | |
///Update cursor | |
//A successful write to the LCD will advance the cursor | |
u8t = status.cursor; | |
//circular increment of cursor | |
u8t = ((u8t +1) < (LCD_ROW*LCD_COL))?(u8t +1):(0); | |
//write back cursor | |
status.cursor = u8t; | |
DPRINTF("S3 - Send DATA LSB\n"); | |
} | |
//If: I have sent DATA(L), but I should have quit earlier (no need to strobe again). This is an error | |
else | |
{ | |
// | |
LCD_ERROR( LCD_WTF ); | |
} | |
//Jump to S1) I have setup DATA and RS lines, I need to strobe EN | |
exe_temp = 1; | |
} //End If: S3 - | |
//Default case (impossibile) | |
else | |
{ | |
//ERROR: This is an impossibile state, something went REALLY wrong | |
LCD_ERROR( LCD_WTF ); | |
} | |
///************************************************************************** | |
/// WRITE BACK | |
///************************************************************************** | |
//save updated status var | |
status.exe = exe_temp; | |
status.scan = scan_temp; | |
///************************************************************************** | |
/// RETURN | |
///************************************************************************** | |
return; | |
} //end function: display_update | |
/**************************************************************************** | |
** LCD CONFIG | |
***************************************************************************** | |
** This function configure certain behaviours of the print function | |
****************************************************************************/ | |
void lcd_config( U8 cfg, U8 val ) | |
{ | |
//*********************************************************************** | |
// STATIC VARIABILE | |
//*********************************************************************** | |
//*********************************************************************** | |
// LOCAL VARIABILE | |
//*********************************************************************** | |
//*********************************************************************** | |
// BODY | |
//*********************************************************************** | |
//Configure number alignment for the print functions | |
if (cfg == LCD_ADJ) | |
{ | |
//If the user wants left adjust (write MSB on pos and go right) | |
if (val == LCD_ADJ_L) | |
{ | |
SET_BIT( lcd_cfg_flags, LCD_ADJ_FLAG ); | |
} | |
//If the user wants right adjust (write LSB on pos and go left) (Default) | |
if (val == LCD_ADJ_R) | |
{ | |
CLEAR_BIT( lcd_cfg_flags, LCD_ADJ_FLAG ); | |
} | |
} | |
//*********************************************************************** | |
// RETURN | |
//*********************************************************************** | |
return; | |
} //End function: | |
/**************************************************************************** | |
***************************************************************************** | |
** PRINT GROUP | |
***************************************************************************** | |
****************************************************************************/ | |
/**************************************************************************** | |
** LCD PRINT CHAR | |
***************************************************************************** | |
** Write a char in the show vector, update the flag is required | |
** Input: | |
** >pos: Vector position | |
** >data: Display data | |
****************************************************************************/ | |
void lcd_print_char( U8 pos, U8 data ) | |
{ | |
///************************************************************************** | |
/// STATIC VARIABILE | |
///************************************************************************** | |
///************************************************************************** | |
/// LOCAL VARIABILE | |
///************************************************************************** | |
///************************************************************************** | |
/// PARAMETER CHECK | |
///************************************************************************** | |
if (pos > (LCD_ROW*LCD_COL)) | |
{ | |
//error, Out Of Bound write | |
LCD_ERROR( LCD_OOB ); | |
//I'm done | |
return; | |
} | |
///************************************************************************** | |
/// BODY | |
///************************************************************************** | |
// I only do something if I'm writing something different in lcd_show | |
// This already is a huge improvement from V2.0 which always sent commands | |
//If: I'm writing a different byte to a location | |
if (lcd_show[ pos ] != data ) | |
{ | |
//write the new data | |
lcd_show[ pos ] = data; | |
//Rise the update bit | |
// Location inside the vector is pos/8 | |
// Bit position inside the Location is the 3LSB of pos (reminder of division by 8) | |
SET_BIT( lcd_updt[ ((pos & 0x18) >> 3) ], (pos & 0x07) ); | |
} | |
///************************************************************************** | |
/// RETURN | |
///************************************************************************** | |
return; | |
} //end Function: lcd_print_char | |
/**************************************************************************** | |
** PRINT STR | |
***************************************************************************** | |
** Print a string starting from screen position | |
** If reach EOL, it starts from the following one | |
****************************************************************************/ | |
void lcd_print_str( U8 pos, U8 *str ) | |
{ | |
//*********************************************************************** | |
// STATIC VARIABILE | |
//*********************************************************************** | |
//*********************************************************************** | |
// LOCAL VARIABILE | |
//*********************************************************************** | |
U8 t; | |
//*********************************************************************** | |
// PARAMETER CHECK | |
//*********************************************************************** | |
//If: NULL pointer | |
if (str == NULL) | |
{ | |
return; | |
} | |
//*********************************************************************** | |
// MAIN | |
//*********************************************************************** | |
//Initialise | |
t = 0; | |
//While: I have char or I have space on screen | |
while ( ((pos +t) <= (LCD_ROW *LCD_COL)) && (str[ t ] != '\0') ) | |
{ | |
//Print char on screen | |
lcd_print_char( pos +t, str[ t ] ); | |
//next char | |
t++; | |
} | |
//*********************************************************************** | |
// RETURN | |
//*********************************************************************** | |
return; | |
} //End function: print_str | |
/**************************************************************************** | |
** LCD PRINT U8 | |
***************************************************************************** | |
** Print a U8 number on screen | |
** I print the spaces in the empty digit too otherwise if | |
** I write a 1 digit number after a 3 digit one in the same position, | |
** the old numbers will still be there | |
** SETTINGS | |
** ALIGNMENT | |
** POS |v |v | |
** RIGHT |123 LEFT |123 | |
** | 1 |1 | |
****************************************************************************/ | |
void lcd_print_u8( U8 pos, U8 num ) | |
{ | |
//*********************************************************************** | |
// STATIC VARIABILE | |
//*********************************************************************** | |
//*********************************************************************** | |
// LOCAL VARIABILE | |
//*********************************************************************** | |
//Temp string to accomodate the number | |
U8 str[ MAX_DIGIT8 +1 ]; | |
//return var of the number to string conversion | |
U8 ret; | |
//Temp vars | |
U8 u8t, u8t1; | |
//*********************************************************************** | |
// BODY | |
//*********************************************************************** | |
//convert the number | |
ret = u8_to_str( num, str ); | |
//For: every digit | |
for (u8t = 0;u8t < MAX_DIGIT8; u8t++) | |
{ | |
//If: left alignment | |
if (IS_BIT_ONE( lcd_cfg_flags, LCD_ADJ_FLAG )) | |
{ | |
//If: I'm outside the number | |
if (u8t >= ret) | |
{ | |
//I write spaces to blank older numbers | |
u8t1 = ' '; | |
} | |
else | |
{ | |
//Print number | |
u8t1 = str[ u8t ]; | |
} | |
} //End If: left alignment | |
//If: right alignment | |
else | |
{ | |
//If: I'm outside the number (yes, that's the condition,right adjustment is messy) | |
if ((u8t +ret) < MAX_DIGIT8) | |
{ | |
//I write spaces to blank older numbers | |
u8t1 = ' '; | |
} | |
//If: I'm writing a digit | |
else | |
{ | |
//print number, I have to write digit in a mesi order with this adjust | |
u8t1 = str[ u8t +ret -MAX_DIGIT8 ]; | |
} | |
} //End If: right alignment | |
//I want to call a single print, I have calculated the argument based on the settings of the print | |
lcd_print_char( pos +u8t, u8t1 ); | |
} //End For: every digit | |
//*********************************************************************** | |
// RETURN | |
//*********************************************************************** | |
return; | |
} //End function: lcd_print_u8 | |
/**************************************************************************** | |
** LCD PRINT S8 | |
***************************************************************************** | |
** Print a S8 number on screen | |
** I print the spaces in the empty digit too otherwise if | |
** I write a 1 digit number after a 3 digit one in the same position, | |
** the old numbers will still be there | |
** SETTINGS | |
** ALIGNMENT | |
** POS |v |v | |
** RIGHT |123 LEFT |123 | |
** | 1 |1 | |
****************************************************************************/ | |
void lcd_print_s8( U8 pos, S8 num ) | |
{ | |
//*********************************************************************** | |
// STATIC VARIABILE | |
//*********************************************************************** | |
//*********************************************************************** | |
// LOCAL VARIABILE | |
//*********************************************************************** | |
//Temp string to accomodate the number | |
U8 str[ MAX_DIGIT8 +1 ]; | |
//return var of the number to string conversion | |
U8 ret; | |
//Temp vars | |
U8 u8t, u8t1; | |
//*********************************************************************** | |
// BODY | |
//*********************************************************************** | |
//convert the number | |
ret = s8_to_str( num, str ); | |
//For: every digit | |
for (u8t = 0;u8t < (MAX_DIGIT8 +1); u8t++) | |
{ | |
//If: sign handling | |
if (u8t == 0) | |
{ | |
//Print sign | |
u8t1 = str[ u8t ]; | |
} //End If: sign handling | |
//If: left alignment | |
else if (IS_BIT_ONE( lcd_cfg_flags, LCD_ADJ_FLAG )) | |
{ | |
//If: I'm outside the number | |
if (u8t >= ret) | |
{ | |
//I write spaces to blank older numbers | |
u8t1 = ' '; | |
} | |
else | |
{ | |
//Print number | |
u8t1 = str[ u8t ]; | |
} | |
} //End If: left alignment | |
//If: right alignment | |
else | |
{ | |
//If: I'm outside the number (yes, that's the condition,right adjustment is messy) | |
if ((u8t +ret ) <= (MAX_DIGIT8 +1)) | |
{ | |
//I write spaces to blank older numbers | |
u8t1 = ' '; | |
} | |
//If: I'm writing a digit | |
else | |
{ | |
//print number, I have to write digit in a mesi order with this adjust | |
u8t1 = str[ u8t -1 +ret -MAX_DIGIT8 ]; | |
} | |
} //End If: right alignment | |
//I want to call a single print, I have calculated the argument based on the settings of the print | |
lcd_print_char( pos +u8t, u8t1 ); | |
} //End For: every digit | |
//*********************************************************************** | |
// RETURN | |
//*********************************************************************** | |
return; | |
} //End function: lcd_print_s8 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment