シフトレジスタ(U74HC595AG)をつかって、大量のＬＥＤを制御しよう(ShiftOut関数不使用)

# 初めに
アクセスしていただきありがとうございます。
こちらの記事はシフトレジスタをarduinoで制御するプログラムとそれに関する情報を記載したものです。
# シフトレジスタとは
シフトレジスタは少量のデータを保存するICです。
D-FFを直列につなげて作られており、8bitのデータを保存できます。
# シフトレジスタのメリット
マイコンの代名詞ともいえるarduinoには18本のピンがあり、このピンを使って信号を送受信することができる。
18ピンというと多いように感じますが、8×8ドットマトリクスなどの電子部品を使用したり、多数のセンサーを搭載するとなると、18ピンでは足りない場合があります。
そんなときに使うのが、シフトレジスタです。
出力を増やすか入力を増やすかは、シフトレジスタの種類によって異なり、
今記事では出力を増やす方法について書いてあります。

# 配線と役割(8個の制御)
配線図とピンの役割を示した画像を載せます。
![Untitled Sketch 2_ブレッドボード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3195955/d98e57b3-c06f-071c-6bcb-b1ecb850ebb7.png)
![U74HC595AG (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3195955/9980f1b3-4792-c533-3de5-ee34a4a4571e.png)

# プログラム(8個の制御)
どのようなプログラムを書くかは皆さんの自由ですが、ここでは理解の助けになるようなプログラムを書きます。
複数のプログラムを書きます、Arduino IDEに書いてくださいね。
```c++:最初に書かなきゃいけないプログラム.INO
#define SER		(2)
#define RCLK	(3)
#define SRCLK	(4)
 
void setup() {
  pinMode( SER,  OUTPUT );
  pinMode( RCLK, OUTPUT );
  pinMode( SRCLK, OUTPUT );
}
void initShiftRegister(){
  digitalWrite(RCLK, LOW);
  for(int i = 0; i < 8;i++){
    digitalWrite(SER, LOW);
//クロックパルスを立ち上げる
	digitalWrite(SRCLK, HIGH);
	digitalWrite(SRCLK, LOW);
//-----------------------------------------
  }
  digitalWrite(RCLK, HIGH);
}
```
これは最初に書くプログラムです、これを毎回書くのは行が増えてしまうので短縮のために、先に書きました。
initShiftRegister()は最初にLEDを全部消すための、関数です。今後説明するときがあるのでそれを読もう。
```c++:loop内のプログラム.INO
void loop() {
  initShiftRegister();
  for(;;){
    //データを送る前に呼び出す必要がある
    digitalWrite(RCLK, LOW);
    //送るデータ HIGH
    digitalWrite(SER, HIGH);
    //おまじないです考えるな
	digitalWrite(SRCLK, HIGH);
	digitalWrite(SRCLK, LOW);
    //----------------------
    //データを送った後に呼び出す必要がある
    digitalWrite(RCLK, HIGH);
    delay(500);
    for(int i = 0; i < 7;i++){
      digitalWrite(RCLK, LOW);
      //送るデータ LOW
      digitalWrite(SER, LOW);
			
	  digitalWrite(SRCLK, HIGH);
	  digitalWrite(SRCLK, LOW);
      digitalWrite(RCLK, HIGH);
      delay(500);
    }
  }
}
```
このプログラムをコンパイルして出力すると、出力1から出力8へシフトしていく動きになります。
これがその動画です。
![イメージ](https://lh3.googleusercontent.com/lLyjmGpEzpMWPr-IKonM1KRMHjRl55cDP_9pe23VWfy-Ij2KXEtbu8qc8V-D1VSmyetHDyfB469MRf_qwN-_869TuLUgCv2zOeUF7PuhnNTHL2xWuMETMdkTEwytwrlHNYdLY4fA-2mmtuB1cUXbs8Wd-JdaJQ9RqQsE7hIdGA652274UEp0cnnFDZHMIot-GmeTNMAIb53K9CjFrirNk_ZjJkbg04N6uDExv6eazEhbmsgLLKbw2FJC-l-StzeK3mGyTWXLbm8YBLnIvb7HSQYHEGja-CEWI0Z19WO3mOSklwXRjN1s_RsIIPy4Yy8ltLVdbVRpUj3f4C0VoA9y-MdL5aHfpehNmfcRUfEhyzbGt7kN03Npzz8LgCfIZik4Waz6kAvjLbxvGGDHUJo7kn_HyTBtcp-nNnj3lNtZJP7xM_t3dR5eMefoP1nPaRFRLGdVv2HxiQAnEW_5AGlwZ00L_Q7vHIDQPsfGKEJQZSwKXgJehp2Mn52hhXkmuLwePBVg9Dx7duU2DXyDuAM4OdmCdJNlQpnoIR1Ew-iKK5PqgPufWvz7Mo-skGlIXzIUAwsm3CwhVw2IdrdCFIQ5XFcCOeR3POIf9KplcfDDw5FzKGKx8dUtTI-7zuFOE53S138CoFcVCpslOFYs1DbCtFGclvpn6TXQYaEjIdViV0neTobt_twREaML7EQahnHF42507PatWhlS7jUr7egTzdzsI_d_GOdGOF4wGke9AIGTZ-553kgB-kBIM3OUeWwlscP0s3XzHm_H9f6jJoE7-wytmjRjO5xHCNlrCPVVugp_7TFVi0kPkpz2wRnM0ROux729fhUmhYywxs6R7sfC3BbIfmxdyTtdGbuD9Kb_o_F5WRwbK0-EanjBvxIDYQEV03kcJRw7vSTiYQrKM5PXaJS5542iiqM_Ay9GcqUyQ4OjRebHsi58STmE7ZBXZjgq0H5WNRtsBAHsNYTgSdhlNfhvgRwTv_qweMi2LJerDRmhINTmHligUj0IRrUQ9QdmWvJE_r2xVv9ZbSCN69KENHIAYoc=w380-h214-no?authuser=0)
SERがHIGHだと出力1をHIGHにして、LOWだと出力1をLOWにする仕組みで、
SERを送るとその前のデータは出力1から出力2にシフトします、
このプログラムは最初にHIGHを出力した後7回LOWを出力しています。
例えば以下のようにSERにデータを出力したとします。
出力(SERを"H,H,L,L,H,H,L,L"の順に出力) //RCLKも出力の合間に入れてある。
LEDはこのように出力されます。
LED L, L, H, H, L, L, H, H
イメージ図
![イメージ](https://lh3.googleusercontent.com/pAkcuJh34sHK-Lt_p1a3HmSG_26vqZ82hY7sK2GBeETITHGyts5y6o3Wj50ywDwDuJGe0ynKfggtnPQ2T7SIrFNxufuNdei9Yhh_aELslqZIo_Zsub2_73kcAr--pQkW3eOWzC6ZQCQiw9lQVbTCNH_oZz_VwL9gyOv_-a3-zN5fWG1dSUZ_pDH7w4Mh30sTt9ovuYyKG5UILLr276WL8xdrXrata4pl4oCQAYR8eGz6yU45Lnh1JbZcBb4m3OCRdXNWYtOEQAl14ToCDkM4XsnhXHYZ7Afec4xOdR5TfYexHylEKdIndlDRM2x0p0lOUDvQkfQstRthEZmRyxSnIaKqzjd4QoNonE1wE9AEi7Ey-AMA4F6_LrJXK23BcO7kVKuKrPGSAmC1YX_4vgi6XVCka5PUj42D9lsX_HZFlKhdJD1wrSZtx2rXSbzgXLyJ-XeK3--IiM-IZ-N1w0QourBwR6nhPFXsf9v4YTDrOcPFWr3z937F2H0e6bwNmnKcbsOYvjAG1DMpR0eeW-uzJhWDJq3kL9Mwex4IaY-x6tIfKvWguOPYLQ-kednWgf1ms7ZQYq6emUNnv9TMjqdlcwT_nlfuvfAooDhtukXdCeuuG1AMzMuGh8pasbPPZhmK-S_sHfW-kNFF7MU_WEINmDA7DD8_iEVDu-5z0hgA62sZyIvQk8LKa7ka84kZefhA97eioswp40ld-Yc74Z9CTdd0J7lfUm26SbktMaEGwpr8HurGuP26LlVSa8gRqzM9qof41N7sJxH7y6jkTZI1KNiThxY9MdiDcljs92WCkNa5V2Qyw0Lt6Bhf7dXmigzE2H97l5ROM2WOsbDDrHdh2-ZOx7husQ0i143I0VAd0YraR9ErNtFQ9Qga1sZAU1IrgCyciCV8nUKJr4joUfBlJ81mG6N5XCE384BDnuLmyo3UoDJexQZBhTc5nqRtOmXbsi7Jx_uWmNdk2lh-3KmbFzqZw6GVuG3i1JTpIJUP4VmOvqVyPU797L9tfE2H9UvVg1QX9y27ZLBaaQ3Gt23cP64s3uQ=w426-h240-s-no?authuser=0)
わかってきましたかでは次にRCLKの役割をメインにプログラムを書きます。
```c++:配列で光る位置を指定するやり方.INO
void loop() {
  int Row = 0, Column = 0;
  int Array1[3][8] = {{1,1,1,0,1,1,1,0},
                     {1,1,0,1,1,1,0,0},
                     {1,1,1,1,1,0,0,0}};
  for(;;){
    initShiftRegister();
    Row++;
    if(Row == 3){
      Row = 0;
    }
    digitalWrite(RCLK, LOW);
    for(int Column = 0; Column < 8;Column++){
      if(Array1[Row][Column] == 1){
        digitalWrite(SER, HIGH);
      }
      else {
        digitalWrite(SER, LOW);
      }
			
		digitalWrite(SRCLK, HIGH);
	  digitalWrite(SRCLK, LOW);
    }
    digitalWrite(RCLK, HIGH);
    delay(1000);
  }
}
```
配列内に光る位置を指定するプログラムですが、RCLKの位置を見てください、
For分の外にあることでデータがすべてシフトレジスタに転送された後、出力されています。
これはinitShiftRegister()でも同じことをしています。
イメージはこのような感じです。
![イメージ](https://lh3.googleusercontent.com/f6vblaSqMfTwgZax28MWsoda5eQqQWTWOdZ44zHpI9bir2XIpHfAWce5jvoYZTA7ElujO9WQPS3hdOLx5QL_h44zFBnGzAUweMMMfNawgyPk00u2aDkrkMkB3gZJOrj0E59rG5GdXh44RtGklCrDKme3KRL5d5bIwSmnxJcsteG1UQYAHqHN8TzhTE7NeuAvSKzbvAZ9h-GMN5kBvhsC1qp-qpc2_5--YMinLMrzJdBdyurPmXUDDBbWjM5KBJ4JUjtnVvEK16Xgm7B--RUiJDPiRYOn8nhDj5vSg13kPqwV_z5810oi57sozxR_5nS9rdORaH4bgo0aqktZpyUV5wOY7u0UynDqj4Hvi9T7REZKz-r2BTZT-wDAX0hmQprgLTzmPtZnT_usYppjkeSD3dZUL44KZOs2GCkTJX5ykpiagQgWc4OFR1iRInfPc_MgFPYrc3WOecJvnY5mYXsfaiIj9RErbfdZfQwrO7FVJeOyM6ghe7_snx2A-6AwyNKN-AY9XBr2F2pwBoJkzwWKmGqIn1DTw0gzFJ-gdUHJDeaeJXQm0k5Eyo7sELqi-ZOMMMK3g4wXKu6Dw_EiPc0zfQngXvHujpTlRKBX0GWS1jq5grMVLoVhExTBbL7CK0_fsiYY6_NgK7U4im_6UnZxqU0GDObyaPh532K1x3e8eksIWH-DbM0GNlqBCjaBmWRWbMyMkH9pd-cz_9Gvmr6aUyRhruGohAeHEOB_7HkzmzSy6vrGTIpYyiXYRu15Pefku9c9iU99DbzbefyrMBWxgdBl3M1b9KMFcxIEVapZB7RyrVcPxVnkMoFgyWVGvG-nzKnxU-nYHvfS9lUujfO7zyUcmS_BnMe8gP6Z7NhNLXp_-nbLlhVKcZV-JhsGfZtq_toSXvFGnibIlkdqVtzCGvgusFG2jmuWWFqlZpQMMTmV8tvEADxOcSH1J_YWhnawR3Rkklp1q8MzAgGKaklmSeUtRIUBklo_JnWovvsmGT5RRW9TZLzUZDsL1bl0l13pd-uZjqeqb9mNN69CdwNDrhjaOsY=w373-h210-no?authuser=0)
(余談ですがここで使っているinitShiftRegisterはイニシャライズ関数として使っています、
しかしこのプログラムでは、何度もResetのために使っています。
この場合initShiftRegisterという名称は有効でなくResetShiftRegisterに変えるのが良いと思います。)

このプログラムは少し複雑で、2次配列を使うため２重の繰り返しを行っています。
# シフトレジスタを複数個使うには?
さて、いままで書いてきた内容はすべてシフトレジスタを１つだけ使ったときのことでした。
しかしもっとたくさんpinを使いたいという強欲な方もいるでしょう。
実はシフトレジスタは複数個並べて使用することもできます
つまりピン3個で16個のLEDを使うこともできます。
# 配線図(16個の制御)
配線図です。
![イメージ](https://lh3.googleusercontent.com/K9A65Qkmg0tpfkrKLo4SBt5SC4tkR5TXbZpuyDu1StZIVbdnZLkhLHUtignj4wwHBw5fn7Re3Nu_gMNhQKcybAMvu99pgDTn6j_4JALqieqZ0kj3IKnjxGDijHfNFuQ3sb6mEct3wIDsru5F2PaSID7bgP095oAmjlrGUI9ZTcLMr8uPUqQKk5bl3JHntxioDM8TIOdhDo-rgucjyhtJMly7qIE5kh6JvBpNRjWZio8l0zuA-NI0c-pROth1hPMKMjlLobKApwWI7K0DJZVT09h77xQ0edqv9hEb4bgnwzTFppNfpCkKEbpRA330iQiMELVQeWVCLDnmvLc4Zd7OXn28AVyiwAh8mChRzd2-FG-t3VK-M5JS8ZkpXJtFrns7zy2f1SBhb9WabBMvaCVajfQ6qdL9LfGSRFqutrRgYYmPx0y9-Wypwu5pjewgKc4emLPoKwgm65b2V7CgJlxSvJ-eFTBRg59Wt2YlVQFlBGh7nljqjzYZkft-jZdsO-hmsv-dsv0zNcAp22yAsgLQxLRhzopxIueZbfGvwZaPMsobu_NZoZA1ILFl4UhuzFBYFVJ7negCztSvp3Ia8nxeIL2lXt_vQRBSo6pUnFEp2MlY9BP0Xun8QhKLYXEY5bfNau6x2Kk3eW_qgGtlWsOwm_n-jEgyl4GpAv7FIpQjcJbh0sOl5_ngV3rG5WsXQKohGQQjgaoXou09H9x4tyYSDXqIWdBqkhGy2X93Ia46HE1_pg-lrkj-Pb4vw_LCzKiKvRKjxmmGhVeXkWKbXO5oldWjnX5b8g3R3jC7QdaxpcfLo1nFKs4Hc1z9ac1ayq2WEoNzVNqq46NTdxMBzSYehl6oRmnfEnh4iW7uehX9QzEyzFgKQ4FhUUgQq5AFOmhENVhNYGyFA1rq_K89fcRUoik9eT_nAuQzY910omsa75kNdjZxrDR_qx8AAbNnhjR7SwMOdPZPq1V42IAM0WwCucVf7VglM5IxlVlrhBWirzjqaXRIzH7ioAwJ6mPlDUZFIid9hJ0_2olbXh1F0dzfc0S4B8c=w979-h929-s-no?authuser=0)
見ると、SER(N)と書いてあったピンの役割のところがSERに接続されています。
それ以外は特に変わったことはないです。
今後は下のシフトレジスタがシフトレジスタ2上のシフトレジスタが1とします
# プログラム(16個の制御)
これも特に変わったことはなく、入れられるデータが8個から16個に変わっただけです。
シフトレジスタ1の出力1から シフトレジスタ2の出力8までHIGHとLOWを繰り返しながらシフトするプログラムです
```c++:シフトしていく.INO
#define SER		(2)
#define RCLK	(3)
#define SRCLK	(4)
 
void setup() {
  pinMode( SER,  OUTPUT );
  pinMode( RCLK, OUTPUT );
  pinMode( SRCLK, OUTPUT );
}
void initShiftRegister(){
  digitalWrite(RCLK, LOW);
  for(int i = 0; i < 16;i++){
    digitalWrite(SER, LOW);
			
		digitalWrite(SRCLK, HIGH);
	  digitalWrite(SRCLK, LOW);
  }
  digitalWrite(RCLK, HIGH);
}
void loop() {
  initShiftRegister();
  for(;;){
    digitalWrite(RCLK, LOW);
    digitalWrite(SER, HIGH);

	digitalWrite(SRCLK, HIGH);
	digitalWrite(SRCLK, LOW);
    
    digitalWrite(RCLK, HIGH);
    delay(500);

    digitalWrite(RCLK, LOW);
    digitalWrite(SER, LOW);

	digitalWrite(SRCLK, HIGH);
	digitalWrite(SRCLK, LOW);

    digitalWrite(RCLK, HIGH);
    delay(500);
  }
}
```
見て頂くと、initShiftRegisterの条件文がi < 8　から　i < 16に変わっています。
それ以外はいつも通りのプログラムですね。
動作動画です
![イメージ](https://lh3.googleusercontent.com/gbDyR2dJdjzDroGo5IhrLnysdJKrEPN9-q23UcKHjhU_7exOgMLJfOsISGFNkGNjdJobLpiasGndLkpBJyW4cqZSWWVwbeEmzDZdbEP4jfl5a4GfjVQODy9L5oqS-gm90hHo54s1mC0S7Yei-1b4SIZieOeeEf-EPQpK4R-osfZpNyq2qdOT1DRN18Fn-Spt9F-TMvYfzI2eljfIAHvQRXd0XRBPqQ4M7QzYrDE3bLaBSlv-z2_2ZWYMCklFfvTsL_3dkc9oBnJMyKXCYo0HIwPyhkIkT_-0-IqDdWlcFx4XImv22EPz48JvnUKss5risIeheDK5aRLq2pStZY7CrpDhBjMOUZfHkgtXZGIPQ_qgictowTBD3EA_Y7rlEYQtk9GQFFDOXIxa3v44sFh3iGWt9h5U6Ge4Q-Ge_IVNjh1FupBqn2Cma4nHOUpBGTiWAKvj7S2g19IilJOKDmiom9yt4pITY7Gndy1H3vPu__WkvYWXoosWPfiE7ZRRyuZ5yFFxDsdcex80w7QorySc0zmxTQlvAEYlY1RgsLHAcGaAKn_wwkMCst3AUQlAtd1LG1BlXjXlbv_stfWiBBbdzwbiCgzeZ_4lsDOW7QyxBpMDz2ywk4LZSqXytaORoMpeBr2R4dPPXswJJAC9Qwl5_VSWflbZgrX78dFQrET9WV7rvM7rHQ31xI5z5Oxbg2wC08zasji_qwnX0PVAAwcqKXoODgsUo8R4tPq-Cx61OjfwD2VaGUUwTdt8hY1PESPLFmcNSTMoD1g2XG3p5GFldS7BytoXyGWunaQYxQwoU42hMVxmf5fo0iMPAhBGhuv-L1PCe7XE4BxJi0HpPaWi1Mbremi1V08QXTzLdUi5lf1hK0xPsthy3MapnJSbuniadMXjnwcZ_r3013OQLOgrsAFQk7UP2O-v2GdOXO16PZYW7dpBnCC9sfoB3UrgyUQxwpZgq6XMO_ghL1Xv6RuOLNcPkzs3CtmeVcRtD1Tj3IGr496Z0H03SP4IpfgJ5TVdtbdMI2quS0g3NSKDpNc0vO5YEQc=w426-h240-s-no?authuser=0)
最後にまとめのプログラムとして、2次配列に光らせる位置を書いてそれを実行するプログラムです。
```c++:配列に光らせる位置を指定するプログラム
#define SER		(2)
#define RCLK	(3)
#define SRCLK	(4)
//シフトレジスタの個数(Number of shift registers)
#define NSR (2)
void setup() {
  pinMode( SER,  OUTPUT );
  pinMode( RCLK, OUTPUT );
  pinMode( SRCLK, OUTPUT );
}
//初期化の関数
void initShiftRegister(){
  digitalWrite(RCLK, LOW);
  for(int i = 0; i < NSR * 8;i++){
    digitalWrite(SER, LOW);
			
	digitalWrite(SRCLK, HIGH);
	digitalWrite(SRCLK, LOW);
  }
  digitalWrite(RCLK, HIGH);
}
//array : 光らせる位置の入れる２次配列
//Rows  : ２次配列の行数(縦のこと)
void SetLED(int array[][8], int Rows){
  digitalWrite(RCLK, LOW);
  for(int Row = 0; Row < Rows; Row++){
    for(int Column = 0; Column < 8; Column++){
      if(array[Row][Column] == 1){
        digitalWrite(SER, HIGH);
		digitalWrite(SRCLK, HIGH);
	    digitalWrite(SRCLK, LOW);
      }
      else {
        digitalWrite(SER, LOW);
		    digitalWrite(SRCLK, HIGH);
	      digitalWrite(SRCLK, LOW);
      }
    }
  }
  digitalWrite(RCLK, HIGH);
}

void loop() {
  initShiftRegister();
  //LEDの点灯パターン
  int LEDPattern1[2][8] = {{1,0,1,0,1,0,1,0},{0,1,0,1,0,1,0,1}};
  int LEDPattern2[2][8] = {{0,1,0,1,0,1,0,1},{1,0,1,0,1,0,1,0}};
  for(;;){
    SetLED(LEDPattern1, 2);
    delay(500);
    SetLED(LEDPattern2, 2);
    delay(500);
  }
}
```
SetLEDを使えば簡単に光らせる位置を変えることができます。
# 終わりに
シフトレジスタは便利なICですが、扱いが難しいです。
そしてほとんどのサイトがShiftOut関数を使用しています。
この関数は2進数を使った制御文を書かないといけないのですが、2進数って扱いずらいしイメージがわかないから、配列にして書きました。
プログラムのソースコードはgithubで公開されています。
以下のURLを飛んでください。
[GithubのURL](https://github.com/yuusyaisami/ShiftRegisterForArduino "github")
2023/6/23 記事を書き換えました