LCD1602-Steuerung

Mit dem LCD1602 können zweizeilige Texte zu je 16 Zeichen dargestellt werden. Auf dieser Seite finden Sie eine Anleitung zum Anschließen an den Raspberry Pi und den Code für eine Oberfläche zur Steuerung in C++11 und mit Qt 5.

Das LCD1602 wird über die wiringPi Bibliothek angesteuert. Die Oberfläche erlaubt es dem Benutzer, den Text und Symbole wie Smileys einzugeben, die angezeigt werden sollen. Dabei werden Umlaute und viele Sonderzeichen korrekt angezeigt. Der Benutzer kann Effekte wie Fließtext oder blinkenden Text einschalten.

Die Oberfläche

Der anzuzeigende Text wird in die beiden Eingabefelder eingegeben und durch Drücken der Eingabetaste oder durch das Klicken auf Anzeigen angezeigt.
Es können Symbole wie Smileys, Pfeile, Copyrights und Herzen angezeigt werden.
Für die Texte können die nachfolgenden Effekte benutzt werden.

Fest

Zeigt den Text ohne Effekte an. Es können maximal 16 Zeichen/Symbole eingegeben werden. Dem Benutzer wird rechts von den Symbolen angezeigt, wieviele Zeichen/Symbole er noch eingeben kann.

Fließend

Der Text ist in seiner Länge nicht beschränkt und fließt in einer Endlosschleife. Die Geschwindigkeit kann vom Benutzer eingestellt werden.

Effekt

In einer Endlosschleife fliegen die Buchstaben von rechts an den Text dran. Es können maximal 16 Zeichen/Symbole eingegeben werden. Die Geschwindigkeit kann vom Benutzer eingestellt werden.

Blinkend

Der Text blinkt mit einstellbarer Geschwindigkeit. Es können maximal 16 Zeichen/Symbole eingegeben werden.

Die vorangehenden Effekte lassen sich für beide Zeilen einzelnd einstellen. Der folgende Effekt bezieht sich auf beide Zeilen zusammen.

Getippt

Der Text wird über beide Zeilen in einer Endlosschleife getippt. Die Geschwindigkeit, mit der getippt werden soll, kann der Benutzer angeben. Pro Zeile können maximal 16 Zeichen/Symbole eingegeben werden.

Das LCD1602

Das LCD1602 hat 16 beschriftete Eingänge. Ohne Löten können wir sie nicht benutzen, da beim Starten eigene Symbole definiert werden und ein Wackelkontakt diese wieder löschen würde.
Eingang Erläuterung
VSS GND (0 V)
VDD 5 V
V0 Anschluss an das Potenziometer, um den Kontrast einzustellen.
RS Wählt aus, ob das data register (bei eingeschalteter Spannung) oder das instruction register ausgewählt werden soll. Wir müssen bei wiringPi nur angeben, über welchen GPIO-Port das gesteuert werden soll.
RW Signale werden bei eingeschalteter Spannung gelesen und bei ausgeschalteter geschrieben. Bei uns ist es mit 0 V verbunden, d. h. es werden nur Daten geschrieben.
E Die Pins werden bei ausgeschalteter Spannung aktiviert. Wir müssen bei wiringPi nur angeben, über welchen GPIO-Port dieser Eingang gesteuert werden soll.
D0 - D7 Die Pins, um Daten zu lesen und zu schreiben. Wir benutzen nur den 4-Bit-Modus und daher nur die Pins D4 - D7.
A Anschluss für die Hintergrundbeleuchtung (Anode).
K Anschluss für die Hintergrundbeleuchtung (Kathode).

Aufbau der Schaltung

Raspberry Pi an das LCD1602

Raspberry Pi LCD1602
GPIO 1 D7
GPIO 2 RS
GPIO 3 E
GPIO 4 D6
GPIO 5 D5
GPIO 6 D4
5 V VDD
3,3 V A
GND VSS, RW, K

Anschluss des Potenziometers

Es regelt den Kontrast. Empfohlen wird ein 10 kΩ Potenziometer, aber es geht auch eins mit 50 kΩ.
Potenziometer
Links GND
Mitte V0
Rechts 5 V

Download

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

LCD1602-sources.zip

