2015年10月29日 星期四

{ 單晶片 + Arduino + 樹莓派 } 整合型 LCD ( @ I2C 模式 ) 的漂亮數字顯示 ( 自訂字型或圖案 )

網頁修改時間:2015/10/31

前一陣子在找一些資料的時候,無意間在 youtube 網站上看到一個影片,內容是在 LCD 上顯示環境中的溫、濕度與塵埃濃度。一聽之下或許覺得沒什麼,因為在部落格中早就有這些東西存在!但是令我駐足觀看的是:它所使用的數字顯示很大、很清楚也很漂亮,作為一眼就能清楚得知數值大小,效果很好!因此就產生了這篇網頁!

/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
有購買商品的使用者,網頁中所需相關資料已放置於雲端硬碟,請自行下載使用!

  • { Raspberry Pi }:{ 3V3 整合型 LCD }/codes/RaspberryPi/IIC_CGRAM/IIC_Numeric/
  • { Arduino }:{ 5V 整合型 LCD }/codes/Arduino/
  • { 單晶片 }: { 5V 整合型 LCD }/codes/AT89S51/KeilC/Numeric_IIC_Demo/

其餘的使用者,程式碼複製貼上使用 (單晶片程式碼在此不提供)!
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*

*********************************************************************************
整合型 LCD請至露天賣場訂購:
*********************************************************************************
賣場所賣的整合型 LCD 跟市面上常用的 16 x 2 LCD 不同,可使用 4/8 bit、I2C 和 SPI 三種通訊方式來做控制 ( 預設的控制模式是 I2C 通訊 ),由於控制命令與格式都是相同的,因此下面自定字元的說明與程式碼的講解,觀念上都是相同的。如果想要自己定義顯示的字元或是圖型的話,可以參考接下來的說明。

自訂 LCD 字型或圖案 ( KS0066i ):

LCD 裡面的每一個字型,都是侷限在一個 5 x 8 的像素矩陣。像素矩陣的每一個點,可以想像就是一個 LED:亮為 1,暗為 0。基本上,一些通用的字型與圖形 ( ASCII Code ) 都已內建在其中,要取出只要給定 ASCII Code 馬上就可以將這些內建的字元或圖形取出。

但是若是 ASCII Code 或是 LCD 內建的字型沒有辦法顯示出使用者想要的字元或是圖形時,那麼就必須自己創造一個出來!

打開整合型 LCD 的資料手冊第 18 頁 (表 11,KS0066i-0A  字型檔表 ),就可以看到全部支援的字元與圖形
S0066i-0A  字型檔表
注意左上角的前面填寫 CGRAM[00] ~ CGRAM[08] 個格子,這幾個是預留給使用者自定字元或是圖形用的。
S0066i-0A  字型檔表 - 左上角放大
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
名稱解釋:
  • CGRAM ( Character Generation RAM )
    將字型資料寫入到 CGRAM,就可以使用使用者自訂的字元或是圖形
  • DDRAM ( Display Data RAM )
    儲存顯示的資料,最多 80 x 8 bits ( 80 個字元 )
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*

如何自訂呢 ? 首先,先創建一個 5 x 8 大小的表格,然後依照自己心中想要的字元或是圖形,將其描繪並填滿在表格的欄位中;填顏色的填入數字 1,沒填顏色的就填入 0。

為了要將這個自訂的字元或是圖形寫入到 CGRAM 中,必須輸入 8 個 Bytes 的資料到 CGRAM 八個其中的一個位址中。但是上面的字元由 5 個行 (5 行 x 8 列) 組成,為了要讓每一行形成一個 Byte,因此加入了 bit 7、6 和 5 三個虛擬的位元 (bit),輸入 0 或 1 都可以。如此,就可以得到完整的 8 個 Byte 的字元或是圖形資料。

每一個字元或是圖形的資料要輸入到八個 CGRAM 中的其中一個位址,每一個 CGRAM 位址又分為 8 個部分,分別對應到上面自訂字元或是圖形的 8 個 Bytes。選擇一個 CGRAM 位址,輸入自定字元或是圖形 8 個 Bytes 的資料之後,才能從 CGRAM 取出該位址的字元或是圖形顯示在整合型 LCD 上。

