M5StickV MicroPython Instructions

# 使えないメソッド
 MaixPy DOCとかに記載があるが使えないメソッドやら機能やら
・`cpufreq.get_current_frequencies()`
　cpufreqをインポートできない
・`cpufreq.get_kpu_supported_frequencies()`
　cpufreqをインポートできない
・`lcd.set_backlight(bool)`
　実行時もエラーにならないが，バックライトの輝度は変わらない．
　このメソッドの代わりに，レジスタを書き換える方法で動的に輝度変更可能
・`lcd.get_backlight()`
　エラーにならないが，常にNoneが返ってくる．
・`FFT.run()`
　~~FFTモジュールがインポートできない~~
 ~~ハードウェア的にはFFT機能を持っているみたいだけど，現状デフォルトのファーム（micropython実行環境）ではインポートできない．~~ 
`from Maix import FFT`でインポートできる

・`cpufreq.set_frequency(cpu = cpu_freq, kpu = kpu_freq)`
　cpufreqモジュールがインポートできない
・`image.crop()`
・`Image.scale()`
・`Image.find_number()`
・`Image.classify_object()`

・IMU（MPU6886，SH200Q）周り．micropython（MaixPy)にモジュールが無いので使えない．
・omv周り．omvのインポートができない
・SPI周り．pybのインポートができない
・lvglモジュールのインポートができない
・nes周り．デフォルトのファーム・ウェアではインポートは出来るが，nes.run()ができない．ファームをビルドしなおすと，menuconfigでnesモジュールの選択ができる（多分勝手にオンになっている）ので，ビルド後nes.run()出来るようなるが，画面サイズが合っていないので（memory関係のエラーが出る），ソースコードの修正が必要．下記サイトが参考になる．
https://github.com/sipeed/MaixPy/pull/150（多分画面周りの部分だけ修正で動く）


# 使えるメソッド

# ●ガベージコレクタ
`gc.isenabled()`    #標準ではTrueが返ってくる
`gc.disable()`
`gc.enable()`
`gc.mem_alloc()`
`gc.mem_free()`
　手元の環境で空ヒープを見てみる．デフォルトのファーム・ウェアで起動した状態で次の通り．

```
>>> gc.mem_free()
388160
>>> gc.collect()
>>> gc.mem_free()
500736
```

`micropython.mem_info()`
　ガベージコレクタとは関係ないが，メモリ・インフォを出力
`micropython.mem_info(1)`
　メモリ・ブロックの内容を出力
`gc.collect()`    #ガベージコレクションを起動
`gc.threshold(int)`
　ガベージコレクタを起動するメモリ占有量の閾値を設定．


~~`gc.get_stats()`~~
~~`gc.get_count()`~~
~~`gc.garbage`~~


### gcヒープサイズを調整

```
from Maix import utils
utils.gc_heap_size(2000000) 
```

<br>
# ●cmath関係
micropythonでもなにもimportしなくても，複素数を扱える（complexが使える)

`cmath.cos(float)`    #単位はラジアン
`cmath.sin(float)`
`cmath.exp(float)`
`cmath.log(float)`    #オリジナルのcmathのような基数の指定はできない
`cmath.log10(float)`
`cmath.phase(complex(float,float))`    #極座標から角度をラジアンで得る．返り値の範囲はπ～-π．
`cmath.polar(complex(float,float))`    #x,y座標から極座標を得る．角度はラジアン．
`cmath.sqrt(float)`    #返り値はcomplex
`cmath.rect(float, float)`    #cmath.polarの逆変換

`cmath.pi`    #3.141593
`cmath.e`    #2.718282

~~`cmath.tan()`~~    #使えませ～ん
~~`cmath.acos()`~~
~~`cmath.asin()`~~
~~`cmath.atan()`~~
~~`cmath.acosh()`~~
~~`cmath.cosh()`~~
~~`cmath.isinf()`~~
~~`cmath.isfinite()`~~
<br>

# ●math関係
math.pow(float, float)
math.sqrt(float)    #返り値はfloat
math.trunk(float)    #0方向へ丸める（正数は少数部切り捨て，負数は切り上げ）
math.acos(float)
math.atan(float)
math.asin(float)
math.atan2(float,float)    #高さ/底辺をそれぞれ引数として与える
math.acosh(float)
math.asinh(float)
math.atanh(float)
math.pi
math.e
　etc
<br>

