-
福利一海量deepseek资料包(持续更新)
-
福利二ComfyUI工作流&模型&插件
-
福利三AI工具集合包以及AI绘画解决方案
1602LCD是指显示的内容为16X2,即可以显示两行,每行16个字符液晶模块(显示字符和数字)市面上字符液晶大多数是基于HD44780液晶芯片的
1602 液晶显示屏除了电源、地以外,有 3 个控制引脚 RS R/W E 和 8 个数据引脚 DB0-7。
由图可知,LCD1602由很多引脚,使用单片机驱动LCD1602需要很多的IO引脚,为了减少IO引脚的使用数量,采用IIC通信减少IO口的使用数量
LCD1602的16个接口与单片机IO相连,其中8位数据总线D0-D7,和RS、R/W、EN三个控制端口,工作电压为5V,并且带有字符对比度调节V0和背光源AK。
| 序号 | 符号 | 功能 | 序号 | 符号 | 功能 |
|---|---|---|---|---|---|
| 1 | VSS | 电源地 | 9 | D2 | 数据 |
| 2 | VDD | 电源正极 | 10 | D3 | 数据 |
| 3 | V0 | 液晶显示偏压 | 11 | D4 | 数据 |
| 4 | RS | 数据/命令选择 | 12 | D5 | 数据 |
| 5 | R/W | 读/写选择 | 13 | D6 | 数据 |
| 6 | EN | 使能信号 | 14 | D7 | 数据 |
| 7 | D0 | 数据 | 15 | A | 背光源正极 |
| 8 | D1 | 数据 | 16 | K | 背光源负极 |
间接控制方式也称为四线制工作方式,实际上给1602屏增加一块IIC驱动版,将1602的16个管脚连接到由PCF8574T作为主要芯片的驱动版上,将接口转换为IIC再连接开发板
| 符号 | 功能 |
|---|---|
| VCC | 电源5V |
| GND | 接地 |
| SDA | 数据线 |
| SCK | 时钟线 |
由于 1602 的管脚数过多,如果直接与 ESP32 开发板连接需要占用大量的 GPIO 管脚,不但容易造成资源浪费,连接也非常不方便。
因此实际使用时往往会给 1602 屏增加一块 IIC 驱动版,将 1602 的 16 个管脚连接到由 PCF8574T 作为主要芯片的驱动版上,将接口转换为 IIC 再连接开发板
IIC 是一种硬件设备间常用的接口通讯协议,全称是 Inter-Integrated Circuit,也可以写为 I2C。他的设计时的理念是:信号线尽量少并且速率要尽量高。 信号线少,可以减少引脚占用,这对早期的芯片(引脚很少)的很重要。
使用 IIC 接口时一共需要连接四根线,包括:VCC、GND、SDA、SCL,其中 SDA 和 SCL 需要占用 GPIO 管脚,连接到开发板上任何一组 IIC 接口的对应管脚都可以。
标准的 I2C 需要两根信号线:
ESP32———I2C协议——->PCF8574———->1602LCD屏幕
| 模块引脚 | ESP32引脚 |
|---|---|
| GND | GND |
| VCC | 5V |
| SDA | GPIO13 |
| SCL | GPIO14 |

注意
注意需要使用开发板上的 5V 电压,而不是 3.3V。真实环境下使用 3.3V 会无法显示或者显示很暗。
下图标注的就是我们常常所说的 I2C 引脚接口,这里的接口指的就是硬件 I2C 接口,我们在软件中仅用 I2C 表示即可。
特点:
machine.SoftI2C(scl, sda, freq=400000, timeout=255): 构造一个新的 SoftI2C 对象。scl 应该是一个 pin 对象,指定用于 SCL 的 pin。sda 应该是一个 pin 对象,指定用于 SDA 的 pin。freq 应该是一个整数,用于设置 SCL 的最大频率。timeout 是等待时钟延长(SCL 被总线上的另一个设备保持为低电平)的最长时间(以微秒为单位),之后会引发OSError(ETIMEDOUT) 异常。machine.I2C(id, scl, sda, freq=400000, timeout=255): 构造一个新的硬件 I2C 对象。id 是 0、1,表示默认的 I2C 引脚,0 表示 scl=Pin(18), sad=Pin(19); 1 表示 scl=Pin(25), sad=Pin(26),注意:不能与 scl、sda 共用;scl 应该是一个 pin 对象,指定用于 SCL 的 pin。sda 应该是一个 pin 对象,指定用于 SDA 的 pin。freq 应该是一个整数,用于设置 SCL 的最大频率。timeout 是等待时钟延长(SCL 被总线上的另一个设备保持为低电平)的最长时间(以微秒为单位),之后会引发OSError(ETIMEDOUT) 异常。from machine import Pin, I2C
i2c = SoftI2C(scl=Pin(12), sda=Pin(13), freq=100000)
# 扫描设备,I2C 协议
i2c.scan()
# read 4 bytes from device with address 0x3a
i2c.readfrom(0x27, 4)
# write '12' to device with address 0x3a
i2c.writeto(0x27, '12')
# create a buffer with 10 bytes
buf = bytearray(10)
# write the given buffer to the peripheral
i2c.writeto(0x27, buf)
驱动代码, lcd_api.py :
'''libs/lcd_api.py'''
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)
第三方模块导入完成后,我们就可以写主程序了。
我们知道硬件 I2C 和软件 I2C 的区别在于,软件 I2C 是通过软件编程使 CPU 拉高拉低 SDA 和 SCL 引脚,模拟出 I2C 总线的;而硬件 I2C 则是使用 ESP32 内部的 I2C 硬件驱动器实现总线的读写。
很明显的,硬件 I2C 比软件 I2C 更加节约 CPU 资源,因为 CPU 不用去频繁操作 SDA 和 SCL 引脚了。
如果你操作屏幕频繁,硬件 I2C 将是你最佳的选择。如果我们使用硬件 I2C 的话,在 shell 中会跳出提示 Warning: I2C(-1, …) is deprecated, use SoftI2C(…) instead,意思不建议你使用 I2C,建议你使用 SoftI2C,所以我们的代码还是使用软件 I2C 总线吧。
from machine import Pin, SoftI2C, I2C
from libs.i2c_lcd import I2cLcd
# 定义 SoftI2C 控制对象
i2c = SoftI2C(sda=Pin(13), scl=Pin(14), freq=100000)
# 获取 I2C 从机地址
address = i2c.scan()[0]
# 定义 I2CLCD 对象
i2c_lcd = I2cLcd(i2c, address, 2, 16)
# 显示 Hello world
i2c_lcd.putstr('Hello, world!')