micro:bit / MicroPython / SPIデバイス(ADコンバーターMCP3008)を外づけする / それに意味のないとき

# 概要  
SPIデバイスをmicro:bitに接続してみる。SPIデバイスにはADコンバーターMCP3008を使う。10ビット、8チャンネルのADCである。  
micro:bitにもアナログ入力はあるので、アナログ入力の数を増やしたい場合を除けばADコンバーターを外づけする意味はない。SPIバスの動作を確かめるだけである。  

下の回路で試してみる。  

- SPI関連の端子はデフォルトのままとする。!CS端子にはP16を使うことにする。
- SPIモードは0(0,0)とする。


<img src="https://qiita-image-store.s3.amazonaws.com/0/193936/70fe512c-bfa4-b15d-5c64-547fd0df7311.png" width=70%>  

# spiモジュールの使いかた    
- 初期化: ```microbit.spi.init(baudrate=1000000, bits=8, mode=0, sclk=pin13, mosi=pin15, miso=pin14)``` <br>(引数は全部オプション)
- read操作: ```microbit.spi.read(バイト数)```
- write操作: ```microbit.spi.write(バイトバッファー)```
- write/read操作: ```microbit.spi.write_readinto(書き込みバイトバッファー, 読み出しバイトバッファー)```  <br>読み書きに使う2つのバイトバッファーはサイズが同じでなければならない。両方とも同じバイトバッファーを指定してもよい。

# spiモジュールを使ってMCP3008から10ビットのADC値を読み出す方法  
以下の手順で3バイトをwriteして3バイトをreadし、readした3バイトの中から10ビットを取り出す。ビットの送り順はMSBファーストである。  
[0]バイト目:<br>0b 0000 000<font color="red">1</font>をwriteする。この<font color="red">1</font>がスタートビットとして働く。<br>→同時に、ADCから1バイトが返ってくる。このバイトデータは使わない。  
[1]バイト目: <br>ビット7でシングルエンド(1)か差動(0)か指定し、ビット6:4でチャンネル(0～7)を指定する。下の例では、シングルエンドでチャンネル0を使うので0b <font color="red">1</font><font color="blue">000</font> ----をwriteしている。<br>→同時に、ADCから0b ---- -0<font color="red">??</font>が返ってくる。<font color="red">??</font>の位置にある2ビットが10ビットデータの上位2ビットである。  
[2]バイト目:<br>ダミーバイトをwriteする。下の例では0をwriteしたが何でもよい。<br>→同時に、ADCから10ビットデータの下位8ビットが返ってくる。  
## まとめ  
まとめると次のとおりである。これで、チャンネル0に印加された電圧が10ビットにAD変換される。?で表した10ビットがそのAD変換値である。spiを使わずに独自に実装するのであればもはや8ビット単位にこだわる必要はないのでスタートビットから始めればよい。
  
|  | [0]バイト目 | [1]バイト目 | [2]バイト目 |
|:-:|:-:|:-:|:-:|
| マスターからスレーブへ転送するデータ | 0b 0000 000<font color="red">1</font> | 0b <font color="red">1</font><font color="blue">000</font> ---- | 0b ---- ---- |
| スレーブからマスターへ転送されるデータ | 0b ---- ---- | 0b ---- -0<font color="red">??</font> | 0b <font color="red">???? ????</font> |  




# 1回だけADC値を読み出すためのMicroPythonスクリプト  
上の手順をそのままMicroPythonで再現する。spiモジュールのおかげでえらく単純である。AD変換値を電圧値に戻してみる(要するにディジタル電圧計である)。  

```python
from microbit import *

mosi = bytearray([1, 0b10000000, 0])
miso = bytearray([0, 0, 0])

pin16.write_digital(1)
spi.init()

pin16.write_digital(0)
spi.write_readinto(mosi, miso)
pin16.write_digital(1)

print("MOSI data: %s" % ([bin(b) for b in mosi]))
print("MISO data: %s" % ([bin(b) for b in miso]))

ADdata = (raw[1]<<8 | raw[2]) & 1023
volt   = ADdata * 3.23/1024.0 # 3.23はMCP3008のVREF端子の実測値。

print("AD %d, %.2f(V)" % (ADdata, volt))
```  

## 実行結果  
<img src="https://qiita-image-store.s3.amazonaws.com/0/193936/333a39e2-b23e-fa9e-f459-5065efb33ad9.png">  
<img src="https://qiita-image-store.s3.amazonaws.com/0/193936/b318e1b4-877e-5997-baf8-06043f595ac3.png">  
<img src="https://qiita-image-store.s3.amazonaws.com/0/193936/b225f609-b7b8-0fbd-29e7-5c7a5e290bac.png" width=600>  




# 整理してクラス化した  
  
```python
from microbit import *
class MCP3008:
    def __init__(self, CSpin=pin16):
        self.CS = CSpin.write_digital
        self.CS(1)

        self.mosi = [bytearray([1, (1<<7)|(ch<<4), 0]) for ch in range(8)]
        self.miso = bytearray([0, 0, 0])

    def read(self, CH):
        self.CH = CH

        self.CS(0)
        spi.write_readinto(self.mosi[CH], self.miso)
        self.CS(1)
        return (self.miso[1]<<8 | self.miso[2]) & 1023

##############
# how to use #
##############
if __name__ == "__main__":
    # Initialize the microbit.spi module.
    # You can also specify any of the following options:
    # baudrate=1000000, bits=8, mode=0, sclk=pin13, mosi=pin15, and miso=pin14
    spi.init()

    # Instantiate the MCP3008 class with !CS assigned to pin16.
    adc = MCP3008(CSpin=pin16)

    while True:
        # Get 10-bit data by converting the voltage applied to the channel 0 of MCP3008.
        ADdata = adc.read(CH=0)
        volt   = ADdata * 3.23/1024.0

        print("AD %d, %.2f(V)" % (ADdata, volt))
        sleep(1000)
```
## 実行結果
![image.png](https://qiita-image-store.s3.amazonaws.com/0/193936/f2dc48d0-c25a-a915-edbb-3892fee6a10b.png)  

# 参考
- http://microbit-micropython.readthedocs.io/en/latest/spi.html
- Harry Fairhead, _micro:bit IoT in C_, pp.139-144

# 関連  
[はてなブログ / ADコンバーターMCP3008 / SPI](http://ti-nspire.hatenablog.com/archive/category/AD%E3%82%B3%E3%83%B3%E3%83%90%E3%83%BC%E3%82%BF%E3%83%BCMCP3008%20%2F%20SPI)