diff --git a/lcdLib.c b/lcdLib.c index b7686a0..d0989f9 100644 --- a/lcdLib.c +++ b/lcdLib.c @@ -249,9 +249,112 @@ void writeCharToLCD(char c) { } } +/* + Given a character string, and a uint8_t pointer, reads the character string until a + non-numerical ASCII character, returning the integer representation of the number read. At + the end of the functions execution, the found_num uint8_t pointer will indicate how many + digits were read. + */ +uint8_t readASCIINumber(const char* str, uint8_t* found_num) { + const uint8_t num_digits = 4; + uint8_t nums[num_digits]; + + while (*str != '\0' && *found_num < num_digits - 1) { + if (*str >= 0x30 && *str <= 0x39) { + // Use *str as a number (specified in ASCII) + nums[*(found_num++)] = *str - 0x30; + } else { + break; + } + + str++; + } + + uint8_t ret = 0; + for (uint8_t i = 0; i < num_digits; i++) + ret += nums[num_digits - i] * pow(10, i); + return ret; +} + void writeStringToLCD(const char* str) { while (*str != '\0') { - writeCharToLCD(*str); + // Check for ANSI CSI (Control Sequence Introducer) + if (*str == '\e') { + if (*(++str) != '\0' && *str == '[') { + // Read optional variable length number in ASCII (0x30 - 0x3f) where 0x3a - 0x3f are + // ignored (they are used as flags by some terminals + uint8_t fnd0; + uint8_t num0 = readASCIINumber(str++, &fnd0); + + // Read optional (semicolon followed by optional variable length number) + uint8_t fnd1; + uint8_t num1; + if (*(++str) != '\0' && *str == ';') { + num1 = readASCIINumber(str++, &fnd1); + + // Read control character (between 0x40 - 0x7e) for two argument sequences + switch (*str) { + case 'f': // HVP - Horizontal and vertical position + case 'H': // CUP - Cursor position + num0 = fnd0 ? num0 : 1; + num1 = fnd1 ? num1 : 1; + setCursorPosition(num0, num1); + break; + ;; + default: // Invalid control character + break; + ;; + } + } else if (*str != '\0') { + // Read control character (between 0x40 - 0x7e) for single argument sequences + switch (*str) { + case 'A': // CUU - Cursor up + num0 = fnd0 ? num0 : 1; + moveCursorUp(num0); + break; + ;; + case 'B': // CUD - Cursor down + num0 = fnd0 ? num0 : 1; + moveCursorDown(num0); + break; + ;; + case 'C': // CUF - Cursor forward + num0 = fnd0 ? num0 : 1; + moveCursorForward(num0); + break; + ;; + case 'D': // CUB - Cursor back + num0 = fnd0 ? num0 : 1; + moveCursorBackward(num0); + break; + ;; + case 'E': // CNL - Cursor next line + num0 = fnd0 ? num0 : 1; + moveCursorNextLine(num0); + break; + ;; + case 'F': // CPL - Cursor previous line + num0 = fnd0 ? num0 : 1; + moveCursorPreviousLine(num0); + break; + ;; + case 'G': // CHA - Cursor horizontal absolute + num0 = fnd0 ? num0 : 1; + moveCursorToColumn(num0); + break; + ;; + default: // Invalid control character + break; + ;; + } + } else { + return; // Invalid escape sequence (terminated early) + } + } + } else { + writeCharToLCD(*str); + } + str++; } } @@ -280,6 +383,88 @@ void returnHome(void) { currentLineChars = 0; } +void getCursorPosition(uint8_t* row, uint8_t* column) { + *row = currentLineNum + 1; + *column = currentLineChars + 1; +} + +void setCursorPosition(uint8_t row, uint8_t column) { + // Set currentLineNum and currentLineChars + currentLineNum = row ? row - 1 : 0; + currentLineChars = column ? column - 1 : 0; + + writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars)); +} + +void moveCursorUp(uint8_t n) { + if (n < currentLineNum + 1) { + currentLineNum -= n; + } else { + currentLineNum = 0; + } + + writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars)); +} + +void moveCursorDown(uint8_t n) { + if (n + currentLineNum > LCD_NUMBER_OF_LINES - 1) { + currentLineNum += n; + } else { + currentLineNum = LCD_NUMBER_OF_LINES - 1; + } + + writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars)); +} + +void moveCursorForward(uint8_t n) { + if (n + currentLineChars > LCD_CHARACTERS_PER_LINE - 1) { + currentLineChars += n; + } else { + currentLineChars = LCD_CHARACTERS_PER_LINE - 1; + } + + writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars)); +} + +void moveCursorBackward(uint8_t n) { + if (n < currentLineChars + 1) { + currentLineChars += n; + } else { + currentLineChars = 0; + } + + writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars)); +} + +void moveCursorNextLine(uint8_t n) { + if (n + currentLineNum > LCD_NUMBER_OF_LINES - 1) { + currentLineNum += n; + } else { + currentLineNum = LCD_NUMBER_OF_LINES - 1; + } + + writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars)); +} + +void moveCursorPreviousLine(uint8_t n) { + if (n < currentLineNum + 1) { + currentLineNum -= n; + } else { + currentLineNum = 0; + } + + writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars)); +} + +void moveCursorToColumn(uint8_t n) { + if (n < LCD_CHARACTERS_PER_LINE) { + currentLineChars = n ? n - 1 : 0; + writeLCDInstr(INSTR_DDRAM_ADDR | (lineBeginnings[currentLineNum] + currentLineChars)); + } // else index out of range (off screen column) +} + +//----------------------------------------------------------------------------------------------- + /* char readCharFromLCD(void) { */ /* loop_until_LCD_BF_clear(); // Wait until LCD is ready for new instructions */ diff --git a/lcdLib.h b/lcdLib.h index 39800fb..1d873af 100644 --- a/lcdLib.h +++ b/lcdLib.h @@ -25,6 +25,7 @@ // Includes ------------------------------------------------------------------------------- #include +#include #include "lcdLibConfig.h" //------------------------------------------------------------------------------------------ @@ -65,6 +66,11 @@ // Set DD RAM address instruction #define INSTR_DDRAM_ADDR 0x80 +//------------------------------------- + + +#define LCD_CHARACTERS_PER_SCREEN (LCD_CHARACTERS_PER_LINE * LCD_NUMBER_OF_LINES) + //------------------------------------ // Function definitions @@ -133,6 +139,9 @@ void writeCharToLCD(char); */ void writeStringToLCD(const char*); +//----------------------------------------------------------------------------------------------- +// LCD command functions + /** Clears the display and positions the cursor in the top left of the LCD screen. */ @@ -143,11 +152,60 @@ void clearDisplay(void); */ void returnHome(void); +/** + Gets the current row and column of the LCD cursor. +*/ +void getCursorPosition(uint8_t* row, uint8_t* column); + +/** + Sets given pointers row and column to the current row and column occupied by the LCD cursor. + */ +void setCursorPosition(uint8_t row, uint8_t column); + +/** + Moves the cursor n positions up. If the cursor is at the edge of the screen this has no effect. + */ +void moveCursorUp(uint8_t n); + +/** + Moves the cursor n positions down. If the cursor is at the edge of the screen this has no effect. + */ +void moveCursorDown(uint8_t n); + +/** + Moves the cursor n positions forward. If the cursor is at the edge of the screen this has no effect. + */ +void moveCursorForward(uint8_t n); + +/** + Moves the cursor n positions backwards. If the cursor is at the edge of the screen this has no effect. + */ +void moveCursorBackward(uint8_t n); + +/** + Moves the cursor to the beginning of the line n lines down. + */ +void moveCursorNextLine(uint8_t n); + +/** + Moves the cursor to the beginning of the line n lines up. + */ +void moveCursorPreviousLine(uint8_t n); + +/** + Moves the cursor to column n. If n is off screen go to the last line. + */ +void moveCursorToColumn(uint8_t n); + +//----------------------------------------------------------------------------------------------- + /* UNIMPLEMENTED */ char readCharFromLCD(void); +//----------------------------------------------------------------------------------------------- + /** Initialize the LCD display via software initialization as specified by the datasheet. */ diff --git a/lcdOutput.c b/lcdOutput.c index 1ebc7b7..63421a1 100644 --- a/lcdOutput.c +++ b/lcdOutput.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "lcdLib.h" #include "ansi_escapes.h" @@ -38,6 +39,8 @@ #define STATUS_LED_DDR DDRC #define STATUS_LED PC5 +//-------------------------------------------------- + void flashLED(uint8_t times) { while (times > 0) { STATUS_LED_PORT |= 1 << STATUS_LED; // turn on status LED @@ -47,12 +50,27 @@ void flashLED(uint8_t times) { times--; } } +//-------------------------------------------------- + +/* + Initialize previous, current, and next screen variables to spaces +*/ +char* initScreenMem(const uint8_t len) { + char *scrn = (char*) malloc(len * sizeof(char)); + for (uint8_t i = 0; i < len; i++) { + scrn[i] = ' '; + } + return scrn; +} int main(void) { clock_prescale_set(clock_div_1); STATUS_LED_DDR |= 1 << STATUS_LED; // DEBUG + // Allocate and init first block of heap for storing LCD data + char* scrn = initScreenMem(LCD_CHARACTERS_PER_SCREEN); + initUSART(); char serialChar; @@ -60,6 +78,45 @@ int main(void) { //initLCDByInternalReset(); flashLED(5); // DEBUG + + writeStringToLCD("Backwards 4" CUB(4)); + _delay_ms(5000); + + clearDisplay(); + writeStringToLCD("Backwards 3" CUB(3)); + _delay_ms(5000); + + clearDisplay(); + writeStringToLCD("Backwards 2" CUB(2)); + _delay_ms(5000); + + clearDisplay(); + writeStringToLCD("Backwards 1" CUB(1)); + _delay_ms(5000); + + clearDisplay(); + writeStringToLCD("Forwards 4" CUF(4)); + _delay_ms(5000); + + /* writeStringToLCD(CNL(1)); */ + /* _delay_ms(5000); */ + + /* writeStringToLCD(CUF(10)); */ + /* _delay_ms(5000); */ + + /* writeStringToLCD(CPL(1)); */ + /* _delay_ms(5000); */ + + /* writeStringToLCD(CHA(20)); */ + /* _delay_ms(5000); */ + + /* writeStringToLCD(CUP(20,20)); */ + /* _delay_ms(5000); */ + + /* writeStringToLCD(CUP(1,1)); */ + /* _delay_ms(5000); */ + + while (1) { serialChar = receiveByte();