使用Python控制1602液晶屏实时显示时间(附PyCharm远程调试)

时间:2024-03-02 18:16:08

前言

原创文章,转载引用务必注明链接。水平有限,如有疏漏,欢迎指正。

本文介绍一下UP板的GPIO资源使用,以及一个使用Python演示一个简单的demo。

本文使用Markdown写成,为获得更好的阅读体验和正常的图片、链接,请访问我的博客:

http://www.cnblogs.com/sjqlwy/p/up_1602.html

本文环境:ublinux 3.0;Win7_x64

通过阅读本文你可以学到:

  • UP Board GPIO 接口的介绍以及使用
  • PyCharm 远程调试 UP上的Python代码
  • Lemaker LN IO 拓展板的使用
  • 使用RPi.GPIO这个Python库控制1602液晶屏

UP板载GPIO接口介绍

UP板的GPIO接口兼容树莓派40 Pin。实现起来比较复杂,部分从Intel Atom Z8350引出(需要电平转换),部分由板载CPLD实现。

操作GPIO

官方提供了三种方式:用户空间sysfs (shell)、RPi.GPIO库(Python)和libMRAA(多种编程语言)。

Lemaker LN IO拓展板介绍

之前在云汉社区试用Lemaker Guitar开发板时一并入手的。兼容树莓派引脚。个人非常喜欢乐美客公司的产品,包括BananaPi、BananaPi Pro、Lemaker Guitar、96boards Hikey (Lemaker Version),以及包括LN IO在内的三款拓展板,做工优良,可以在官方微店买到。LN IO 介绍页面电路原理图

【下面是Lemaker Guitar开发板,上面就是LN IO 拓展板】我们下面将会利用板载的4个按键、LED灯以及1602接口。

Python控制LN IO 扩展板按键和LED

最近在学习Python,恰巧ubilinux移植了RPi.GPIO库,让我们可以非常方便地操作GPIO资源。吐槽一下,由于被动散热片的存在,使用转接线等会卡到无法完全贴合。

Blink!——控制发光二极管闪烁

我们以点亮LN IO上的led2为例:

【LED电路原理图】LCD和LED是切换显示的。可以看到LED2连接到GPIO0,那么GPIO0是对应树莓派是哪个引脚呢?

【底板对应引脚】GPIO0对应物理引脚11。

【UP Board 引脚定义图】为了方便起见,我们统一使用BOARD物理引脚编号而非BCM引脚编号。

  • 关于树莓派GPIO的操作可以参考芒果爱吃胡萝卜这个博客,写的非常不错,由浅入深。本文部分以他的博文为基础进行演示。
  • ubilinux移植的RPi.GPIO库仅兼容Python 2.x版本
  • 为方便转换,我们以BOARD编码GPIO引脚顺序(物理顺序)
  • LN IO Board的LED和LCD可以切换显示,连接帽导通不同引脚即可。

下面创建一个文件lcd.py,内容如下,然后运行看看:sudo python lcd.py

#!/usr/bin/env python
# encoding: utf-8

import RPi.GPIO as GPIO
import time

# 为保持兼容性,选择GPIO引脚主板编号模式,也就是物理引脚编号
GPIO.setmode(GPIO.BOARD)
LedPin = 11
# 指定11引脚(就是LED长针连接的GPIO针脚)的模式为输出模式
# LN IO 的GPIO0,主板编号是11,对应BCM模式引脚为17
GPIO.setup(LedPin, GPIO.OUT)

# 循环10次
for i in range(0, 10):
	# 让11引脚输出高电平(LED灯亮)
	GPIO.output(LedPin, True)
	# 持续一段时间
	time.sleep(0.5)
	# 让11引脚输出低电平(LED灯灭)
	GPIO.output(LedPin, False)
	# 持续一段时间
	time.sleep(0.5)

# 最后清理GPIO口(不做也可以,建议每次程序结束时清理一下,好习惯)
GPIO.cleanup()

效果如图所示:

按键控制LED开关

有了上面的我们再来试试用按键控制LED,很多用过Arduino的应该轻车熟路啦。

