树莓派通过I2C驱动LCD1602显示屏

    xiaoxiao2025-07-09  7

    问题来源

    在此之前学习了Arduino驱动LCD1602(Arduino通过I2C控制1602LCD显示屏),其过程比较简单,现在想通过树莓派实现控制功能,提升树莓派编程控制能力,在此过程中虽然能正常让LCD显示文字,但是对于网上代码有些不理解,遂形成这篇博文,希望有人能解答文末问题。

    LCD1602知识

    LCD1602引脚

    指令寄存器(IR)和数据寄存器(DR) 本系列模块内部具有两个 8 位寄存器:指令寄存器(IR)和数据寄存器(DR)。用户可以通过 RS 和 R/W 输入信号的组合选择指定的寄存器,进行相应的操作。下表中列出了组合选择方式:

    LCD1602的基本操作   1. 读状态:输入RS=0,RW=1,E=高脉冲。输出:D0—D7为状态字。   2. 读数据:输入RS=1,RW=1,E=高脉冲。输出:D0—D7为数据。   3. 写命令:输入RS=0,RW=0,E=低脉冲。输出:无。(写完置E=高脉冲)   4. 写数据:输入RS=1,RW=0,E=低脉冲。输出:无。 注意:E(或EN)端为使能(enable)端,写操作时,下降沿使能。读操作时,E高电平有效

    显示位置设置 若想在00H处显示数据的话,则必须将00H加上80H,即0x00H+0x80H,若要在01H处显示数据,也必须加0x80H, 例如要将某字符显示在第2行第5列,则确定地址的指令代码应为80H+44H=C4H。依次类推

    I2C转接板引脚

    I2C引脚

    GND ------ 地线 VCC ------ 电源(5V or 3.3v 电源不同显示效果有点差别) SDA ------ I2C 数据线 SCL ------ I2C 时钟线

    树莓派40Pin引脚图 这个接线还是比较简单的。

    树莓派设置

    安装wiringPi库 相关操作自行搜索,有很多。也可以参看官方指南开启I2C

    树莓派需要开启I2C功能,这个可以通过VNC远程或者直接树莓派接显示屏进行设置,相关操作搜索一下有很多。一个是在raspi-config中设置,另一个是在图形界面start按钮中修改树莓派preference。注意:这两个有可能是重复的,我不清楚,我是啷个都设置了

    程序代码

    将下列代码保存为".c"文本,树莓派终端编译运行即可。在此请看下一节对代码提出的一个问题,我没搞懂。此外,程序中的初始化未必是必须的,因为我看1602手册时说上电电压超过4.5V时会自动初始化。

    #include <stdio.h> #include <wiringPi.h> #include <wiringPiI2C.h> #include <string.h> int LCDAddr = 0x27; //LCD设备地址 int BLEN = 1; //BLEN是控制啥的 int fd; void write_word(int data){ int temp = data; //BLEN是控制啥的 if ( BLEN == 1 ) temp |= 0x08; else temp &= 0xF7; wiringPiI2CWrite(fd, temp); } /**************************************************************** RS为寄存器选择,高电平1时选择数据寄存器、低电平0时选择指令寄存器 R/W为读写选择,高电平(1)时进行读操作,低电平(0)时进行写操作。 E(或EN)端为使能(enable)端,写操作时,下降沿使能。读操作时,E高电平有效 ****************************************************************/ //发送命令的函数 void send_command(int comm){ int buf; // Send bit7-4 firstly buf = comm & 0xF0; buf |= 0x04; // RS = 0(低电平0时选择指令寄存器), RW = 0(此时发送指令), EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0,EN从1——>0,下降沿,进行写操作 write_word(buf); // Send bit3-0 secondly buf = (comm & 0x0F) << 4; buf |= 0x04; // RS = 0, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); } /******************* 问题 *********************************** 此处发送数据的函数与Arduino锁使用的LiquidCrystal_I2C.h函数区别很大; 问题是不论是发送命令还是发送数据,所传送的一个字节的后4位都是用于 控制的,只有高4位才是具有实际意义的,不知道为什么。官方在wiringPiI2C.h 介绍中wiringPiI2CWrite()函数介绍比较简单。 ****************************************************************/ //发送数据的函数 void send_data(int data){ int buf; // Send bit7-4 firstly buf = data & 0xF0; buf |= 0x05; // RS = 1, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); // Send bit3-0 secondly buf = (data & 0x0F) << 4; buf |= 0x05; // RS = 1, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); } //初始化函数 void init(){ send_command(0x33); // Must initialize to 8-line mode at first delay(5); send_command(0x32); // Then initialize to 4-line mode delay(5); send_command(0x28); // 2 Lines & 5*7 dots delay(5); send_command(0x0C); // Enable display without cursor delay(5); send_command(0x01); // Clear Screen wiringPiI2CWrite(fd, 0x08); } //清屏 void clear(){ send_command(0x01); //clear Screen } void write(int x, int y, char data[]){ int addr, i; int tmp; if (x < 0) x = 0; if (x > 15) x = 15; if (y < 0) y = 0; if (y > 1) y = 1; // Move cursor //第一行地址起止为00H,第二行起止为40H; //但这个0x80干嘛的,为什么要加? addr = 0x80 + 0x40 * y + x; send_command(addr); tmp = strlen(data); for (i = 0; i < tmp; i++){ send_data(data[i]); } } void main(){ fd = wiringPiI2CSetup(LCDAddr); init(); write(0, 0, "Greetings!"); write(1, 1, "WWW.HNZHIYU.CN"); delay(2000); //clear(); }

    问题

    此处发送数据的函数与Arduino锁使用的LiquidCrystal_I2C.h函数区别很大;问题是不论是发送命令还是发送数据,所传送的一个字节的后4位都是用于控制的,只有高4位才是具有实际意义的,不知道为什么。官方在wiringPiI2C.h介绍中wiringPiI2CWrite()函数介绍比较简单。 而LCD1602的手册中发送数据是可以一次发送8位的,如下图, 不知是否有专业人士解答一下这个。

    另外在写数据是,写数据函数执行了两次。我觉得原因是第一次只是是EN变成高电平,第二次是EN变成低电平,因为EN是下降沿有效。

    buf = comm & 0xF0; buf |= 0x04; // RS = 0(低电平0时选择指令寄存器), RW = 0(此时发送指令), EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0,EN从1——>0,下降沿,进行写操作 write_word(buf);

    解答

    地址中0x80的原因 0x80是因为在设置DDRAM地址时,DB7固定是为1的。DB7是BF忙碌标志位, 输出字符与命令为什么分两次 因为LCD1602字符发生器RAM中 GROM 中,字符码与字符字模之间的对应关系决定。每个字符分高四位与第四位,对应关系如下图,可以看到字母A高四位为LHLL,第四位为LLLH,即0100 0001,在上述程序输出字符的函数中,每次输出时添加打印函数printf()输出到终端,可以发现字母A的两次输出数值为65(0100 0001),与17(0001 0001),这两个数字高四位拼起来为 0100 0001,即与字符A对应。 在附上另一种表达形式的表 附验证所需代码,去除了标注 #include <stdio.h> #include <wiringPi.h> #include <wiringPiI2C.h> #include <string.h> int LCDAddr = 0x27; int BLEN = 1; int fd; void write_word(int data){ int temp = data; if ( BLEN == 1 ) temp |= 0x08; else{ temp &= 0xF7; printf("BLEN is not 1\n"); } wiringPiI2CWrite(fd, temp); } void send_command(int comm){ int buf; // Send bit7-4 firstly buf = comm & 0xF0; buf |= 0x04; // RS = 0, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); printf("send_command buf bit7-4 is %d\n", buf); delay(2000); // Send bit3-0 secondly buf = (comm & 0x0F) << 4; buf |= 0x04; // RS = 0, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); printf("send_command buf bit3-0 is %d\n", buf); } void send_data(int data){ int buf; // Send bit7-4 firstly buf = data & 0xF0; buf |= 0x05; // RS = 1, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); printf("send_data buf bit7-4 is %d\n", buf); delay(2000); // Send bit3-0 secondly buf = (data & 0x0F) << 4; buf |= 0x05; // RS = 1, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); printf("send_data buf bit3-0 is %d\n",buf); } void init(){ send_command(0x33); // Must initialize to 8-line mode at first delay(5); send_command(0x32); // Then initialize to 4-line mode delay(5); send_command(0x28); // 2 Lines & 5*7 dots delay(5); send_command(0x0C); // Enable display without cursor delay(5); send_command(0x01); // Clear Screen wiringPiI2CWrite(fd, 0x08); } void clear(){ send_command(0x01); //clear Screen } void write(int x, int y, char data[]){ int addr, i; int tmp; if (x < 0) x = 0; if (x > 15) x = 15; if (y < 0) y = 0; if (y > 1) y = 1; // Move cursor addr = 0x80 + 0x40 * y + x; send_command(addr); tmp = strlen(data); for (i = 0; i < tmp; i++){ send_data(data[i]); // wiringPiI2CWrite(fd, data[i]); printf("Now %c is printed\n", data[i]); } } void main(){ fd = wiringPiI2CSetup(LCDAddr); init(); write(0, 0, "A1 B2!"); write(1, 4, ""); delay(2000); //clear(); }

    参考

    http://www.elecfans.com/xianshi/20171020567470.html http://www.51hei.com/bbs/dpj-83245-1.html http://bbs.elecfans.com/jishu_451276_1_4.html LCD1602模块如何显示自定义字符 printf()输出格式

    最新回复(0)