最近更新于 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
