簡體   English   中英

Python I2C LCD 驅動程序顯示奇怪的字符

[英]Python I2C LCD Driver displays weird characters

我有一個帶有 I2C 背負式適配器的 2 x 16 LCD 顯示器。 作為一個簡單的開始,我只是顯示編碼器值。 然而,當我轉動編碼器時,我會在不同區域的液晶顯示器上看到奇怪的文字。 此外,有時顯示會恢復並僅顯示“編碼器:X”,第二行為空:

奇怪的顯示

編碼

#!/usr/bin/python3 -u

import time

import RPi.GPIO as GPIO

import lcd_i2c
from encoder import Encoder

GPIO.setmode(GPIO.BCM)

switch_pin = 13
encoder_down_pin = 6
encoder_up_pin = 5

GPIO.setup(switch_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

encoder_value = 0

mylcd = lcd_i2c.lcd()


def valueChanged(value):
    encoder_value = value
    print(encoder_value)

    mylcd.lcd_clear()
    mylcd.lcd_display_string(f"Encoder: {encoder_value}", 1)


e1 = Encoder(encoder_down_pin, encoder_up_pin, callback=valueChanged)
while True:
    time.sleep(60)

# lcd_i2c.py
# -*- coding: utf-8 -*-
# Original code found at:
# https://gist.github.com/DenisFromHR/cc863375a6e19dce359d

"""
Compiled, mashed and generally mutilated 2014-2015 by Denis Pleic
Made available under GNU GENERAL PUBLIC LICENSE

# Modified Python I2C library for Raspberry Pi
# as found on http://www.recantha.co.uk/blog/?p=4849
# Joined existing 'i2c_lib.py' and 'lcddriver.py' into a single library
# added bits and pieces from various sources
# By DenisFromHR (Denis Pleic)
# 2015-02-10, ver 0.1

"""

# i2c bus (0 -- original Pi, 1 -- Rev 2 Pi)
I2CBUS = 1

# LCD Address
ADDRESS = 0x27

import smbus
from time import sleep

class i2c_device:
   def __init__(self, addr, port=I2CBUS):
      self.addr = addr
      self.bus = smbus.SMBus(port)

# Write a single command
   def write_cmd(self, cmd):
      self.bus.write_byte(self.addr, cmd)
      sleep(0.0001)

# Write a command and argument
   def write_cmd_arg(self, cmd, data):
      self.bus.write_byte_data(self.addr, cmd, data)
      sleep(0.0001)

# Write a block of data
   def write_block_data(self, cmd, data):
      self.bus.write_block_data(self.addr, cmd, data)
      sleep(0.0001)

# Read a single byte
   def read(self):
      return self.bus.read_byte(self.addr)

# Read
   def read_data(self, cmd):
      return self.bus.read_byte_data(self.addr, cmd)

# Read a block of data
   def read_block_data(self, cmd):
      return self.bus.read_block_data(self.addr, cmd)


# 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
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

# flags for backlight control
LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00

En = 0b00000100 # Enable bit
Rw = 0b00000010 # Read/Write bit
Rs = 0b00000001 # Register select bit

class lcd:
   #initializes objects and lcd
   def __init__(self):
      self.lcd_device = i2c_device(ADDRESS)

      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x02)

      self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
      self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
      sleep(0.2)


   # clocks EN to latch command
   def lcd_strobe(self, data):
      self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
      sleep(.0005)
      self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))
      sleep(.0001)

   def lcd_write_four_bits(self, data):
      self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
      self.lcd_strobe(data)

   # write a command to lcd
   def lcd_write(self, cmd, mode=0):
      self.lcd_write_four_bits(mode | (cmd & 0xF0))
      self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))

   # write a character to lcd (or character rom) 0x09: backlight | RS=DR<
   # works!
   def lcd_write_char(self, charvalue, mode=1):
      self.lcd_write_four_bits(mode | (charvalue & 0xF0))
      self.lcd_write_four_bits(mode | ((charvalue << 4) & 0xF0))

   # put string function with optional char positioning
   def lcd_display_string(self, string, line=1, pos=0):
    if line == 1:
      pos_new = pos
    elif line == 2:
      pos_new = 0x40 + pos
    elif line == 3:
      pos_new = 0x14 + pos
    elif line == 4:
      pos_new = 0x54 + pos

    self.lcd_write(0x80 + pos_new)

    for char in string:
      self.lcd_write(ord(char), Rs)

   # clear lcd and set to home
   def lcd_clear(self):
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_RETURNHOME)

   # define backlight on/off (lcd.backlight(1); off= lcd.backlight(0)
   def backlight(self, state): # for state, 1 = on, 0 = off
      if state == 1:
         self.lcd_device.write_cmd(LCD_BACKLIGHT)
      elif state == 0:
         self.lcd_device.write_cmd(LCD_NOBACKLIGHT)

   # add custom characters (0 - 7)
   def lcd_load_custom_chars(self, fontdata):
      self.lcd_write(0x40);
      for char in fontdata:
         for line in char:
            self.lcd_write_char(line)
# encoder.py
# Class to monitor a rotary encoder and update a value.  You can either read the value when you need it, by calling getValue(), or
# you can configure a callback which will be called whenever the value changes.

import RPi.GPIO as GPIO

