秋休み
久しぶりの更新なわけで。
実に2ヶ月ぶり!秋休みでし!
しかもその間ほとんど電子工作してないんだよなぁ。
ということでいつか買ったLCDをPiで使ってみようと。
で、どうするかというとC言語勉強ついでにLCDに文字を表示する関数を作ることに。
本題
今回使うのは
です。
今回の主役のLCDの特徴はI2Cで文字を表示できるとこですね。あと日本語(カタカナ)と特殊文字が表示できることです。
ちなみに日本語の表示機能はちょっと面倒くさいので今回はスルーします(おいっ
いやぁCで日本語をいろいろしようと思うと難しいんですよ…Pythonあたりならできそうなんですがね。
今回表示させる関数を作るわけですが、データシート(英語)読んでバッチリ理解できる頭は無いんでネットのパイセンたちのブログをいろいろ見て学ばせていただいた次第で…。あざっした!
じゃ、まずは回路を。

うんシンプル。”ZEALOTエンジニアブログ”さんの「はじめてのRaspberry Pi – その3」がとても参考になりました(そのままやないかい
可変抵抗はLCDに表示する文字のコントラスト調整のようです。
で、スイッチはバックライトのON/OFF用です。(なくてもいい
これだけ組み上がったらRPiとI2Cピンで接続して遊ぶだけ。
がしかし、どうやらこのLCD曲者のようで、そのまま繋いだだけじゃRPiと通信が上手くいかないらしい。
“猫ぱーんち!”さんの「Raspberry Pi でI2C: キャラクタLCD」に書かれているように、
- リードアクセスしない(i2c-toolsのi2cdetectとかもダメ)
- I2Cクロックを50kHzくらいに落とす
なので上記のサイトの通りにRPiを設定して使います。
あとはコードを書くだけ。データシートやパイセン達の情報を読んだ結果。
/* *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- Made by BEAN BLOG : https://beanlog.wordpress.com/ *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- */ #include #include #include #include <linux/i2c-dev.h> #include <sys/ioctl.h> #include #include #define I2C_FILE "/dev/i2c-1" #define I2C_ADDRS 0x50 #define LCD_RS_CMD 0x00 #define LCD_RS_DATA 0x80 #define MAX_CHAR 16 /* 基礎関数 */ int setI2c(int *fd);//i2c通信の初期設定 void lcdSet(int fd);//LCDの初期設定 void setCursor(int fd, int n);//カーソル移動 void clearDisplay(int fd);//LCDのディスプレイを綺麗にする void lcdWrite(int fd, unsigned char rs, unsigned char data);//LCDにデータを書きこむ void lcdPrint(int fd, char *str, int row, int col);//LCDに文字を表示させる /* 応用関数 */ void lcdPrint_flow(int fd, char *str, int row, double speed); void lcdPrint_loop(int fd, char *str, int row, double speed); void lcdPrint_rowp(int fd, char *str, double speed); /* その他処理 */ void strReverse(char *sstr, char *str);//文字列反転させる int main(int argc, char *argv[]) { int fd;//ファイルディスクリプタ char str[MAX_CHAR]; if(!setI2c(&fd)) return 1; lcdSet(fd);//LCDの初期設定 //普通に表示してみる printf("lcdPrint関数実行中\n普通に表示してみる\n\n"); lcdPrint(fd, "beanlog : RPi", 0, 0); lcdPrint(fd, "by BEAN", 1, 9); sleep(3.0); clearDisplay(fd); //電光掲示板ぽい?? printf("lcdPrint_flow関数実行中\n右から左に文字列が流れていきます\n\n"); lcdPrint_flow(fd, "beanlog", 0, 0.5);//文字列流れるような clearDisplay(fd); printf("lcdPrint_loop関数実行中\n電車でよく見るやつです。\n\n"); lcdPrint_loop(fd, "Raspberry Pi", 1, 0.5);//よくある文字列のループ clearDisplay(fd); printf("lcdPrint_rowp関数実行中\nLCD内を文字列が流れていきます。\n\n"); lcdPrint_rowp(fd, "DrPepper", 0.5); clearDisplay(fd); close(fd); puts("プログラム終了"); return 0; } int setI2c(int *fd){ //i2cポートを読み書きでオープン if((*fd = open(I2C_FILE, O_RDWR)) < 0){ fprintf(stderr, "Faild to open i2c port\n"); return -1; } //通信先アドレスの設定 if((ioctl(*fd, I2C_SLAVE, I2C_ADDRS)) < 0){ fprintf(stderr, "Unable to get bud access to talk to slave\n"); return -1; } return 1; } void lcdSet(int fd){ int i; char cmds[4] = {0x01, 0x38, 0x0c, 0x06}; for(i = 0; i < 4; i++){ lcdWrite(fd, LCD_RS_CMD, cmds[i]); } } void lcdWrite(int fd, unsigned char rs, unsigned char data) { usleep(20); unsigned char buf[2]; buf[0] = rs; buf[1] = data; if(write(fd, buf, 2) != 2){ printf("Error writing to i2c slave1\n"); } } void lcdPrint(int fd, char *str, int row, int col){ int char_cnt = 0; int place[2] = {0x00, 0x40}; if(col > 16) col = 16; setCursor(fd, place[row] + col); while (char_cnt < MAX_CHAR && str[char_cnt]) { lcdWrite(fd, LCD_RS_DATA, str[char_cnt]); char_cnt++; } } void setCursor(int fd, int n){ lcdWrite(fd, LCD_RS_CMD, LCD_RS_DATA | n); } void clearDisplay(int fd){ lcdWrite(fd, LCD_RS_CMD, 0x01); } void lcdPrint_flow(int fd, char *str, int row, double speed){ int cnt = 1, cursor, i, len = strlen(str); while(cnt--){ cursor = MAX_CHAR; while(cursor--){ lcdPrint(fd, str, row, cursor); usleep((int)(speed * 1000000)); clearDisplay(fd); } for(i = 1; i < len; i++){ lcdPrint(fd, &str[i], row, 0); usleep((int)(speed * 1000000)); clearDisplay(fd); } } } void lcdPrint_loop(int fd, char *str, int row, double speed){ int cnt = 2, cursor, i, len = strlen(str); while(cnt--){ cursor = MAX_CHAR - len; while(cursor--){ lcdPrint(fd, str, row, cursor); usleep((int)(speed * 1000000)); clearDisplay(fd); } for(i = 1; i < len; i++){ lcdPrint(fd, &str[i], row, 0); lcdPrint(fd, str, row, MAX_CHAR - i); usleep((int)(speed * 1000000)); clearDisplay(fd); } lcdPrint(fd, str, row, MAX_CHAR - len); usleep((int)(speed * 1000000)); clearDisplay(fd); } } void lcdPrint_rowp(int fd, char *str, double speed){ int cnt = 2, cursor, i, len = strlen(str); char sstr[MAX_CHAR] = {0}; strReverse(sstr, str); while(cnt--){ i = 0; cursor = 0; while(cursor < MAX_CHAR){ lcdPrint(fd, str, 0, cursor); if(cursor > MAX_CHAR - len - 1){ lcdPrint(fd, sstr, 1, MAX_CHAR - i); i++; } usleep((int)(speed * 1000000)); clearDisplay(fd); cursor++; } cursor = MAX_CHAR - len; i = 1; while(cursor + len){ lcdPrint(fd, sstr, 1, cursor); if(cursor < 0){ lcdPrint(fd, &str[len - i], 0, 0); i++; } usleep((int)(speed * 1000000)); clearDisplay(fd); cursor--; } } } void strReverse(char *sstr, char *str){ int i, len = strlen(str); for(i = 0; i < len; i++){ sstr[i] = str[len - i - 1]; } }
が出来上がりました。
GitHubはこちら
おおう長い。
CでI2Cやるのは初めてだったのでいろいろ調べた結果、どうやらopen関数なるものがあるらしく、C入門のfopenと似た感じかといえばまぁそんなんですね。
open関数の方が低水準らしく、”株式会社オーキッド”さんの「C言語関数:open」によれば”低水準入出力関数”に分類されるようです。
あと、fopenの場合はファイルポインタが返ってきますが、openの時はファイルディスクリプタという整数値が返ってくるようです。
これまた解説してるとこの字面が難しいこと…。まぁ計算機科学をちゃんと勉強しておけばわかるんじゃないでしょうか。
このopen関数を使ってPiのI2Cの司令塔にアクセスするのがsetI2C関数です。
これは別にmain関数内に書いてもいいだろうけど、ごちゃっとするので関数化しました。
これをした後、今度はLCDの初期設定(初期化)です。
これはlcdSet関数です。(早くも命名に統一性が消失!!
ディスプレイを初期化したり関数のセット(?)したり…と。
まぁいろいろな初期設定をするやつです。(よくわかってない
データシートの19ページに書いてある表に書かれてるんですが、頭がついていってないので”Yura Yulife”さんの「Raspberry Piに接続したLCD(ACM1602NI)をPythonで動かす」と”Raspberry Pi 備忘録 / Mbedもあるよ!”さんのi2c LCDを試すを参考にしました。
その次はlcdPrint関数です。
これは文字通りlcdに文字を表示させる関数です。
引数には(ファイルディスクリプタ, 文字列, 行, カーソル位置)です。
LCDは2行表示なのでそのどっちかの指定を行のとこで、カーソル位置は何文字あとから表示させるかの指定です。
実質的にLCDにデータを送るのはlcdWrite関数なのですが、それだけにすると関数を使うごとにアドレスとか16進数とか考えないとダメになっちゃうんでそれをしないようにlcdPrint関数を作りました。
lcdWrite関数はデータをLCDに送るという関数のため、lcdSet関数内でも使われています。
次に来るのはclearDisplay関数ですね。
これまた文字通り(関数名は文字通りが基本ですからね)のディスプレイを綺麗にする関数です。
これで最低限の関数は出来上がってます。
コードは”Yura Yulife”さんのGitHubと”工作と小物のがらくた部屋”さんの「Raspberry Pi で I2C その2 ストリナI2C LCD」を勉強して影響されてます。(神!
あとに出てくる関数は文字を動かす関数ですね。
lcdPrint_flow関数は文字が流れるように、
lcdPrint_loop関数は文字が流れるのはflowと同じですが、見切れた部分が反対側から出てくるよくあるやつです。
lcdPrint_rowp関数は文字がLCD内を環状に移動していくような関数です。
↑のコードを動かした動画をアップしておきます。
電電電子
と、これで終了です。
にしても忙しすぎて忙しすぎて。師走は忙しいですが何か更新できれば!と夢灯籠。
いろいろと難しくて頭がスパークルを発しそうですが、なんでもないやと。
とわかる人にはわかる前フリを盛大に置くという。
そうです。何週間か前か「君の名は。」を見に行ってきました。良すぎて良すぎて。感動!
特に瀧くんが糸守を最初に見たときに「んっ!!!」となりましたね。息を飲むというか驚くというか。その後の山の上での再会とかもなかなか良くて。
普段メディアで感動して泣くということはないのですが、途中人生でマックスの泣きそう度になりましたね。
映画見た後の2日は映像が頭から離れなくてバイト中にぼぅーとしてたり授業中集中力トリップしたり大変でした。
そんな影響もあってかCDと本をご購入いたすという。
個人的に夢灯籠が一番好きです。最初の歌詞に入るとこと「いつか行こう 全生命も未踏 未開拓の」ってところネ申!(←最近見かけなくなった。
円盤全裸待機です。
そんなわけで全く別の話で今回はシメました。あざしたー。
今回のまとめ:UNISON SQUARE GARDENいいよね!(話の流れはどこへ
コメントを残す