树莓派折腾记#2:使用GPIO驱动LCD1602液晶屏模块

前言

树莓派素有吃灰神器之称,与Kindle齐名(Kindle:我怎么中枪的来着)。虽然树莓派很便宜,但一直把它晾着也不是事儿,正好各种实验材料也到了,是时候弄点小玩意儿出来了。

认识GPIO

树莓派主板上方有两排共40 pin引脚。

195230-1ce18fd6f9bb42cb.png

在终端执行pinout命令,就可以打印出一个简易的主板布局图,其中包含引脚定义图示。

195230-b542d226b3025910.png

详细版本的布局图如下。

195230-e57b830e69b017f5.png
来自https://pinout.xyz/

图中一共有26个名称为“BCMx”或者“GPIOx”的引脚,它们就是GPIO(General Purpose I/O,通用输入输出)引脚。顾名思义,所有GPIO引脚都可以通过编程来实现自定义的输入输出逻辑,用途非常灵活,也是树莓派好吃的精华所在。

GPIO引脚也可以被复用来实现其他特定的功能(图中括号所示),如PWM、I2C、SPI等,之后用到了再说咯。

除去GPIO之外,剩下的就是一些最基本的:

  • 2个3.3V电源(1、17);
  • 2个5V电源(2、4);
  • 8个接地(6、9、14、20、25、30、34、39)。
LCD1602简介及接线

LCD1602是一个点阵液晶屏模块,支持ASCII字符、日文假名和希腊字母的显示,最多能同时显示16*2=32个字符(所以叫1602)。

195230-bc0ddb6688231e4f.png

它一共有16个引脚,定义如下表。

195230-3356728fb77346d2.png

由此可以确定出LCD1602与树莓派之间的接线方案:

  • Vss和Vdd是供电,前者接地,后者接5V;
  • V0用于调整屏幕的对比度,接10kΩ电位器中间引脚,另外的两侧引脚之一要接地;
  • RS是寄存器选择,控制数据输入和指令输入。选择GPIO引脚即可,这里接GPIO7(26号pin),并且记住这些对应关系,后面写程序要用;
  • R/W是读写选择,接地,表示写模式;
  • E是使能信号,接GPIO8(24号pin);
  • DB0~DB7是数据总线,其中DB0~DB3只在8位传输模式下用,这次用简单的4位传输模式,于是空着。DB4~DB7分别接GPIO 25/24/23/18(对应的是22/18/16/12号pin);
  • BLA、BLK是背光的阳极和阴极,阳极接5V,阴极接地。

以下几点要注意:

  • 接线时必须断电。注意GPIO引脚编号和pinout编号的区别;
  • 树莓派不能用于LCD1602的读模式,因为GPIO引脚的标准工作电压是3.3V,而LCD1602输出电压是5V,会烧掉树莓派;
  • 如果LCD1602是无背光版本,就不用接BLA、BLK。为了保护背光,也可以给阳极串一个小电阻(1kΩ以下的),不过我就省略了。
写代码,跑起来

由于LCD1602基本都基于日立HD44780主控,所以资料也通用。关于其时序、内存、指令等信息,有大把说明文档可以参考,比如https://wenku.baidu.com/view/a517e38903020740be1e650e52ea551810a6c9ef.html,不再赘述。

LCD1602上电后,应该会等待初始化,背光亮并且第一排显示16个黑方块。如果没有任何显示,就旋转V0引脚上接的电位器,调整一下对比度。

编写如下Python程序,让它以3秒为周期循环显示几条信息。

#!/usr/bin/python

import RPi.GPIO as GPIO
import time

# GPIO to LCD mapping
LCD_RS = 7   # Pi pin 26
LCD_E = 8    # Pi pin 24
LCD_D4 = 25  # Pi pin 22
LCD_D5 = 24  # Pi pin 18
LCD_D6 = 23  # Pi pin 16
LCD_D7 = 18  # Pi pin 12

# Device constants
LCD_CHR = True    # Character mode
LCD_CMD = False   # Command mode
LCD_CHARS = 16    # Characters per line (16 max)
LCD_LINE_1 = 0x80  # LCD memory location for 1st line
LCD_LINE_2 = 0xC0  # LCD memory location 2nd line


