树莓派使用 I2C LCD1602

最近更新于 2024-05-05 14:19

发布于: blog.iyatt.com

简介

LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。不同厂家生产的LCD1602芯片可能有所不同,但使用方法都是一样的。为了降低成本,绝大多数制造商都直接将裸片做到板子上。 —— 来自百度百科

该显示屏分辨率为 16×2,每行可显示16个字符,共两行,每个字符的点阵大小 5×8。

根据从网上查到的信息,I2C 接口的 LCD1602 地址有 0x27 和 0x3F 两种,不过目前我只遇到过 0x27 的(如下图,其中 3c 是我接的 OLED,可忽略),查看 i2c 设备地址可以使用 i2cdetect

sudo apt install -y i2c-tools

测试环境

树莓派官方系统 64位 (Debian 10)

驱动

该驱动是基于 WiringPi 库对 i2c 进行操作的,如果要使用该库则需要先安装 WiringPi。

sudo apt update && sudo apt install git build-essential make

git clone https://github.com/WiringPi/WiringPi.git --depth=1

sudo bash WiringPi/INSTALL

另外因为涉及到 I2C 的使用,树莓派默认是没有开启 I2C 的,需要手动设置打开。

/**
 * @file I2C_LCD1602.h
 * @author IYATT-yx
 * @brief I2C LCD1602 树梅派驱动
 * @version 0.1
 * @date 2022-02-23
 * 
 * @copyright Copyright (C) 2022 IYATT-yx iyatt@iyatt.com
 * Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 * 
 */

#include "wiringPi.h"
#include "wiringPiI2C.h"

#define ADDR 0x27  // 地址

int fd = 0;  // 设备描述符
int blen = 1;  // 背光灯

void write_word(int data)
{
    int tmp = data;
    if (1 == blen)
    {
        tmp |= 0x08;
    }
    else
    {
        tmp &= 0xf7;
    }
    wiringPiI2CWrite(fd, tmp);
}

void command(int cmd)
{
    int buf = cmd & 0xF0;
    buf |= 0x04;
    write_word(buf);
    delay(2);
    buf &= 0xFB;
    write_word(buf);
    buf = (cmd & 0x0F) << 4;
    buf |= 0x04;
    write_word(buf);
    delay(2);
    buf &= 0xFB;
    write_word(buf);
}

/**
 * @brief 初始化,使用前必须调用
 * 
 */
void i2c_lcd1602_init()
{
    fd = wiringPiI2CSetup(ADDR);
    command(0x33);
    delay(5);
    command(0x32);
    delay(5);
    command(0x28);
    delay(5);
    command(0x0C);
    delay(5);
    command(0x01);
    wiringPiI2CWrite(fd, 0x08);
}

/**
 * @brief LCD 显示字符
 *      注意不支持自动换行,假如第一行剩下的位置不够显示,也不会自动换到第二行显示
 * @param x 开始列,可 0~15
 * @param y 开始行,可 0~1
 * @param s 要显示的字符串
 */
void write(int x, int y, const char *s)
{
    if (x < 0)
    {
        x = 0;
    }
    if (x > 15)
    {
        x = 15;
    }
    if (y < 0)
    {
        y = 0;
    }
    if (y > 1)
    {
        y = 1;
    }
    command(0x80 + 0x40 * y + x);
    for (int i = 0; s[i] != '\0'; ++i)
    {
            int buf = s[i] & 0xF0;
            buf |= 0x05;
            write_word(buf);
            delay(2);
            buf &= 0xFB;
            write_word(buf);
            buf = (s[i] & 0x0F) << 4;
            buf |= 0x05;
            write_word(buf);
            delay(2);
            buf &= 0xFB;
            write_word(buf);    
    }
}

/**
 * @brief 清空屏幕
 * 
 */
void clear()
{
    command(0x01);
}

使用示例

#include "I2C_LCD1602.h"

int main()
{
    i2c_lcd1602_init();
    write(0, 0, "I2C LCD1602");
    write(0, 1, "Test Demo");
}
gcc main.c -o demo -lwiringPi    -std=c17 -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Werror=format-security -Wextra -pedantic -Wimplicit-fallthrough -Wsequence-point -Wswitch-unreachable -Wswitch-enum -Wstringop-truncation -Wbool-compare -Wtautological-compare -Wfloat-equal -Wshadow=global -Wpointer-arith -Wpointer-compare -Wcast-align -Wcast-qual -Wwrite-strings -Wdangling-else -Wlogical-op -Wconversion -g -O0
#include "I2C_LCD1602.h"
#include "wiringPi.h"

#include <stdio.h>
#include <string.h>
#include <time.h>

/**
 * @brief 获取 CPU 温度
 * 
 * @return int CPU 温度值
 */
int get_cpu_temperature()
{
    FILE *temperature_file = fopen("/sys/class/thermal/thermal_zone0/temp", "r");
    int temperature;
    fscanf(temperature_file, "%i", &temperature);
    fclose(temperature_file);
    return temperature / 1000;
}

/**
 * @brief 获取时间字符串
 * 
 * @param time_string 传入一个长度为 15 的字符数组,执行后得到当前时间
 */
void get_time(char *time_string)
{
    time_t total_sec;
    time(&total_sec);
    struct tm *time_struct = localtime(&total_sec);
    sprintf(time_string, "%i.%i %i:%i:%i",
            time_struct->tm_mon,
            time_struct->tm_mday,
            time_struct->tm_hour,
            time_struct->tm_min,
            time_struct->tm_sec);
}

int main()
{
    i2c_lcd1602_init();
    char temperature[16];
    char time_string[15];
    while (1)
    {
        clear();
        memset(time_string, '\0', sizeof(time_string));
        get_time(time_string);
        write(0, 0, time_string);
        memset(temperature, '\0', sizeof(temperature));
        sprintf(temperature, "temperature: %d", get_cpu_temperature());
        write(0, 1, temperature);
        delay(800);
    }
}
#include "I2C_LCD1602.h"
#include "wiringPi.h"

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netinet/in.h>

/**
 * @brief 获取 IP
 * 
 * @param ip 传入一个大小为 16 的字符数组,执行后得到本机 IP 地址
 * @return int 1 有线连接; 2 无线连接
 */
int get_ip(char *ip)
{
    struct ifaddrs *ifAddrStruct = NULL;
    void *tmpAddrPtr = NULL;

    getifaddrs(&ifAddrStruct);
    while (ifAddrStruct != NULL)    
    {
        if (ifAddrStruct->ifa_addr->sa_family == AF_INET)
        {
            tmpAddrPtr = &((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

            if (strcmp(ifAddrStruct->ifa_name, "eth0") == 0)
            {
                strcpy(ip, addressBuffer);
                return 1;
            }
            else if (strcmp(ifAddrStruct->ifa_name, "wlan0") == 0)
            {
                strcpy(ip, addressBuffer);
                return 2;
            }
        }
        ifAddrStruct = ifAddrStruct->ifa_next;
    }
    return -1;
}

int main()
{
    i2c_lcd1602_init();
    char ip[16];
    while (1)
    {
        clear();
        memset(ip, '\0', sizeof(ip));
        int ret = get_ip(ip);
        if (ret == 1)
        {
            write(0, 0, "eth0:");
        }
        else if (ret == 2)
        {
            write(0, 0, "wlan0:");
        }
        write(0, 1, ip);
        delay(1000);
    }
}
树莓派使用 I2C LCD1602
Scroll to top