Dort benötigen Sie Qt 5 und den Qt Creator, um das Programm zu erstellen und zu starten.

Quellcode

Hier befinden sich die Qt Projektdatei und der C++ Quellcode. Die Formulardatei, die Ressourcendatei und die Bilder finden Sie im gezippten Quellcode.

lcd_modul.h
#ifndef LCD_MODUL_H
#define LCD_MODUL_H
#include <string>
#include <thread>
#include <mutex>
#include <lcd.h>

using namespace std;

enum class Mode { fixed, flowing, effect, blinking, typed };

class LCD
{
    int fd;
    Mode mode[2] { Mode::fixed, Mode::fixed };
    void displayFixedText(int line, const string& text);
    void displayFlowingText(int line, const string& text);
    void displayEffectText(int line, const string& text);
    void displayBlinkingText(int line, const string& text);
    void displayTypedText(const string& text1, const string& text2);
    inline void displayChar(int line, int pos, const u_char c);
    bool stopDisplaying[2] {false, false};
    mutex uniqueAccessToLcdWrite;
    mutex blockLine[2];
    int velocity {40};
public:
    LCD();
    ~LCD();
    void clear();
    void display(int line, const string& text, const string& text2 = "");
    void setMode1(Mode mode) { this->mode[0] = mode; }
    void setMode2(Mode mode) { this->mode[1] = mode; }
    void setVelocity(int velocity) { this->velocity = velocity; }
    static const char COPYRIGHT {6};
    static const char SZLIG {7}; // ß
    static const char SMILE {8};
    static const char SAD {9};
    static const char HEART {10};
    static const char ARROW_LEFT {127};
    static const char ARROW_RIGHT {126};
    static const char UPPER_A_UMLAUT {11};
    static const char LOWER_A_UMLAUT {225};
    static const char O_UMLAUT {239};
    static const char U_UMLAUT {245};
    static const char DEGREE {223};
    static const char MY {228};
};

#endif // LCD_MODUL_H

widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QRadioButton>
#include <QTextEdit>
#include <QLabel>
#include <list>
#include "lcd_modul.h"

using namespace std;

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
    LCD lcd;

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_showButton_clicked();
    void on_radioButtonFixed1_clicked();
    void on_radioButtonFlowing1_clicked();
    void on_radioButtonEffect1_clicked();
    void on_radioButtonBlinking1_clicked();
    void on_radioButtonFixed2_clicked();
    void on_radioButtonFlowing2_clicked();
    void on_radioButtonEffect2_clicked();
    void on_radioButtonBlinking2_clicked();
    void on_radioButtonTyped_clicked(bool checked);
    void on_radioButtonTypedHidden2_toggled(bool checked);
    void on_radioButtonTypedHidden1_toggled(bool checked);
    void on_sliderVelocity_valueChanged(int value);
    void on_textEdit1_textChanged();
    void on_textEdit2_textChanged();
    void handleEnterAndCheckInput(QTextEdit* textEdit, bool restrictInputSize, QLabel* label, QString& fixedText);
    void on_pushButtonSmiley1_clicked();
    void on_pushButtonSad1_clicked();
    void on_pushButtonCopyright1_clicked();
    void on_pushButtonHeart1_clicked();
    void on_pushButtonArrowLeft1_clicked();
    void on_pushButtonArrowRight1_clicked();
    void on_pushButtonSmiley2_clicked();
    void on_pushButtonSad2_clicked();
    void on_pushButtonCopyright2_clicked();
    void on_pushButtonHeart2_clicked();
    void on_pushButtonArrowLeft2_clicked();
    void on_pushButtonArrowRight2_clicked();

private:
    Ui::Widget *ui;
    enum class Icon { SMILEY, SAD, COPYRIGHT, HEART, ARROW_LEFT, ARROW_RIGHT };
    void addIcon(int line, Icon icon);
    QString fixedTexts[2];
};

#endif // WIDGET_H

lcd_modul.cpp
#include "lcd_modul.h"
#include <wiringPi.h>
#include <iostream>

using namespace std;

