8 ビットシリアル－パラレル変換 IC、NJU3711の使い方

## 導入
秋月で適当に物色していたらNJU3711なるものを見つけたので、使い方を調べました。

https://akizukidenshi.com/catalog/goods/search.aspx?search=x&keyword=NJU3711&search=search

2025/02/25現在では一つ80円と手ごろな価格です。

環境は win11 , arduino IDE 2.3.4 , arduino UNO r4 です

## データシート
データシートを読みます。データシートを読めば大体のことは分かりますからね。
https://akizukidenshi.com/goodsaffix/nju3711.pdf
構造としては、送られてきた8bitのシリアルデータをシフトレジスタで8本に分割して、ラッチ回路で保持して出力する構造です。
![スクリーンショット2025-03-01 135151.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3922522/22c20d9d-a08d-4b29-8c4f-289d849d9370.png)

CLR端子は基本的にHIGHにしておいて、出力をクリアしたいときにLOWにします。
ですが出力をクリアしなくても、次の信号を入力できるのでVccに直接接続してもいいです。

STB端子はHIGHの時にシリアルデータが、シフトレジスタで8本に分割されます。
LOWの際にシフトレジスタの出力信号がラッチ回路に転送され、出力されます。

まあこの辺は理解しなくても後のプログラムを使えば大丈夫です。

動作電圧は4.5V~5.5Vなので大体のマイコンはレベル変換しなくても大丈夫そう。
出力電流は±25mAまでなので、LED等のVcc側にもGND側にも使えます。


## プログラム
ひとまず世界一使いやすいブラウザGoogle Chromeで"NJU3711 使い方"と調べます。
うーん。ことごとく10年程度前ですね。
とりあえずプログラムが短く、理解しやすそうな↓の記事を参考にさせていただきます。

http://www.felix.or.jp/MPU/Exp/LED/AVR.html

NJU3711の項目にあるプログラムをArduino IDEに書き写します。
元記事のプログラムは画像なので、コピペ用にこちらにも書いておきます。


<details><summary>サンプルコード</summary>

```ruby:
#define CDATA 8
#define CCLK  9
#define CSTB 10
#define RDATA 1
#define RCLK  2
#define RSTB  3
#define ALLB  B11111111
int moji[] = {2,37,127,42,44,41,6,0};

void setup() {
  pinMode(CDATA, OUTPUT);
  pinMode(CCLK, OUTPUT);
  pinMode(CSTB, OUTPUT);
  digitalWrite(CSTB, HIGH);
  pinMode(RDATA, OUTPUT);
  pinMode(RCLK, OUTPUT);
  pinMode(RSTB, OUTPUT);
  digitalWrite(RSTB, HIGH);
  delay(3000);
}

void loop(){
  int i;
  for(i=0 ; i<8 ; i++){
    shiftOut(CDATA, CCLK, MSBFIRST, ALLB ^(1<<i));
    shiftOut(RDATA, RCLK, LSBFIRST, moji[i]);
    delay(1);
    digitalWrite(CSTB, LOW);
    digitalWrite(RSTB, LOW);
    digitalWrite(CSTB, HIGH);
    digitalWrite(RSTB, HIGH);
  }
}
```

</details>

元記事は1.0.5でしたが、2.4.3でもコンパイルすることができました。

見たことない関数が一つあります。
"shiftOut"とは何でしょうか？
http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=2369
どうやら1バイトのデータをシリアルで送信するものだそうです。
shiftOut(データ用pin, クロック用pin, bitOrder, データ);
bitOrderは、上位ビットから送信するか下位ビットから送信するかを選択するようです。

ではいい感じに書き換えてみます。
### プログラム１

