普冉PY32系列(六) 通過I2C介面驅動PCF8574擴展的1602LCD

来源:https://www.cnblogs.com/milton/archive/2023/03/04/17177742.html
-Advertisement-
Play Games

1602LCD 是工業上常用的模塊, 在工廠交通運輸設備上經常能見到. 驅動晶元為 HD44780, 1602LCD 的字元顯示為兩行, 每行16個字元, 字元基於5×8的像素矩陣 ...


目錄

1602 LCD

1602LCD 是工業上常用的模塊, 在工廠交通運輸設備上經常能見到. 驅動晶元為 HD44780, 1602LCD 的字元顯示為兩行, 每行16個字元, 字元基於5×8的像素矩陣

PIN腳功能

Pin Name Function
1 Ground 地 (0V)
2 Vcc 供電 5V (4.7V – 5.3V), 註意不能用3.3V供電
3 Vo / VEE 對比度調節. 連接一個可變電阻, 過高無法分辨顯示的字元, 過低字元太淡無顯示
4 RS 寄存器選擇(Register Select), 低電平為命令寄存器, 高電平為數據寄存器
5 Read/write 讀寫選擇, 低電平寫入, 高電平讀取
6 Enable EN 閑時處於低電平, 當需要執行指令前幾個毫秒將EN拉高, 執行完再拉低
7~14 DB0~DB7 8-bit 數據pin
15 Led+ LED 背光電源
16 Led- LED 背光接地

RS (Register Select)

1602LCD有兩組寄存器, 命令寄存器和數據寄存器, RS用於數據和命令寄存器的切換

實際使用時, 需要配合 EN 和 R/W

  • 在 EN = 1, R/W = 0, RS = 1 時, 往數據寄存器寫入的字元會用於展示.
  • EN = 1, R/W = 0, RS = 0 時, 往命令寄存器寫入, 用於發送指令, 例如: 初始化, 清空屏幕, 設置游標位置, 控制顯示等

指令編碼

以下是各指令位的說明

/*-------------------------------------------------------------
*   Instruction     D7  D6  D5  D4  D3  D2  D1  D0             
*   ==============================================             
*   Display clear   0   0   0   0   0   0   0   1              
*   Cursor home     0   0   0   0   0   0   1   *              
*   Entry Mode Set  0   0   0   0   0   1  I/D  S              
*   Display On/Off  0   0   0   0   1   D   C   B              
*   Curs/Disp shift 0   0   0   1  S/C R/L  *   *              
*   Function Set    0   0   1   DL  N   F   *   *              
*   CG RAM addr set 0   1   ---------Acg---------              
*   DD RAM addr set 1   -------------Add---------              
*                                                              
*   Meaning:                                                   
*   *     - nonvalid bit                                       
*   Acg   - CG RAM address (CHARACTER GENERATOR)               
*   Add   - DD RAM address (DATA DISPLAY)                      
*   AC    - adress counter                                     
*                                                              
*   I/D   - 1-increment, 0-decrement                           
*   S     - 1-display shift, 0-no display shift                
*   D     - 1-display ON, 0-display OFF                        
*   C     - 1-cursor ON, 0-cursor OFF                          
*   B     - 1-blink ON, 0-blink OFF                            
*   S/C   - 1-display shift, 0-cursor movement                 
*   R/L   - 1-right shift, 0-left shift                        
*   DL    - 1-8 bits data transfer, 0-4 bits data transfer     
*   N     - 1-1/16 duty, 0-1/8 or 1/11 duty                    
*   F     - 1-5x10 dot matrix, 0-5x7 dot matrix                
*   BF    - 1-internal operation in progress, 0-display ready  
*                                                              
\**************************************************************/

配合 EN = 1, R/W = 0, RS = 0 時, 往命令寄存器寫入, 可以執行以下指令

