课程导入

生活中,我们处处可见显示屏,电视、电脑、手机、车载显示屏、商场里的液晶广告牌……各种各样的可视化设备,为人机交互提供了各种可能。在我们之前的项目中,要想将各种数据可视化,我们一般都依赖于 Thonny 自带的交互区,但在更多的项目中,并不能总是将 Pico 连接到电脑,这时我们就需要使用一些其他的电子硬件来完成这部分工作,这时各种各样的显示屏设备就派上了用场。在各种开源硬件项目中,常见的显示屏有 LCD 显示屏、 LED 显示屏、 3D 显示屏、 OLED 显示屏等,他们作为显示设备各有优劣,根据各自的特征可以被应用于不同的领域和场景。在本节课中,我们就需要使用其中一种显示屏—— LCD 显示屏来实现数据的可视化。

知识锦囊

LCD显示屏

LCD 显示屏,就是我们常说的液晶显示屏,1888年,奥地利植物学家莱尼茨尔发现了液晶,它是一种拥有两个熔点的奇怪有机化合物。
Friedrich_Reinitzer_01.jpgOtto_Lehmann.jpg
莱尼茨尔 莱曼
当科学家将固态的液晶晶体加热到145℃时,液晶会第一次融化为浑浊的液体,如果继续将液晶加热到175℃时,它会再次熔化,变成清澈透明的液体。后来,德国物理学家莱曼通过仔细的观察发现,当液晶荣华围浑浊液体时,外观上虽然属于液体,但却显示出晶体特有的双折射性。于是莱曼将其命名为“液态晶体”,这就是“液晶”名称的由来。莱尼茨尔和雷曼后来被誉为液晶之父。液晶自被发现后,人们并不知道它有何用途,直到1968年人们才把它作为电子工业上的的材料。
第9课 数据的可视化之旅,LCD屏的使用 - 图3
1968年美国 RCA 公司发现,当液晶受不同电场影响时液晶分子会转向不同的方向,从而改变液晶对光线的折射系数,进而影响光线的行进方向,之后通过偏光片过滤掉行进方向不正确的光线,以此来决定 LCD 显示屏的最终成像效果。
pico插图-09.png
值得注意的是,液晶本身并不发光,它只能改变穿过它的光的行进方向,所以在液晶显示器中,除了液晶层,我们往往还需要一个背光面板来作为光源。通过这一特性,该公司研制了动态散射形液晶显示器,并于1972年利用该技术制造出了第一款液晶手表,从这时起 LCD 技术才逐渐走向实用化。
在本节课中我们所使用的 Grove - 16 x 2 LCD 模块即为一块 LCD 显示屏,16x2表示该 LCD 显示屏可以分两行显示数据,且每行可以显示16个字符,两行共32个字符。
image.png

I2C总线

我们所使用的 Grove - 16 x 2 LCD 模块是一种使用 I2C 通信协议与主板通信的电子模块。I2C是由 Philips 公司开发的一种简单、双向二线制同步串行总线。它只需要两根数据线即可在连接于总线上的器件之间传送信息。I2C 上的通信发生在两条线路上:一条时钟线(通常被称为 SCL )和一条数据线(通常被称为 SDA )。
所以在对 I2C 接入的模块配置引脚时,我们需要同时为两条线路都配置好引脚。

引入外部函数库

