一起玩转树莓派(11)——使用LCD屏(二)

二、带I2C模块的LCD 1602


前面我们说过,LCD1602有16个引脚。原则上我们已经可以使用树莓派来控制显示屏的显示了,但是16个引脚全部连接到树莓派会使接线十分的复杂,而且程序代码的编写也非常繁琐,要对太多的GPIO引脚进行操作,十分不便。本次实验,我们采用的是带I2C模块的LCD元件,I2C模块本身将一些独立的功能进行了封装,通过I2C模块,我们可以以8位数据为标准来传输任何我们想要执行的指令或让LCD显示的字符,非常方便。


一起玩转树莓派(11)——使用LCD屏(二)


如上图所示,有了IC2模块的LCD1602元件,只需要4个引脚即可实现显示功能。下面我们来分析下如何使用此I2C模块。


首先关于I2C通信的相关内容,之前博客已经有详细的介绍,这里不再赘述。我们先来介绍下为何通过4个引脚通过I2C总线传输8位的数据集合实现所有功能。


LCD引脚1与引脚2:用I2C模块的电源引脚和接地引脚代替。


LCD引脚15和引脚16:LCD的这两个引脚功能为控制背光,此逻辑被封装进了I2C模块中,I2C模块每次写入的8位数据中的第4位用来控制背光。


LCD的引脚3:LCD的此引脚用来设置显示的对比度,在I2C模块中,通过一个可调节的电阻来实现此功能,在后面的实验中如果发现屏幕显示不清,可以尝试调节此电阻器。


LCD的引脚4,引脚5和引脚6:这几个功能引脚也被封装进了I2C模块中,I2C模块每次写入的8位数据中的第1位,的2位和第3位分别用来控制这些引脚。


LCD剩下的数据引脚的数据由I2C传输的8位数据中的高4位来对应,在LCD的8位数据模式下,I2C分两次传输一次完整的数据,前传输的4位为LCD所需数据的低4位,后传输的数据为LCD所需数据的高4位。在LCD的4位数据模式下,因为LCD需要获取到完整的8位数据,因此也需要通过两次数据传输,只是此时先传输的数据为LCD所需数据的高4位,后传输的数据为LCD所需数据的低4位,这点需要特别注意。


下面总结了I2C在传输数据时每一位的意义:


第8位 第7位 第6位 第5位 第4位 第3位 第2位 第1位

数据/指令 数据/指令 数据/指令 数据/指令 背光控制位 Enable控制位 RW控制位 RS控制位

三、编码实验


   使用I2C模块封装的LCD1602只有4个引脚,接线非常简单,如下:


LCD 树莓派

GND GND

VCC +5V

SDA 树莓派SDA功能引脚

SCL 树莓派SCL功能引脚

将LCD1602与树莓派连接完成后,在树莓派的终端执行如下指令查看I2C设备:


sudo i2cdetect -y 1

输出入下图所示:


一起玩转树莓派(11)——使用LCD屏(二)


可以看到,目前我们只连接了一个I2C设备,设备号为27。


   由于背光的控制位相对独立,我们封装单独的函数来处理,如下:


# 是否开启背光 由PCF8574T的低4位中的第4位决定

BLEN = 1

# 补充背光控制位

def addBlenControl(data):

   global BLEN

   tmpData = data

   if BLEN:

       # 将第4位背光控制位强制设置1

       tmpData = data | 0b00001000

   else:

       # 将第4位背光控制位强制设置为0

       tmpData = data & 0b11110111

   return tmpData    

同理,可以将Enable位的控制,RS位的控制都封装成函数:


# 补充Enable控制位

def addEnableControl(data, high):

   tempData = data

   # 第3位控制Enable

   if high:

       tempData |= 0b00000100

   else:

       tempData &= 0b11111011

   return tempData


# 补充RS控制位

def addRSControl(data, high):

   tempData = data

   # 第1位控制RS

   if high:

       tempData |= 0b00000001

   else:

       tempData &= 0b11111110

   return tempData

在向I2C发送数据前,根据配置的背光设置来决定背光控制位的值:


# 通过I2C总线写入数据

def writeI2C(addr, data):

   # 添加背光控制

   temp = addBlenControl(data)

   # 写数据到I2C总线

   BUS.write_byte(addr ,temp)

准备好了这些工具函数,我们只需要根据LCD1602的指令手册来设置具体的功能,发送要展示的数据即可,完整的代码如下:


#coding:utf-8

import time

import smbus

BUS = smbus.SMBus(1)

# LCD屏幕的硬件地址

LCD_ADDR = 0x27

# 是否开启背光 由PCF8574T的低4位中的第4位决定

BLEN = 1


# 补充背光控制位

def addBlenControl(data):

   global BLEN

   tmpData = data

   if BLEN:

       # 将第4位背光控制位强制设置1

       tmpData = data | 0b00001000

   else:

       # 将第4位背光控制位强制设置为0

       tmpData = data & 0b11110111

   return tmpData    