No. Hex Binary 命令說明
1 01 0000 0001 清除顯示
2 02 0000 001x 游標回原位
3 04 0000 0100 向左移動游標(兩個bit分別控制方向左右, 游標還是屏幕)
4 06 0000 0110 向右移動游標
5 05 0000 0101 向右移顯示
6 07 0000 0111 向左移動顯示
7 08 0000 1000 顯示關閉, 游標關閉(三個bit分別控制屏幕顯示, 游標顯示, 游標閃爍)
8 0A 0000 1010 顯示關閉, 游標打開
9 0C 0000 1100 顯示打開, 游標關閉
10 0E 0000 1110 顯示打開, 游標閃爍
11 0F 0000 1111 顯示打開, 游標閃爍
12 10 0001 00xx 將游標位置向左移動(兩個bit分別控制游標還是屏幕, 左移還是右移)
13 14 0001 01xx 將游標位置向右移動
14 18 0001 10xx 將整個顯示屏向左移動
15 1C 0001 11xx 將整個顯示屏向右移動
16 28 0010 10xx 4位, 2行, 5x8矩陣(三個bit分別控制4位還是8位,一行還是兩行,5x10還是5x8)
17 20 0010 00xx 4位, 1行, 5x8矩陣
18 38 0011 10xx 8位, 2行, 5×8
19 4x 01xx xxxx 設置 CGRAM 地址, 後面6個bit是地址, 在這個指令之後發送或接收數據
20 8x 1xxx xxxx 設置 DDRAM 地址, 後面7個bit是地址, 在這個指令之後發送或接收數據
21 80 1000 0000 將游標強制移動到開頭(第一行)
22 C0 1100 0000 將游標強制移動到開頭(第二行)

顯示自定義字元

自定義字元要在 CG-RAM 中設置. CG-RAM地址從 0x40 開始, 大小為 64 byte, 可以創建8個字元, 每個字元8個byte. 在這些地址創建字元後, 就可以在LCD中顯示.

CG-RAM 地址和命令

No. Addr. Command
0 0x40 0
1 0x48 1
2 0x56 2
3 0x64 3
4 0x72 4
5 0x80 5
6 0x88 6
7 0x96 7

上面的表中可以看到每個字元的起始地址及其列印命令, 例如第一個字元的地址是 [0x40, 0x47], 使用命令0可以輸出這個字元, 第二個字元的地址[0x48, 0x55], 使用命令1輸出.

自定義字元時, 每個字元是5x8的點陣, 5是列數, 8是行數. 對應字母b的點陣可以表示為

char b[7] = {0x10,0x10,0x16,0x19,0x11,0x11,0x1E};

將其發送到對應的地址就可以創建字元.

I2C 介面 PCF8574 擴展板

因為1602LCD本身刷新率不高, 為節省IO, 可以通過 PCF8574 擴展模塊, 將I2C協議轉為並口輸出. 1602屏直連MCU, 需要至少7個IO(RS, R/W, EN, 4位對應4根數據線)才能驅動起來, 使用 PCF8574 模塊只需要2個IO口.

PCF8574 是一個IIC協議的IO口擴展晶元, 包含一個8位準雙向口, 一個匯流排介面, 還有三條地址線. 每個IO口可以單獨的分配為輸入或者輸出. 作為輸入時, 可以用於監控中斷或者鍵盤. 作為輸出時, 可以用於驅動LED. 可以通過單獨的寄存器讀取輸入埠狀態或者配置輸出埠狀態. PCF8574 的三個地址管腳, 可以分配8個地址, 也就是同一個系統中可以最多存在8個這樣的模塊.

PCF8574 電流消耗很低, 並且輸出鎖存, 具有大電流驅動能力, 可直接驅動LED. 還帶一個中斷接線, 可以作為MCU的外部中斷. 通過中斷通知MCU 是否有數據輸入, 因此 PCF8574 也可以作為一個單被控器.

通過 PCF8574 控制 1602LCD, 可以將命令發送到 I2C 介面, PCF8574 的輸出與 1602LCD 的連接為

[tu]

VSS =  Ground
VDD =  Connects to VCC on the I2C header
VO  =  Display contrast.  Connects to the potentiometer on the module
RS  =  P0 on PCF8574
RW  =  P1 on PCF8574
E   =  P2 on PCF8574
D0 – D3 no connects
D4  =  P4 on PCF8574
D5  =  P5 on PCF8574
D6  =  P6 on PCF8574
D7  =  P7 on PCF8574
A   =  Backlight Anode.  Typically connects to 5V.
K   =  Backlight Cathode.  Connects to ground