在前面的课程中我们在程序中定义函数并进行了使用,但如果你想要重新编写一个新的程序,那么你定义的所有的函数和变量就都消失了。
为此 Python 提供了一个解决办法,把这些定义函数或变量的程序都存放在特定的文件中,当我们想要使用这些定义时,只需要在程序的开头使用 “import” 引用这个文件即可,这个文件就被我们称为库文件。
简单来讲,库文件是一个包含所有你定义的函数和变量的文件,其后缀名是 .py。库文件可以被别的程序引入,以使用该库文件中的函数等功能。
对于 Python 来说,库一般分为两大类,其一是 Python 自带的标准库, Python 的标准库中包含许多在项目开发时常用的函数库,在之前的课程中我们所使用的 machine 库,utime 库都是 Python 的标准库之一。
另一种库是由其他 Python 使用者自主开发的第三方库,得益于 Python 社区的壮大,在使用 Python 完成项目时,你可以找到许多非常有帮助的函数库。
对于标准库而言,在使用标准库时我们不需要进行额外的操作,直接在程序中引用它们即可,而对于第三方库来说,我们在使用它们时,需要先将库文件保存到 Pico 中,再进行使用。
例如在本节课中,我们需要使用第三方库来控制Grove - 16 x 2 LCD 显示屏,就需要先将其保存到 Pico 中。
1.首先点击下载我们所要使用的lcd1602.py第三方库文件,将它保存到电脑中。
2.将 Pico 连接到电脑,使用 Thonny 打开该文件,打开左上方的文件选项选择另存为。
9-0.png
3.选择 “Raspbarry Pi Pico” ,将其保存到 Pico 中
9-1.png
在进行存储时,Thonny 会让你为该文件命名,我们在此填写 lcd1602.py 作为该文件的名字。值得注意的是在命名时我们需要同时填写该文件的后缀名,不然无法进行使用。
9-3.png

实践操作

项目一:使用 LCD 屏显示 Hello,world!

添加好库文件后,我们先来编写一个简单的程序,让屏幕显示Hello,World。

硬件连接

在该项目中,我们所需要使用的硬件有:

  • 树莓派 Pico
  • 树莓派 Pico 拓展板
  • Grove - 16 x 2 LCD 显示屏

插接好 Pico 和拓展板,使用 Grove 数据线将 LCD 显示屏连接到 I2C1 接口。
页面_10.png

编写程序

首先引入需要使用的函数库,包括刚才已经保存到 Pico 中的第三方函数库 lcd1602 。

  1. from lcd1602 import LCD1602
  2. from machine import I2C,Pin
  3. from utime import sleep

lcd 库中可用的函数如下:

  • display() 开启显示屏
  • no_display() 关闭显示屏
  • clear() 清除当前显示,并使光标归位
  • setCursor(col, row) 设置显示位置,col 为行数,row 为列数。
  • print(text) 显示字符

接着进行引脚的定义,与其他引脚定义不同,I2C 引脚需要分别定义 SCL 和 SDA 两条数据线。

  1. i2c = I2C(1,scl=Pin(7), sda=Pin(6), freq=400000)

它的写法为 machine.I2C(id,*,scl,sda,freq=400000),其中

  • id 标识特定的 I2C 外设。允许的值取决于特定的端口/板
  • scl 会使用 Pin 函数指定用于 SCL 的引脚
  • sda 会使用 Pin 函数指定用于 SDA 的引脚
  • freq 是设置 SCL 的最大频率的整数,一般根据所使用设备不同来进行调整,在这里我们将其设置为40000

最后使用库中函数完成显示 “Hello,World” 的操作。程序就完成了,完整的程序如下:

  1. from lcd1602 import LCD1602
  2. from machine import I2C,Pin
  3. from utime import sleep
  4. i2c = I2C(1,scl=Pin(7), sda=Pin(6), freq=400000)
  5. d = LCD1602(i2c, 2, 16) #标注数据类型,行数和每一行的字符数
  6. d.display() #启动显示屏
  7. sleep(1)
  8. d.clear() #清理显示
  9. d.print('Hello ') #显示字符
  10. sleep(1)
  11. d.setCursor(0, 1) #规定显示位置,行和列都从0开始
  12. d.print('world ')

image.png
使用 USB 数据线将 Pico 连接到电脑,点击运行按钮将程序保存到任意位置,你就能看到程序的实际运行效果。
image.png

项目二:LCD 屏显示旋转电位计读数

除了显示我们编写好的字符,LCD 显示屏还可以显示其他传感器返回的数值。

硬件连接

在该项目中,我们所需要使用的硬件有:

  • 树莓派 Pico
  • 树莓派 Pico 拓展板
  • Grove - 16 x 2 LCD 显示屏
  • Grove - 旋转电位计

