ロジックアナライザを試すその４：ハードウェアSPIとソフトウェアSPI

# MCP3008でのハードウェアSPIとソフトウェアSPIの処理時間
A/Dコンバータである[MCP3008](https://cdn-shop.adafruit.com/datasheets/MCP3008.pdf)を用いたハードウェアSPIとソフトウェアSPIの処理時間に差があることに気がついたので、ここに記録する。

## ハードウェアSPIとソフトウェアSPI
[こちら](https://synapse.kyoto/glossary/spi/page001.html)に説明あり。ハードウェアSPIは専用回路を用い、ソフトウェアSPIはGPIOを用いる。

## 環境
[ロジックアナライザを試すその３（SPI）](https://qiita.com/infinite1oop/items/01d2dbbc9ed24c2458df)と同じ。ArduinoでA/DコンバータMCP3008と[温度センサーMCP9700](https://switch-education.com/datasheets/mcp9700.pdf)とを利用。

## MCP3008データシート
### ハードウェアSPI処理
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/23304439-5800-4e04-1a3e-a6122a2db628.png", width=120%>
![MCP3008Datasheet_Hard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/b640da26-d1ce-251e-a1ba-87f1d5a5e205.png)
ハードウェアSPIでは8ビットをまとめて処理する、‘leading zeros’がきっかけとなっているらしい。これをもとにコーディングする（[前回のソースコード参照](https://qiita.com/infinite1oop/items/01d2dbbc9ed24c2458df)）

### ソフトウェアSPI処理
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/767c12ae-aab3-7e7a-8a83-4bbc30c0ce81.png", width=60%>
![MCP3008Datasheet_Soft.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/05b57c5e-09a8-348d-94a2-86330ffed6e5.png)
シリアルに1ビットずつ処理する。

## ソースコード
### ハードウェアSPI
[前回](https://qiita.com/infinite1oop/items/01d2dbbc9ed24c2458df)と全く同じ。

```c++
#include <SPI.h>
#define SPI_CS 10
float Vref = 5.0 ;
SPISettings settings(1000000,MSBFIRST,SPI_MODE0);
  
void setup() {
  Serial.begin(9600);
  pinMode(SPI_CS, OUTPUT);
  SPI.begin();
}

void loop(){
  // SPI setting and write&read
  SPI.beginTransaction(settings);
  digitalWrite(SPI_CS, LOW);
  SPI.transfer(0b00000001);                 // Start bit 1
  byte highByte = SPI.transfer(0b10000000); // SingleEnd Channel 0
  byte lowByte = SPI.transfer(0x00);        // dummy
  digitalWrite(SPI_CS, HIGH);
  SPI.endTransaction();
  unsigned int dataCh0 = ((highByte & 0x03) << 8) + lowByte;
 
  // Conversion of read data according to MCP9700 spec
   float volts = dataCh0 * Vref / 1024;
  float temperature = (volts - 0.5) / 0.01; // : 500 mv at 0 degree and 10 mv / degree 
  Serial.println("dataCh0: " + String(dataCh0,HEX) + "  Voltage: " + String(volts,3));
  Serial.println("Temperature: " + String(temperature,2) + " degrees");
  Serial.println();
  delay(3000);
}
```

### ソフトウェアSPI
[こちら](https://github.com/adafruit/Adafruit_MCP3008)にあるものとほぼ同じ。

```c++
#include <SPI.h>
#define SPI_CS   7
#define SPI_MOSI 6
#define SPI_MISO 5
#define SPI_CLK  4
float Vref = 5.0;

void setup() {
  Serial.begin(9600);
  pinMode(SPI_CS, OUTPUT);
  pinMode(SPI_CLK, OUTPUT);
  pinMode(SPI_MOSI, OUTPUT);
  pinMode(SPI_MISO, INPUT);
  digitalWrite(SPI_CLK, LOW);
  digitalWrite(SPI_MOSI, LOW);
  digitalWrite(SPI_CS, HIGH);
}

void loop() {
  // SPI setting and write&read
  unsigned int outBuffer, inBuffer = 0;
  byte command = ((0x01 << 7) |        // Start bit
                  (1 << 6) |           // 1: SingleEnd
                  ((0 & 0x07) << 3));  // Channel 0
  digitalWrite(SPI_CS, LOW);
  outBuffer = command << 8;
  for (int c = 0; c < 16; c++) {
    digitalWrite(SPI_MOSI, (outBuffer >> (15 - c)) & 0x01);
    digitalWrite(SPI_CLK, HIGH);
    digitalWrite(SPI_CLK, LOW);
    inBuffer <<= 1;
    if (digitalRead(SPI_MISO))
      inBuffer |= 0x01;
  }
  digitalWrite(SPI_CS, HIGH);
  unsigned int dataCh0 = inBuffer & 0x3ff;
                
  // Conversion of read data according to MCP9700 spec
  float volts = dataCh0 * Vref / 1024;
  float temperature = (volts - 0.5) / 0.01; // : 500 mv at 0 degree and 10 mv / degree 
  Serial.println("dataCh0: " + String(dataCh0,HEX) + "  Voltage: " + String(volts,3));
  Serial.println("Temperature: " + String(temperature,2) + " degrees");
  Serial.println();
  delay(3000);
}
```

## ロジックアナライザ
### ハードウェアSPI
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/f1a3c2af-2289-2c67-1506-30313dbf00c1.png", width=140%>

### ソフトウェアSPI
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/dc4e60ee-7238-8863-4420-4826987f3693.png", width=140%>
所持するロジックアナライザのSPIデコードはハードウェアSPIを前提にしているようで、そのままではデコードできず。試しに、Clock polarityとClock phaseを適当に選ぶとデコードしてくれた。
![SoftSPISetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/6474388b-264b-3eae-6213-54a4b5b73ed7.png)

## 比較
どちらもMISOにらしいデータ（'9c'および'9f'、変換後は26−27度）が見られる。ここでのポイントは処理速度。ロジックアナライザでの取得データの処理時間（赤枠）を見ると、
- ハードウェアSPI  　26μs
- ソフトウェアSPI  　340μs
となっている。これだけ差があるのは少々びっくり。

ソフトウェアSPIについては用いるマイコンなどの能力にも依存するのであろう。また、他のSPI対応デバイスでどんな結果となるかは不明。