# ●LCD初期化
　ここでの色指定が何に影響を与えるかは不明（電源を入れた後，この色になります．一度lcd.clear()すると黒になるので，もう影響はなくなるのかも）

`lcd.init(freq=15000000)`
`lcd.init(freq=15000000, color=(255,0,0))`

M5Stick Vの新しめのファームウェアでは，初期化時の必須引数が増えた模様．
指定しないと，エラーの出ないが，画面に何も表示されない．

`lcd.init(type=3)`

<br>

# ●LCDに文字と画像描画

```python
import lcd, image

lcd.init(freq=15000000)
lcd.draw_string(50,50,"Hello world", lcd.RED, lcd.WHITE)    #LCDに文字描画
img = image.Image()    #新しい画像生成
img2 = img.copy(15, 15, 60, 60)    #元画像左上から60, 60の位置からサイズ15×15で新しいイメージ切り出し．元画像に変更なし．
time.sleep(2)
img.draw_string(10,50,"Hello M5StickV", scale=2)    #画像imgにサイズ2で文字を描画
img.draw_image(img2, 30, 30)    #画像imgにx30, y30の位置でimg2を描画
lcd.display(img)
```

<br>

# ●LCDに画像表示
　`lcd.display(img, roi)`ではroiに元画像の表示したい部分を矩形で指定する．画面サイズより小さい領域となる場合は，常にセンタリングされ，空いた部分は黒となる．
　roiはx1, y1, w, hで指定するが， ~~x1もしくはy1が元画像領域をはみ出している場合，実行時エラーとなる．~~ 元画像に対して，領域のオーバーラップが無い場合には，実行時エラーとなる．幅や高さが元画像に入りきらない場合は，はみ出し部分は黒塗りとなる．

```python
import lcd, time, sensor

img = sensor.snapshot()
lcd.display(img)    #roiを省略した場合，img全体を描画(はみ出す場合はセンタリング)
time.sleep(2)
roi = (10,10,64,64)
lcd.display(img, roi)
time.sleep(2)
lcd.display(img, (100,100,64,64))
```
<br>

# ●LCDの表示をクリア
　指定した色で画面全体を塗りつぶす．色を省略した場合は黒で塗りつぶす．lcd.initの色指定の影響は受けない．

```python
lcd.clear((255,0,0))    #赤で画面クリア
```

<br>

# ●LCDを回転

```
lcd.rotation(2)    #M5StickVの場合，90°を2回するとカメラの向きと液晶の向きが合う
```

<br>

# ●CPU/KPU動作周波数
　`freq.set(400)`で周波数の設定可能なようであるが，リブートしてしまう．
　`freq.set(200)`をやってみたら，動作しなくなりました（笑）. ファームウェア書き換えで復帰．
　`freq.set(kpu_div(2))`でKPUの動作周波数を半分に落とすことには成功．

```
from Maix import freq

print(freq.get_cpu())    #CPU動作周波数
print(freq.get_kpu())    #KPU動作周波数
```


<br>

# ●タイマ割込み
　コールバック関数はタイマ・オブジェクトを引数として渡す形で呼び出されるため，引数を少なくとも1つ取る形にしなければならない．

＜第1引数＞タイマを選択: Timer.TIMER0 or Timer.TIMER1 or Timer.Timer2
＜第2引数＞タイマ・チャネル：Timer.CHANNEL0～Timer.CHANNEL3
＜mode＞モード： [Timer.MODE_ONE_SHOT or Timer.MODE_PERIODIC or Timer.MODE_PWM]
　省略した場合はワンショット
＜period＞タイマ周期：　省略すると1000
＜unit＞単位[Timer.UNIT_S or Timer.UNIT_MS or Timer.UNIT_US orTimer.UNIT_NS]
　省略した場合はms
＜callback＞コールバック関数

```python
import time
from machine import Timer

def timer1(timer):
    print("arg: ", timer)

Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_ONE_SHOT, period=3000, callback=timer1)

time.sleep(5)    #5秒スリープ(このスリープではtimerも止まる）

Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PERIODIC, callback=timer1)
```

<br>