插接好 Pico 和拓展板,使用 Grove 数据线将 LCD 显示屏连接到 I2C1 接口,将旋转电位计连接到A0接口。
页面_12.png

编写程序

在第6课中我们学习了如何使用 ADC 读取旋转电位计的数值,那么,是否直接使用 d.print() 显示其读数就行了呢?我们先来试试下面的程序:

  1. from lcd1602 import LCD1602
  2. from machine import I2C,Pin,ADC
  3. from utime import sleep
  4. ROTARY_ANGLE_SENSOR = ADC(0)
  5. i2c = I2C(1,scl=Pin(7), sda=Pin(6), freq=400000)
  6. d = LCD1602(i2c, 2, 16)
  7. d.display()
  8. while True:
  9. sleep(1)
  10. d.clear()
  11. d.setCursor(0, 0)
  12. d.print(ROTARY_ANGLE_SENSOR.read_u16())
  13. sleep(1)

运行该程序,Thonny 会直接告诉我们程序有错误,且错误就在第14行,错误信息是:
TypeError: ‘int’ object isn’t iterable
9-4.png
这代表我们传入 print() 函数的参数并非它可以执行的类型。

数据类型
在我们进行编程时,由于各种数据在进行存储时所需要的容量各不相同,不同类型的数据就需要匹配不同的内存空间大小来存储,所以我们需要将各种数据划分为不同的数据类型。当我们每次在 Python 中创建变量时,其实就是向存储器申请一个存储空间并将某一段数据存储到对应的内存空间。 Python 中我们将各种数据分为六大基本类型:
- Number(数字)
- String(字符串)
- List(列表)
- Tuple(元组)
- Set(集合)
- Dictionary(字典)

在 print() 函数中,我们在 lcd1602 函数库中规定了该函数只能输入数据类型为字符串的参数,而ROTARY_ANGLE_SENSOR.read_u16() 返回的是一个数字类型的整数int,所以如果我们想要使用 print() 函数输出滑动电位计的数值,就需要将数字类型的数据转化为字符串类型数据,而在 Python 内置函数中,就有这样一个函数:str() ,使用 str() 函数可以将其他类型的数据转化为字符串类型函数。这样我们就可以使用 print() 输出旋转电位计的数值了。
修改后的程序如下:

  1. from lcd1602 import LCD1602
  2. from machine import I2C,Pin,ADC,PWM
  3. from utime import sleep
  4. ROTARY_ANGLE_SENSOR = ADC(0)
  5. i2c = I2C(1,scl=Pin(7), sda=Pin(6), freq=400000)
  6. d = LCD1602(i2c, 2, 16)
  7. d.display()
  8. while True:
  9. sleep(1)
  10. d.clear()
  11. d.setCursor(0, 0)
  12. d.print(str(ROTARY_ANGLE_SENSOR.read_u16()))
  13. sleep(1)

image.png
使用USB数据线将 Pico 连接到电脑,点击运行按钮将程序保存到任意位置,旋转旋转电位计,你就能看到程序的实际运行效果。
LCD屏显示旋转电位计读数.mp4
image.png

思维拓展

请尝试在本节课的程序中加入 LED 灯效,并让 LCD 显示屏显示其亮度。

  1. from lcd1602 import LCD1602
  2. from machine import I2C,Pin,ADC,PWM
  3. from utime import sleep
  4. ROTARY_ANGLE_SENSOR = ADC(0)
  5. LED_PWM = PWM(Pin(18))
  6. i2c = I2C(1,scl=Pin(7),sda=Pin(6),freq=400000)
  7. LED_PWM.freq(500)
  8. d = LCD1602(i2c,2,16)
  9. d.display()
  10. while True:
  11. val=ROTARY_ANGLE_SENSOR.read_u16()
  12. LED_PWM.duty_u16(val)
  13. sleep(1)
  14. d.clear()
  15. d.setCursor(0,0)
  16. d.print(str(val))
  17. sleep(1)

image.png