問題來源
在此之前學習了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()輸出格式