LCD::LCD()
{
    if (wiringPiSetup() == -1) {
        cerr << "wiringPiSetup() failed\n";
        exit(1);
    }
    /*
    lcdInit (const int rows, const int cols, const int bits,
    const int rs, const int strb,
    const int d0, const int d1, const int d2, const int d3, const int d4,
    const int d5, const int d6, const int d7)
    */
    fd = lcdInit(2, 16, 4, 2, 3, 6, 5, 4, 1, 0, 0, 0, 0);
    if (fd == -1) {
        cerr << "lcdInit() failed\n";
        exit(1);
    }

    //Definiere eigene Zeichen
    u_char copyright[] = {0b01110, 0b10001, 0b11101, 0b11001,
                          0b11001, 0b11101, 0b10001, 0b01110};
    lcdCharDef(fd, COPYRIGHT, copyright);

    u_char szlig[] = {0b01110, 0b10001, 0b10001, 0b11110,
                      0b10001, 0b10001, 0b11110, 0b10000};
    lcdCharDef(fd, SZLIG, szlig);

    u_char smile[] = {0b01110, 0b10001, 0b11011, 0b10001,
                      0b11011, 0b10101, 0b10001, 0b01110};
    lcdCharDef(fd, SMILE, smile);

    u_char sad[] = {0b01110, 0b10001, 0b11011, 0b10001,
                    0b10101, 0b11011, 0b10001, 0b01110};
    lcdCharDef(fd, SAD, sad);

    u_char heart[] = {0b00000, 0b01010, 0b10101, 0b10001,
                      0b01010, 0b01010, 0b00100, 0b00000};
    lcdCharDef(fd, HEART, heart);

    u_char upperAWithDots[] = {0b01010, 0b00000, 0b01110, 0b10001,
                               0b11111, 0b10001, 0b10001, 0b00000};
    lcdCharDef(fd, UPPER_A_UMLAUT, upperAWithDots);

    clear();
    display(0, "LCD Steuerung");
    display(1, COPYRIGHT + string {"hulemule.de"});
}

LCD::~LCD()
{
    for (int i {0}; i != 2; ++i) {
        stopDisplaying[i] = true;
        unique_lock<mutex> lockLine {blockLine[i]};
        mode[i] = Mode::fixed;
    }
    clear();

    display(0, string {"Und tsch"} + U_UMLAUT + "ss,");
    display(1, string {"bis demn"} + LOWER_A_UMLAUT + "chst!");
    delay(5000);
    clear();
}

void LCD::clear()
{
    lcdClear(fd);
}

void LCD::displayFixedText(int line, const string& text)
{
    unique_lock<mutex> lock {uniqueAccessToLcdWrite};
    lcdPosition(fd, 0, line);
    lcdPuts(fd, text.c_str());
}

void LCD::displayChar(int line, int pos, const u_char c)
{
    lcdPosition(fd, pos, line);
    lcdPutchar(fd, c);
}

void LCD::displayFlowingText(int line, const string& text)
{
    u_int pos {0};
    unique_lock<mutex> lockLine {blockLine[line]};
    while (!stopDisplaying[line]) {
        {
            unique_lock<mutex> lockWrite {uniqueAccessToLcdWrite};
            for (u_int i {pos}; i != pos + 16; ++i) {
                displayChar(line, i - pos,
                            i < 16 || i > 15 + text.size() ? ' ' : text[i - 16]);
            }
        }
        delay(500 - 4*velocity);
        ++pos;
        pos %= (16 + text.size());
    }
}

static const char blankLine[] {"                "};

void LCD::displayEffectText(int line, const string& text)
{
    unique_lock<mutex> lockLine {blockLine[line]};
    while (true) {
        for (u_int i {0}; i != text.size(); ++i) {
            for (u_int j {15}; j != i - 1; --j) {
                {
                    unique_lock<mutex> lockWrite {uniqueAccessToLcdWrite};
                    if (j != 15)
                        displayChar(line, j + 1, ' ');
                    displayChar(line, j, text[i]);
                }
                if (stopDisplaying[line]) {
                    displayFixedText(line, blankLine);
                    return;
                }
                if (text[i] != ' ')
                    delay(300 - 2.5*velocity);
            }
            if (i == text.size() -1)
                delay(1000 - 9*velocity);
        }
        displayFixedText(line, blankLine);
    }
}