#!/usr/bin/env python
# encoding: utf-8

import RPi.GPIO as GPIO
import time

# 为保持兼容性,选择GPIO引脚主板编号模式,也就是物理引脚编号
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
LedPin = 11
BtnPin = 13

# 11引脚(LED2)为输出模式,13引脚(Key1)为输入模式
GPIO.setup(LedPin, GPIO.OUT)
GPIO.setup(BtnPin, GPIO.IN)

try:
    GPIO.output(LedPin, True)
    while True:
        time.sleep(0.01)
        if (GPIO.input(BtnPin)) == False:
            GPIO.output(LedPin, False)
        else:
            GPIO.output(LedPin, True)
except KeyboardInterrupt:
    pass

# 最后清理GPIO口(不做也可以,建议每次程序结束时清理一下,好习惯)
GPIO.cleanup()

有兴趣的可以做一个防按键抖动(Debounce)版本。

Python控制1602液晶屏显示当前时间

感谢Hugo Zhu的这篇《如何使用Raspberry Pi在1602液晶屏上显示当前时间--电子钟》博文,他的博客非常棒,受益匪浅。以下仍然以BOARD编码为例。

硬件包括LN IO 拓展板;1602液晶屏;USB无线网卡;UPBoard。注意LN IO拓展板将连接帽切换到LCD引脚。

1602液晶屏的引脚定义:

  1. VSS,接地
  2. VDD,接3.3V电源
  3. VO,液晶对比度调节,接电位器中间的引脚(板载R2)
  4. RS,寄存器选择,接PB 03,Pin 29
  5. RW,读写选择,接地,表示写模式
  6. EN,使能信号,接PB 13,Pin 33
  7. D0,数据位0,4位工作模式下不用,不接
  8. D1,数据位1,4位工作模式下不用,不接
  9. D2,数据位2,4位工作模式下不用,不接
  10. D3,数据位3,4位工作模式下不用,不接
  11. D4,数据位4,接GPIO 4,Pin 16
  12. D5,数据位5,接GPIO 5,PIN 18
  13. D6,数据位6,接GPIO 6,PIN 22
  14. D7,数据位7,接GPIO 7,PIN 7
  15. A,液晶屏背光+,接3.3v
  16. K,液晶屏背光-,接地

源代码可以从github页面下载,修改相关引脚序号,如下:

#!/usr/bin/python

#
# based on code from lrvick and LiquidCrystal
# lrvic - https://github.com/lrvick/raspi-hd44780/blob/master/hd44780.py
# LiquidCrystal - https://github.com/arduino/Arduino/blob/master/libraries/LiquidCrystal/LiquidCrystal.cpp
#

from time import sleep
from datetime import datetime
from time import sleep

class Adafruit_CharLCD:

    # commands
    LCD_CLEARDISPLAY            = 0x01
    LCD_RETURNHOME              = 0x02
    LCD_ENTRYMODESET            = 0x04
    LCD_DISPLAYCONTROL          = 0x08
    LCD_CURSORSHIFT             = 0x10
    LCD_FUNCTIONSET             = 0x20
    LCD_SETCGRAMADDR            = 0x40
    LCD_SETDDRAMADDR            = 0x80

    # flags for display entry mode
    LCD_ENTRYRIGHT              = 0x00
    LCD_ENTRYLEFT               = 0x02
    LCD_ENTRYSHIFTINCREMENT     = 0x01
    LCD_ENTRYSHIFTDECREMENT     = 0x00

    # flags for display on/off control
    LCD_DISPLAYON               = 0x04
    LCD_DISPLAYOFF              = 0x00
    LCD_CURSORON                = 0x02
    LCD_CURSOROFF               = 0x00
    LCD_BLINKON                 = 0x01
    LCD_BLINKOFF                = 0x00

    # flags for display/cursor shift
    LCD_DISPLAYMOVE             = 0x08
    LCD_CURSORMOVE              = 0x00

    # flags for display/cursor shift
    LCD_DISPLAYMOVE             = 0x08
    LCD_CURSORMOVE              = 0x00
    LCD_MOVERIGHT               = 0x04
    LCD_MOVELEFT                = 0x00

    # flags for function set
    LCD_8BITMODE                = 0x10
    LCD_4BITMODE                = 0x00
    LCD_2LINE                   = 0x08
    LCD_1LINE                   = 0x00
    LCD_5x10DOTS                = 0x04
    LCD_5x8DOTS                 = 0x00


