Raspberry Pi Pico
16x2 Character LCD
I had a 3V3 16x2 character LCD display that I had been trying to get running on the Pico. You can find displays like these with 'backpacks' that connect the inputs to port expanders. This isn't one of those. It is just the display itself. If you are doing this, make sure to check that your LCD is for 3V3.
Here is the display connected to my Pimoroni Pico LiPo. Under the jumpers, you might just be able to make out the potentiometer. This is used to set the contrast.
This is a Fritzing diagram showing the connections more clearly.
I find that a list of connections is a little easier to work with. So,
- VSS to GND
- VDD to 3V3
- VO to Potentiometer Wiper
- RS to GP15
- R/W to GND
- E to GP14
- DB4 to GP16
- DB5 to GP17
- DB6 to GP18
- DB7 to GP19
- A to 3V3
- K to GND
After several attempts at getting the display to work, I turned to Adafruit's CircuitPython library.
(https://github.com/adafruit/Adafruit_CircuitPython_CharLCD)
I made the changes needed to convert to MicroPython and left out some of the stuff I didn't need. I left in all of the constants. The library was saved as lcd.py.
from time import sleep_us, sleep_ms from micropython import const # Commands _LCD_CLEARDISPLAY = const(0x01) _LCD_RETURNHOME = const(0x02) _LCD_ENTRYMODESET = const(0x04) _LCD_DISPLAYCONTROL = const(0x08) _LCD_CURSORSHIFT = const(0x10) _LCD_FUNCTIONSET = const(0x20) _LCD_SETCGRAMADDR = const(0x40) _LCD_SETDDRAMADDR = const(0x80) # Entry flags _LCD_ENTRYLEFT = const(0x02) _LCD_ENTRYSHIFTDECREMENT = const(0x00) # Control flags _LCD_DISPLAYON = const(0x04) _LCD_CURSORON = const(0x02) _LCD_CURSOROFF = const(0x00) _LCD_BLINKON = const(0x01) _LCD_BLINKOFF = const(0x00) # Move flags _LCD_DISPLAYMOVE = const(0x08) _LCD_MOVERIGHT = const(0x04) _LCD_MOVELEFT = const(0x00) # Function set flags _LCD_4BITMODE = const(0x00) _LCD_2LINE = const(0x08) _LCD_1LINE = const(0x00) _LCD_5X8DOTS = const(0x00) # Offset for up to 4 rows. _LCD_ROW_OFFSETS = (0x00, 0x40, 0x14, 0x54) class LCD: def __init__(self, rs, en, d4, d5, d6, d7, columns, lines): self.columns = columns self.lines = lines self.reset = rs self.enable= en self.dl4 = d4 self.dl5 = d5 self.dl6 = d6 self.dl7 = d7 self._write8(0x33) self._write8(0x32) # Initialise display control self.displaycontrol = _LCD_DISPLAYON | _LCD_CURSOROFF | _LCD_BLINKOFF # Initialise display function self.displayfunction = _LCD_4BITMODE | _LCD_1LINE | _LCD_2LINE | _LCD_5X8DOTS # Initialise display mode self.displaymode = _LCD_ENTRYLEFT | _LCD_ENTRYSHIFTDECREMENT # Write to displaycontrol self._write8(_LCD_DISPLAYCONTROL | self.displaycontrol) # Write to displayfunction self._write8(_LCD_FUNCTIONSET | self.displayfunction) # Set entry mode self._write8(_LCD_ENTRYMODESET | self.displaymode) self.clear() self._message = None self._enable = None self._direction = None # track row and column used in cursor_position # initialize to 0,0 self.row = 0 self.column = 0 self._column_align = False def home(self): self._write8(_LCD_RETURNHOME) sleep_ms(3) def clear(self): self._write8(_LCD_CLEARDISPLAY) sleep_ms(3) def column_align(self, enable): if isinstance(enable, bool): self._column_align = enable else: raise ValueError("The column_align value must be either True or False") def cursor(self, show): if show: self.displaycontrol |= _LCD_CURSORON else: self.displaycontrol &= ~_LCD_CURSORON self._write8(_LCD_DISPLAYCONTROL | self.displaycontrol) def cursor_position(self, column, row): # Clamp row to the last row of the display if row >= self.lines: row = self.lines - 1 # Clamp to last column of display if column >= self.columns: column = self.columns - 1 # Set location self._write8(_LCD_SETDDRAMADDR | (column + _LCD_ROW_OFFSETS[row])) # Update self.row and self.column to match setter self.row = row self.column = column def blink(self, blink): if blink: self.displaycontrol |= _LCD_BLINKON else: self.displaycontrol &= ~_LCD_BLINKON self._write8(_LCD_DISPLAYCONTROL | self.displaycontrol) def display(self, enable): if enable: self.displaycontrol |= _LCD_DISPLAYON else: self.displaycontrol &= ~_LCD_DISPLAYON self._write8(_LCD_DISPLAYCONTROL | self.displaycontrol) def message(self, message): self._message = message line = self.row initial_character = 0 for character in message: if initial_character == 0: if self.displaymode & _LCD_ENTRYLEFT > 0: col = self.column else: col = self.columns - 1 - self.column self.cursor_position(col, line) initial_character += 1 if character == "\n": line += 1 if self.displaymode & _LCD_ENTRYLEFT > 0: col = self.column * self._column_align else: if self._column_align: col = self.column else: col = self.columns - 1 self.cursor_position(col, line) else: self._write8(ord(character), True) self.column, self.row = 0, 0 def create_char(self, location, pattern): location &= 0x7 self._write8(_LCD_SETCGRAMADDR | (location << 3)) for i in range(8): self._write8(pattern[i], char_mode=True) def _write8(self, value, char_mode=False): sleep_ms(1) self.reset.value(char_mode) # set character/data bit. (charmode = False) self.reset.value(char_mode) # WRITE upper 4 bits self.dl4.value(((value >> 4) & 1) > 0) self.dl5.value(((value >> 5) & 1) > 0) self.dl6.value(((value >> 6) & 1) > 0) self.dl7.value(((value >> 7) & 1) > 0) # send command self._pulse_enable() # WRITE lower 4 bits self.dl4.value((value & 1) > 0) self.dl5.value(((value >> 1) & 1) > 0) self.dl6.value(((value >> 2) & 1) > 0) self.dl7.value(((value >> 3) & 1) > 0) self._pulse_enable() def _pulse_enable(self): self.enable.value(0) sleep_us(1) self.enable.value(1) sleep_us(1) self.enable.value(0) sleep_us(0)
Here is some code testing a handful of the features of the library. I have also created a custom character. You can have up to 8 of these, stored at locations 0 to 7. You use a hex escape sequence to insert them into your message. The character is defined as a list of binary integers, each representing the pixel data of a 5 column row. The characters are 5 pixels wide and 8 pixels tall. I did a stickperson but you may need to design characters that otherwise would not display.
from machine import Pin from time import sleep from lcd import LCD rs = Pin(15, Pin.OUT) en = Pin(14, Pin.OUT) d4 = Pin(16, Pin.OUT) d5 = Pin(17, Pin.OUT) d6 = Pin(18, Pin.OUT) d7 = Pin(19, Pin.OUT) # make a display object display = LCD(rs, en, d4, d5, d6, d7, 16, 2) # basic message test display.message("Hello World!") sleep(3) # clear the screen display.clear() sleep(1) # show the cursor display.cursor(True) sleep(2) display.message("Hello again!") sleep(2) # hide the cursor display.cursor(False) sleep(2) #fill the display display.home() display.message("0123456789ABCDEF\n0123456789ABCDEF") sleep(3) display.clear() # create character stickperson = [0xe,0xe,0x4,0x1f,0x4,0x4,0xa,0x11] display.create_char(0, stickperson) display.message("\x00 is a custom\ncharacter.") sleep(3) display.clear() for i in range(100): display.message(str(i)) sleep(0.25)