pxt-common-packages
Version:
Microsoft MakeCode (PXT) common packages
317 lines (285 loc) • 11.4 kB
text/typescript
namespace lcd {
//% shim=lcd::__write8
function __write8(value: number, char_mode: boolean): void {
}
// Commands
const _LCD_CLEARDISPLAY = 0x01
const _LCD_RETURNHOME = 0x02
const _LCD_ENTRYMODESET = 0x04
const _LCD_DISPLAYCONTROL = 0x08
const _LCD_CURSORSHIFT = 0x10
const _LCD_FUNCTIONSET = 0x20
const _LCD_SETCGRAMADDR = 0x40
const _LCD_SETDDRAMADDR = 0x80
// Entry flags
const _LCD_ENTRYLEFT = 0x02
const _LCD_ENTRYSHIFTDECREMENT = 0x00
// Control flags
const _LCD_DISPLAYON = 0x04
const _LCD_CURSORON = 0x02
const _LCD_CURSOROFF = 0x00
const _LCD_BLINKON = 0x01
const _LCD_BLINKOFF = 0x00
// Move flags
const _LCD_DISPLAYMOVE = 0x08
const _LCD_MOVERIGHT = 0x04
const _LCD_MOVELEFT = 0x00
// Function set flags
const _LCD_4BITMODE = 0x00
const _LCD_2LINE = 0x08
const _LCD_1LINE = 0x00
const _LCD_5X8DOTS = 0x00
// Offset for up to 4 rows.
const _LCD_ROW_OFFSETS = [0x00, 0x40, 0x14, 0x54]
export class CharacterLCD {
columns: number
lines: number
reset: DigitalInOutPin;
enable: DigitalInOutPin;
dl4: DigitalInOutPin;
dl5: DigitalInOutPin;
dl6: DigitalInOutPin;
dl7: DigitalInOutPin;
displaycontrol: number
displayfunction: number
displaymode: number
_message: string;
_enable: boolean;
_rtl: boolean;
// pylint: disable-msg=too-many-arguments
constructor(rs: DigitalInOutPin, en: DigitalInOutPin,
d4: DigitalInOutPin, d5: DigitalInOutPin, d6: DigitalInOutPin, d7: DigitalInOutPin,
columns: number, lines: number) {
this._rtl = false;
this.columns = columns;
this.lines = lines;
// save pin numbers
this.reset = rs;
this.enable = en;
this.dl4 = d4;
this.dl5 = d5;
this.dl6 = d6;
this.dl7 = d7;
// set all pins as outputs
for (const pin of [rs, en, d4, d5, d6, d7]) {
pin.digitalWrite(false);
}
// Initialise the display
this._write8(0x33)
this._write8(0x32)
// Initialise display control
this.displaycontrol = _LCD_DISPLAYON | _LCD_CURSOROFF | _LCD_BLINKOFF
// Initialise display function
this.displayfunction = _LCD_4BITMODE | _LCD_1LINE | _LCD_2LINE | _LCD_5X8DOTS
// Initialise display mode
this.displaymode = _LCD_ENTRYLEFT | _LCD_ENTRYSHIFTDECREMENT
// Write to displaycontrol
this._write8(_LCD_DISPLAYCONTROL | this.displaycontrol)
// Write to displayfunction
this._write8(_LCD_FUNCTIONSET | this.displayfunction)
// Set entry mode
this._write8(_LCD_ENTRYMODESET | this.displaymode)
this.clear()
this._message = ""
this._enable = null
}
/**
* Moves the cursor "home" to position (1, 1).
**/
public home(): void {
this._write8(_LCD_RETURNHOME)
pause(3)
}
/** Clears everything displayed on the LCD.
* The following example displays, "Hello, world!", then clears the LCD.
**/
public clear(): void {
this._write8(_LCD_CLEARDISPLAY)
this._message = "";
pause(3)
}
/**
* True if cursor is visible. False to stop displaying the cursor.
*/
get cursor(): boolean {
return (this.displaycontrol & _LCD_CURSORON) == _LCD_CURSORON;
}
set cursor(show: boolean) {
const dc = this.displaycontrol;
if (show)
this.displaycontrol |= _LCD_CURSORON
else
this.displaycontrol &= ~_LCD_CURSORON
if (dc != this.displaycontrol)
this._write8(_LCD_DISPLAYCONTROL | this.displaycontrol)
}
/**
* Move the cursor to position ``column``, ``row``
**/
public setCursorPosition(column: number, row: number): void {
column = Math.max(0, Math.min(this.columns - 1, column | 0));
row = Math.max(0, Math.min(this.lines - 1, row | 0));
//console.log(`cursor ${_LCD_SETDDRAMADDR | column + _LCD_ROW_OFFSETS[row]}`)
this._write8(_LCD_SETDDRAMADDR | column + _LCD_ROW_OFFSETS[row])
}
/**
* Blink the cursor. True to blink the cursor. False to stop blinking.
*/
get blink(): boolean {
return (this.displaycontrol & _LCD_BLINKON) == _LCD_BLINKON
}
set blink(value: boolean) {
const dc = this.displaycontrol;
if (value)
this.displaycontrol |= _LCD_BLINKON
else
this.displaycontrol &= ~_LCD_BLINKON
if (dc != this.displaycontrol)
this._write8(_LCD_DISPLAYCONTROL | this.displaycontrol)
}
/**
* Enable or disable the display. True to enable the display. False to disable the display.
*/
get display(): boolean {
return (this.displaycontrol & _LCD_DISPLAYON) == _LCD_DISPLAYON
}
set display(enable: boolean) {
const dc = this.displaycontrol;
if (enable)
this.displaycontrol |= _LCD_DISPLAYON
else
this.displaycontrol &= ~_LCD_DISPLAYON
if (dc != this.displaycontrol)
this._write8(_LCD_DISPLAYCONTROL | this.displaycontrol)
}
/**
* Display a string of text on the character LCD.
*/
get message(): string {
return this._message
}
set message(message: string) {
if (message === undefined || message === null) message = "";
if (this._message === message) return; // nothing to do here
const oldMessage = this._message;
this._message = message;
//console.log(`'${oldMessage}' => '${message}'`);
const ltr = !!(this.displaymode & _LCD_ENTRYLEFT);
const oldLines = oldMessage.split('\n');
const lines = this._message.split('\n');
const rn = Math.min(this.lines, Math.max(oldLines.length, lines.length));
let cursorrow = -1;
let cursorcol = -1;
for (let row = 0; row < rn; ++row) {
let oldLine = oldLines[row];
if (oldLine === undefined) oldLine = "";
let line = lines[row];
if (line === undefined) line = "";
const cn = Math.min(this.columns, Math.max(oldLine.length, line.length));
for (let column = 0; column < cn; ++column) {
const oc = oldLine.charCodeAt(column);
const c = line.charCodeAt(column);
if (oc !== c) {
// letter is already shown, skip
// move cursor if needed
if (row != cursorrow || column != cursorcol) {
//console.log(`set cursor pos ${column}, ${row}`);
this.setCursorPosition(ltr ? column : this.columns - 1 - column, row);
cursorrow = row;
cursorcol = column;
}
//console.log(`write ${String.fromCharCode(c)} (${String.fromCharCode(oc)}) at ${cursorcol}, ${cursorrow}`);
if (column >= line.length)
this._write8(32 /* space */, true);
else
this._write8(c, true);
cursorcol++;
}
}
}
}
/**
* Moves displayed text left one column.
**/
public moveLeft(): void {
this._write8(_LCD_CURSORSHIFT | _LCD_DISPLAYMOVE | _LCD_MOVELEFT)
}
/**
* Moves displayed text right one column.
**/
public moveRight(): void {
this._write8(_LCD_CURSORSHIFT | _LCD_DISPLAYMOVE | _LCD_MOVERIGHT)
}
get rightToLeft(): boolean {
return this._rtl;
}
set rightToLeft(direction: boolean) {
if (this._rtl != direction) {
this._rtl = direction;
if (this._rtl)
this._right_to_left();
else
this._left_to_right();
}
}
private _left_to_right(): void {
// Displays text from left to right on the LCD.
this.displaymode |= _LCD_ENTRYLEFT
this._write8(_LCD_ENTRYMODESET | this.displaymode)
}
private _right_to_left(): void {
// Displays text from right to left on the LCD.
this.displaymode &= ~_LCD_ENTRYLEFT
this._write8(_LCD_ENTRYMODESET | this.displaymode)
}
/**
* Fill one of the first 8 CGRAM locations with custom characters.
* The location parameter should be between 0 and 7 and pattern should
* provide an array of 8 bytes containing the pattern. E.g. you can easily
* design your custom character at http://www.quinapalus.com/hd44780udg.html
* To show your custom character use, for example, ``lcd.message = ""``
*/
public create_char(location: number, pattern: Buffer): void {
// only position 0..7 are allowed
location &= 0x7
this._write8(_LCD_SETCGRAMADDR | location << 3)
for (let i = 0; i < 8; ++i) {
this._write8(pattern[i], true)
}
}
/**
* Sends 8b ``value`` in ``char_mode``.
* @param value bytes
* @param char_mode character/data mode selector. False (default) for data only, True for character bits.
*/
private _write8(value: number, char_mode = false): void {
__write8(value, char_mode);
// one ms delay to prevent writing too quickly.
pause(1)
// set character/data bit. (charmode = False)
this.reset.digitalWrite(char_mode)
// WRITE upper 4 bits
this.dl4.digitalWrite(!!(value >> 4 & 1));
this.dl5.digitalWrite(!!(value >> 5 & 1));
this.dl6.digitalWrite(!!(value >> 6 & 1));
this.dl7.digitalWrite(!!(value >> 7 & 1));
// send command
this._pulse_enable()
// WRITE lower 4 bits
this.dl4.digitalWrite(!!(value & 1));
this.dl5.digitalWrite(!!(value >> 1 & 1));
this.dl6.digitalWrite(!!(value >> 2 & 1));
this.dl7.digitalWrite(!!(value >> 3 & 1));
this._pulse_enable()
}
private _pulse_enable(): void {
// Pulses (lo->hi->lo) to send commands.
this.enable.digitalWrite(false);
control.waitMicros(1);
this.enable.digitalWrite(true);
control.waitMicros(1);
this.enable.digitalWrite(false);
control.waitMicros(1);
}
}
}