```ruby:NJU3711D_1
#define DATA 2   // データピン
#define CLK  3   // クロックピン
#define STB  4   // 制御ピン
#define n    8   // データの数 
int moji[n] = {
  0b10000000,
  0b01000000,
  0b00100000,
  0b00010000,
  0b00001000,
  0b00000100,
  0b00000010,
  0b00000001};   //データ

void setup() {
  pinMode(DATA, OUTPUT);
  pinMode(CLK , OUTPUT);
  pinMode(STB , OUTPUT);
  digitalWrite(STB, HIGH);
  delay(1000); //1秒待ち
}

void loop(){
  for(int i=0 ; i<n ; i++){
    shiftOut(DATA, CLK, MSBFIRST, moji[i]); //上位ビットからデータ送信
    delay(100);
    digitalWrite(STB, LOW);  //制御信号送信
    digitalWrite(STB, HIGH);
  }
    }
```

![スクリーンショット 2025-03-12 184214.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3922522/442a967b-b940-4643-a816-ca9ca9f013bf.png)
(ちなみにKiCadのシンボルは存在しなかったので自作しました。)

P1からP8まで0.1秒ごとに点灯するプログラムです。
int moji[n]を変更することで、点灯するプログラムを変更できます。
表示するデータの数に変更があった場合は"データの数"を変えてください。

### プログラム２

```ruby:NJU3711D_2
#define DATA 2   // データピン
#define CLK  3   // クロックピン
#define STB  4   // 制御ピン
int val=0;
int deta=0;
int a[8];

void setup() {
  pinMode(DATA, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(STB, OUTPUT);
  digitalWrite(STB, HIGH);
  Serial.begin(9600);  // シリアル設定
  delay(1000);         // 1秒待ち
}

void loop(){
  if(Serial.available() > 0){        // 入力された文字が何バイトか調べその回数分繰り返す
    val = Serial.parseInt();         // 1バイト分のデータを読み取る
    Serial.println(val); // シリアルで文字を送信
    int b =1;
    deta=0;
    for(int i=0 ; i<=8 ; i++){
      a[i] = (val % 10);  // i桁目を取り出す
      val /= 10; 
      deta += a[i]*b;  // i桁目*2^iをdataに足す
      b *= 2;
    }
    Serial.println(deta);
    shiftOut(DATA, CLK, MSBFIRST, deta); // 下位ビットからデータ送信
    delay(100);
    while (Serial.available() > 0) {//受信バッファクリア
      char t = Serial.read();
    }
    digitalWrite(STB, LOW);             // 制御信号送信
    digitalWrite(STB, HIGH);
  }
}
```
シリアル通信で送信した文字列に応じてLEDが点灯します。
前点灯なら11111111、全消灯なら000000000、10101010なら交互に点灯します。

下のプログラムは255で全点灯、0で全消灯するものです。
2進数を10進数に変換して使います。
他のプログラムに組み込む場合は、こちらの方が使い勝手がいいと思います。
<details><summary>NJU3711D_2_2</summary>

```ruby:NJU3711D_2_2
#define DATA 2   // データピン
#define CLK  3   // クロックピン
#define STB  4   // 制御ピン
int val=0;

void setup() {
  pinMode(DATA, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(STB, OUTPUT);
  digitalWrite(STB, HIGH);
  Serial.begin(9600);  // シリアル設定
  delay(1000);         // 1秒待ち
}

void loop(){
  if(Serial.available() > 0){        // 入力された文字が何バイトか調べその回数分繰り返す
    val = Serial.parseInt();            // 1バイト分のデータを読み取る
    Serial.println(val); // シリアルで文字を送信
    shiftOut(DATA, CLK, MSBFIRST, val); // 下位ビットからデータ送信
    delay(100);
    while (Serial.available() > 0) {//受信バッファクリア
      char t = Serial.read();
    }
    digitalWrite(STB, LOW);             // 制御信号送信
    digitalWrite(STB, HIGH);
  }
}
```

</details>


## 終わりに
少ない本数で多くのi/oを制御できるので、ATtinyなどで真価を発揮できると思います。
そのうちやります。
また、参考にさせていただいた記事では、LEDマトリクスを制御していたので、そちらも手を付けたいです。

お読みいただきありがとうございました。