LCD mit Schieberegister

Hier befindet sich ein Programmierbeispiel einer LCD-Steuerung mit Schieberegister für den Raspberry Pi mit C++11 und WiringPi.

An den Raspberry Pi werden das Schieberegister 74HC595 und ein 7-Segment LCD angeschlossen.
Das Programm kann direkt auf dem Raspberry Pi erstellt werden.

Das Schieberegister 74HC595

Vcc Q0 DS OE STCP SHCP MR Q7S Q1 Q2 Q3 Q4 Q5 Q6 Q7 GND 74HC595
Mit dem Schieberegister lässt sich serieller Input in parallelen Output umwandeln: nacheinander wird ein Bit über einen Stromimpuls an DS (Serial data input) gesetzt, über einen Impuls an SHCP (Shift Register Clock Pin) weitergeschoben, und wenn alle Bits gesetzt sind, wird durch einen Impuls an STCP (Storage Register Clock Pin) das gesetzte Bitmuster gleichzeitig an den Ausgängen Q0 bis Q7 aktiviert.
An den Ausgängen wird der Strom des Netzteils des Raspberry Pi (in der Regel 2,5 A) und nicht dessen Prozessorstrom (50 mA) benutzt, so dass etwas stärkere Verbraucher angeschlossen werden können. Mit dem Prozessorstrom können nämlich nur drei LEDs gleichzeitig zum Leuchten gebracht werden, wie man mit dem Ohmschen Gesetz berechnen kann, aber mit einem oder mehreren hintereinandergeschalteten 74HC595s viel mehr.

Die Anzeige

6 5 K 0 1 4 3 K 2 7 0 6 3 5 4 1 2 7
Es wird ein LCD mit 7 Segmenten, einem Dezimalpunkt und zwei Kathodenausgängen K benutzt.

Aufbau der Schaltung

Raspberry Pi an 74HC595

Raspberry Pi 74HC595
GPIO 0 DS
GPIO 1 STCP
GPIO 2 SHCP
5 V Vcc, MR
GND OE, GND

LCD an 74HC595

LCD 74HC595
i Qi für 0 <= i <= 7

Raspberry Pi an das LCD

Eine der Kathoden K wird über einen 220 Ω Widerstand mit GND des Raspberry Pi verbunden.

Download

Sie können den Quellcode des Programms herunterladen und auf dem Raspberry Pi entpacken.

LCD-sources.zip

Falls Sie dort den Qt Creator installiert haben, können Sie damit das Programm erstellen und ausführen.
Aber Sie brauchen kein Qt, es geht auch in der Shell mit
g++ main.cpp -o lcd -O2 -lwiringPi
./lcd
Es werden in einer Endlosschleife die hexadezimalen Ziffern mit einer Animation angezeigt, Abbruch mit Strg + C.

Quellcode

Hier befinden sich die Qt Projektdatei und der C++ Quellcode.

TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += main.cpp

unix:!macx: LIBS += -L$$PWD/usr/lib/ -lwiringPi

HEADERS += \
    binary.h

#include <iostream>
#include <wiringPi.h>
#include "binary.h"

static const int DS {0};
static const int STCP {1};
static const int SHCP {2};

u_char round[] = {1_b, 10_b, 100_b, 1000_b, 1.0000_b, 10.0000_b};
u_char digits[] = {11.1111_b, 110_b, 101.1011_b, 100.1111_b, 110.0110_b,
                   110.1101_b, 111.1101_b, 111_b, 111.1111_b, 110.1111_b,
                   111.0111_b, 111.1100_b, 11.1001_b, 101.1110_b, 111.1001_b, 111.0001_b};

void init()
{
    for (int pin: {DS, STCP, SHCP}) {
        pinMode(pin, OUTPUT);
        digitalWrite(pin, LOW);
    }
}

inline void pulse(int pin)
{
    digitalWrite(pin, HIGH);
    delay(1);
    digitalWrite(pin, LOW);
}

inline void sipo(u_char data)
{
    for (int i {0}; i != 8; ++i) {
        digitalWrite(DS, (0x80 & (data << i)) ? HIGH : LOW);
        pulse(SHCP);
    }
    pulse(STCP);
}

void goRound()
{
    for (u_char d: round) {
        sipo(d);
        delay(40);
    }
}

void showDigits()
{
    bool showPoint {false};
    for (u_char d: digits) {
        sipo(d | (showPoint ? 1000.0000_b : 0));
        showPoint = !showPoint;
        delay(500);
    }
}

int main()
{
    if (wiringPiSetup() == -1) return 1;
    init();
    while (true) {
        goRound();
        goRound();
        showDigits();
    }
    return 0;
}

#ifndef BINARY_H
#define BINARY_H

typedef unsigned char u_char;

constexpr u_char twoPow(int n) {
    return n > 0 ? 2*twoPow(n-1) : 1;
}

template<char...> struct binaryHelper;

template<char c> struct binaryHelper<c> {
    static_assert(c == '0' || c == '1', "keine Binärziffer");
    static constexpr u_char value() { return c - '0'; }
};

template<char c, char... tail> struct binaryHelper<c, tail...> {
    static_assert(c == '.' || c == '0' || c == '1', "keine Binärziffer");
    static_assert(((sizeof...(tail) + 1) % 5) || c == '.',
                  "die Zahl muss in mit '.' getrennte Viererblöcke unterteilt sein");
    static constexpr u_char value() {
        return (c == '.' ? 0 : (c - '0')*twoPow(sizeof...(tail) - sizeof...(tail)/5))
                + binaryHelper<tail...>::value();
    }
};

template<char... chars> constexpr u_char operator"" _b() {
    return binaryHelper<chars...>::value();
}

#endif // BINARY_H

Darstellung einer 8-Bit Zahl in Binärschreibweise und in Viererblöcken

6 5 K 0 1 4 3 K 2 7 0 6 3 5 4 1 2 7
Wenn wir die Zahl 2 anzeigen wollen, müssen das erste, das zweite, das vierte, das fünfte und das siebte Segment des LCDs leuchten, d. h. beim 74HC595 muss an Q0, Q1, Q3, Q4 und Q6 eine Spannung anliegen. Im Programm speichern wir die einzelnen Ziffern in einem Character Array, daher können wir die 2 binär als 0b1011011 oder hexadezimal als 0x5b speichern. Ich finde das Umformen der Binärzahl in eine Hexadezimalzahl und umgekehrt anstrengend, und bei der Binärdarstellung hätte ich gerne eine Unterteilung in Viererblöcke. Daher benutze ich binary.h, damit der Compiler mit 8-Bit Binärzahlen in zwei Viererblöcken zurechtkommt. Damit kann ich statt 0b1011011 oder 0x5b das lesbarere 101.1011_b schreiben.
Leider können wir nicht 101.1011b (ohne Unterstrich) benutzen, da das Suffix b in C++ reserviert ist.