Raspberry Pi + LCD20x4 (HD44780 kompatibel)

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

Samstag 30. August 2014, 21:17

Liebe Python-Freunde,

ich habe mich die letzten Tage einmal daran versucht ein 20x4 LCD (HD44780 kompatibel) mit dem Raspberry Pi anzusteuern. Dabei kam folgender Code heraus:

Code: Alles auswählen

#!/usr/bin/python3

import RPi.GPIO as GPIO
import time

class LCD20x4(object):
    
    PIN_MAPPING = {'RS':7, 'E':8, 'DB4':25, 'DB5':24, 'DB6':23, 'DB7':18, 'DB0': 11, 'DB1': 9, 'DB2':10, 'DB3': 22}
    PIN_NUMBERING = GPIO.BCM
    BIT_MODE = 4 #-> 4: 4-bit, 8: 8-bit
    LINE_OFFSETS = [0x00, 0x40, 0x14, 0x54]
    LINE_WIDTH = 20
    LINE_NUMBER = len(LINE_OFFSETS)
    
    def __init__(self):
        GPIO.setmode(LCD20x4.PIN_NUMBERING)
        GPIO.setup(LCD20x4.PIN_MAPPING['RS'], GPIO.OUT)
        GPIO.setup(LCD20x4.PIN_MAPPING['E'], GPIO.OUT)
        for i in range(LCD20x4.BIT_MODE):
            GPIO.setup(LCD20x4.PIN_MAPPING['DB{0}'.format(7 - i)], GPIO.OUT)
            
        self.reset()
    
    def __del__(self):
        GPIO.cleanup()

    def _cmd(self, data, char_mode=False, delay=0.00005):
        GPIO.output(LCD20x4.PIN_MAPPING['RS'], char_mode)
        
        for n in range(8 // LCD20x4.BIT_MODE):
            for i in range(LCD20x4.BIT_MODE):
                bit_shift = (7 - (i + (LCD20x4.BIT_MODE * n)))
                GPIO.output(LCD20x4.PIN_MAPPING['DB{0}'.format(7 - i)], bool(data & (1 << bit_shift)))
            for _ in range(2):
                GPIO.output(LCD20x4.PIN_MAPPING['E'], not GPIO.input(LCD20x4.PIN_MAPPING['E']))
            time.sleep(delay)
            
    def reset(self):
        
        for i in range(LCD20x4.BIT_MODE):
            GPIO.output(LCD20x4.PIN_MAPPING['DB{0}'.format(7 - i)], False)
        
        function_set = (1 << 5) | ((1 if LCD20x4.BIT_MODE == 8 else 0) << 4) | (1 << 3)
        if LCD20x4.BIT_MODE < 8:
            self._cmd(0x33, delay = 0.015)
            self._cmd(0x32, delay = 0.015)
            self._cmd(function_set, delay = 0.015)
        else:
            for _ in range(3): 
                self._cmd(function_set, delay = 0.015)
            
        self.display_off()
        self.clear_display()
        self.display_on()
    
    def clear_display(self):
        self._cmd(0x01, delay = 0.010)
    
    def display_off(self):
        self._cmd(0x08)
    
    def display_on(self):
        self._cmd(0x0C)
    
    def set_cursor_position(self, line, column):
        if line in range(1, LCD20x4.LINE_NUMBER + 1) and column in range(1, LCD20x4.LINE_WIDTH + 1):
            self._cmd(0x80 + LCD20x4.LINE_OFFSETS[line - 1] + (column - 1))
        else:
            raise ValueError('... invalid line or column position')
    
    def write_line(self, line, column, text):
        self.set_cursor_position(line, column)
        for char in text[:(LCD20x4.LINE_WIDTH - column + 1)]:
            self.write_char(char)
    
    def clear_line(self, line, column=1):
        if line in range(1, LCD20x4.LINE_NUMBER + 1) and column in range(1, LCD20x4.LINE_WIDTH + 1):
            self.write_line(line, column, ' ' * (LCD20x4.LINE_WIDTH - column + 1))
    
    def write_char(self, char):
        self._cmd(ord(char), True)

if __name__ == '__main__':
    
    lcd = LCD20x4()
    for i in range(LCD20x4.LINE_NUMBER):
        lcd.write_line(i + 1, i + 1, 'Hello world!')
        time.sleep(0.5)
Falls jemand mal Zeit für ein 'Code-Review' hat, so würde mich über Kritik und Anregungen sehr freuen. Vielen Dank.

Schöne Grüße
Sirius3
User
Beiträge: 9237
Registriert: Sonntag 21. Oktober 2012, 17:20

Sonntag 31. August 2014, 08:23

@__LC__: range-Objekte sind eigentlich nicht für Vergleiche gemacht. Besser:

Code: Alles auswählen

if 1 <= line <= LCD20x4.LINE_NUMBER and 1 <= column <= LCD20x4.LINE_WIDTH:
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

Sonntag 31. August 2014, 11:40

@ Sirius3: Danke für den Hinweis. Hier dann nochmal die geänderte Fassung des Codes.

Code: Alles auswählen

#!/usr/bin/python3

import RPi.GPIO as GPIO
import time

class LCD20x4(object):
    
    PIN_MAPPING = {'RS':7, 'E':8, 'DB4':25, 'DB5':24, 'DB6':23, 'DB7':18, 'DB0': 11, 'DB1': 9, 'DB2':10, 'DB3': 22}
    PIN_NUMBERING = GPIO.BCM
    BIT_MODE = 4 #-> 4: 4-bit, 8: 8-bit
    LINE_OFFSETS = [0x00, 0x40, 0x14, 0x54]
    LINE_WIDTH = 20
    LINE_NUMBER = len(LINE_OFFSETS)
    
    def __init__(self):
        GPIO.setmode(LCD20x4.PIN_NUMBERING)
        GPIO.setup(LCD20x4.PIN_MAPPING['RS'], GPIO.OUT)
        GPIO.setup(LCD20x4.PIN_MAPPING['E'], GPIO.OUT)
        for i in range(LCD20x4.BIT_MODE):
            GPIO.setup(LCD20x4.PIN_MAPPING['DB{0}'.format(7 - i)], GPIO.OUT)
            
        self.reset()
    
    def __del__(self):
        GPIO.cleanup()

    def _cmd(self, data, char_mode=False, delay=0.00005):
        GPIO.output(LCD20x4.PIN_MAPPING['RS'], char_mode)
        
        for n in range(8 // LCD20x4.BIT_MODE):
            for i in range(LCD20x4.BIT_MODE):
                bit_shift = (7 - (i + (LCD20x4.BIT_MODE * n)))
                GPIO.output(LCD20x4.PIN_MAPPING['DB{0}'.format(7 - i)], bool(data & (1 << bit_shift)))
            for _ in range(2):
                GPIO.output(LCD20x4.PIN_MAPPING['E'], not GPIO.input(LCD20x4.PIN_MAPPING['E']))
            time.sleep(delay)
            
    def reset(self):
        
        for i in range(LCD20x4.BIT_MODE):
            GPIO.output(LCD20x4.PIN_MAPPING['DB{0}'.format(7 - i)], False)
        
        function_set = (1 << 5) | ((1 if LCD20x4.BIT_MODE == 8 else 0) << 4) | (1 << 3)
        if LCD20x4.BIT_MODE < 8:
            self._cmd(0x33, delay = 0.015)
            self._cmd(0x32, delay = 0.015)
            self._cmd(function_set, delay = 0.015)
        else:
            for _ in range(3): 
                self._cmd(function_set, delay = 0.015)
            
        self.display_off()
        self.clear_display()
        self.display_on()
    
    def clear_display(self):
        self._cmd(0x01, delay = 0.015)
    
    def display_off(self):
        self._cmd(0x08)
    
    def display_on(self):
        self._cmd(0x0C)
    
    def set_cursor_position(self, line, column):
        if line in range(1, LCD20x4.LINE_NUMBER + 1) and column in range(1, LCD20x4.LINE_WIDTH + 1):
            self._cmd(0x80 + LCD20x4.LINE_OFFSETS[line - 1] + (column - 1))
        else:
            raise ValueError('... invalid line or column position')
    
    def write_line(self, line, column, text):
        self.set_cursor_position(line, column)
        for char in text[:(LCD20x4.LINE_WIDTH - column + 1)]:
            self.write_char(char)
    
    def clear_line(self, line, column=1):
        if 1 <= line <= LCD20x4.LINE_NUMBER and 1 <= column <= LCD20x4.LINE_WIDTH:
            self.write_line(line, column, ' ' * (LCD20x4.LINE_WIDTH - column + 1))
    
    def write_char(self, char):
        self._cmd(ord(char), True)

if __name__ == '__main__':
    
    lcd = LCD20x4()
    for i in range(LCD20x4.LINE_NUMBER):
        lcd.write_line(i + 1, i + 1, 'Hello world!')
        time.sleep(0.5)
EyDu
User
Beiträge: 4872
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Sonntag 31. August 2014, 11:55

Du hast den selben Vergleich noch in einer anderen Zeile.

__del__ solltest du übrigens nicht verwenden, der Aufruf ist nicht garantiert. Wenn du möchtest, dass GPIO.cleanup in jedem Fall aufgerufen wird, dann musst du das explizit machen. Der einfachste Weg ist eine close-Methode unter der Verwendung des with-Statements und contextlib.closing.

Den Ausdruck

Code: Alles auswählen

LCD20x4.PIN_MAPPING['DB{0}'.format(7 - i)]
hast du an diversen stellen, da solltest du eine Funktion draus machen. Im Prinzip kannst du auch alle Pins vorher in einer Liste sammeln, dann sparst du dir das ständige generieren der Namen.

Der Ausdruck

Code: Alles auswählen

1 if LCD20x4.BIT_MODE == 8 else 0
ist nichts anderes als

Code: Alles auswählen

LCD20x4.BIT_MODE == 8
schreiben.
Das Leben ist wie ein Tennisball.
Benutzeravatar
__LC__
User
Beiträge: 32
Registriert: Dienstag 1. März 2011, 14:08
Wohnort: 127.0.0.1
Kontaktdaten:

Sonntag 31. August 2014, 13:21

@EyDu: Auch dir vielen Dank für deine Anregungen.

Zu deinen Vorschlag mit der Funktionsdefinition:
EyDu hat geschrieben: Den Ausdruck

Code: Alles auswählen

LCD20x4.PIN_MAPPING['DB{0}'.format(7 - i)]
hast du an diversen stellen, da solltest du eine Funktion draus machen. Im Prinzip kannst du auch alle Pins vorher in einer Liste sammeln, dann sparst du dir das ständige generieren der Namen.
.
Ich stehe hier gerade im Zweispalt welche Umsetzung am "besten" erscheint. Definition als einfache 'Modul Funktion' oder doch eher im Kontext der Klasse als @staticmethod? Ich würde es gern als letzteres machen, um die "Zusammenhang" zur Klasse selbst zu waren. Oder ist das eher untypisch?

Schöne Grüße
__LC__
Sirius3
User
Beiträge: 9237
Registriert: Sonntag 21. Oktober 2012, 17:20

Sonntag 31. August 2014, 14:26

@__LC__: ich würde das ja vorberechnen und in einer List speichern:

Code: Alles auswählen

PINS = [PIN_MAPPING['DB{0}'.format(7 - i)] for i in range(BIT_MODE)]
dann vereinfachen sich die Schleifen zu:

Code: Alles auswählen

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