# 补充Enable控制位

def addEnableControl(data, high):

   tempData = data

   # 第3位控制Enable

   if high:

       tempData |= 0b00000100

   else:

       tempData &= 0b11111011

   return tempData


# 补充RS控制位

def addRSControl(data, high):

   tempData = data

   # 第1位控制RS

   if high:

       tempData |= 0b00000001

   else:

       tempData &= 0b11111110

   return tempData


# 通过I2C总线写入数据

def writeI2C(addr, data):

   # 添加背光控制

   temp = addBlenControl(data)

   # 写数据到I2C总线

   BUS.write_byte(addr ,temp)


# 发送指令到LCD1602

def sendCommand(comm):

   # comm高4位数据传输

   # 低4位先清空

   buf = comm & 0b11110000

   # 先将Enable置为高电平

   buf = addEnableControl(buf, 1)

   # 设置为指令模式

   buf = addRSControl(buf, 0)

   # 写入指令

   writeI2C(LCD_ADDR ,buf)

   time.sleep(0.002)

   # 将Enable置为低电平 使产生低电平跳变来执行指令

   buf = addEnableControl(buf, 0)          

   writeI2C(LCD_ADDR ,buf)

   

   # comm低4位数据传输

   # 高4位先清空 并将低4位的数据移动到高4位

   buf = (comm & 0b00001111) << 4

   # 当次指令的低4位用来 做enable re rew的控制

   # 先将Enable置为高电平

   buf = addEnableControl(buf, 1)

   writeI2C(LCD_ADDR ,buf)

   time.sleep(0.002)

   # 将Enable置为低电平 使产生低电平跳变来执行指令

   buf = addEnableControl(buf, 0)    

   writeI2C(LCD_ADDR ,buf)


# 发送数据到LCD

def sendData(data):

   # data高4位数据传输

   # 低4位先清空

   buf = data & 0b11110000

   # 先将Enable置为高电平

   buf = addEnableControl(buf, 1)

   # 设置为数据模式

   buf = addRSControl(buf, 1)  

   writeI2C(LCD_ADDR ,buf)

   time.sleep(0.002)

   # 将Enable置为低电平 使产生低电平跳变来执行指令

   buf = addEnableControl(buf, 0)  

   writeI2C(LCD_ADDR ,buf)

   

   # data低4位数据传输

   buf = (data & 0b00001111) << 4

   # 先将Enable置为高电平

   buf = addEnableControl(buf, 1)

   # 设置为数据模式

   buf = addRSControl(buf, 1)

   writeI2C(LCD_ADDR ,buf)

   time.sleep(0.002)

   # 将Enable置为低电平 使产生低电平跳变来执行指令

   buf = addEnableControl(buf, 0)  

   writeI2C(LCD_ADDR ,buf)


# 初始化方法

def initLCD():

   # 启动时,LCD1602为8位模式 I2C传输数据时先传输的为低位数据

   # 因此实际上的指令为 0b00100011

   # 为指令6 将LCD1602设置为4位总线模式

   sendCommand(0b00110010)

   time.sleep(0.005)


   # 之后的指令都是4位总线模式

   sendCommand(0b00110010)

   time.sleep(0.005)

   # 指令4 设置屏幕开启,无光标,无闪烁

   sendCommand(0b00001100)

   time.sleep(0.005)

   # 指令1 清屏

   sendCommand(0b00000001)


# 设置屏幕要展示的文案 x,y确定位置

def printLCD(x, y, str):

   # 2行 16 列

   if x < 0:

       x = 0

   if x > 15:

       x = 15

   if y <0:

       y = 0

   if y > 1:

       y = 1

   # 指令7 设置数据要展示的位置

   addr = 0b10000000 + 0b00000100 * y + x

   sendCommand(addr)

   # 开始发送字符数据到LCD1602的数据寄存器

   for chr in str:

       # ord函数可以获取字符的ascil

       sendData(ord(chr))

# 主程序

initLCD()

printLCD(0, 0, 'Hello, world!')

time.sleep(2)

sendCommand(0b00000001)

time.sleep(0.002)

printLCD(0, 0, 'Love China!')

time.sleep(2)

sendCommand(0b00000001)

time.sleep(0.002)

printLCD(0, 0, 'Great Raspberry!')

如上代码所示,所有的指令都采用的二进制的方式,便于对比指令手册进行理解。更多时候我们会采用十六进制数字来编写指令,这样会使代码看的干净很多。上面的示例代码是一个简单的应用程序,运行后可以直接在LCD屏幕上展示3句话:


Hello World!

Love China!

Great Raspberry!

其实此程序也是一个完整的LCD1602驱动,初始化完成后,我们可以通过其提供额sendData方法来实现各种各样的显示需求。效果如下图所示:

一起玩转树莓派(11)——使用LCD屏(二)

上一篇:一起玩转树莓派(14)——超声测距


下一篇:一起玩转树莓派(13)——雨滴检测传感器