# LN IO Board: RS=PB03=29, EN=PB13=33; DB4-7=GPIO4-7=16,18,22,7
    def __init__(self, pin_rs=29, pin_e=33, pins_db=[16,18,22,7], GPIO = None):
        # Emulate the old behavior of using RPi.GPIO if we haven\'t been given
        # an explicit GPIO interface to use
        if not GPIO:
            import RPi.GPIO as GPIO
        GPIO.setwarnings(False)

        self.GPIO = GPIO
        self.pin_rs = pin_rs
        self.pin_e = pin_e
        self.pins_db = pins_db

        self.GPIO.setmode(GPIO.BOARD)
        self.GPIO.setup(self.pin_e, GPIO.OUT)
        self.GPIO.setup(self.pin_rs, GPIO.OUT)

        for pin in self.pins_db:
            self.GPIO.setup(pin, GPIO.OUT)

        self.write4bits(0x33) # initialization
        self.write4bits(0x32) # initialization
        self.write4bits(0x28) # 2 line 5x7 matrix
        self.write4bits(0x0C) # turn cursor off 0x0E to enable cursor
        self.write4bits(0x06) # shift cursor right

        self.displaycontrol = self.LCD_DISPLAYON | self.LCD_CURSOROFF | self.LCD_BLINKOFF

        self.displayfunction = self.LCD_4BITMODE | self.LCD_1LINE | self.LCD_5x8DOTS
        self.displayfunction |= self.LCD_2LINE

        """ Initialize to default text direction (for romance languages) """
        self.displaymode =  self.LCD_ENTRYLEFT | self.LCD_ENTRYSHIFTDECREMENT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode) #  set the entry mode

        self.clear()


    def begin(self, cols, lines):

        if (lines > 1):
                self.numlines = lines
                self.displayfunction |= self.LCD_2LINE
                self.currline = 0


    def home(self):

        self.write4bits(self.LCD_RETURNHOME) # set cursor position to zero
        self.delayMicroseconds(3000) # this command takes a long time!


    def clear(self):

        self.write4bits(self.LCD_CLEARDISPLAY) # command to clear display
        self.delayMicroseconds(3000)    # 3000 microsecond sleep, clearing the display takes a long time


    def setCursor(self, col, row):

        self.row_offsets = [ 0x00, 0x40, 0x14, 0x54 ]

        if ( row > self.numlines ): 
                row = self.numlines - 1 # we count rows starting w/0

        self.write4bits(self.LCD_SETDDRAMADDR | (col + self.row_offsets[row]))


    def noDisplay(self): 
        """ Turn the display off (quickly) """

        self.displaycontrol &= ~self.LCD_DISPLAYON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)


    def display(self):
        """ Turn the display on (quickly) """

        self.displaycontrol |= self.LCD_DISPLAYON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)


    def noCursor(self):
        """ Turns the underline cursor on/off """

        self.displaycontrol &= ~self.LCD_CURSORON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)


    def cursor(self):
        """ Cursor On """

        self.displaycontrol |= self.LCD_CURSORON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)


    def noBlink(self):
        """ Turn on and off the blinking cursor """

        self.displaycontrol &= ~self.LCD_BLINKON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)


    def noBlink(self):
        """ Turn on and off the blinking cursor """

        self.displaycontrol &= ~self.LCD_BLINKON
        self.write4bits(self.LCD_DISPLAYCONTROL | self.displaycontrol)


    def DisplayLeft(self):
        """ These commands scroll the display without changing the RAM """

        self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVELEFT)


    def scrollDisplayRight(self):
        """ These commands scroll the display without changing the RAM """

        self.write4bits(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVERIGHT);


    def leftToRight(self):
        """ This is for text that flows Left to Right """

        self.displaymode |= self.LCD_ENTRYLEFT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode);


    def rightToLeft(self):
        """ This is for text that flows Right to Left """
        self.displaymode &= ~self.LCD_ENTRYLEFT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)


    def autoscroll(self):
        """ This will \'right justify\' text from the cursor """

        self.displaymode |= self.LCD_ENTRYSHIFTINCREMENT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)


    def noAutoscroll(self): 
        """ This will \'left justify\' text from the cursor """

        self.displaymode &= ~self.LCD_ENTRYSHIFTINCREMENT
        self.write4bits(self.LCD_ENTRYMODESET | self.displaymode)


    def write4bits(self, bits, char_mode=False):
        """ Send command to LCD """

        self.delayMicroseconds(1000) # 1000 microsecond sleep

        bits=bin(bits)[2:].zfill(8)

        self.GPIO.output(self.pin_rs, char_mode)

        for pin in self.pins_db:
            self.GPIO.output(pin, False)

        for i in range(4):
            if bits[i] == "1":
                self.GPIO.output(self.pins_db[::-1][i], True)

        self.pulseEnable()

        for pin in self.pins_db:
            self.GPIO.output(pin, False)

        for i in range(4,8):
            if bits[i] == "1":
                self.GPIO.output(self.pins_db[::-1][i-4], True)

        self.pulseEnable()


    def delayMicroseconds(self, microseconds):
        seconds = microseconds / float(1000000) # divide microseconds by 1 million for seconds
        sleep(seconds)


    def pulseEnable(self):
        self.GPIO.output(self.pin_e, False)
        self.delayMicroseconds(1)               # 1 microsecond pause - enable pulse must be > 450ns 
        self.GPIO.output(self.pin_e, True)
        self.delayMicroseconds(1)               # 1 microsecond pause - enable pulse must be > 450ns 
        self.GPIO.output(self.pin_e, False)
        self.delayMicroseconds(1)               # commands need > 37us to settle


    def message(self, text):
        """ Send string to LCD. Newline wraps to second line"""

        for char in text:
            if char == \'\n\':
                self.write4bits(0xC0) # next line
            else:
                self.write4bits(ord(char),True)