# ●時間を取得
timeでもutimeでも違いは無いみたい．
起動してからの経過時間を取得できる．

```python
import utime

currentTime = utime.ticks_ms()    #milli second
currentTime1 = utime.ticks_us()    #micro second
```

<br>

# ●フレーム・レート及び時間差を取得
　下記のプログラムを動かすと，`clock.avg()`と`clock.fps()`の動きがよくわかる．
　ただし筆者の環境では2段目の`fps()`は理論値の800msではなく600msくらいを出してきた．1秒未満とかの小さい数字にはあまり，追従しないようである．(`time.sleep()`の方の問題？）

```python
import time

clock = time.clock()    #コンストラクタ
while(1):
    clock.tick()
    time.sleep(0.5)
    print(clock.avg())    #tick()からの経過時間[ms]<この場合，理論値は500>
    print(clock.fps())    #↑の逆数[frame/sec]<理論値は2>
    time.sleep(0.3)
    print(clock.avg())    #<理論値は800>
    print(clock.fps())    #<理論値は1.25>
```

<br>

# ●ローカル・タイムを取得
　`utime.localtime()`の引数にEPOCHタイムを与えると（sec単位のint値）長さ8のタプルが返ってくる．
　ただし，基準は1970年ではなく，2000年．
　引数を省略した場合，よくわからない時間（タプル）が返ってくる．ループで値を定期的に取得し，秒の数字に着目してみると，時間に応じて更新されていくようである（引数なしの場合）．

```python
import utime, lcd

lcd.init(freq=15000000)

difference = 946652400    #Difference between 1970 and 2000
now = 1568642215   　　　 #16th September 2019
currentTime = utime.localtime(now - difference)
year,month,mday,hour,minute,second,weekday,yearday = currentTime
lcd.draw_string(10,0, 'year: ' + str(year), lcd.RED, lcd.WHITE)
lcd.draw_string(10,16, 'month: ' + str(month), lcd.RED, lcd.WHITE)
lcd.draw_string(10,32, 'mday: ' + str(mday), lcd.RED, lcd.WHITE)
lcd.draw_string(10,48, 'hour: ' + str(hour), lcd.RED, lcd.WHITE)
lcd.draw_string(10,64, 'minute: ' + str(minute), lcd.RED, lcd.WHITE)
lcd.draw_string(10,80, 'second: ' + str(second), lcd.RED, lcd.WHITE)
lcd.draw_string(10,96, 'weekday: ' + str(weekday), lcd.RED, lcd.WHITE)
lcd.draw_string(10,112, 'yearday: ' + str(yearday), lcd.RED, lcd.WHITE)
```

<br>

# ●スリープ

```python
import utime

utime.sleep(1)    #1秒スリープ
utime.sleep(0.1)    #float も可
utime.sleep_ms(1)    #1msスリープ
utime.sleep_us(1)    #1usスリープ
```

<br>
# ●GPIOの操作
　ペリフェラルを自由にアサインできる，fpioaと呼ばれるシステムになっているらしい（ESP32のIOマトリックスのようなものか？）
`fm.register()`の第1引数に指定する物は下記のファイルに記述してあるようである．
https://github.com/sipeed/MaixPy/blob/master/projects/maixpy_m5stickv/builtin_py/board.py
M5StickVの円筒型容器のふたに貼ってある丸いシールに記載の番号でペリフェラルを指定することもできる．つまりboard_info.BUTTON_A は 36と同義．

```python
from Maix import GPIO
from fpioa_manager import fm
from board import board_info

fm.register(board_info.BUTTON_A, fm.fpioa.GPIO1)
button_a = GPIO(GPIO.GPIO1, GPIO.IN, GPIO.PULL_UP)

while(1):
    if button_a.value() == 0:
        print("Button A pushed")
````

<br>
# ●GPIO操作でLED
　本体内蔵のフルカラーLEDはGPIOで制御します．
　下記のサンプル・プログラムはMaxiPy DOCから転載．

```python
fm.register(board_info.LED_R, fm.fpioa.GPIO0)    #GPIOピンの対応付け（board_info.LED_Rが6番ピンを表す．6とリテラルを書いても良い）
led_r=GPIO(GPIO.GPIO0,GPIO.OUT)    #GPIO0（←ピンの指定ではない）を出力に設定
led_r.value(0) 
```
<br>

# ●PWMでLEDを制御
PWMの使用例（コンストラクタ）
`Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM)`

 - *Timer.TIMER0* ：Timerは0～2の3つを使用可能
 - *Timer.Timer.CHANNEL0* ：チャネルは0～3の4つを使用可能
 - *mode* ：Timer.MODE_PWMを指定

`PWM(tim, freq=500000, duty=50, pin=board_info.LED_G)`
 - *tim* ：上で作ったタイマ
 - *freq* ：PWMの周波数
 - *duty* ：PWMのデューティー比
 - *pin* ：PWM出力にするピン番号

　下記のサンプル・プログラムはMaixPy DOCから転載．

```python
tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM)　　　　#タイマ0，チャネル0でPWMタイマ生成
ch = PWM(tim, freq=500000, duty=50, pin=board_info.LED_G)    #board_info.LED_G(9番ピン）をPWM出力に設定
duty=0    #デューティー比を0で初期化
dir = True
while True:
    if dir:
        duty += 10
    else:
        duty -= 10
    if duty>100:
        duty = 100
        dir = False
    elif duty<0:
        duty = 0
        dir = True
    time.sleep(0.05)    #スリープ
    ch.duty(duty)    #デューティー比を設定
