MicroPythonを使ってみる（その４）＆エレキ素人が何か考える（その７）：DAコンバーター

# MicroPythonとエレキ素人のコラボ（若干こじつけ）
MicroPythonを用いて、Wio TerminalのDAコンバーター経由で正弦波をつくり、オシロスコープで観察してみる。

## Wio Terminal
本家サイトは[こちら](https://wiki.seeedstudio.com/jp/Wio-Terminal-Getting-Started/)である。後ほどオシロスコープで波形を観察するので、ピン配置を下記に記す。
![ピンアサイン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/39b24ab5-1bdd-c9c2-b4bc-e8d3c6247452.png)
ピン11：DAC0（DAコンバーター）、ピン6：GNDとをオシロスコープにつなげる。

### MicroPython環境
Wio Terminalでは、本家サイトの[ArduPy](https://wiki.seeedstudio.com/ArduPy/)というMicroPythonの環境がある。その手順に従い、ArduPy FirmwareをWio Terminalに入れる。プログラム作成やWio Terminalへの書き込みに関しては、他のIDEを使っても良い。今回は、MicroPythonで有名どころの[Thonny](https://thonny.org)を利用した。

## 正弦波を作り出すソースコード
[ここ](https://docs.micropython.org/en/latest/library/pyb.DAC.html)にMicroPythonでDACを扱うコードがあったので、そのまま使おうとしたが、DMA転送によりDACを実現するwrite_timed()という関数が、Wio Terminalに存在しなかった。

```
>>> from machine import DAC
>>> help(DAC)
object <class 'DAC'> is of type type
  write -- <function>
  resolution -- <function>
  reference -- <function>
```

他にMicroPythonを扱えるボード（ESP32, ESP8266, Seeeduino XIAO）を所持していたが、いずれにもwrite_timed()には存在しなかった。どうも、write_timed()が存在するのはpyboardのみらしい（執筆時点）。

### sleep_us()で実現
そこで、write()とsleep_us()を用いて、ソフトウェア的に無理やり実現する。下記コードとなる。

```py:
import time
import math
from machine import DAC, Pin

freq = 100
num = 100
ary = list(range(num))
for i in range(num):
  ary[i]=int(4095*(math.sin(2*math.pi*i/num)+1)/2)
dac = DAC(Pin(11))
while 1:
  for i in range(num):
    dac.write(ary[i])
    time.sleep_us(int(1000000/(num*freq)))
```

上記コードでは、sin値を整数値化(0-1)し、100Hz(freq)、1周期を100分割(num)して出力している。Wio Terminal DACの分解能は10bit(=4096)であり、分割されたポイントごとの値を事前に作成しておき（配列ary）、freqに従って、アナログ出力（write）する。4096をwriteすると、0Vが出力されたので、最大値を4095としている。

## 検証の前に
time.sleep_us()の精度が気になるところ。そこで、次のようなコードで確認。

```py:
import time
start = time.ticks_us()
time.sleep_us(1)       # 1,10,100,100と変化させる。sleep_ms()にも置き換える。
end = time.ticks_us()
print(str(end-start) + " ticks")
```

|sleep_us()|1|10|100|1000|
|---|---|---|---|---|
|ticks|26|36|126|1026|

|sleep_ms()|1|10|100|1000|
|---|---|---|---|---|
|ticks|1029|10028|100030|1000029|

これらから、sleepの確からしさは、1 ms以上と思われる。ここでは、テストなので、100 us以上を考慮した。

## 検証
今回はオシロスコープとして、pokitMeter（[スイッチサイエンスさんで購入](https://www.switch-science.com/catalog/5759/)）を利用。オシロスコープとWio Terminalとの接続は下記状況。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/88a95aff-35db-2740-ead9-1a7d16f61d1a.png", width=60%>

### 結果
#### 100Hz(freq)、分割数100(num)と設定した結果（sleep:100us）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/c1887033-d49c-1ee0-cbf2-d8871665fa41.png)
ソフトウェア処理の限界か、100Hzに程遠く、62.9Hzと表示（赤枠）。このオシロスコープでは、周波数解析も可能、下記のとおりの結果。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/9695ba58-1522-ea4e-1c97-a3ad1f155ca4.png)
ピークは68.226Hz。

#### 100Hz(freq)、分割数50(num)と設定（sleep:200us）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/c4cdf1f4-d293-a617-8810-780176aed325.png", width=70%>
77.7Hzと表示。分割数を減らしたので、若干、波がデジタルチックになってる。

#### 100Hz(freq)、分割数25(num)と設定（sleep:400us）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/88a930e5-1bac-c499-a766-51fbb70575c2.png", width=80%>
87.8Hzと表示。波がひどくなってきたが、sleepの回数が減ったため、周波数が100Hzに近づいてきている。

#### 50Hz(freq)、分割数100(num)と設定（sleep:200us）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/9f69e421-9ec5-6f18-f721-e6d40c2d6a43.png", width=60%>
31.9Hzと表示。

#### 25Hz(freq)、分割数100(num)と設定（sleep:400us）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/044be303-938e-987b-2820-a3bf3ae744e3.png", width=60%>
22.5Hzと表示。

#### 10Hz(freq)、分割数100(num)と設定（sleep:1000us = 1ms）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/0cf13bcf-8069-ea33-c609-78a097a2f389.png", width=60%>
9.5Hzと表示。

#### 5Hz(freq)、分割数100(num)と設定（sleep:2ms）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/be029f70-c0dd-066e-ce9d-315937b52831.png", width=60%>
4.9Hzと表示。ここまで来ると精度が高くなっている。

## 最後に
ソフトウェア処理の限界が一部垣間見える。DMAを利用しないとダメか。