void LCD::displayBlinkingText(int line, const string& text)
{
    unique_lock<mutex> lockLine {blockLine[line]};
    bool showText {true};
    while (!stopDisplaying[line]) {
        displayFixedText(line, showText ? text : blankLine);
        delay(2000 - 19*velocity);
        showText = !showText;
    }
    displayFixedText(line, blankLine);
}

void LCD::displayTypedText(const string& text1, const string& text2)
{
    unique_lock<mutex> lockLine1 {blockLine[0]};
    unique_lock<mutex> lockLine2 {blockLine[1]};
    while (true) {
        lcdCursor(fd, 1);
        for (int i {0}; i != 2; ++i) {
            const string& text = i == 0 ? text1: text2;
            for (u_int j {0}; j != text.size(); ++j) {
                displayChar(i, j, text[j]);
                if (stopDisplaying[0] || stopDisplaying[1])
                    break;
                if (i == 1 && j == 15)
                    lcdCursor(fd, 0);
                delay(300 - 2.5*velocity);
            }
        }
        if (stopDisplaying[0] || stopDisplaying[1])
            break;
        delay(1000 - 8*velocity);
        clear();
    }
    clear();
    lcdCursor(fd, 0);
}

void LCD::display(int line, const string& text, const string& text2)
{
    stopDisplaying[line] = true;
    unique_lock<mutex> lockLine {blockLine[line]};
    displayFixedText(line, blankLine);
    stopDisplaying[line] = false;
    if (mode[line] == Mode::fixed) {
        displayFixedText(line, text);
    }
    else if (mode[line] == Mode::flowing) {
        thread t{&LCD::displayFlowingText, this, line, text};
        t.detach();
    }
    else if (mode[line] == Mode::effect) {
        thread t{&LCD::displayEffectText, this, line, text};
        t.detach();
    }
    else if (mode[line] == Mode::blinking) {
        thread t{&LCD::displayBlinkingText, this, line, text};
        t.detach();
    }
    else if (mode[line] == Mode::typed) {
        stopDisplaying[1] = true;
        unique_lock<mutex> lockLine2 {blockLine[1]};
        displayFixedText(1, blankLine);
        stopDisplaying[1] = false;
        thread t{&LCD::displayTypedText, this, text, text2};
        t.detach();
    }
}

main.cpp
#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <iostream>

using namespace std;

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

static void cutOversizedText(QTextEdit* textEdit, QLabel* label, QString& fixedText)
{
    if (!fixedText.isEmpty()) {
        textEdit->setHtml(fixedText);
        label->setText("0");
    }
}

void Widget::on_radioButtonFixed1_clicked()
{
    lcd.setMode1(Mode::fixed);
    ui->label1->setVisible(true);
    cutOversizedText(ui->textEdit1, ui->label1, fixedTexts[0]);
}

void Widget::on_radioButtonFlowing1_clicked()
{
    lcd.setMode1(Mode::flowing);
    ui->label1->setVisible(false);
}

void Widget::on_radioButtonEffect1_clicked()
{
    lcd.setMode1(Mode::effect);
    ui->label1->setVisible(true);
    cutOversizedText(ui->textEdit1, ui->label1, fixedTexts[0]);
}

void Widget::on_radioButtonBlinking1_clicked()
{
    lcd.setMode1(Mode::blinking);
    ui->label1->setVisible(true);
    cutOversizedText(ui->textEdit1, ui->label1, fixedTexts[0]);
}

void Widget::on_radioButtonFixed2_clicked()
{
    lcd.setMode2(Mode::fixed);
    ui->label2->setVisible(true);
    cutOversizedText(ui->textEdit2, ui->label2, fixedTexts[1]);
}

void Widget::on_radioButtonFlowing2_clicked()
{
    lcd.setMode2(Mode::flowing);
    ui->label2->setVisible(false);
}

void Widget::on_radioButtonEffect2_clicked()
{
    lcd.setMode2(Mode::effect);
    ui->label2->setVisible(true);
    cutOversizedText(ui->textEdit2, ui->label2, fixedTexts[1]);
}

void Widget::on_radioButtonBlinking2_clicked()
{
    lcd.setMode2(Mode::blinking);
    ui->label2->setVisible(true);
    cutOversizedText(ui->textEdit2, ui->label2, fixedTexts[1]);
}