def main():
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)       # Use BCM GPIO numbers
    GPIO.setup(LCD_E, GPIO.OUT)  # Set GPIO's to output mode
    GPIO.setup(LCD_RS, GPIO.OUT)
    GPIO.setup(LCD_D4, GPIO.OUT)
    GPIO.setup(LCD_D5, GPIO.OUT)
    GPIO.setup(LCD_D6, GPIO.OUT)
    GPIO.setup(LCD_D7, GPIO.OUT)

    # Initialize display
    lcd_init()

    # Loop - send text and sleep 3 seconds between texts
    while True:
        lcd_text('Hello World!', LCD_LINE_1)
        lcd_text('', LCD_LINE_2)
        time.sleep(3)  # 3 second delay

        lcd_text('Raspberry Pi 3B+', LCD_LINE_1)
        lcd_text('LCD 1602', LCD_LINE_2)
        time.sleep(3)

        lcd_text('ABCDEFGHIJKLMNOP', LCD_LINE_1)
        lcd_text('qrstuvwxyz345789', LCD_LINE_2)
        time.sleep(3)

        lcd_text('No tengo dinero', LCD_LINE_1)
        lcd_text('Ni nada que dar', LCD_LINE_2)
        time.sleep(3)


# Initialize and clear display
def lcd_init():
    lcd_write(0x33, LCD_CMD)  # Initialize
    lcd_write(0x32, LCD_CMD)  # Set to 4-bit mode
    lcd_write(0x06, LCD_CMD)  # Cursor move direction
    lcd_write(0x0C, LCD_CMD)  # Turn cursor off
    lcd_write(0x28, LCD_CMD)  # 2 line display
    lcd_write(0x01, LCD_CMD)  # Clear display
    time.sleep(0.0005)        # Delay to allow commands to process


def lcd_write(bits, mode):
    GPIO.output(LCD_RS, mode)  # RS
    # High bits
    GPIO.output(LCD_D4, False)
    GPIO.output(LCD_D5, False)
    GPIO.output(LCD_D6, False)
    GPIO.output(LCD_D7, False)

    if bits & 0x10 == 0x10:
        GPIO.output(LCD_D4, True)
    if bits & 0x20 == 0x20:
        GPIO.output(LCD_D5, True)
    if bits & 0x40 == 0x40:
        GPIO.output(LCD_D6, True)
    if bits & 0x80 == 0x80:
        GPIO.output(LCD_D7, True)

    # Toggle 'Enable' pin
    lcd_toggle_enable()

    # Low bits
    GPIO.output(LCD_D4, False)
    GPIO.output(LCD_D5, False)
    GPIO.output(LCD_D6, False)
    GPIO.output(LCD_D7, False)

    if bits & 0x01 == 0x01:
        GPIO.output(LCD_D4, True)
    if bits & 0x02 == 0x02:
        GPIO.output(LCD_D5, True)
    if bits & 0x04 == 0x04:
        GPIO.output(LCD_D6, True)
    if bits & 0x08 == 0x08:
        GPIO.output(LCD_D7, True)

    # Toggle 'Enable' pin
    lcd_toggle_enable()


def lcd_toggle_enable():
    time.sleep(0.0005)
    GPIO.output(LCD_E, True)
    time.sleep(0.0005)
    GPIO.output(LCD_E, False)
    time.sleep(0.0005)


def lcd_text(message, line):
    # Send text to display
    message = message.ljust(LCD_CHARS, ' ')
    lcd_write(line, LCD_CMD)
    for i in range(LCD_CHARS):
        lcd_write(ord(message[i]), LCD_CHR)


# Begin program
try:
    main()
except KeyboardInterrupt:
    pass
finally:
    lcd_write(0x01, LCD_CMD)
    GPIO.cleanup()

如果对比度过大出现虚影的话,就再调节一下电位器;如果仍然停在待初始化的状态,就需要检查接线了。正确的效果如下图。

195230-e72c7e95c5e9c65c.png

感觉不是很喜欢绿色背光(有蓝色背光的版本),并且没用面包板,看起来乱糟糟的。毕竟第一次搞,就酱紫吧。当然,也可以让它输出一些有意义的字符串,比如系统监控信息。对应的Python代码如下。

# CPU温度
with open('/sys/class/thermal/thermal_zone0/temp', 'r') as cpu_temp:
    lcd_text('{:.2f}'.format(float(cpu_temp.read()) / 1000) + ' C', LCD_LINE_1)

# GPU温度
gpu_temp = commands.getoutput('vcgencmd measure_temp | awk -F= \'{print $2}\'').replace('\'C','')
lcd_text('{:.2f}'.format(float(gpu_temp)) + ' C', LCD_LINE_1)

# 当前IP
ip = commands.getoutput('ifconfig wlan0 | grep inet | awk -Faddr: \'{print $2}\' | awk \'{print $1}\'')
lcd_text(ip, LCD_LINE_1)

# 内存
total_mem = commands.getoutput('free -m | grep Mem: | awk \'{print $2}\'')  
free_mem = commands.getoutput('free -m | grep Mem: | awk \'{print $4}\'')
lcd_text(free_mem + ' / ' + total_mem + ' M', LCD_LINE_1)

晚安。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值