同樣的觀念與步驟也同樣適用於其他使用相同 LCD 控制晶片的液晶螢幕。

下面以一個創建數字 2 的例子,建立一個剛剛所說明的一個表格,表格右邊 "HEX" 欄位就是產生的字元 8 個 Byte 資料
自訂數字 2 個字元資料表格

自訂字元或圖形資料的輸入:

LCD 完成初始化之後,將模式切換到控制模式 ( RS=0,R/W = 0) 並設定 CGRAM 位址為 0x40 + [ 0x00 ~ 0x07] 其中的一個,最後將 8 個 Bytes 的資料輸入,即完成輸入。最後的資料輸入,可以一次輸入 8 個自定字元或是圖形的資料,不過取決於微控制器或微處理器不同而有所限制。

以 Arduino 為例,寫入 CGRAM 的方法如下程式碼
void writeCGRAM( const unsigned char dp[], unsigned char charlen, int start )
{
    Wire.beginTransmission(IIC_ADDR_LCD1);
    // To assign CGRAM start address
    Wire.write(0x80);   // 控制指令
    Wire.write( 0x40 + ( ( start - 1 ) << 3 ));  // 設置CGRAM ADDRESS,0x40 表示第 0 個
    // 將字元或圖形ㄌ要寫入 CGRAM
    Wire.write(0x40);   // 控制指令:表示以下傳輸的 n 個字節是數據
    // CGRAM_block 的數目;每一個字元,需要 8-byte 資料
    for( int i = 0; i < charlen * 8; i++ )
    {
        Wire.write(*dp);
        dp = dp + 1;
    }
    Wire.endTransmission();
}

其中,
  • dp[] : 自定字元或圖形的資料,以 16 進制表示。
  • charlen : 一次要輸入的字元或是圖形的數量,最多 3 個字元 ( 也就是 24-byte )。
  • start : 字元或是圖形輸入的開始位置,從 1 開始至 8 結束。
那麼如何顯示這個自定字元呢 ? 假設此字元位於 CGRAM 自定字元的第一個,而 CGRAM 基底位址為 0x40,地一個字元位於 0x00,所以這個字元位於 CGRAM 的位置為 0x40。為了讓使用更方便,只要直接輸入 cgram 的位址在下面的顯示程式碼的參數裡,就可以取出所要的自定字元或是圖形
void displayCGRAM( unsigned char cgramaddr, int row, int col)
{
    // set cursor position to col, row
    Wire.beginTransmission(IIC_ADDR_LCD1);
    Wire.write(0x80); //控制指令
    Wire.write( 0x80 + 0x40 * ( row - 1 ) + ( col - 1 ) );
    delayMicroseconds(100000);
    Wire.write(0x40); //控制指令
    Wire.write( cgramaddr );
    Wire.endTransmission();
}

若要輸入自訂數字 2,依照上面所創建的資料,那麼完整的程式如下:
#include <stdio.h>
#include <Wire.h>

// 每一個數字由六個 5x8 的區塊構成
const unsigned char CGRAM_block[]={
    0x1F,0x01,0x1F,0x1F,0x10,0x10,0x1F,0x1F, // 自訂數字 2
};

const unsigned char IIC_ADDR_LCD1 = 0x3C;

// 函數宣告
void initLCD();
void clearLCD();
void writeCGRAM( const unsigned char dp[], unsigned char charlen);
void displayCGRAM( unsigned char cgramaddr, int row, int col);

void setup() {
    Wire.begin();
    // 整合型 LCD 初始化 @ I2C 模式
    initLCD();
    delay(100); // delay 100 ms
    clearLCD();
    delay(100); // delay 100 ms

    // 寫入自定字元至整合型 LCD @ I2C 模式
    writeCGRAM( &CGRAM_block[0],  1, 1 );
    // 顯示自訂數字 2
    for(int i = 0; i < 16; i++)
        displayCGRAM( 0x00, 1, i + 1 ); 
    for(int i = 0; i < 16; i++)
        displayCGRAM( 0x00 * i, 2, i + 1 );
}

void loop()
{ }

//***--- 整合型 LCD @ I2C 模式 ---***
void initLCD()
{
    Wire.beginTransmission(IIC_ADDR_LCD1);
    Wire.write( 0x00 ); // N x commands
    Wire.write( 0x38 ); // Function set
    Wire.write( 0x0C ); // Display ON/OFF
    Wire.write( 0x01 ); // Clear display
    Wire.write( 0x06 ); // Entry mode set
    Wire.endTransmission();
}

void clearLCD()
{

    Wire.beginTransmission(IIC_ADDR_LCD1);

    Wire.write( 0x80 ); // One command
    Wire.write( 0x01 ); // Clear display    

    Wire.endTransmission();
}

void writeCGRAM( const unsigned char dp[], unsigned char charlen, int start )
{
    Wire.beginTransmission(IIC_ADDR_LCD1);
    // To assign CGRAM start address
    Wire.write(0x80);   // 控制指令
    Wire.write( 0x40 + ( ( start - 1 ) << 3 )); // 設置CGRAM ADDRESS
    // 寫入 CGRAM
    Wire.write(0x40);   // 控制指令
    // CGRAM_block 的數目;每一個字元,需要 8-byte 資料
    for( int i = 0; i < charlen * 8; i++ )
    {
        Wire.write(*dp);
        dp = dp + 1;
    }
    Wire.endTransmission();
}

void displayCGRAM( unsigned char cgramaddr, int row, int col)
{
    // set cursor position to col, row
    Wire.beginTransmission(IIC_ADDR_LCD1);
    Wire.write(0x80); // 控制指令
    Wire.write( 0x80 + 0x40 * ( row - 1 ) + ( col - 1 ) );
    delayMicroseconds(100000);
    Wire.write(0x40); //控制指令
    Wire.write( cgramaddr );
    Wire.endTransmission();
}


上傳程式所得到的結果
 整合型 LCD @ I2C 模式自訂數字 2
只要能好好利用這幾個創建好的整合型 LCD (@I2C模式)函式,配合之前原有的顯示函式,很容易就能控制內建與自定字元或圖形的顯示。

接著,繼續上面的函式,我們要用幾個自訂字元來創建跟網頁最上面影片中顯示的漂亮數字!

大型數字拆解:

若仔細觀看影片中的數字顯示,應該不難發現這些數字的組成其實是由 5 種格式 加上一個逗號的像素陣列所構成;其中逗號是用來顯示點號 "." 與分號 ""
構成大型數字的六個圖形

以數字 2 的構成為例,一但要在 LCD 上顯示,就會個佔據 LCD 第一行與第二行各三個 5 x 8 的像素矩陣。


將構成大型數字的六個圖形中的前面 5 個編號對照上面的圖形,就可以得到構成數字 2 的像素矩陣順序 { 3, 3, 4, 4, 2, 2 };前三個是數字構成的第一行三個像素矩陣,後三個是數字構成的第二行三個像素矩陣。

依照上面說明的方式完成0 - 9 每個數字 8-byte 的資料,並且將各個數字組成的像素矩陣編號寫出,就可以得到如下兩個宣告的陣列
// 每一個數字由六個 5x8 的區塊構成
// 這些區塊定義如下:
const unsigned char CGRAM_block[]={
    0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, // 0,全白, 如同 ASCII 0X20
    0X1F,0X1F,0X1F,0X00,0X00,0X00,0X00,0X00, // 1,上黑
    0X00,0X00,0X00,0X00,0X00,0X1F,0X1F,0X1F, // 2,下黑
    0X1F,0X1F,0X1F,0X00,0X00,0X1F,0X1F,0X1F, // 3,上下黑
    0X1F,0X1F,0X1F,0X1F,0X1F,0X1F,0X1F,0X1F, // 4, 全黑, 如同 ASCII 0XFF
    0X00,0X00,0X00,0X00,0X00,0X0E,0X0E,0X0E, // 5,下大點
};

// Numerics[0-9][]:表示一個數字組成的六個字元
// Numerics[][0-2]:表示數字第一列的三個字元
// Numerics[][3-6]:表示數字第二列的三個字元
const unsigned char Numerics[10][6]={
    {4,1,4,4,2,4},  // 0
    {1,4,0,2,4,2},
    {3,3,4,4,2,2},
    {1,3,4,2,2,4},
    {4,2,4,0,0,4},
    {4,3,3,2,2,4},
    {4,3,3,4,2,4},
    {1,1,4,0,4,0},
    {4,3,4,4,2,4},
    {4,3,4,0,0,4}, // 9
};


整合型 LCD 的顯示區域中,螢幕上下各佔 16 個字元 ( 1 - 16 )。因此,劃分這些區域之後,總共可以顯示四個大型數字,每個數字前方的上下各預留一個 5 x 8 的像素矩陣做為顯示點號 "." 與分號 "" 的區域,由此可以得到下面兩個標記數字該顯示的位置的陣列,總共可以顯示包括點號與分號 8 個資料量
// 數字可以容納四個,位置由 LCD 的 2, 6, 10, 14 起頭
const int nix[] = { 2, 6, 10, 14 };
// . 與 : 位置在 1, 5, 9, 13
const int pix[] = { 1, 5, 9, 13 };

知道上面資訊之後,顯示大型數字的函式就可以撰寫如下
void displayNumeric( const char dp[], const int len )
{
    int p =0, n = 0;
    for( int i = 0; i < len; i++ )
    {
        switch(dp[i])
        {
            case 0x2e:  // .
                if(p<n) p = n;
                // line 1
                displayCGRAM( 0x00, 1, pix[p] );
                // line 2
                displayCGRAM( 0x05, 2, pix[p] );
                p++;                
            break;
            case 0x3A:  // :
                if(p<n) p = n;
                // line 1
                displayCGRAM( 0x05, 1, pix[p] );
                // line 2
                displayCGRAM( 0x05, 2, pix[p] );
                p++;
            break;
            case 0x20:  // space
                // line 1
                for( int f = 0; f < 3; f++ )
                {
                    displayCGRAM( 0x00 , 1, nix[n] + f );
                }
                // line 2
                for( int f = 3; f < 6; f++ )
                {
                    displayCGRAM( 0x00, 2, nix[n] + f - 3 );
                }
                n++;
            break;
            case 0x30:  // 0
            case 0x31:  // 1
            case 0x32:  // 2
            case 0x33:  // 3
            case 0x34:  // 4
            case 0x35:  // 5
            case 0x36:  // 6
            case 0x37:  // 7
            case 0x38:  // 8
            case 0x39:  // 9
                // line 1
                for( int f = 0; f < 3; f++ )
                {
                    displayCGRAM( Numerics[ dp[i] - 0x30 ][f] , 1, nix[n] + f );
                }
                // line 2
                for( int f = 3; f < 6; f++ )
                {
                    displayCGRAM( Numerics[ dp[i] - 0x30 ][f], 2, nix[n] + f - 3 );
                }
                n++;
            break;
        }
    }
}

完整的程式 ( 一些函式的內容已在上面寫出,這邊省略,請自行補上 )
Numerics.ino
#include <stdio.h>
#include <Wire.h>

// 每一個數字由六個 5x8 的區塊構成
// 這些區塊定義如下:
const unsigned char CGRAM_block[]={
    0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, // 0,全白, 如同 ASCII 0X20
    0X1F,0X1F,0X1F,0X00,0X00,0X00,0X00,0X00, // 1,上黑
    0X00,0X00,0X00,0X00,0X00,0X1F,0X1F,0X1F, // 2,下黑
    0X1F,0X1F,0X1F,0X00,0X00,0X1F,0X1F,0X1F, // 3,上下黑
    0X1F,0X1F,0X1F,0X1F,0X1F,0X1F,0X1F,0X1F, // 4, 全黑, 如同 ASCII 0XFF
    0X00,0X00,0X00,0X00,0X00,0X0E,0X0E,0X0E, // 5,下大點
};

// Numerics[0-9][]:表示一個數字組成的六個字元
// Numerics[][0-2]:表示數字第一列的三個字元
// Numerics[][3-6]:表示數字第二列的三個字元
const unsigned char Numerics[10][6]={
    {4,1,4,4,2,4},  // 0
    {1,4,0,2,4,2},
    {3,3,4,4,2,2},
    {1,3,4,2,2,4},
    {4,2,4,0,0,4},
    {4,3,3,2,2,4},
    {4,3,3,4,2,4},
    {1,1,4,0,4,0},
    {4,3,4,4,2,4},
    {4,3,4,0,0,4}, // 9
};

// 數字可以容納四個,位置由 LCD 的 2, 6, 10, 14 起頭
const int nix[] = { 2, 6, 10, 14 };
// . 與 : 位置在 1, 5, 9, 13
const int pix[] = { 1, 5, 9, 13 };

const unsigned char IIC_ADDR_LCD1 = 0x3C;

// 函數宣告
void initLCD();
void clearLCD();
void writeCGRAM( const unsigned char dp[], unsigned char charlen);
void displayCGRAM( unsigned char cgramaddr, int row, int col);
void displayNumeric( const char dp[], const int len );

void setup() {

    Serial.begin(9600);
    Wire.begin();
    // 整合型 LCD 初始化 @ I2C 模式
    initLCD();
    delay(100); // delay 100 ms
    clearLCD();
    delay(100); // delay 100 ms

    // 寫入自定字元至整合型 LCD @ I2C 模式
    // Note: 一次輸入太多自定字元會有輸入不完全的情況出現
    // 所以適量的將自定字元陣列多次的輸入,然後再將其顯示在 LCD 上做確認
    writeCGRAM( &CGRAM_block[0],  3, 1 );
    writeCGRAM( &CGRAM_block[24], 3, 4 );
}

char nums[8]="";

void loop() {
    
    // 顯示自定字元在LCD上,確認自定字元已經正常的輸入到為控制裡
    for(int i = 0; i < 6; i++)
        displayCGRAM( (0x01 * i), 1, i + 1 );    
    for(int i = 0; i < 6; i++)
        displayCGRAM( (0x01 * i), 2, i + 1 );
    delay(1500);
    clearLCD();

    // 顯示大型數字空白、:、.、0 - 9
    displayNumeric( "....", 4 );
    delay(1500);
    clearLCD();
    displayNumeric( "::::", 4 );
    delay(1500);
    clearLCD();
    for( int i = 0; i < 10; i++ )
    {
        snprintf(nums, 8, "%d%d%d%d", i, i, i, i);
        displayNumeric( nums, 4 );
        delay(1500);
        clearLCD();
    }
    displayNumeric( "    ", 4 );
    delay(1500);
    clearLCD();
}

//***--- 整合型 LCD @ I2C 模式 ---***
void initLCD()
{
    // ... 省略 ...
}

void clearLCD()
{
    // ... 省略 ...
}

void writeCGRAM( const unsigned char dp[], unsigned char charlen, int start )
{
    // ... 省略 ...
}

void displayCGRAM( unsigned char cgramaddr, int row, int col)
{
    // ... 省略 ...
}

//***--- 大型數字顯示函式 ---***
void displayNumeric( const char dp[], const int len )
{
    // ... 省略 ...
}


連接 { 5V } 整合型 LCD,上傳程式執行的結果,如下影片


單晶片與樹莓派測試的結果影片:

同樣的方式,很容易地可以將大型數字顯示的方式在樹莓派與單晶片中實現!

* 樹莓派 ( 每一個硬體版本都適用 )
樹莓派使用 {3V3} 的整合型 LCD,接線相對簡單,不需要裝設可變電阻調整螢幕亮度,而且因為提供了整合型 LCD 函式庫的關係,自定字元或圖形資料的輸入很容易不需要再撰寫,只需要直接呼叫即可
IIC_Numeric.c
/*******************************************************************************
 * 
 * IIC_Numeric.c
 * 
 * Author: ruten.proteus
 * Date: 2015/08/21
 * 
 * compile: sudo g++ JLC1602A4IIC.o IIC_Numeric.c -lwiringPi -o iic_numeric
 *   
 * 說明:using hardware IIC of Raspberry Pi
 *      使用自定顯示大型數字
 * 
 ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>
#include "JLX1602A4IIC.h"

// 每一個數字由六個 5x8 的區塊構成
// 這些區塊定義如下:
const unsigned char CGRAM_block[]={
    0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00, // 0,全白, 如同 ASCII 0X20
    0X1F,0X1F,0X1F,0X00,0X00,0X00,0X00,0X00, // 1,上黑
    0X00,0X00,0X00,0X00,0X00,0X1F,0X1F,0X1F, // 2,下黑
    0X1F,0X1F,0X1F,0X00,0X00,0X1F,0X1F,0X1F, // 3,上下黑
    0X1F,0X1F,0X1F,0X1F,0X1F,0X1F,0X1F,0X1F, // 4, 全黑, 如同 ASCII 0XFF
    0X00,0X00,0X00,0X00,0X00,0X0E,0X0E,0X0E, // 5,下大點
};

// Numerics[0-9][]:表示一個數字組成的六個字元
// Numerics[][0-2]:表示數字第一列的三個字元
// Numerics[][3-6]:表示數字第二列的三個字元
const unsigned char Numerics[10][6]={
    {4,1,4,4,2,4},
    {1,4,0,2,4,2},
    {3,3,4,4,2,2},
    {1,3,4,2,2,4},
    {4,2,4,0,0,4},
    {4,3,3,2,2,4},
    {4,3,3,4,2,4},
    {1,1,4,0,4,0},
    {4,3,4,4,2,4},
    {4,3,4,0,0,4},
};

// // 數字可以容納四個,位置由 LCD 的 2, 6, 10, 14 起頭
const int nix[] = { 2, 6, 10, 14 };
// . 與 : 位置在 1, 5, 9, 13
const int pix[] = { 1, 5, 9, 13 };

const unsigned char IIC_ADDR_LCD1 = 0x3C;

const int display = 1;    // display on/off
const int cursor  = 0;    // cursor  on/off
const int blink   = 0;    // blink   on/off   

JLX1602A4IIC lcd1 = JLX1602A4IIC( IIC_ADDR_LCD1, display, cursor, blink );

void displayNumeric( const char dp[], const int len );

int main(void)
{
    char nums[8]="";
    lcd1.clear();
    
    // Wrtie customized font to CGRAM
    //
    // 4: 要輸入多少的自訂字元。
    //    這不是陣列長度值,但每一個字元表示陣列大小以 8 的倍數成長
    //    ex. CGGAM_data 大小為 32,表示輸入字元有 4 個,最大輸入不能超過 4
    //                               |_|
    lcd1.writeCGRAM( &CGRAM_block[0], 4, 1 ); 
    //usleep(10000);
    lcd1.writeCGRAM( &CGRAM_block[32], 2, 5 ); 

    while(1)
    {
        // LCD 顯示數字 0 - 9
            // 顯示自定字元在LCD上,確認自定字元已經正常的輸入
        for(int i = 0; i < 6; i++)
            lcd1.displayCGRAM( (0x01 * i), 1, i + 1 );    
        for(int i = 0; i < 6; i++)
            lcd1.displayCGRAM( (0x01 * i), 2, i + 1 );
        delay(1500);
        lcd1.clear();
    
        // 顯示大型數字空白、:、.、0 - 9
        displayNumeric( "....", 4 );
        delay(1500);
        lcd1.clear();
        displayNumeric( "::::", 4 );
        delay(1500);
        lcd1.clear();
        for( int i = 0; i < 10; i++ )
        {
            snprintf(nums, 8, "%d%d%d%d", i, i, i, i);
            displayNumeric( nums, 4 );
            delay(1500);
            lcd1.clear();
        }
        displayNumeric( "    ", 4 );
        delay(1500);
        lcd1.clear();
    }
    return 0;
}

// 每一個數字佔據六個字元位置(上三個、下三個)
// 1 3 1 3 1 3 1 3
// 1 3 1 3 1 3 1 3
// "下大點" 和 "上下大點" 只能顯示在標示 1 的地方
//
// CGRAM_block[0-4] 只能顯示在標示 3 的位置
// CGRAM_block[5-6] 只能顯示在標示 1 的位置 
volatile int len;

void displayNumeric( const char dp[], const int len )
{
    int p =0, n = 0;
    //printf("len = %d\n", len);
    for( int i = 0; i < len; i++ )
    {
        switch(dp[i])
        {
            case 0x2e:  // .
                if(p<n) p = n;
                // line 1
                lcd1.displayCGRAM( 0x00, 1, pix[p] );
                // line 2
                lcd1.displayCGRAM( 0x05, 2, pix[p] );
                p++;                
            break;
            case 0x3A:  // :
                if(p<n) p = n;
                // line 1
                lcd1.displayCGRAM( 0x05, 1, pix[p] );
                // line 2
                lcd1.displayCGRAM( 0x05, 2, pix[p] );
                p++;
            break;
            case 0x20:  // space
                // line 1
                for( int f = 0; f < 3; f++ )
                {
                    lcd1.displayCGRAM( 0x00 , 1, nix[n] + f );
                }
                // line 2
                for( int f = 3; f < 6; f++ )
                {
                    lcd1.displayCGRAM( 0x00, 2, nix[n] + f - 3 );
                }
                n++;
            break;
            case 0x30:  // 0
            case 0x31:  // 1
            case 0x32:  // 2
            case 0x33:  // 3
            case 0x34:  // 4
            case 0x35:  // 5
            case 0x36:  // 6
            case 0x37:  // 7
            case 0x38:  // 8
            case 0x39:  // 9
                // line 1
                for( int f = 0; f < 3; f++ )
                {
                    lcd1.displayCGRAM( Numerics[ dp[i] - 0x30 ][f] , 1, nix[n] + f );
                }                
                // line 2
                for( int f = 3; f < 6; f++ )
                {
                    lcd1.displayCGRAM( Numerics[ dp[i] - 0x30 ][f], 2, nix[n] + f - 3 );
                }
                n++;
            break;
        }
    }
}

編譯之後執行 ( sudo ./iic_numeric ) 的結果,如下影片


* 單晶片

硬體線路可以自行構建單晶片最小系統 ( 看這篇網頁中的線路 ),或是購買賣場"單晶片燒錄套件",在 Keil C 開啟專案 "Numeric_IIC_Demo.uvproj" 編譯之後燒錄 hex 至單晶片中,就可以得到與下面影片中一樣的執行結果


結論:

這篇網頁主要是為了顯示紫外線強度所寫的網頁,但是用途不只用在這!使用者可以使用在任何需要顯示大型數字的情況下,配合單一字元顯示的函式 ( DisplayChar*** ),就可以做到與網頁最上方參可影片中的結果,這部分可以看接下來的一篇 "{ 樹莓派 + Arduino } 紫外線強度偵測 ( 使用大型數字顯示 )"


<< 整合型 LCD 部落格相關網頁 >>


2 則留言:

  1. 請問編碼方式跟一般iic一樣嗎?
    我用我的顯示竟然是亂碼
    請問你有stdio.h的載點嗎??

    回覆刪除
    回覆
    1. 一般使用iic控充版的LCD與網頁中使用的整合型LCD是不一樣的,(因為它使用IIC通訊的PORT擴充晶片模擬 4/8-bit的LCD通訊 )。
      如果不是使用賣場整合型LCD,只有自定字元的程式碼是可以使用與參考的,概念一樣!其他的,不適用!

      刪除