在另外一个项目中,无头联网设备需要显示当前的设备的IP地址,方便操作,所以用了一块LCD1602来显示。
因为LCD1602的使用非常简单,所以很方便的就能在Purple Pi开发板上使用。
LCD1602就是两行字符,每行16个,控制指令非常简单明了。
我手头的这块LCD1602已经添加了I2C模块:

所以,可以直接I2C接口来发送数据进行控制。
I2C接口的具体位置,可以查看背面丝印:

最下面,就有I2C0的接口,把LCD1602-IIC直接接上开干。


从这块板子的I2C模块上,可以看到A0、A1、A2都没有连接:

那么根据datasheet,可以得到其地址:

三开,所以地址就是0x0f了
我买的这个LCD1602,店家直接提供了标准的Linux下的驱动库程序:
#ifndef LCD1602_H
#define LCD1602_H
//
// LCD1602 2 line by 16 character LCD library
//
// Written by Larry Bank - 12/6/2017
// Copyright (c) 2017 BitBank Software, Inc.
// bitbank@pobox.com
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//
// Opens a file system handle to the I2C device
// Turns on the LCD, sets the 4-bit mode and clears the memory
// Returns 0 for success, 1 for failure
//
int lcd1602Init(int iChannel, int iAddr);
//
// Set the LCD Pulse Period
//
void lcd1602SetPulsePeriod(int val);
//
// Set the cursor position on the LCD
//
int lcd1602SetCursor(int x, int y);
//
// Control the backlight, cursor, and blink
//
int lcd1602Control(int bBacklight, int bCursor, int bBlink);
//
// Print a zero-terminated character string
// Only 1 line at a time can be accessed
//
int lcd1602WriteString(char *szText);
//
// Clear the characters from the LCD
//
int lcd1602Clear(void);
//
// Turn off the LCD and backlight
// close the I2C file handle
//
void lcd1602Shutdown(void);
#endif // LCD1602_H
//
// 2x16 LCD display (HD44780 controller + I2C chip)
//
// Copyright (c) BitBank Software, Inc.
// Written by Larry Bank
// bitbank@pobox.com
// Project started 12/6/2017
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/i2c-dev.h>
//
// The LCD controller is wired to the I2C port expander with the upper 4 bits
// (D4-D7) connected to the data lines and the lower 4 bits (D0-D3) used as
// control lines. Here are the control line definitions:
//
// Command (0) / Data (1) (aka RS) (D0)
// R/W (D1)
// Enable/CLK (D2)
// Backlight control (D3)
//
// The data must be manually clocked into the LCD controller by toggling
// the CLK line after the data has been placed on D4-D7
//
#define PULSE_PERIOD 500
#define CMD_PERIOD 4100
#define BACKLIGHT 8
#define DATA 1
static int iBackLight = BACKLIGHT;
static int file_i2c = -1;
static int iPulsePeriod = PULSE_PERIOD;
static int lcd1602_write(int file_i2c_hd, const void *buf, size_t len)
{
// int j;
// for(j = 0; j < len; ++j)
// printf("HEX: %02x\n", ((uint8_t*) buf)[j]);
int ret = write(file_i2c_hd, buf, len);
return ret;
}
static void lcd1602_writeCommand(unsigned char ucCMD)
{
unsigned char uc;
uc = (ucCMD & 0xf0) | iBackLight; // most significant nibble sent first
lcd1602_write(file_i2c, &uc, 1);
usleep(iPulsePeriod); // manually pulse the clock line
uc |= 4; // enable pulse
lcd1602_write(file_i2c, &uc, 1);
usleep(iPulsePeriod);
uc &= ~4; // toggle pulse
lcd1602_write(file_i2c, &uc, 1);
usleep(CMD_PERIOD);
uc = iBackLight | (ucCMD << 4); // least significant nibble
lcd1602_write(file_i2c, &uc, 1);
usleep(iPulsePeriod);
uc |= 4; // enable pulse
lcd1602_write(file_i2c, &uc, 1);
usleep(iPulsePeriod);
uc &= ~4; // toggle pulse
lcd1602_write(file_i2c, &uc, 1);
usleep(CMD_PERIOD);
} /* lcd1602_writeCommand() */
//
// Control the backlight, cursor, and blink
// The cursor is an underline and is separate and distinct
// from the blinking block option
//
int lcd1602Control(int bBacklight, int bCursor, int bBlink)
{
unsigned char ucCMD = 0xc; // display control
if (file_i2c < 0)
return 1;
iBackLight = (bBacklight) ? BACKLIGHT : 0;
if (bCursor)
ucCMD |= 2;
if (bBlink)
ucCMD |= 1;
lcd1602_writeCommand(ucCMD);
return 0;
} /* lcd1602Control() */
//
// lcd1602_write an ASCII string (up to 16 characters at a time)
//
int lcd1602WriteString(char *text)
{
unsigned char ucTemp[2];
int i = 0;
if (file_i2c < 0 || text == NULL)
return 1;
while (i < 16 && *text)
{
ucTemp[0] = iBackLight | DATA | (*text & 0xf0);
lcd1602_write(file_i2c, ucTemp, 1);
usleep(iPulsePeriod);
ucTemp[0] |= 4; // pulse E
lcd1602_write(file_i2c, ucTemp, 1);
usleep(iPulsePeriod);
ucTemp[0] &= ~4;
lcd1602_write(file_i2c, ucTemp, 1);
usleep(iPulsePeriod);
ucTemp[0] = iBackLight | DATA | (*text << 4);
lcd1602_write(file_i2c, ucTemp, 1);
ucTemp[0] |= 4; // pulse E
lcd1602_write(file_i2c, ucTemp, 1);
usleep(iPulsePeriod);
ucTemp[0] &= ~4;
lcd1602_write(file_i2c, ucTemp, 1);
usleep(CMD_PERIOD);
text++;
i++;
}
return 0;
} /* lcd1602_writeString() */
//
// Erase the display memory and reset the cursor to 0,0
//
int lcd1602Clear(void)
{
if (file_i2c < 0)
return 1;
lcd1602_writeCommand(0x0E); // clear the screen
return 0;
} /* lcd1602Clear() */
//
// Open a file handle to the I2C device
// Set the controller into 4-bit mode and clear the display
// returns 0 for success, 1 for failure
//
int lcd1602Init(int iChannel, int iAddr)
{
char szFile[32];
int rc;
sprintf(szFile, "/dev/i2c-%d", iChannel);
file_i2c = open(szFile, O_RDWR);
if (file_i2c < 0)
{
fprintf(stderr, "Error opening i2c device; not running as sudo?\n");
return 1;
}
rc = ioctl(file_i2c, I2C_SLAVE, iAddr);
if (rc < 0)
{
close(file_i2c);
fprintf(stderr, "Error setting I2C device address\n");
return 1;
}
iBackLight = BACKLIGHT; // turn on backlight
lcd1602_writeCommand(0x02); // Set 4-bit mode of the LCD controller
lcd1602_writeCommand(0x28); // 2 lines, 5x8 dot matrix
lcd1602_writeCommand(0x0c); // display on, cursor off
lcd1602_writeCommand(0x06); // inc cursor to right when writing and don't scroll
lcd1602_writeCommand(0x80); // set cursor to row 1, column 1
lcd1602Clear(); // clear the memory
return 0;
} /* lcd1602Init() */
//
// Set the LCD cursor position
//
int lcd1602SetCursor(int x, int y)
{
unsigned char cCmd;
if (file_i2c < 0 || x < 0 || x > 15 || y < 0 || y > 1)
return 1;
cCmd = (y == 0) ? 0x80 : 0xc0;
cCmd |= x;
lcd1602_writeCommand(cCmd);
return 0;
} /* lcd1602SetCursor() */
//
// Set the LCD Pulse Period
//
void lcd1602SetPulsePeriod(int val)
{
iPulsePeriod = val;
} /* lcd1602SetPulsePeriod() */
void lcd1602Shutdown(void)
{
iBackLight = 0; // turn off backlight
lcd1602_writeCommand(0x08); // turn off display, cursor and blink
close(file_i2c);
file_i2c = -1;
} /* lcd1602Shutdown() */
上面的程序,经过一些小的修改,方便在其他地方调用。
另外,还需要获得IP的程序,经过了解,得到如下的代码:
// 获取本机ip eth_inf网卡名称 调用方法get_local_ip("wlan0");
int get_local_ip(const char *eth_inf)
{
int sd;
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sd)
{
printf("socket error: %s\n", strerror(errno));
return -1;
}
strncpy(ifr.ifr_name, eth_inf, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0;
// if error: No such device
if (ioctl(sd, SIOCGIFADDR, &ifr) < 0)
{
printf("ioctl error: %s\n", strerror(errno));
close(sd);
return -1;
}
printf("interfac: %s, ip: %s\n", eth_inf, inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr));
close(sd);
return 0;
}
有了上面的程序,就可以直接编写主程序lcd1602_demo.c了:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include "include/lcd1602.h"
struct ifreq ifr;
// 获取本机ip eth_inf网卡名称 调用方法get_local_ip("wlan0");
int get_local_ip(const char *eth_inf)
{
int sd;
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sd)
{
printf("socket error: %s\n", strerror(errno));
return -1;
}
strncpy(ifr.ifr_name, eth_inf, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0;
// if error: No such device
if (ioctl(sd, SIOCGIFADDR, &ifr) < 0)
{
printf("ioctl error: %s\n", strerror(errno));
close(sd);
return -1;
}
printf("interfac: %s, ip: %s\n", eth_inf, inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr));
close(sd);
return 0;
}
int main(int argc, char *argv[])
{
int rc;
char str[17] = {0};
char ip[16] = {0};
get_local_ip("wlan0");
sprintf(ip, "%s", inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr));
rc = lcd1602Init(0, 0x3f);
if (rc)
{
printf("Initialization LCD1602 failed; aborting...\n");
return 0;
} else {
printf("Initialization LCD1602 ok!\n");
}
// welcome
lcd1602SetCursor(0, 0);
sprintf(str, "%-16s", "Hello World!");
lcd1602WriteString(str);
lcd1602SetCursor(0, 1);
sprintf(str, "%-16s", "I'm Purple Pi!");
lcd1602WriteString(str);
sleep(3);
// ip
lcd1602SetCursor(0, 0);
sprintf(str, "%-16s", "My IP addr is:");
lcd1602WriteString(str);
lcd1602SetCursor(0, 1);
sprintf(str, "%-16s", ip);
lcd1602WriteString(str);
sleep(5);
// quit
lcd1602SetCursor(0, 0);
lcd1602WriteString(str);
lcd1602SetCursor(0, 1);
sprintf(str, "%-16s", "ENTER to quit");
lcd1602WriteString("ENTER to quit");
// blonk
lcd1602Control(1, 0, 1); // backlight on, underline off, blink block on
printf("ENTER to quit...\n");
getchar();
// shutdown
lcd1602Shutdown();
return 0;
} /* main() */
上述代码的逻辑,从上往下走,比较简单:
要编译上面的LCD1602驱动库程序,还需要编写一个Makefile:
CFLAGS=-c -Wall -O2
LIBS = -lm -lpthread
all: liblcd1602.a
liblcd1602.a: lcd1602.o
$(AR) -rc liblcd1602.a lcd1602.o ;\
cp liblcd1602.a ./lib ;\
cp lcd1602.h ./include
lcd1602.o: lcd1602.c
$(CC) $(CFLAGS) lcd1602.c
clean:
rm *.o liblcd1602.a
首先交叉编译库文件和demo程序,然后在上传到开发板:
mkdir lib include
make CC=arm-unknown-linux-gnueabihf-gcc AR=arm-unknown-linux-gnueabihf-ar
arm-unknown-linux-gnueabihf-gcc lcd1602_demo.c -lm -llcd1602 -lpthread -L./lib -o lcd1602_demo
ls -lh lcd1602_demo
scp lcd1602_demo root@192.168.1.30:/root/
编译上传完成后,到开发板上执行:【注意cd切换到/root/再执行】

LCD1602就能显示出IP地址了。

虽然只能显示16*2的字符,但是作为一些简短信息的显示,还是非常方便的。
更多回帖