基於 PY32F0 的演示

硬體

  • 已焊接 PCF8574 擴展模塊的 1602LCD
  • 基於 PY32F0 系列的開發板
  • USB2TTL 用於觀察地址輸出

接線

大多數 PY32F0 都有 PF1/PF0, 可以通過復用將I2C介面換成其他的pin腳.

註意 PCF8574+1602LCD 供電電壓為5V, 3.3V供電無法驅動字元顯示. 如果 PY32F0 使用 3.3V 供電, 則需要另接5V電源, 與 PY32F0 共地即可.

PY32          PCF8574 1602 LCD       USB2TTL
 PF1/PA9       SCL
 PF0/PA10      SDA
               VCC    ->  5V
 GND           GND    ->  GND        GND
 PA2                                 RX
 PA3                                 TX

軟體

以下的實現基於LL庫, 完整的示例代碼位於

https://github.com/IOsetting/py32f0-template/tree/main/Examples/LL/I2C/PCF8574_1602LCD

指令和地址的巨集定義

/* I2C address
 * - 7 bit slave address, left aligned, bits 7:1 are used, LSB bit is not used
 * - 0x4E or 0x7E
*/
#define LCD1602_I2C_ADDR  0x7E
/* Delay in millisecond */
#define LCD1602_DELAY 5
/* Register selection */
#define PIN_RS    (1 << 0)
/* Read/Write */
#define PIN_RW    (1 << 1)
/* Chip enable */
#define PIN_EN    (1 << 2)
/* Back light - might not be available on some PCF8574 modules */
#define BACKLIGHT (1 << 3)

/* Clear display */
#define LCD1602_CMD_CLEAR_DISPLAY               0b00000001
/* Move cursor home */
#define LCD1602_CMD_HOME                        0b00000010

// Entry Mode, Set cursor/display moving direction
#define LCD1602_CMD_DIRECTION_RIGHT             0b00000110
#define LCD1602_CMD_DIRECTION_LEFT              0b00000100
#define LCD1602_CMD_DIRECTION_RIGHT_SHIFT       0b00000111
#define LCD1602_CMD_DIRECTION_LEFT_SHIFT        0b00000101
// Display mode
#define LCD1602_CMD_MODE_OFF                    0b00001000
#define LCD1602_CMD_MODE_ON_CURSOR_OFF          0b00001100
#define LCD1602_CMD_MODE_ON_CURSOR_ON           0b00001110
#define LCD1602_CMD_MODE_ON_CURSOR_BLNK         0b00001111
// Cursor/Display Shift
#define LCD1602_CMD_CURSOR_MOVE_LEFT            0b00010000
#define LCD1602_CMD_CURSOR_MOVE_RIGHT           0b00010100
#define LCD1602_CMD_DISPLAY_SHIFT_LEFT          0b00011000
#define LCD1602_CMD_DISPLAY_SHIFT_RIGHT         0b00011100

/* Function set: 4-bit, 1 row, 5X8 matrix */
#define LCD1602_CMD_FUNC_4B_1L_5X8              0b00100000
/* Function set: 4-bit, 2 row, 5X8 matrix */
#define LCD1602_CMD_FUNC_4B_2L_5X8              0b00101000
/* Function set: 8-bit, 1 row, 5X8 matrix */
#define LCD1602_CMD_FUNC_8B_1L_5X8              0b00110000
/* Function set: 8-bit, 2 row, 5X8 matrix */
#define LCD1602_CMD_FUNC_8B_2L_5X8              0b00111000
/* Set/Read CGRAM address */
#define LCD1602_CMD_CGRAM_ADDR                  0b01000000
/* Set/Read DDRAM address */
#define LCD1602_CMD_DDRAM_ADDR                  0b10000000

/* First row address */
#define LCD1602_DDRAM_ROW0                      0b10000000
/* Second row address */
#define LCD1602_DDRAM_ROW1                      0b11000000

基礎方法

發送指令, 數據和文本