class Encoder:

    def __init__(self, leftPin, rightPin, callback=None):
        self.leftPin = leftPin
        self.rightPin = rightPin
        self.value = 0
        self.state = '00'
        self.direction = None
        self.callback = callback
        GPIO.setup(self.leftPin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.setup(self.rightPin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.add_event_detect(self.leftPin, GPIO.BOTH, callback=self.transitionOccurred)
        GPIO.add_event_detect(self.rightPin, GPIO.BOTH, callback=self.transitionOccurred)

    def transitionOccurred(self, channel):
        p1 = GPIO.input(self.leftPin)
        p2 = GPIO.input(self.rightPin)
        newState = "{}{}".format(p1, p2)

        if self.state == "00": # Resting position
            if newState == "01": # Turned right 1
                self.direction = "R"
            elif newState == "10": # Turned left 1
                self.direction = "L"

        elif self.state == "01": # R1 or L3 position
            if newState == "11": # Turned right 1
                self.direction = "R"
            elif newState == "00": # Turned left 1
                if self.direction == "L":
                    self.value = self.value - 1
                    if self.callback is not None:
                        self.callback(self.value)

        elif self.state == "10": # R3 or L1
            if newState == "11": # Turned left 1
                self.direction = "L"
            elif newState == "00": # Turned right 1
                if self.direction == "R":
                    self.value = self.value + 1
                    if self.callback is not None:
                        self.callback(self.value)

        else: # self.state == "11"
            if newState == "01": # Turned left 1
                self.direction = "L"
            elif newState == "10": # Turned right 1
                self.direction = "R"
            elif newState == "00": # Skipped an intermediate 01 or 10 state, but if we know direction then a turn is complete
                if self.direction == "L":
                    self.value = self.value - 1
                    if self.callback is not None:
                        self.callback(self.value)
                elif self.direction == "R":
                    self.value = self.value + 1
                    if self.callback is not None:
                        self.callback(self.value)

        self.state = newState

    def getValue(self):
        return self.value

i2c lcd 腳本的版權歸其作者DenisFromHR 所有

您的 LCD 模塊內部使用的是 4 位接口模式; 命令或數據的每個字節都寫入兩個 4 位塊中。 除了完全重新初始化 LCD 之外,沒有辦法確定接下來需要哪個塊,也沒有辦法強制開始新字節 - 您只需要小心總是成對發送塊。 如果您丟失了其中一個塊,則 LCD 將從該點開始顯示亂碼(直到另一個塊丟失),因為它接收的字節將由發送的兩個不同字節的一半組成。

而這正是這里出什么問題。 這是“編碼器:”消息的前幾個字節,以十六進制表示:

45 6E 63 6F 64 65 72 3A

這是你得到的重復垃圾字符串的開始,同樣是十六進制:

56 E6 36 F6 46 57 23 A2

請注意,這是完全相同的數據,只是移動了一位十六進制數字。

我看不出有什么明顯的原因為什么會發生,但我想嘗試的第一件事就是提高這些sleep() S IN lcd_strobe()由100個左右的因素,如果他們沒有足夠長的大塊被 LCD 模塊可靠地讀取。

有用

我確實擺脫了這個問題。 從回調中更新 lcd 似乎是一個問題。 所以我解決這個問題的方法是僅僅改變程序的狀態並定期檢查該狀態下的更新,然后相應地顯示結果:

#!/usr/bin/python3 -u

import os
import time

import RPi.GPIO as GPIO

import lcd_i2c
from encoder import Encoder

GPIO.setmode(GPIO.BCM)

os.system('say start')

switch_pin = 13
encoder_down_pin = 6
encoder_up_pin = 5
encoder_value = 0
dirty = True
pressed = True
mylcd = lcd_i2c.lcd()


def switch_pressed(v):
    global pressed, dirty
    print("OK")
    pressed = True
    dirty = True


GPIO.setup(switch_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(switch_pin, GPIO.FALLING)
GPIO.add_event_callback(switch_pin, switch_pressed)


def update():
    global encoder_value, dirty, pressed

    if not dirty: return
    mylcd.lcd_clear()
    mylcd.lcd_display_string(f"Encoder: {encoder_value}", 1)

    if pressed:
        mylcd.lcd_display_string("Pressed", 2)
        pressed = False
    dirty = False


def valueChanged(value):
    global encoder_value
    global dirty

    dirty = True
    encoder_value = max(0, value)
    print(encoder_value)

e1 = Encoder(encoder_down_pin, encoder_up_pin, callback=valueChanged)
update()

try:
    while True:
        update()
        time.sleep(.5)

finally:
    print("Cleanup")
    GPIO.cleanup()

我正在使用一個名為dirty的變量來確定是否應該更新顯示。 這可以防止顯示閃爍。

那個搭載是使用 5v 邏輯電平還是 3.3v? 這在我看來像

  • 一個浮動時鍾/數據線(可能用示波器檢查它們是否被 Pi 的上拉電阻正確拉高)
  • 邏輯電平不匹配。 如果設備期望“高”為 5v,但 RPi 將其拉高到 3.3v,當總線空閑時會發生奇怪的行為,因為設備可能會將 3.3v 解釋為好像主機將總線拉低。

本文檔描述了您對電氣的期望

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM