C言語

I2Cを使ってLCDに文字を表示させる!②(C言語)

スポンサーリンク

前回の続きを行っていきます。

前回の記事はこちら→「I2Cを使ってLCDに文字を表示させる!①(C言語)

サンプルプログラムがうまく動かず、約1日ググりまくりました(泣)

原因追及

中華製だったせいかあまり有力なデータシート等は見つけられなかったので、先人の方々にお世話になりました。

色々調べていたところ、このようなサイトを見つけました。

キャラクタLCDをアクセス

そしてこの部分が気になりました。4ビットバスモード?

さらに調べてみると、

LCDモジュールへのコマンド及びデータは8ビット、そのため、信号線が基本は8本。

しかし、ピン数の少ないラズパイなどでは上位4ビットの4本の信号線で接続して、8ビットデータを2回に分け4ビットずつ受け渡しするモードを利用することが多いが、その分ソフトウェアの負担は大きくなるそうです。

つまりデータシートより

下図赤枠の部分が4ビットバスを表していると分かりました。

上の表とも対応していますね。(D7,D6,D5,D4,light,E,RW,RS)

これを踏まえて、前回のプログラムを一つずつ確認していきたいと思います。

ソースコード

ソースコード(修正後)

全体のプログラムは以下の通りです。

修正を入れたとこには日本語でコメントを加えてあります。

#include <stdio.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <string.h>
int LCDAddr = 0x27;//I2c address of LCD, some LCD i2c address might be 0x27
int BLEN = 1;//1--open backlight.0--close backlight ブルーライトON
int fd;//linux file descriptor
//send an  16 bits data to LCD buffer
void write_word(int data){
    int temp = data;
    if ( BLEN == 1)//0でしたが1に変えてます
        temp |= 0x08;
    else
        temp &= 0xF7;
    wiringPiI2CWrite(fd, temp);
}
//send control command to lcd
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);
    // Send bit3-0 secondly
    buf = (comm & 0x0F)<<4; //下位ビットなので4ビット左シフトしています
    buf |= 0x04;            // RS = 0, RW = 0, EN = 1
    write_word(buf);
    delay(2);
    buf &= 0xFB;            // Make EN = 0
    write_word(buf);
}
//send character to lcd
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;////下位ビットなので4ビット左シフトしています
    buf |= 0x05;            // RS = 1, RW = 0, EN = 1
    write_word(buf);
    delay(2);
    buf &= 0xFB;            // Make EN = 0
    write_word(buf);
}
//initialize the lcd
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);
}
//clear screen
void clear(){
    send_command(0x01); //clear Screen
}
//Print the message on the lcd
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]);
    }
}
void print_info()
{
    printf("\n");
    printf("|***************************|\n");
    printf("|    IIC 1602 LCD test      |\n");
    printf("| --------------------------|\n");
    printf("| | LCD |            | Pi   |\n");
    printf("| --------------------------|\n");
    printf("| | GND | connect to | GND  |\n");
    printf("| | VCC | connect to | 5V   |\n");
    printf("| | SDA | connect to | SDA.1|\n");
    printf("| | SCL | connect to | SCL.1|\n");
    printf("| --------------------------|\n");
    printf("|                     OSOYOO|\n");
    printf("|***************************|\n");
    printf("Program is running...\n");
    printf("Press Ctrl+C to end the program\n");
}
int main(){
    //init I2C, assign a buffer handler to variable fd
    fd = wiringPiI2CSetup(LCDAddr);
    init();
    print_info();
    write(0, 0, "Hi man.Welcome ");
    write(0, 1, "to osoyoo.com");
    delay(3000);
    clear();
    while(1){
        write(0,0,"ABCDEFGHIJKLMNOP");
        write(0,1,"QRSTU0123456789");
        delay(1000);
    }
    return 0;
}

プログラムがしっかりと動作しました!16*2がわかりやすいように文字列だけ変えています。

とりあえず、目的は達成しました!

次はプログラム詳細です。

ソースコード解説

#include <stdio.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <string.h>
int LCDAddr = 0x27;//I2c address of LCD, some LCD i2c address might be 0x27
int BLEN = 1;//1--open backlight.0--close backlight
int fd;//linux file descriptor
//send an  16 bits data to LCD buffer
void write_word(int data){
    int temp = data;
    if ( BLEN == 1)//ブルーライト点灯の場合
        temp |= 0x08;//4ビット目はライト用なのでそこだけ1を立てる
    else
        temp &= 0xF7;//4ビット目を0に
    wiringPiI2CWrite(fd, temp);
}

・I2Cに関する関数を使用する際は<wiringPiI2C.h>をインクルードします。

・最初の関数void write_word()はブルーライト点灯用の関数です。

・or,and演算を使用して、4ビット目(ブルーライトON、OFF)を操作しています。

void send_command(int comm){
    int buf;
    // Send bit7-4 firstly
    buf = comm & 0xF0;    //下位ビットをすべて0に
    buf |= 0x04;            // RS = 0, RW = 0, EN = 1
    write_word(buf);    //ライト点灯用関数で4ビット目を立てる
    delay(2);
    buf &= 0xFB;            // Make EN = 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);
}

・この関数はコマンド送信用の関数です。4ビット通信を行っています。

・前提として、I2C通信は、立下りでコマンドデータを送信します。つまり、ENビットが0になったときにデータを転送します。

・ENビットを1にし、ライト点灯をします。

・8行目で上位4ビットの送信を行っています。

・11行目からは下位4ビットを送信します。下位ビット送信のため、左に4ビットシフト演算しています。

・あとは上位ビットの時と同様です。

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);
}

ここからはデータ送信の関数です。送信方法は4ビットなので先ほどの関数と同様の送信方法です。

・5行目で1ビット目のRS(ragister select)を立てています。これにより、文字のデータを送る設定になります。

(RSの設定は自信がありません。違ってたら教えてください。)

・8行目で、ENビットを下げ、データの送信を行っています。

・あとの説明は省略します。

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);
}

次はLCDの初期化関数です。初期化には決められたコマンドを使います。

**この部分の説明は訳しただけなのでまだ完璧には理解できていません**

・3行目 0x33(00110011) 8ビット送信の設定。必ずこの設定はいるみたいですね。

・5行目 0x32(00110010) 4ビットモードに切り替えています

・7行目 0x28(00101000) 7ビットのアドレス空間を利用

・9行目 0x0c(00001100) カーソルクリア

・10行目 0x01(00000001) スクリーンの初期化

このサイトがとってもわかりやすいです。

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]);
    }
}

LCDに書き込む文字を作成する関数です。

・write(何番目か(0~15),何列目か(0か1),表示したい文字)

・4~7行目 上記の引数でのエラー対策の式です。

・9行目   表示をする位置を計算しています。少し詳しく説明します。

下の表のようなアドレスになっています。

表示アドレスデータのビット構成としてMSB(最上位ビット)は1と決まっているそうです。なので、0x80があらかじめ書かれています。

例を見てみましょう。write(0,1,"A")の場合、0x80+0x40*1+0=0x80+0x40となり、2行目の一番最初にAと出力されます。

12~14行目は、文字列の転送になります。一文字ずつ送信して文字列を作っています。

int main(){
    //init I2C, assign a buffer handler to variable fd
    fd = wiringPiI2CSetup(LCDAddr);
    init();
    print_info();
    write(0, 0, "Hi man.Welcome ");
    write(0, 1, "to osoyoo.com");
    delay(3000);
    clear();
    while(1){
        write(0,0,"ABCDEFGHIJKLMNOP");
        write(0,1,"QRSTU0123456789");
        delay(1000);
    }
    return 0;
}

最後に、メイン関数ですが、説明は省略します。

まとめ

  • LCDの転送方式には4ビット、8ビットの転送方法がある
  • 4ビット転送をする際は、上位ビット→下位ビットで送る(ビットシフトを忘れずに)
  • I2Cを用いた設定はかなりめんどくさいが、I2C通信の学習には最適である

今回はここまでになります。

中華製のLCD,I2Cだからか、全然詳細な情報が見つかりませんでした…

無難に秋月電子などのショップで買った方がいいのかもしれないと少しだけ思いました。

ありがとうございました。

関連記事
ラズパイでSPI通信に挑戦!(理論編)| C言語

続きを見る

関連記事
ラズパイでSPI通信に挑戦!(実装編)| C言語

続きを見る

スポンサーリンク

-C言語