ErrorStatus LCD_SendInternal(uint8_t lcd_addr, uint8_t data, uint8_t flags)
{
  ErrorStatus status;
  for(;;)
  {
    status = BSP_I2C_IsDeviceReady(lcd_addr, 5000);
    if(status == SUCCESS)
    {
      break;
    }
  }

  uint8_t up = data & 0xF0;
  uint8_t lo = (data << 4) & 0xF0;

  uint8_t data_arr[4];
  data_arr[0] = up|flags|BACKLIGHT|PIN_EN;
  data_arr[1] = up|flags|BACKLIGHT;
  data_arr[2] = lo|flags|BACKLIGHT|PIN_EN;
  data_arr[3] = lo|flags|BACKLIGHT;

  status = BSP_I2C_MasterTransmit(lcd_addr, data_arr, sizeof(data_arr), 5000);
  LL_mDelay(LCD1602_DELAY);
  return status;
}

void LCD_SendCommand(uint8_t lcd_addr, uint8_t cmd)
{
  LCD_SendInternal(lcd_addr, cmd, 0);
}

void LCD_SendData(uint8_t lcd_addr, uint8_t data)
{
  LCD_SendInternal(lcd_addr, data, PIN_RS);
}

void LCD_SendString(uint8_t lcd_addr, char *str)
{
  while (*str)
  {
    LCD_SendData(lcd_addr, (uint8_t)(*str));
    str++;
  }
}

初始化設置

void LCD_Init(uint8_t lcd_addr)
{
  // need at least 40ms after power rises above 2.7V
  LL_mDelay(50);
  // start in 8-bit mode, 3 commands
  LCD_SendCommand(lcd_addr, LCD1602_CMD_FUNC_8B_1L_5X8);
  LCD_SendCommand(lcd_addr, LCD1602_CMD_FUNC_8B_1L_5X8);
  LCD_SendCommand(lcd_addr, LCD1602_CMD_FUNC_8B_1L_5X8);
  // set it to 4-bit mode, interface is still 8-bit
  LCD_SendCommand(lcd_addr, LCD1602_CMD_FUNC_4B_1L_5X8);

  // now interface is 4-bit, set it to 2 lines and 5x8 font
  LCD_SendCommand(lcd_addr, LCD1602_CMD_FUNC_4B_2L_5X8);
  // display & cursor home
  LCD_SendCommand(lcd_addr, LCD1602_CMD_HOME);
  // display on, right shift, underline off, blink off
  LCD_SendCommand(lcd_addr, LCD1602_CMD_MODE_ON_CURSOR_BLNK);
  // move direction right
  LCD_SendCommand(lcd_addr, LCD1602_CMD_DIRECTION_RIGHT);
  // clear display (optional here)
  LCD_SendCommand(lcd_addr, LCD1602_CMD_CLEAR_DISPLAY);
}

功能操作

清除屏幕

LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_CMD_CLEAR_DISPLAY);

移動游標, 輸出文字

// move cursor to 0,0
LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_DDRAM_ROW0|0);
LCD_SendString(LCD1602_I2C_ADDR, " Using 1602 LCD");
// move cursor to 1,0
LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_DDRAM_ROW1|0);
LCD_SendString(LCD1602_I2C_ADDR, "  over I2C bus");

通過 CGRAM 設置 自定義字元

// CGRAM test
for (i = 0; i < 8; i++)
{
  LCD_SetCGRAM(LCD1602_I2C_ADDR, i, &cgrom[i * 8]);
}

展示自定義字元

for (i = 0; i < 8; i++)
{
  LCD_SendData(LCD1602_I2C_ADDR, i);
  LL_mDelay(200);
}

顯示整體左右平移

// Shift display test
LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_CMD_CLEAR_DISPLAY);
LL_mDelay(500);
LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_DDRAM_ROW0|9);
LCD_SendString(LCD1602_I2C_ADDR, "Shift");
LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_DDRAM_ROW1|8);
LCD_SendString(LCD1602_I2C_ADDR, "<<<->>>");
LL_mDelay(500);
for (i = 0; i < 8; i++)
{
  LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_CMD_DISPLAY_SHIFT_LEFT);
  LL_mDelay(200);
}
LL_mDelay(500);
for (i = 0; i < 8; i++)
{
  LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_CMD_DISPLAY_SHIFT_RIGHT);
  LL_mDelay(200);
}

左右移動游標

