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