07 January 2013

Raspberry piにi2c LCD接続する

Raspberry piにStrawberry Linuxの「I2C低電圧キャラクタ液晶モジュール(16x2行) SB1602B」を接続して利用する方法

■ i2cドライバを有効にする

ドライバを追加する

/etc/modules
snd-bcm2835 i2c-dev ← この行を追加

さらに、ブラックリストから削除する

/etc/modprobe.d/raspi-blacklist.conf
# blacklist spi and i2c by default (many users don't need them) blacklist spi-bcm2708 #blacklist i2c-bcm2708 ← この行をコメントアウト

上記の設定後Raspberry piを再起動

■ i2cツールのインストール

$ sudo apt-get install i2c-tools

LCDのアドレスを調べる

$ sudo i2cdetect -y 0 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- $ sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --

チャンネル1(/dev/i2c-1)のアドレス0x3eに接続されているのがわかった。

■ 回路図

Raspberry piのGPIO P1ポートのピン配置は「RPi Low-level peripherals」を参考にした。なお、Rpiのi2cポートはプルアップされているため、ユーザ側でプルアップ抵抗を用意する必要は無い。

20130107-rpi-i2c-lcd.png

なお、LCD側のRST端子とVDD(3.3V)端子を10kの抵抗で接続してプルアップしている(0Vにするとリセットされるため)。

20130107-rpi-i2c-lcd.jpg


■ シェルスクリプトで利用する場合

Raspberry PiにI2C液晶をつける」のスクリプトを丸ごとコピーした

i2c_lcd.sh
#!/bin/bash function usage { echo "Usage: $0 [-ic] [-p pos] message" > /dev/stderr; echo " -i : LCD init, -c : Clear Screen" > /dev/stderr echo " -p : position (0:top left, 40:bottom left)" > /dev/stderr exit 1 } [ $# = 0 ] && usage while getopts "icp:" flag; do case $flag in \?) usage ;; i) i2cset -y 1 0x3e 0 0x38 0x39 0x14 0x78 0x5e 0x6c i sleep 0.25 i2cset -y 1 0x3e 0 0x0c 0x01 0x06 i sleep 0.05 ;; c) i2cset -y 1 0x3e 0 0x01 ;; p) i2cset -y 1 0x3e 0 $((OPTARG+128)) ;; esac done shift $((OPTIND-1)) [ $# = 0 ] && exit LANG=C MSG=`echo -n "$1" | perl -pe '$_=join" ",map{ord }split//'` #echo $MSG i2cset -y 1 0x3e 0x40 $MSG i

このスクリプトをroot権限で次のように実行する

$ sudo ./i2c_lcd.sh -i "i2c test program" $ sudo ./i2c_lcd.sh -p 40 "Raspberry pi"

または、piをi2cグループに追加すれば、ユーザ権限で実行可能になる。

$ sudo adduser pi i2c

■ C言語で利用する場合

Raspberry Pi で I2C その2 ストリナI2C LCD」のソースコードを丸ごとコピーした。