```

<br>
# ●ファイル・システムの操作
　micropythonの実行環境は疑似的なファイル・システムを持っている．
　Maix DOCによるとモジュールは`uos`となっているが，`os`でもアクセスできる．CPythonの同モジュールのサブセットらしい．

### `os.listdir()`
　：カレント・ディレクトリのファイル一覧取得．下記の様なリストが返ってくる．
['boot.py', 'ding.wav', 'startup.jpg', 'freq.conf']

### `os.ilistdir()`
　：ファイル・リストのiteratorオブジェクトを返す．
　iteratorの各要素は下記の様な，長さ4のタプルである．

 - ファイル名
 - ファイルかディレクトリかを表す数字（ディレクトリ：16384， ファイル：32768）
 - 0（0が返ってくる．．．？　おそらくinodeの欄だが，inodeを持たないシステムの場合0が返る）
 - ファイルサイズ（単位はバイト）

```python
ite = os.ilistdir()    #イテレイターを取得
print(next(ite))    #次の要素をプリント
```

### `os.mkdir(name)`
　：ディレクトリを作る
### `os.rmdir(name)`
　：ディレクトリを削除
### `os.stat(name)`
　：ファイルの詳細を返す

### `os.statvfs(name)` ：

 - f_bsize – ファイルシステム・ブロック・サイズ
 - f_frsize – フラグメント・サイズ
 - f_blocks – size of fs in f_frsize units
 - f_bfree – 空きブロック数
 - f_bavail – 非特権ユーザ用空ブロック・サイズ
 - f_files – inodeの数
 - f_ffree – 空きinodeの数
 - f_favail – 非特権ユーザ向け空きinodeの数
 - f_flag – mount flags
 - f_namemax – ファイル名の最大長さ

port specificな実装のシステムでは，inode関連のf_files, f_ffree, f_avail, f_flagsの4つのプロパティは0を返します．

Parameters related to inodes: f_files, f_ffree, f_avail and the f_flags parameter may return 0 as they can be unavailable in a port-specific implementation.

### `os.getcwd()`
　：カレントディレクトリの名前を返す
### `os.chdir(path)`
　：カレントディレクトリを変更する（移動する）
### `os.rename(old_path, new_path)`
　：パスの指定が独特かも？パスを間違うと，エラーが返ってくるが，old_pathで指定したファイルは消えている（笑）
### `os.remove(path)`
　：pathで指定したファイルを削除する
　カレントディレクトリのファイル`startup.jpg`を指定する場合は，`./startup.jpg`とする．

### `os.uname()`
　：システムの情報を返す．筆者の環境では次の情報が返ってくる．
(sysname='MaixPy', nodename='MaixPy', release='0.4.0', version='v0.4.0-46-gf46e4c423-dirty on 2019-08-30', machine='M5StickV with Kendryte K210')

### `uos.urandom(int)`
　：乱数を生成する．引数で乱数のバイト数を与える．Maix DOCによるとハードウェア乱数生成器を使っている．

### RAMディスク
　プログラムはMaixPy DOCに掲載されているものを転用．

```python
import uos

