LCD(液晶显示器)是一种使用液晶材料的显示技术。
[接线图占位,后续添加]
文字说明(具体接法见下方代码注释):
VCC → Pico VBUS(5V,背光更亮;接 3V3 也能用但对比度偏弱)GND → Pico GNDSCL → Pico GP9(I2C0 SCL)SDA → Pico GP8(I2C0 SDA)0x27 或 0x3F,跑不出来就换另一个# ============ 接线 ============
# LCD VCC → Pico VBUS(5V;接 3V3 也可用但对比度弱)
# LCD GND → Pico GND
# LCD SCL → Pico GP9(I2C0 SCL)
# LCD SDA → Pico GP8(I2C0 SDA)
# I2C 地址常见 0x27 或 0x3F,下面 I2cLcd() 里地址不对就换
# =============================
from machine import I2C, Pin
from lcd_api import LcdApi
from pico_i2c_lcd import I2cLcd
import time
# 初始化 I2C
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400000)
# 初始化 LCD(16x2)
lcd = I2cLcd(i2c, 0x27, 2, 16) # 地址 0x27,2行16列
print('LCD 显示测试')
# 清屏
lcd.clear()
# 显示文本
lcd.putstr('Hello, World!')
time.sleep(2)
# 第二行显示
lcd.clear()
lcd.putstr('Line 1')
lcd.move_to(0, 1)
lcd.putstr('Line 2')
time.sleep(2)
# 闪烁光标
lcd.clear()
lcd.putstr('Blinking')
lcd.blink_cursor_on()
time.sleep(2)
lcd.blink_cursor_off()
# ============ 接线 ============
# 接线同基础示例:VCC→VBUS,GND→GND,SCL→GP9,SDA→GP8
# 电位器:两端脚 → 3V3 和 GND,中间抽头 → GP26(ADC0)
# =============================
from machine import I2C, Pin, ADC
from lcd_api import LcdApi
from pico_i2c_lcd import I2cLcd
import time
# 初始化 I2C 和 LCD
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400000)
lcd = I2cLcd(i2c, 0x27, 2, 16)
# 初始化 ADC(电位器)
adc = ADC(Pin(26))
print('LCD 显示传感器数据')
while True:
# 读取电位器值
value = adc.read_u16()
voltage = value * 3.3 / 65535
# 清屏
lcd.clear()
# 显示数据
lcd.putstr(f'Value: {value}')
lcd.move_to(0, 1)
lcd.putstr(f'Voltage: {voltage:.2f}V')
time.sleep(0.5)
# ============ 接线 ============
# 接线同基础示例:VCC→VBUS,GND→GND,SCL→GP9,SDA→GP8
# =============================
from machine import I2C, Pin
from lcd_api import LcdApi
from pico_i2c_lcd import I2cLcd
import time
# 初始化 I2C 和 LCD
i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=400000)
lcd = I2cLcd(i2c, 0x27, 2, 16)
print('LCD 时钟')
while True:
# 获取当前时间
t = time.localtime()
time_str = f'{t[3]:02d}:{t[4]:02d}:{t[5]:02d}'
date_str = f'{t[0]}-{t[1]:02d}-{t[2]:02d}'
# 清屏
lcd.clear()
# 显示时间和日期
lcd.putstr(time_str)
lcd.move_to(0, 1)
lcd.putstr(date_str)
time.sleep(1)
Q: LCD 不显示怎么办? A: 检查接线是否正确,确保 I2C 地址正确,检查电源是否正常。
Q: 显示内容乱码怎么办? A: 检查字符编码,确保使用正确的字体。
Q: 如何提高显示速度? A: 减少刷新频率,只更新变化的部分。
本页示例用到 lcd_api 和 pico_i2c_lcd 两个库,MicroPython 固件不自带。
注意:两个都要存,少存一个都会报错(pico_i2c_lcd 内部会 from lcd_api import LcdApi)。
操作步骤(Thonny)
lcd_api.py 的代码整段粘贴进去lcd_api.py,保存pico_i2c_lcd.py 的代码粘贴进去pico_i2c_lcd.py,存到 Pico 上import time
class LcdApi:
# Implements the API for talking with HD44780 compatible character LCDs.
# This class only knows what commands to send to the LCD, and not how to get
# them to the LCD.
#
# It is expected that a derived class will implement the hal_xxx functions.
#
# The following constant names were lifted from the avrlib lcd.h header file,
# with bit numbers changed to bit masks.
# HD44780 LCD controller command set
LCD_CLR = 0x01 # DB0: clear display
LCD_HOME = 0x02 # DB1: return to home position
LCD_ENTRY_MODE = 0x04 # DB2: set entry mode
LCD_ENTRY_INC = 0x02 # DB1: increment
LCD_ENTRY_SHIFT = 0x01 # DB0: shift
LCD_ON_CTRL = 0x08 # DB3: turn lcd/cursor on
LCD_ON_DISPLAY = 0x04 # DB2: turn display on
LCD_ON_CURSOR = 0x02 # DB1: turn cursor on
LCD_ON_BLINK = 0x01 # DB0: blinking cursor
LCD_MOVE = 0x10 # DB4: move cursor/display
LCD_MOVE_DISP = 0x08 # DB3: move display (0-> move cursor)
LCD_MOVE_RIGHT = 0x04 # DB2: move right (0-> left)
LCD_FUNCTION = 0x20 # DB5: function set
LCD_FUNCTION_8BIT = 0x10 # DB4: set 8BIT mode (0->4BIT mode)
LCD_FUNCTION_2LINES = 0x08 # DB3: two lines (0->one line)
LCD_FUNCTION_10DOTS = 0x04 # DB2: 5x10 font (0->5x7 font)
LCD_FUNCTION_RESET = 0x30 # See "Initializing by Instruction" section
LCD_CGRAM = 0x40 # DB6: set CG RAM address
LCD_DDRAM = 0x80 # DB7: set DD RAM address
LCD_RS_CMD = 0
LCD_RS_DATA = 1
LCD_RW_WRITE = 0
LCD_RW_READ = 1
def __init__(self, num_lines, num_columns):
self.num_lines = num_lines
if self.num_lines > 4:
self.num_lines = 4
self.num_columns = num_columns
if self.num_columns > 40:
self.num_columns = 40
self.cursor_x = 0
self.cursor_y = 0
self.implied_newline = False
self.backlight = True
self.display_off()
self.backlight_on()
self.clear()
self.hal_write_command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC)
self.hide_cursor()
self.display_on()
def clear(self):
# Clears the LCD display and moves the cursor to the top left corner
self.hal_write_command(self.LCD_CLR)
self.hal_write_command(self.LCD_HOME)
self.cursor_x = 0
self.cursor_y = 0
def show_cursor(self):
# Causes the cursor to be made visible
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
self.LCD_ON_CURSOR)
def hide_cursor(self):
# Causes the cursor to be hidden
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
def blink_cursor_on(self):
# Turns on the cursor, and makes it blink
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
self.LCD_ON_CURSOR | self.LCD_ON_BLINK)
def blink_cursor_off(self):
# Turns on the cursor, and makes it no blink (i.e. be solid)
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
self.LCD_ON_CURSOR)
def display_on(self):
# Turns on (i.e. unblanks) the LCD
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
def display_off(self):
# Turns off (i.e. blanks) the LCD
self.hal_write_command(self.LCD_ON_CTRL)
def backlight_on(self):
# Turns the backlight on.
# This isn't really an LCD command, but some modules have backlight
# controls, so this allows the hal to pass through the command.
self.backlight = True
self.hal_backlight_on()
def backlight_off(self):
# Turns the backlight off.
# This isn't really an LCD command, but some modules have backlight
# controls, so this allows the hal to pass through the command.
self.backlight = False
self.hal_backlight_off()
def move_to(self, cursor_x, cursor_y):
# Moves the cursor position to the indicated position. The cursor
# position is zero based (i.e. cursor_x == 0 indicates first column).
self.cursor_x = cursor_x
self.cursor_y = cursor_y
addr = cursor_x & 0x3f
if cursor_y & 1:
addr += 0x40 # Lines 1 & 3 add 0x40
if cursor_y & 2: # Lines 2 & 3 add number of columns
addr += self.num_columns
self.hal_write_command(self.LCD_DDRAM | addr)
def putchar(self, char):
# Writes the indicated character to the LCD at the current cursor
# position, and advances the cursor by one position.
if char == '\n':
if self.implied_newline:
# self.implied_newline means we advanced due to a wraparound,
# so if we get a newline right after that we ignore it.
pass
else:
self.cursor_x = self.num_columns
else:
self.hal_write_data(ord(char))
self.cursor_x += 1
if self.cursor_x >= self.num_columns:
self.cursor_x = 0
self.cursor_y += 1
self.implied_newline = (char != '\n')
if self.cursor_y >= self.num_lines:
self.cursor_y = 0
self.move_to(self.cursor_x, self.cursor_y)
def putstr(self, string):
# Write the indicated string to the LCD at the current cursor
# position and advances the cursor position appropriately.
for char in string:
self.putchar(char)
def custom_char(self, location, charmap):
# Write a character to one of the 8 CGRAM locations, available
# as chr(0) through chr(7).
location &= 0x7
self.hal_write_command(self.LCD_CGRAM | (location << 3))
self.hal_sleep_us(40)
for i in range(8):
self.hal_write_data(charmap[i])
self.hal_sleep_us(40)
self.move_to(self.cursor_x, self.cursor_y)
def hal_backlight_on(self):
# Allows the hal layer to turn the backlight on.
# If desired, a derived HAL class will implement this function.
pass
def hal_backlight_off(self):
# Allows the hal layer to turn the backlight off.
# If desired, a derived HAL class will implement this function.
pass
def hal_write_command(self, cmd):
# Write a command to the LCD.
# It is expected that a derived HAL class will implement this function.
raise NotImplementedError
def hal_write_data(self, data):
# Write data to the LCD.
# It is expected that a derived HAL class will implement this function.
raise NotImplementedError
def hal_sleep_us(self, usecs):
# Sleep for some time (given in microseconds)
time.sleep_us(usecs)
import utime
import gc
from lcd_api import LcdApi
from machine import I2C
# PCF8574 pin definitions
MASK_RS = 0x01 # P0
MASK_RW = 0x02 # P1
MASK_E = 0x04 # P2
SHIFT_BACKLIGHT = 3 # P3
SHIFT_DATA = 4 # P4-P7
class I2cLcd(LcdApi):
#Implements a HD44780 character LCD connected via PCF8574 on I2C
def __init__(self, i2c, i2c_addr, num_lines, num_columns):
self.i2c = i2c
self.i2c_addr = i2c_addr
self.i2c.writeto(self.i2c_addr, bytes([0]))
utime.sleep_ms(20) # Allow LCD time to powerup
# Send reset 3 times
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
utime.sleep_ms(5) # Need to delay at least 4.1 msec
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
utime.sleep_ms(1)
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
utime.sleep_ms(1)
# Put LCD into 4-bit mode
self.hal_write_init_nibble(self.LCD_FUNCTION)
utime.sleep_ms(1)
LcdApi.__init__(self, num_lines, num_columns)
cmd = self.LCD_FUNCTION
if num_lines > 1:
cmd |= self.LCD_FUNCTION_2LINES
self.hal_write_command(cmd)
gc.collect()
def hal_write_init_nibble(self, nibble):
# Writes an initialization nibble to the LCD.
# This particular function is only used during initialization.
byte = ((nibble >> 4) & 0x0f) << SHIFT_DATA
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))
gc.collect()
def hal_backlight_on(self):
# Allows the hal layer to turn the backlight on
self.i2c.writeto(self.i2c_addr, bytes([1 << SHIFT_BACKLIGHT]))
gc.collect()
def hal_backlight_off(self):
#Allows the hal layer to turn the backlight off
self.i2c.writeto(self.i2c_addr, bytes([0]))
gc.collect()
def hal_write_command(self, cmd):
# Write a command to the LCD. Data is latched on the falling edge of E.
byte = ((self.backlight << SHIFT_BACKLIGHT) |
(((cmd >> 4) & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))
byte = ((self.backlight << SHIFT_BACKLIGHT) |
((cmd & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))
if cmd <= 3:
# The home and clear commands require a worst case delay of 4.1 msec
utime.sleep_ms(5)
gc.collect()
def hal_write_data(self, data):
# Write data to the LCD. Data is latched on the falling edge of E.
byte = (MASK_RS |
(self.backlight << SHIFT_BACKLIGHT) |
(((data >> 4) & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))
byte = (MASK_RS |
(self.backlight << SHIFT_BACKLIGHT) |
((data & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))
gc.collect()