i2c_lcd.c
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <linux/i2c-dev.h> #include <fcntl.h> #include <sys/ioctl.h> #define LCD_ADDRESS (0b0111110) #define LCD_CONTRAST 100 #define LCD_RS_CMD (0x00) #define LCD_RS_DATA (0x40) #define LCD_CMD_CLEAR (0x01) #define LCD_CMD_HOME (0x03) // 関数プロトタイプ宣言。 void LCD_init(int fd); void LCD_write(unsigned char rs, unsigned char data, int fd); void LCD_clear(int fd); void LCD_setCursor(unsigned char col, unsigned char row, int fd); void LCD_putc(unsigned char c, int fd); void LCD_puts(char *str, int fd); int main(int argc, char **argv){ int lcd; // ファイルディスクリプタ。 char *i2cFileName = "/dev/i2c-1"; // I2Cドライバファイル名。 int lcdAddress = LCD_ADDRESS; // I2C LCD のアドレス。 printf("***** start i2c lcd test program *****\n"); // I2CポートをRead/Write属性でオープン。 if ((lcd = open(i2cFileName, O_RDWR)) < 0){ printf("Faild to open i2c port\n"); exit(1); } // 通信先アドレスの設定。 if (ioctl(lcd, I2C_SLAVE, lcdAddress) < 0){ printf("Unable to get bus access to talk to slave\n"); exit(1); } usleep(100*1000); // 100ミリ秒 // LCD初期化。 LCD_init(lcd); // メッセージ表示。 LCD_setCursor(0, 0, lcd); LCD_puts("i2c LCD access.", lcd); LCD_setCursor(0, 1, lcd); LCD_puts("Raspberry pi", lcd); return 0; } void LCD_init(int fd){ usleep(40*1000); // 40ミリ秒 // ST7032 Initial Program Code Example For 8051 MPU(8 Bit Interface) の初期化サンプルの通り // Function Set : 8bit bus mode, 2-line mode,normal font,normal instruction mode LCD_write(LCD_RS_CMD, 0b00111000, fd); // 0x38 // Function Set : extension instruction mode LCD_write(LCD_RS_CMD, 0b00111001, fd); // 0x39 // Internal OSC frequency(extension instruction mode) LCD_write(LCD_RS_CMD, 0b00010100, fd); // 0x14 // Contrast set(extension instruction mode) コントラスト値下位4bit設定 LCD_write(LCD_RS_CMD, 0b01110000 | (LCD_CONTRAST & 0xF), fd); // 0x78 = 0x70 + 0x8 // Power/ICON/Contrast set(extension instruction mode) コントラスト値上位2bit設定 LCD_write(LCD_RS_CMD, 0b01011100 | ((LCD_CONTRAST >> 4) & 0x3), fd); // 0x5c + 0 // Follower control。internal follower on, LCD_write(LCD_RS_CMD, 0b01101100, fd); // 0x6c usleep(300*1000); // 300ミリ秒 // Function Set。normal instruction mode。 // LCD_write(LCD_RS_CMD, 0b00111000, fd); // 0x38 // Display On LCD_write(LCD_RS_CMD, 0b00001100, fd); // 0x0c // Clear Display LCD_write(LCD_RS_CMD, 0b00000001, fd); // 0x01 // Entry Mode Set LCD_write(LCD_RS_CMD, 0b00000110, fd); // 0x06 // 時間待ち。 usleep(2*1000); } void LCD_write(unsigned char rs, unsigned char data, int fd) { unsigned char buf[2]; if (rs == LCD_RS_CMD || rs == LCD_RS_DATA){ // LCD_RS_CMD ならコマンドモード。LCD_RS_DATA ならデータモード。 buf[0] = rs; buf[1] = data; if (write(fd, buf, 2) != 2){ printf("Error writeing to i2c slave1\n"); } } else{ // rsの指定がLCD_RS_CMD,LCD_RS_DATA以外ならなにもしない。 } } void LCD_clear(int fd) { LCD_write(LCD_RS_CMD, LCD_CMD_CLEAR, fd); usleep(2*1000); LCD_write(LCD_RS_CMD, LCD_CMD_HOME, fd); usleep(2*1000); } void LCD_setCursor(unsigned char col, unsigned char row, int fd) { unsigned char offset[] = {0x00, 0x40}; if (row > 1) row = 1; if (col > 16) col = 16; LCD_write(LCD_RS_CMD, 0x80 | (col + offset[row]), fd); } void LCD_putc(unsigned char c, int fd) { LCD_write(LCD_RS_DATA, c, fd); } void LCD_puts(char *str, int fd) { int i; for (i = 0; i < 16; i++){ if (str[i] == 0x00){ break; } else{ LCD_putc((unsigned int)str[i], fd); } } }

このソースコードをコンパイルしてroot権限で実行

$ gcc i2c_lcd.c -lrt $ sudo ./a.out