class RAMBlockDev:
    def __init__(self, block_size, num_blocks):
        self.block_size = block_size
        self.data = bytearray(block_size * num_blocks)

    def readblocks(self, block_num, buf):
        for i in range(len(buf)):
            buf[i] = self.data[block_num * self.block_size + i]

    def writeblocks(self, block_num, buf):
        for i in range(len(buf)):
            self.data[block_num * self.block_size + i] = buf[i]

    def ioctl(self, op, arg):
        if op == 4: # get number of blocks
            return len(self.data) // self.block_size
        if op == 5: # get block size
            return self.block_size
            
bdev = RAMBlockDev(512, 50)
uos.VfsFat.mkfs(bdev)
vfs = uos.VfsFat(bdev)
uos.mount(vfs, '/ramdisk')

uos.chdir("/")    #ルートディレクトリに移動
print(uos.listdir())    #ファイル一覧を表示
uos.chdir("/ramdisk")    #/ramdiskディレクトリに移動
print(uos.listdir())    #ファイル一覧を表示
uos.mkdir("/ramdisk/newdir")    #新しいディレクトリnewdirを作る
print(uos.listdir())    #ファイル一覧を表示
uos.chdir("newdir")    #newdirに移動
print(uos.listdir())    #ファイル一覧を表示
```

## ●python関係
<br>
menu configの中身

board config
	(240) LCD default width
	(135) LCD default height
	(40000000) LCD default driver clock frequency
	(24000000) Sensor clock(xclk) frequency


Drivers configuration
	Force SPI SD Card high speed mode
	Enable ws2812 over i2s component

Kendryte SDK configurations
	log level, 5:LOG_VERSOSE, 4:LOG_DEBUG, 3:LOG_INFO, 2:LOG_WARN, 1:LOG_ERROR,

Micropython configurations
	Module configurations
		[* ] Enable NES emulator
		[* ] Enable video module
		[ ] Enable touch screen module
		[ ] Minimum size OMV function
		[* ] Add YUV convert table, close this will reduce code but lower speed
		[ ] Enbale LittlvGL
		[ ] Enable Ws2812

	Debug configuration
		Use UARTHS to debug(pin9, pin10(, UART2 to REPL(pin4, pin5)

SPIFFS Configuration
	(0x300000) SPIFFS size
	(0xD00000) Start address of SPIFFS
	(0x1000) Erease size of SPIFFS, see the datasheet
	(0x20000) Logical block size of SPIFFS(default 32*4k)
	(0x1000) Logical page size of SPIFFS(default 4k(SPIFFS_LOGICAL_BLOCK_SIZE/32))
	(128) Set SPIFFS Maximum Name Length
	[* ] Enable SPIFFS Filesystem Magic
	[* ]     Enable SPIFFS Filesystem Length Magic
	(0) Size of per-file metadata field
    	Debug Configuration  --->
		[ ] Enable general SPIFFS debug
		[ ] Enable SPIFFS API debug
		[ ] Enable SPIFFS Garbage Cleaner debug
		[ ] Enable SPIFFS Cache debug
		[ ] Enable SPIFFS Filesystem Check debug

	SPIFFS Cache Configuration
		[* ]     Enable SPIFFS Write Caching
		[ ]     Enable SPIFFS Cache Statistics


<br>
# ●KPUで顔検出
プログラムはMaixPy DOCから転載

```python
task = kpu.load(0x300000)    #フラッシュから顔認識モデルを読み込み
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658,5.155437, 6.92275, 6.718375, 9.01025)    #アンカーデータ用バウンディングボックス
a = kpu.init_yolo2(task, 0.5, 0.3, 5, anchor)    #yolo2ネットワークに初期化パラメータを渡す

while(True):
    clock.tick()
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img)    #検出処理
    if code:
        for i in code:
            print(i)
            a = img.draw_rectangle(i.rect())
    a = lcd.display(img)
    print(clock.fps())
a = kpu.deinit(task)
```

<br>
https://maixpy.sipeed.com/en/
https://docs.openmv.io/index.html


![uploading-0]()