if __name__ == \'__main__\':

    lcd = Adafruit_CharLCD()
    lcd.noBlink()
    # lcd.clear()
    # lcd.message("Hello, Jessica!\nHow are you? .....abcdefghijg ")
    # lcd.scrollDisplayRight()

    while True:
        sleep(1)
        lcd.clear()
        lcd.message(datetime.now().strftime(\'  %I : %M : %S \n%a %b %d %Y\'))

每秒更新,显示当前时间,效果如图所示:

进阶

分析代码可知,该代码段可作为1602驱动库,支持1602的基本显示控制。后面可以自定义显示自己的信息,例如做一个小闹钟。

PyCharm远程调试UP Board上的Python程序

由于在UP Board上使用终端界面调试Python确实不很方便(其实是PyCharm用起来太爽了),所以使用Windows 上的PyCharm调试UP Board上的程序,可以直接使用UP的GPIO硬件资源,并且可以非常方便地安装各种库,简直停不下来,当然前文提到的cloud9也不错。远程调试功能只有专业版(Professional)可用,免费的社区版(Community)无此功能。通过edu邮箱验证可以免费使用专业版。

参考这篇文章pycharm 远程调试进行设置即可,注意点如下:

  • 因为我们使用ubilinux移植的RPi.GPIO库,所以解释器只能选择python2
  • 需要启用root账户并更改ssh设置允许root登录
  • 可以通过PyCharm更新UP板上的Python库
sudo passwd root #启用root账户
sudo nano /etc/ssh/sshd_config # 添加 PermitRootLogin yes,Ctrl+O保存,Ctrl+X退出
sudo systemctl restart sshd # 重启SSH服务,使更改生效

效果如图所示: