DongshanPI-D1s开发板使用教程之使用LCD1602(I2C)液晶显示屏

经过 DongshanPI-D1s开发板使用教程之SHT30温湿度传感器(I2C)数据读取 的学习研究,已经可以使用I2C设备了。

进过进一步的研究,又点亮了LCD1602(I2C)液晶显示屏,下面给大家分享。
网上的一段介绍如下:查看原文

LCD1602液晶显示器是一种使用广泛的字符型液晶显示模块。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。该显示屏的优点是耗电量低、体积小、辐射低。
LCD1602主要用来显示数字、字母、图形以及少量自定义字符。可以显示2行16个字符 ,拥有16个引脚,其中8位数据总线D0-D7 ,和RS、R/W、EN三个控制端口 ,工作电压为5V,并且带有字符对比度调节V0和背光源AK。
为了方便使用,通常会添加I2C转换模块,这样只要两根数据线,就能够进行控制使用,大大减少了IO口的占用。

一、硬件材料

  1. D1s开发板:DongshanPI-D1s开发板
  2. LCD1602:I2C接口
  3. 连接线:杜邦线若干,需要2.0转2.54线;手头没有转接线,直接用测试钩

LCD1602(I2C)液晶显示屏:



背板上的这个小板子,就是I2C转换模块。
背板上,有两个著名的牛屎芯片!!!

二、原理图
通过查看DongshanPI-D1s开发板的原理图,了解到I2C接口使用和连接:

在上图的左下角,可以看到:
PE12:SCL
PE13:SDA
需要注意的是,默认系统中,开启的是I2C2。

三、实物接线



需要注意的是,LCD1602需要使用5V电源。如果使用3.3V,LCD背光会亮,但是字符不会显示。

四、编译固件
参考 DongshanPI-D1s开发板使用教程之SHT30温湿度传感器(I2C)数据读取 进行。
如果已经编译烧录过,则不用重新编译烧录,直接使用即可。

五、I2C设备检测
DongshanPI-D1s开发板启动后,参考 DongshanPI-D1s开发板使用基础文档【编译、烧录、adb、gpio-led、c】 使用OTG接口通过adb连接,再进行下面的操作。

  1. 执行i2cdetect -l 查看当前的I2C控制器:

    可以看到,当前可用的i2c设备为i2c-2
  2. 执行i2cdetect -r -y 2,查看识别的I2C外部设备:

    可以看到,LCD1602(I2C)的地址0x3f被识别出来了。
    不同厂家的LCD1602(I2C),设备地址可能不同,请仔细查阅所用设备的说明。
    后续代码中,也要做对应的处理。

如果在列表中,没有看到传感器的地址,请仔细检查传感器的I2C的接线是否正常。
如果因为程序错误,导致I2C设备打开后,进入busy状态,那就把开发板断电,然后重新上电启动,再次进行检查。

六、编写代码
原始代码,来自于: bitbank2/LCD1602: A C library (Linux + Arduino) to control 2 line x 16 character I2C LCD displays
但是改代码,不能直接使用,需要做一些修改处理,才能够正常运行
最终,使用的实际代码如下:

文件:lcd1602.h

//
// LCD1602 2 line by 16 character LCD library
//

#ifndef LCD1602_H
#define LCD1602_H

//
// 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

文件:lcd1602.c

//
// 2x16 LCD display (HD44780 controller + I2C chip)
//

#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>

#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() */

文件:lcd1602_demo.c

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#include "lcd1602.h"

int main(int argc, char *argv[])
{
	int rc;
	char str[17] = {0};
	char ip[16] = {0};

	sprintf(ip, "%s", "192.168.0.3");

	rc = lcd1602Init(2, 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 DongShan 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.h:LCD1602控制调用头文件
  • lcd1602.c:LCD1602控制调用具体实现
  • lcd1602_demo.c:测试程序

程序的基本逻辑如下:

  • 初始化LCD1602
  • 输出欢迎信息"Hello World!" 和 “I’m DongShan Pi!”
  • 延迟3秒
  • 输出"My IP addr is:“和"192.168.0.3”
  • 延迟3秒
  • 输出IP信息,及"ENTER to quit"
  • 如果在命令行回车,就退出

七、编译代码并执行
这里需要注意的是,编写、编译、上传、执行,是在不同的环境:

  • 编写:可以在编译固件的环境,也可以在主机环境编写好以后在编译环境编译
  • 编译:在编译固件的环境中编译
  • 上传:在主机环境,通过adb上传到开发板
  • 执行:在开发板上执行

那就先编译代码:

# 设置编译工具路径
export PATH=/sdk/tina-d1-h/prebuilt/gcc/linux-x86/riscv/toolchain-thead-glibc/riscv64-glibc-gcc-thead_20200702/bin/:$PATH

# 编译
riscv64-unknown-linux-gnu-gcc -o lcd1602_demo lcd1602_demo.c lcd1602.c

# 查看结果
ls -lh lcd1602_demo
    -rwxr-xr-x 1 root 17K Nov  6 10:02 lcd1602_demo

编译完成后,在主机上,使用adb上传:

adb push lcd1602_demo /root/

最后,在开发板上执行:

/root/lcd_demo

image

LCD液晶屏上实际输出如下:



虽然这个LCD1602的液晶屏上,只能显示16*2的字符,但是用于输出一点IP地址,或者提示信息,还是挺方便的。

1 个赞