void Widget::on_radioButtonTyped_clicked(bool checked)
{
    if (!checked) {
        ui->radioButtonTyped->setChecked(true);
        return;
    }
    lcd.setMode1(Mode::typed);
    lcd.setMode2(Mode::typed);
    ui->radioButtonTypedHidden1->setCheckable(true);
    ui->radioButtonTypedHidden2->setCheckable(true);
    ui->radioButtonTypedHidden1->setChecked(true);
    ui->radioButtonTypedHidden2->setChecked(true);
    ui->label1->setVisible(true);
    ui->label2->setVisible(true);
    cutOversizedText(ui->textEdit1, ui->label1, fixedTexts[0]);
    cutOversizedText(ui->textEdit2, ui->label2, fixedTexts[1]);
}

void Widget::on_radioButtonTypedHidden1_toggled(bool checked)
{
    if (!checked && ui->radioButtonTyped->isChecked()) {
        ui->radioButtonTypedHidden1->setCheckable(false);
        ui->radioButtonTypedHidden2->setCheckable(false);
        ui->radioButtonTyped->setChecked(false);
        ui->radioButtonFixed2->setChecked(true);
        on_radioButtonFixed2_clicked();
    }
}

void Widget::on_radioButtonTypedHidden2_toggled(bool checked)
{
    if (!checked && ui->radioButtonTyped->isChecked()) {
        ui->radioButtonTypedHidden1->setCheckable(false);
        ui->radioButtonTypedHidden2->setCheckable(false);
        ui->radioButtonTyped->setChecked(false);
        ui->radioButtonFixed1->setChecked(true);
        on_radioButtonFixed1_clicked();
    }
}

void Widget::on_sliderVelocity_valueChanged(int value)
{
    lcd.setVelocity(value);
}

static QString nonPrintableChars[] {"§", "\\", "~", "€",
    "á", "à", "â", "é", "è", "ê", "í", "ì", "î", "ó", "ò", "ô", "ú", "ù", "û",
    "ń", "ǹ", "ḿ", "ź", "ẑ", "ẃ", "ẁ", "ŵ"};
inline static bool containsAnyNonPrintableChar(const QString& s)
{
    for (const QString c: nonPrintableChars) {
        if (s.contains(c))
            return true;
    }
    return false;
}

const static QRegularExpression regexpGetInputForEnter {".+<body\\s.+?>\\s<p\\s.+?>(.*?)</p>",
                                                        QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption};
const static QString noInputIndicator {"-qt-paragraph-type:empty; "};

void Widget::handleEnterAndCheckInput(QTextEdit* textEdit, bool restrictInputSize, QLabel* label, QString& fixedText)
{
    QString plainText {textEdit->toPlainText()};
    if (plainText.contains('\n')) {
        QString html = textEdit->toHtml();
        html = html.indexOf(noInputIndicator) >= 0 ? regexpGetInputForEnter.match(html).captured(1) : "";
        textEdit->setHtml(html);
        on_showButton_clicked();
        return;
    }
    //Entferne Zeichen, die nicht angezeigt werden können.
    if (containsAnyNonPrintableChar(plainText)) {
        QString html = textEdit->toHtml();
        for (const QString c: nonPrintableChars)
            html.remove(c);
        textEdit->setHtml(html);
        return;
    }
    int textSize {plainText.size()};
    if (textSize == 16)
        fixedText = textEdit->toHtml();
    else if (textSize > 16) {
        if (restrictInputSize)
            textEdit->setHtml(fixedText);
    }
    else
        fixedText.clear();
    if (restrictInputSize)
        label->setText(textSize > 16 ? "0" : QString::number(16 - textSize));
}

void Widget::on_textEdit1_textChanged()
{
    handleEnterAndCheckInput(ui->textEdit1, !ui->radioButtonFlowing1->isChecked(), ui->label1, fixedTexts[0]);
}

void Widget::on_textEdit2_textChanged()
{
    handleEnterAndCheckInput(ui->textEdit2, !ui->radioButtonFlowing2->isChecked(), ui->label2, fixedTexts[1]);
}

