aitendo『I2CキャラクタLCDモジュール』をArduinoとRaspberry Piに接続する

# 0. はじめに

[aitendo製](https://www.aitendo.com)『[I2CキャラクタLCDモジュール★16x2★[ATD1602C-P]](https://www.aitendo.com/product/16205)』を組み立て、ArduinoとRaspberry Pi Zeroに接続するまでの流れをまとめておく。
価格は税別550円とリーズナブルで、いちいちシリアルモニタに出さなくてもLCD出力で用が足りる時のために購入した。

# 1. 組み立て

<img width="300" alt="IMG_9733.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/52ee035c-cc91-fac9-2ea2-313ff53f0849.png" />

このLCDモジュールは**組み立てキット**のため、ハンダ付けが必要である。組み立てに関する説明書等は一切ないため、商品ページの説明及び商品写真だけで組み立てる必要がある。とはいえ、部品は全部で5点だけのため普段からハンダ付けしている方にとっては、難しくは無いだろう。

**部品リスト**
1. I2CキャラクタLCDモジュール
1. LEDバックライトユニット
1. キャリー基板
1. FFCコネクタ
1. チップコンデンサ(2個)

袋から取り出して並べると↓こんな感じ。
<img width="300" alt="IMG_9734.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/52c22a40-5487-1180-c208-45edebc3e203.png" />

ハンダ付けで一番厄介なのが、12ピンFFCコネクタである。おそらくリフローが一番楽だと思うが、リフロー炉（ホットプレート）を持っていないので、手ハンダで行った。

<img width="300" alt="IMG_9735.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/3a27aed2-a6b0-5f28-1f88-66762205808a.png" />

ちょっとハンダがもっこりしているが、無事にハンダ付けできた。上記の写真では影になって見えにくいが、コネクタ両サイドの基板に固定させる爪部分はそれほど横に出ていないので、ここのハンダ付けの際にコテ先がコネクタに触れてしまってコネクタ横を少し溶かしてしまった。反対側は気を付けたので大きな失敗なくできた。このモジュールの組み立て時の最大の難所だと思う。
続いて、1206サイズのチップコンデンサ2個もハンダ付けする。LEDバックライトユニットの電源の配線は、LCDユニットを基板に取り付けてから最後に行う。
（後述するが、ピンヘッダーのハンダ付けはここのタイミングで行うと楽だと思う。）

次はLEDバックライトユニットにLCDモジュールを取り付ける。LCDを取り付ける側には、薄いプラスチックフィルムが貼られているので、まず、これを剥がす。（下記写真）ピンセットでつまんで剥がした時の様子。
<img width="300" alt="IMG_9736.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/74580195-d13b-b4ad-98fd-bd218b4ad7a7.png" />
続いて、両面テープが上下に貼ってあるので、その剥離紙を剥がしLCDモジュールを置いて貼り付ける。（下記写真）上部の両面テープの幅は非常に狭い。
<img width="300" alt="IMG_9737.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/bdd9f0cb-9755-c934-db2b-6915853ef679.png" />
裏返して基板に貼る側の剥離紙を剥がし、基板に固定する。（下記写真）上下の両面テープの幅は同じぐらい。
<img width="300" alt="IMG_9738.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/3870f1e5-8f55-ba6e-c72a-ea5a0bd64d5b.png" />
（下記写真）基板に貼り付けた様子。LCDのフラットケーブルとバックライトの電源ケーブルは、基板裏側に通しておく。
<img width="300" alt="IMG_9740.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/2a755eda-2440-7570-639b-814b2d52437c.png" />
（下記写真）基板裏側の様子。最後にバックライトの電源ケーブル赤黒の2本を基板のランドにハンダ付けする。

<img width="300" alt="IMG_9739.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/cf285d34-d1e8-7527-4f12-31642b88358d.png" />

最後にLCDのフラットケーブルをFFCコネクタに接続する。さらに、手持ちのピンヘッダをハンダ付けして組み立ては完了。ピンヘッダは付属していなかったので忘れていた。本来なら、ピンヘッダのハンダ付けは、コンデンサの半田付け後がよい。

<img width="300" alt="IMG_9741.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/9cce4f89-6ddf-b4b9-7c7a-3ed8c98fb6ad.png" />


# 2. Arduinoで動作確認

ここで注意が必要なのは、このLCDモジュールの電源及びロジックレベルは3.3V仕様であることである。5V仕様のArduino Uno等に普通に接続すると、このモジュールを破壊する可能性がある。その場合は、ロジックレベル変換が必要。 
今回は、ロジックレベルが3.3Vである[Seeeduino XIAO](https://wiki.seeedstudio.com/jp/Seeeduino-XIAO/)を使用する。

ピンアサインは次の通り。

|LCDモジュール|Seeduino XAIO|
|:-:|:-:|
|LED_A|D6|
|RST|  D6|
|SCL|  SCL (A5)|
|SDA|  SDA (A4)|
|GND|  GND|
|VIN|  3V3|



テスト用のスケッチは、このLCDモジュールの商品ページで紹介されている、[Sim's blogさんのスケッチ](https://blog.goo.ne.jp/sim00/e/ab138be751d447bcdb0eecaaca232214)を参考にさせていただいき、下記のコードで確認した。

```c++
#include <Wire.h>

int resetPin = 6;
int i2cadr = 0x3e;
byte contrast = 40; // 0-63

void lcd_cmd(byte x) {
  Wire.beginTransmission(i2cadr);
  Wire.write(0x00);
  Wire.write(x);
  Wire.endTransmission();
  delay(2);
}

void lcd_data(byte x) {
  Wire.beginTransmission(i2cadr);
  Wire.write(0x40);
  Wire.write(x);
  Wire.endTransmission();
  delay(2);
}

void lcd_puts(const char *s) {
  while (*s) lcd_data(*s++);
}

void lcd_init() {
  // reset
  delay(500);
  pinMode(resetPin, OUTPUT);
  digitalWrite(resetPin, LOW);
  delay(10);
  digitalWrite(resetPin, HIGH);
  delay(10);
  // LCD initialize
  delay(40);
  Wire.begin();
  lcd_cmd(0x38); // function set
  lcd_cmd(0x39); // function set
  lcd_cmd(0x14); // interval osc
  lcd_cmd(0x70 | (contrast & 15)); // contrast low
  lcd_cmd(0x5c | (contrast >> 4 & 3)); // contrast high / icon / power
  lcd_cmd(0x6c); // follower control
  delay(300);
  lcd_cmd(0x38); // function set
  lcd_cmd(0x0c); // display on
  delay(2);
}

void lcd_move(byte pos) {
  lcd_cmd(0x80 | pos);
}

void setup() {
  lcd_init();
}

void loop() {
  lcd_cmd(0x01); // clear display
  delay(1000);
  lcd_puts("Hello, Arduino!");
  lcd_move(0x40);
  lcd_puts("i2c LCD module");
  delay(1000);
}
```

下記写真のように、無事に表示できた。しかし、最初は上手くいかず、色々調べる中でいくつか分かったことがあるので記しておく。
<img width="300" alt="IMG_9746.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/671b81c5-51f5-f3fe-7cf8-0756c6d81879.png" />
注）バックライトは相当明るい。


## a) SDA/SCLのプルアップ抵抗が必要な場合がある

このLCDモジュールの商品説明ページには、『プルアップ抵抗はなくても使用可能』と明記されているが、今回使用した**Seeeduino XIAO**では、SDA/SCLと3v3との間に10KΩのプルアップ抵抗を入れないと動かなかった。これは、使用するArduinoボードに依るところが大きいと思うが、キャリー基板上にプルアップ用のチップ抵抗をハンダ付けするためのランドが用意されている。（下記写真）パターンを調べたところ、R1がSDA用、R2がSCL用。固定的に使用するなら、ここにプルアップ抵抗をハンダ付けするのが配線上は楽だと思う。
<img width="300" alt="pullup.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/a35d7614-5b99-b5eb-8ea6-1aab22f7f68d.png" />

なお、この[Arduino互換機](https://wiki.keyestudio.com/KS0486_Keyestudio_PLUS_Development_Board_(Black_And_Eco-friendly))では、プルアップ抵抗不要で表示できた。[このフォーラムの記事](https://forum.arduino.cc/t/uno-board-i2c-pullups-can-be-enabled-disabled/147107)によると、Arduino Unoは、回路上のプルアップ抵抗はないが、マイコンの内部プルアップが有効になっているそうだ。


## b) SPLC792A信号

LCDコントローラに使用している**SPLC792A**の[**データシート**](http://aitendo3.sakura.ne.jp/aitendo_data/product_img/lcd/fstn/16X2-SPLC792-I2C/SPLC792A_V03_HAOTIAN.pdf)と公開されている[接続回路図](http://aitendo3.sakura.ne.jp/aitendo_data/product_img/lcd/fstn/2017/atd1602_connection.pdf)から、次のことが分かった。

- VOUTとVSSの間にコンデンサを接続している。（内蔵ブースターを使用）
- VINはVDDに接続している。（ブースター用電源入力）
- EXT=1（追加の命令セットを選択）

このLCDモジュールは↑そういう使い方をしているということである。


## c) 表示方向が不安定

起動時のタイミングで表示が裏返る現象が発生した。
<img width="300" alt="IMG_9747.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/f5c696dc-374f-4cad-71da-97ed3afeead3.png" />

キャリー基板上に表示方向を決める2つの信号(DIRC/SHL)のジャンパランドが用意されているので、ここをハンダ付けして向きを固定するのが良い。先ほどはこの信号をオープンにしていたため、表示方向が不安定になったと思われる。
<img width="300" alt="dir.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/5b408b8a-38c0-dc00-8cd1-04f72e212610.png" />

色々な向きで使用するのであれば、ここにワイヤーをハンダ付けして適用なデジタルピンに接続すればプログラムから動的に決めることもできる。

|SEG<br>＼<br>COM|SHL：L|SHL：H|
|:-:|:-:|:-:|
|DIRC：L |![IMG_9750.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/d7a57e24-e1f6-75ae-be21-96430466a775.png)|![IMG_9748.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/1f48c652-0767-6629-a5ea-bcdd41771ea9.png)|
|DIRC：H |![IMG_9751.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/ccaf3a2b-37f8-2113-f772-2a98b839d52d.jpeg)|![IMG_9749.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/ef7e42b7-6cd9-4529-4245-8198eae48249.png)|

上記の通りの表示となったが、両信号ともLOWの時が**正面通常の向き**でないことが気になる。勝手な想定だが、テストスケッチのLCDイニシャル処理で送信しているコマンドと関係があるのでないかと考え、今後、使用しているコマンドを解析して解明したい。


# 3. Raspberry Pi Zeroに接続

Raspberry PiのSDA/SCLは、基板上で1.8KΩの抵抗でプルアップされているそうだ。このため、このLCDはそのままでは接続できないようだ。事実、このLCDをRaspberry Pi Zeroに接続してi2cdetectコマンドを叩いてもi2cのスレーブアドレスは検出できなかった。

[こちらの記事](https://www.denshi.club/pc/raspi/i2c.html)に対処法が載っていた。
どうやら、標準の[I2C](https://www.nxp.com/docs/ja/user-guide/UM10204.pdf)とRasPiが採用している[SMBus](http://smbus.org/specs/SMBus_3_2_20220112.pdf)では、HIGH／LOWの電圧レベルに違いがあるため、そのままではお互いは通信できないそうだ。

**対処法**
**1) 3KΩの抵抗でSDAをプルダウンする**
手持ちに3KΩの抵抗がなかったので、3.3KΩの抵抗を使って試したが上手くいかず（2.2KΩでもNG）

**2) バスリピータを入れる**
[I2Cバスリピータ](https://akizukidenshi.com/catalog/g/gI-10882/)を経由して相互を接続すると解決するとのことであった。さっそく、部品を調達しハンダ付けしてDIP化した。

<img width="300" alt="IMG_9767.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/72272a57-151c-f468-2caf-2104f00f9683.png" />


その結果、下記写真のように無事にRaspberry Piからも表示できることが確認できた。
<img width="300" alt="IMG_9769.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/e8a26371-d200-12eb-0971-0317363d1b66.png" />

#### プログラムは、既出のArduinoスケッチをPythonに書き直しただけだ。

```python
import smbus
import time	
import RPi.GPIO as GPIO

i2c = smbus.SMBus(1) # 1 is bus number

GPIO.setmode(GPIO.BOARD)
resetPin = 7
i2cadr = 0x3e
contrast = 40

def delay(ms):
  time.sleep(ms / 1000)

def lcd_cmd(cmd):
  i2c.write_byte_data(i2cadr, 0x00, cmd)
  delay(2)

def lcd_data(char):
  i2c.write_byte_data(i2cadr, 0x40, char)
  delay(2)

def lcd_puts(str):
  for char in str:
    lcd_data(ord(char))

def lcd_init():
  # reset
  delay(500)
  GPIO.setup(resetPin, GPIO.OUT)
  GPIO.output(resetPin, False)
  delay(10)
  GPIO.output(resetPin, True)
  delay(10)
  # LCD initialize
  delay(40)
  lcd_cmd(0x38) # function set
  lcd_cmd(0x39) # function set
  lcd_cmd(0x14) # interval osc
  lcd_cmd(0x70 | (contrast & 15)) # contrast low
  lcd_cmd(0x5c | (contrast >> 4 & 3)) # contrast high / icon / power
  lcd_cmd(0x6c) # follower control
  delay(300)
  lcd_cmd(0x38) # function set
  lcd_cmd(0x0c) # display on
  delay(2)    

def lcd_move(pos):
  lcd_cmd(0x80 | pos)

lcd_init()

while True:
  lcd_cmd(0x01) # clear display
  delay(1000)
  lcd_puts("Hello, RasPi!")
  lcd_move(0x40)
  lcd_puts("i2c LCD module")
  delay(1000)
```

Wifi接続時の自IPアドレスの表示や、デジタル時計等で簡単に使えるように、今後、LCD出力処理をコマンド化しておこうと思う。


# おわりに

今回も先人が残してくれた情報をもとに当初の目的を達成することができた。毎度思うことだが本当にありがたい。
この記事も誰かの何かの役に立てれば幸いである。

**以上**