// Move cursor test
for (i = 0; i < 11; i++)
{
  LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_CMD_CURSOR_MOVE_LEFT);
  LL_mDelay(200);
}
LL_mDelay(500);
for (i = 0; i < 12; i++)
{
  LCD_SendCommand(LCD1602_I2C_ADDR, LCD1602_CMD_CURSOR_MOVE_RIGHT);
  LL_mDelay(200);
}

常見問題

1. 屏幕不顯示

不顯示的原因有很多, 如果確認代碼和接線無誤, 可能的原因有

  1. 檢查1602LCD的供電電壓是不是5V, 在3.3V下無法驅動, 只有背光沒有字元
  2. 檢查I2C地址是否正確. 查看串口掃描到的實際的設備I2C地址, 是否和程式中的地址一致, 通常情況下, PCF8574T 的地址是 0x4E, PCF8574AT 的地址是 0x7E

2. 字元顯示亂碼

HD44780對啟動的指令順序和延時是有要求的, 可以參考其數據手冊的P45, 如果延時不夠或指令順序不正確, 會導致屏幕未進入4-bit模式而導致顯示錯亂. 對於部分屏幕, 啟動時需要增大延時, 如果等待時間不足, 會導致輸出亂碼.

參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 什麼是OpenAI https://openai.com OpenAI是一家人工智慧公司,成立於2015年,總部位於美國舊金山。該公司的使命是研究和開發能夠產生廣泛影響的人工智慧技術,並將這些技術傳播到整個社會。OpenAI由一些著名的科學家和企業家共同創立,包括伊隆·馬斯克、Sam Altman等 ...
  • 實現動態aop並註入 創建一個DynamicDispatchProxy類繼承DispatchProxy, public class DynamicDispatchProxy<T> : DispatchProxy { /// <summary> /// 目標類 /// </summary> priva ...
  • 折半查找演算法 前言 最近要考試了,重新回顧一下之前學的演算法,今天是折半查找,它的平均比較次數是Log2 n 思想 給定一個有序數組A[0..n-1],和查找值K,返回K在A中的下標。 折半查找需要指定3個指針,left、right、mid,分別是左指針指向下標0,右指針指向元素末尾,mid中間值指向 ...
  • 背景 當我們在用Avalonia開發項目的時候會涉及到路徑的問題,在Linux下麵路徑的寫法和Window路徑的寫法有區別 問題描述 用Avalonia開發項目發現我們之前的路徑寫法在Linux下麵運行報錯,尤其是在結合Prism開發寫項目進行模塊化掃描的方式載入我們的模塊程式就會出現報錯 解決方案 ...
  • 頁眉位於文檔中每個頁面的頂部區域,常用於顯示文檔的附加信息,可以插入時間、圖形、公司微標、文檔標題、文件名或作者姓名等;頁腳位於文檔中每個頁面的底部的區域,常用於顯示文檔的附加信息,可以在頁腳中插入文本或圖形。今天這篇文章就將為大家展示如何以編程的方式在在 Word 文檔中添加頁眉和頁腳。下麵是我整 ...
  • 實現背景 最近需要把大數據測試環境的虛擬機遷移到另一臺物理機上,其中資料庫文件過大,一般的上傳文件有大小限制不能超過4GB,可以通過NFS共用目錄解決文件上傳問題。 NFS介紹 NFS介紹NFS 即網路文件系統(Network File-System),可以通過網路讓不同機器、不同系統之間可以實現文 ...
  • 原文:What is a Shell? 註意:本文是對原文的一個翻譯! Shell是一個巨集處理器,用於執行命令。巨集處理器意味著,將文本和符號展開,創建一個更大的表達式。 Unix shell 既是一個命令解釋器,也是一門編程語言。作為一個命令解釋器,它提供了豐富的GUN工具集可以與用戶進行交互。作為 ...
  • 前言 本文展示了一個比較完整的企業項目級別的Makefile文件,包括了:文件調用,源文件、頭文件、庫文件指定,軟體版本號、巨集定義,編譯時間,自動目錄等內容。 1、目錄架構 本文中所採用的目錄架構,在企業項目開發中十分常見:源文件都放在src目錄中,頭文件都放在inc目錄中,並且這兩個目錄都可以有對 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...