static QString preIconTag {"<img src=\":/icons/"};
static QString postIconTag {".svg\" width=\"17\" height=\"17\" />"};
struct HtmlReplacement {QString html; QChar qchar;};
static HtmlReplacement iconReplacements[] {
    {preIconTag + "smiley" + postIconTag, LCD::SMILE},
    {preIconTag + "sad" + postIconTag, LCD::SAD},
    {preIconTag + "copyright" + postIconTag, LCD::COPYRIGHT},
    {preIconTag + "heart" + postIconTag, LCD::HEART},
    {preIconTag + "arrow_left" + postIconTag, LCD::ARROW_LEFT},
    {preIconTag + "arrow_right" + postIconTag, LCD::ARROW_RIGHT}
};
static HtmlReplacement htmlSpecialCharsReplacements[] {
    {"&lt;", '<'},
    {"&gt;", '>'},
    {"&quot;", '"'},
    {"&amp;", '&'}
};

const static QRegularExpression regexpGetInput {".+<body\\s.+?>\\s<p\\s.+?>(.*)</p></body></html>$",
                                                QRegularExpression::MultilineOption};

static const string& changeHtmlIntoLCDString(QString html) {
    static string result;
    result.clear();
    //Keine Eingabe?
    if (html.indexOf(noInputIndicator) >= 0)
        return result;
    html = regexpGetInput.match(html).captured(1);
    //Ersetze die Icons durch die Chars des LCDs.
    for (int i {0}; i != sizeof(iconReplacements)/sizeof(HtmlReplacement); ++i) {
        int j {0};
        while ((j = html.indexOf(iconReplacements[i].html)) != -1) {
            html.replace(j, iconReplacements[i].html.size(), iconReplacements[i].qchar);
        }
    }
    //Ersetze HTML kodierte Zeichen wie <, > usw.
    for (int i {0}; i != sizeof(htmlSpecialCharsReplacements)/sizeof(HtmlReplacement); ++i) {
        int j {0};
        while ((j = html.indexOf(htmlSpecialCharsReplacements[i].html)) != -1) {
            html.replace(j, htmlSpecialCharsReplacements[i].html.size(), htmlSpecialCharsReplacements[i].qchar);
        }
    }
    //Ersetze die Sonderzeichen.
    for (QChar qc: html) {
        if (qc == 228) {//ä
            result += LCD::LOWER_A_UMLAUT;
            continue;
        }
        if (qc == 196) {//Ä
            result += LCD::UPPER_A_UMLAUT;
            continue;
        }
        if (qc == 246 || qc == 214) {//ö, Ö
            result += LCD::O_UMLAUT;
            continue;
        }
        if (qc == 252 || qc == 220) {//ü, Ü
            result += LCD::U_UMLAUT;
            continue;
        }
        if (qc == 223) {//ß
            result += LCD::SZLIG;
            continue;
        }
        if (qc == 176) {//°
            result += LCD::DEGREE;
            continue;
        }
        if (qc == 181) {//µ
            result += LCD::MY;
            continue;
        }
        result += qc.toLatin1();
    }
    return result;
}

void Widget::on_showButton_clicked()
{
    if (ui->radioButtonTyped->isChecked()) {
        const string text2 {changeHtmlIntoLCDString(ui->textEdit2->toHtml())};
        lcd.display(0, changeHtmlIntoLCDString(ui->textEdit1->toHtml()),
                    text2);
    }
    else  {
        lcd.display(0, changeHtmlIntoLCDString(ui->textEdit1->toHtml()));
        lcd.display(1, changeHtmlIntoLCDString(ui->textEdit2->toHtml()));
    }
}

void Widget::addIcon(int line, Icon icon)
{
    QTextEdit* textEdit {(line == 0 ? ui->textEdit1 : ui->textEdit2)};
    textEdit->insertHtml(iconReplacements[(int) icon].html);
    textEdit->setFocus();
}

void Widget::on_pushButtonSmiley1_clicked()
{
    addIcon(0, Icon::SMILEY);
}

void Widget::on_pushButtonSad1_clicked()
{
    addIcon(0, Icon::SAD);
}

void Widget::on_pushButtonCopyright1_clicked()
{
    addIcon(0, Icon::COPYRIGHT);
}

void Widget::on_pushButtonHeart1_clicked()
{
    addIcon(0, Icon::HEART);
}

void Widget::on_pushButtonArrowLeft1_clicked()
{
    addIcon(0, Icon::ARROW_LEFT);
}

void Widget::on_pushButtonArrowRight1_clicked()
{
    addIcon(0, Icon::ARROW_RIGHT);
}

void Widget::on_pushButtonSmiley2_clicked()
{
    addIcon(1, Icon::SMILEY);
}

void Widget::on_pushButtonSad2_clicked()
{
    addIcon(1, Icon::SAD);
}

void Widget::on_pushButtonCopyright2_clicked()
{
    addIcon(1, Icon::COPYRIGHT);
}

void Widget::on_pushButtonHeart2_clicked()
{
    addIcon(1, Icon::HEART);
}

void Widget::on_pushButtonArrowLeft2_clicked()
{
    addIcon(1, Icon::ARROW_LEFT);
}

void Widget::on_pushButtonArrowRight2_clicked()
{
    addIcon(1, Icon::ARROW_RIGHT);
}

LCD1602-Steuerung.pro
#-------------------------------------------------
#
# Project created by QtCreator 2019-07-26T19:55:00
#
#-------------------------------------------------

QT       += core gui
CONFIG += c++11

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = LCD1602-Steuerung
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += main.cpp\
        widget.cpp \
    lcd_modul.cpp

HEADERS  += widget.h \
    lcd_modul.h

FORMS    += widget.ui

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

RESOURCES += \
    icons.qrc

RC_ICONS = lcd.ico

Übersicht über die Klassen

Hier finden Sie eine Beschreibung der benutzten Klassen.

Stellt das Qt-Fenster dar. Hier greifen wir auf die grafischen Elemente zu, die in der Formulardatei widget.ui angelegt worden sind.

Die Eingabefelder
Wir benutzen QTextEdit, um den Text und die Symbole anzeigen zu können, die der Benutzer eingeben kann. Wenn wir nur den Text ohne die Symbole hätten, könnten wir QLineEdit benutzen, und unser Leben wäre leichter, da wir die Anzahl der maximal einzugebenden Zeichen mit setMaxLength() beschränken könnten und den Text leichter auslesen könnten. So müssen wir bei jeder Benutzereingabe im Slot on_textEditN_textChanged() überprüfen, ob der Benutzer noch Zeichen eingeben darf und gegebenenfalls den zuvor gespeicherten Text neu setzen. Den eingegebenen Text ziehen wir mit einem regulären Ausdruck heraus und wandeln ihn in changeHtmlIntoLCDString() in einen std::string um, mit dem die LCD-Klasse den Text auf dem LCD1602 anzeigen kann.

Die Radio-Buttons
Die Buttons Fest, Fließend, Effekt und Blinkend beziehen sich immer nur auf eine Zeile. Da autoExclusive eingeschaltet ist, kann immer nur ein einzelner Button aktiv sein. Wenn der Benutzer aber Getippt auswählt, bezieht sich das auf beide Zeilen, und die anderen Buttons dürfen nicht mehr aktiviert sein. Daher habe ich zu den beiden Gruppen mit den Buttons einen versteckten Button hinzugefügt, der in diesem Fall aktiviert wird.

Die Klasse LCD ist für die Anzeige auf dem LCD1602 verantwortlich.

Im Konstruktor werden eigene Zeichen wie ß, die Smileys und das Herz über die wiringPi Methode lcdCharDef() definiert, da sie in dem Zeichensatz des LCD1602 nicht enthalten sind. Allerdings können wir bei einer 5x8-Auflösung bei den Smileys keine allzu großen Kunstwerke erwarten. Weiterhin wird der Begrüßungstext LCD Steuerung ©hulemule.de angezeigt.

Wenn der Destruktor aufgerufen wird, wird die Anzeige bereits bestehender Texte gestoppt. Anschließend wird der Benutzer mit Und tschüss, bis demnächst! verabschiedet.

Wenn der Text mit Effekten wie Blinkend angezeigt werden soll, wird ein neuer Thread gestartet. Damit immer nur ein Thread auf dem LCD1602 gleichzeitig schreiben kann, definieren wir vor den Positionierungs- und Schreiboperationen einen Mutex (mutual exclusion) unique_lock<mutex> lock {anyLock} mit der privaten Membervariablen mutex anyLock. Am Ende des Blocks ist der unique_lock<mutex> nicht mehr gültig, so dass ein anderer Thread schreiben darf.