Try   HackMD

MaixDuino 入門使用 MaixPy

MaixDuino 雖然長的跟 Arduino 很像, 不過除非你是用 Arduino IDE 寫程式, 否則就得瞭解一下它的結構, 不然在使用 MaixPy 寫程式時, 可能會連如何點亮 LED 都做不到, 以下就一步步介紹相關的知識吧。

安裝韌體

  1. 安裝韌體燒錄軟體 KFlash
  2. 安裝 MaixPy 韌體

MaixDuino 與 Arduino 腳位對應

MaixDuino 使用 K210 晶片, K210 晶片外部有 48 個 IO 腳位, 編號從 IO0~IO47, 這些腳位有一些已經被相機、LCD 等用掉, 實際上在 MaixDuino 上接出來的腳位可參考以下的接腳圖或是對照表:

Arduino 腳位 K210 IO 腳編號 board_info 中的名稱
RX←0 IO4 K210_RX
TX→1 IO5 K210_TX
2 IO21
3 IO22
4 IO23
5 IO24
6 IO32
7 IO15
8 IO14 LED_G
9 IO13 LED_R
10 IO12 LED_B
11 IO11
12 IO10
13 IO3 JTAG_TDO
A0 IO33 ADC1_CH5
A1 IO32 ADC1_CH4
A2 IO35 ADC1_CH7
A3 IO34 ADC1_CH6
A4 IO39 ADC1_CH3
A5 IO36 ADC1_CH0
SCL IO30 I2C_SCL
SDA IO31 I2C_SDA

K210 的可程式化 IO 功能

K210 的這些 IO 腳位並不是固定功能, 每一個腳位都可以透過程式設定成不同的功用, 例如 I2C 就可以任意選用你喜歡的腳位, 而不一定要固定使用特定的腳位。

在 MaixPy 中, 要使用這些腳位, 必須要先幫 IO 腳位註冊功能, 才能使用, 例如:

from fpioa_manager import fm, board_info

fm.register(        # 註冊 IO 腳位的功能
    14,             # IO 腳位編號
    fm.fpioa.GPIO0  # IO 腳位功能
)

請注意, 這裡講的腳位編號都是 K210 晶片的 IO 腳位編號, 不是 MaixDuino 板子上的腳位編號。像是上面例子中的 14 就是指 IO14, 如果你查一下上面的腳位對應圖或是表格, 就可以知道這個腳位實際上就是 MaixDuino 板子上的 8 號腳位。

fpioa_manager 是 MaixPy 中管理 IO 腳位功能的模組, 內含 fmboard_info 兩個物件, 其中:

  • fm 就是實際管理 IO 腳位功能的物件, 透過 register 方法為特定腳位依據 fm.fpioa 中定義的功能名稱來註冊, 例如 fm.fpioa.GPIO0 就是第 0 個 GPIO 功能, 可用的名稱清單可參考官方文件, 我也在文末的附錄列出來。

  • board_info 是與特定控制板相關的資訊, 例如對於 MaixDuino 控制板來說, 就定義了 board_info.LED_G 為 14, 實際上這個腳位並沒有接 LED 燈, 但是寫程式時你可以用 14 或是 board_info.LED_G 來代表 IO14 這個腳位。

數位輸出 (digital out)

瞭解腳位的可程式化功能後, 我們就可以嘗試使用數位輸出了。剛剛註冊腳位的只是功能的類別, 例如剛剛只說要用 IO14 當 GPIO, 但沒有說明到底是輸出還是輸入, 實際使用時還要建立 GPIO 物件, 並註明輸出還是輸入, 例如:

from fpioa_manager import fm, board_info
from Maix import GPIO
import time

fm.register(        # 註冊 IO 腳位的功能
    14,             # IO 腳位編號
    fm.fpioa.GPIO0  # IO 腳位功能
)
led = GPIO(         # 建立 GPIO 物件
    GPIO.GPIO0,     # 要使用的 GPIO 腳位
    GPIO.OUT        # 輸出入方向
)

請注意, 在建立 GPIO 物件時, 不是用 IO 腳位編號, 而是用註冊時的功能名稱, 以 GPIO 來說, 可以用的功能名稱有:

  • GPIO0~GPIO7
  • GPIOHS0~GPIOHS26:GPIOHS27~GPIOHS31 已經被用掉了, 程式中不能使用。名稱中的 HSHigh Speed 的意思, 與 GPIO0~7 的功能有些差異, 有需要時會再說明。

建立好 GPIO 物件, 就可以用該物件進行數位輸出了, 例如, 以下就是常見的閃爍 LED 燈的程式:

01_digital_out.py

from fpioa_manager import fm, board_info from Maix import GPIO import time fm.register( # 註冊 IO 腳位的功能 14, # IO 腳位編號 fm.fpioa.GPIO0 # IO 腳位功能 ) led = GPIO( # 建立 GPIO 物件 GPIO.GPIO0, # 要使用的 GPIO 腳位 GPIO.OUT # 輸出入方向 ) for runs in range(20): led.value(1 - led.value()) time.sleep(0.5)

value() 是使用 1 和 0 代表高電位和低電位。

數位輸入 (digital input)

接著我們就可以嘗試數位輸入了, 例如:

02_digital_input.py
from fpioa_manager import fm, board_info from Maix import GPIO import time fm.register( # 註冊 IO 腳位的功能 16, # IO 腳位編號, IO16 接到 BOOT 鈕 fm.fpioa.GPIO0 # IO 腳位功能 ) btn = GPIO( # 建立 GPIO 物件 GPIO.GPIO0, # 要使用的 GPIO 腳位 GPIO.IN, # 輸出入方向 GPIO.PULL_UP # BOOT 按鈕有一腳是接地 ) while True: print(btn.value()) time.sleep(0.1)

這裡我們使用 IO16 腳, 如果查看前面的腳位對照圖, 在右下角會看到控制板上的 BOOT 鈕有一腳是接到 IO16, 而根據電路圖, BOOT 鈕的另外一隻腳是接地的, 因此我們在建立 GPIO 物件時, 指定 PULL_UP 模式, 可用的模式還有 PULL_NONEPULL_DOWN

用中斷偵測數位輸入狀態

除了像是上例輪詢方式查看輸入狀態外, 也可以透過中斷的方式, 在輸入狀態變化的時候引發中斷, 例如:

03_digital_in_IRQ.py
from fpioa_manager import fm, board_info from Maix import GPIO import time def bootPressed(pin): # 參數是產生中斷的 GPIO 物件 print(pin.value()) fm.register( # 註冊 IO 腳位的功能 16, # IO 腳位編號, IO16 接到 BOOT 鈕 fm.fpioa.GPIOHS0 # IO 腳位功能 ) btn = GPIO( # 建立 GPIO 物件 GPIO.GPIOHS0, # 要使用的 GPIO 腳位 GPIO.IN, # 輸出入方向 GPIO.PULL_UP # BOOT 按鈕有一腳是接地 ) btn.irq( bootPressed, # 中斷處理函式 GPIO.IRQ_BOTH, # 中斷時機, RISING/FALLING/BOTH ) while True: pass

第 5 列先定義中斷處理函式, 中斷處理函式的參數只有 1 個, 就是引發中斷的 GPIO 物件本身。然後就可以使用 GPIO 的 irq 方法設定中斷。

官方文件中說中斷處理函式有 2 個參數, 1 個是 GPIO 物件, 第 2 個是 IO 腳位編號, 但實際跑起來, 會發生錯誤, 錯誤訊息說只有 1 個參數:

>>> %Run -c $EDITOR_CONTENT
TypeError: bootPressed() takes 2 positional arguments but 1 were given

要特別注意的是只有 GPIOHS 的功能提供中斷功能, 所以這個範例使用的是 GPIOHS0, 而不是沿用前一個範例的 GPIO0。

類比輸入 (ADC)

MaixDuino 是透過 ESP32 讀取 ADC, 可參考官方的範例檔。不過目前測試似乎有問題, 都會得到以下錯誤:

[MaixPy]: esp32 read adc failed!

根據論壇的說明, 這似乎是 ESP32 上的韌體有問題, 以後再測試看看。

如果使用特定版本的 MaixPy 及 ESP32 韌體, 就可正常運作。請使用 kflash 工具燒錄下載後解開的 mainxpy.bin:

接著再使用 flash_download_tools 將下載解開的 NINA_W102-1.3.1.bin 燒錄到 MaixDuino 上的 ESP32:

請記得 ESP32 是另一個序列埠。完成後, 就可以使用以下程式測試 ADC (MaixDuino 上的 K210 晶片和 ESP32 之間使用 SPI 傳輸資料, 相關接腳可參考電路圖):

import network
import utime
from Maix import GPIO
from fpioa_manager import *

#iomap at MaixDuino
fm.register(25,fm.fpioa.GPIOHS10)#cs
fm.register(8,fm.fpioa.GPIOHS11)#rst
fm.register(9,fm.fpioa.GPIOHS12)#rdy
fm.register(28,fm.fpioa.GPIOHS13)#mosi
fm.register(26,fm.fpioa.GPIOHS14)#miso
fm.register(27,fm.fpioa.GPIOHS15)#sclk

nic = network.ESP32_SPI(cs=fm.fpioa.GPIOHS10,rst=fm.fpioa.GPIOHS11,rdy=fm.fpioa.GPIOHS12,
mosi=fm.fpioa.GPIOHS13,miso=fm.fpioa.GPIOHS14,sclk=fm.fpioa.GPIOHS15)

adc = nic.adc()
print(adc)

adc() 會以 tuple 形式傳回 6 個 ADC 接腳的值:

>>> %Run -c $EDITOR_CONTENT
ESP32_SPI init over
(839, 1169, 192, 16, 0, 0)

類比輸出 (PWM)

K210 的 PWM 輸出是依附在計時器下, 因此要使用 PWM 必須先建立計時器物件:

from fpioa_manager import fm, board_info
from machine import Timer,PWM
import time

pwm_timer = Timer(       # 建立計時器
    Timer.TIMER0,        # 計時器編號 0~2 
    Timer.CHANNEL0,      # 計時器通道 0~3
    mode=Timer.MODE_PWM  # 計時器模式
)

搭配 PWM 用的計時器, 模式一定要設定為 Timer.MODE_PWM。接著就可以建立 PWM 物件:

led = PWM(               # 建立 PWM 物件
    pwm_timer,           # 要使用的計時器
    freq=10000000,       # 頻率
    duty=0,              # 預設的工作週期 (0~100)
    pin=14,              # 要用的 IO 腳位
    enable=True          # 是否立即啟用, 預設為 True
)

其中 IO 腳位並不需要像是數位輸出一樣要先設定功能。完整的使用 PWM 來做呼吸燈的程式如下:

04_pwm.py
from fpioa_manager import fm, board_info from machine import Timer,PWM import time pwm_timer = Timer( # 建立計時器 Timer.TIMER0, # 計時器編號 0~2 Timer.CHANNEL0, # 計時器通道 0~3 mode=Timer.MODE_PWM # 計時器模式 ) led = PWM( # 建立 PWM 物件 pwm_timer, # 要使用的計時器 freq=10000000, # 頻率 duty=0, # 預設的工作週期 (0~100) pin=14, # 要用的 IO 腳位 enable=True # 是否立即啟用, 預設為 True ) duty=0 dir = True while True: if dir: duty += 5 else: duty -= 5 if duty>100: duty = 100 dir = False elif duty<0: duty = 0 dir = True time.sleep(0.05) led.duty(duty) # 設定工作週期

實際測試頻率最大可設到 416000016, 但是超過 30000000 以上呼吸燈就會變得呆滯, 漸變的效果就不明顯。

附錄

官方資源

MaixDuino 有些資源不好找, 特別整理如下:

腳位功能名稱列表

功能名稱 簡述
JTAG_TCLK JTAG Test Clock
JTAG_TDI JTAG Test Data In
JTAG_TMS JTAG Test Mode Select
JTAG_TDO JTAG Test Data Out
SPI0_D0 SPI0 Data 0
SPI0_D1 SPI0 Data 1
SPI0_D2 SPI0 Data 2
SPI0_D3 SPI0 Data 3
SPI0_D4 SPI0 Data 4
SPI0_D5 SPI0 Data 5
SPI0_D6 SPI0 Data 6
SPI0_D7 SPI0 Data 7
SPI0_SS0 SPI0 Chip Select 0
SPI0_SS1 SPI0 Chip Select 1
SPI0_SS2 SPI0 Chip Select 2
SPI0_SS3 SPI0 Chip Select 3
SPI0_ARB SPI0 Arbitration
SPI0_SCLK SPI0 Serial Clock
UARTHS_RX UART High speed Receiver
UARTHS_TX UART High speed Transmitter
RESV6 Reserved function
RESV7 Reserved function
CLK_SPI1 Clock SPI1
CLK_I2C1 Clock I2C1
GPIOHS0 GPIO High speed 0
GPIOHS1 GPIO High speed 1
GPIOHS2 GPIO High speed 2
GPIOHS3 GPIO High speed 3
GPIOHS4 GPIO High speed 4
GPIOHS5 GPIO High speed 5
GPIOHS6 GPIO High speed 6
GPIOHS7 GPIO High speed 7
GPIOHS8 GPIO High speed 8
GPIOHS9 GPIO High speed 9
GPIOHS10 GPIO High speed 10
GPIOHS11 GPIO High speed 11
GPIOHS12 GPIO High speed 12
GPIOHS13 GPIO High speed 13
GPIOHS14 GPIO High speed 14
GPIOHS15 GPIO High speed 15
GPIOHS16 GPIO High speed 16
GPIOHS17 GPIO High speed 17
GPIOHS18 GPIO High speed 18
GPIOHS19 GPIO High speed 19
GPIOHS20 GPIO High speed 20
GPIOHS21 GPIO High speed 21
GPIOHS22 GPIO High speed 22
GPIOHS23 GPIO High speed 23
GPIOHS24 GPIO High speed 24
GPIOHS25 GPIO High speed 25
GPIOHS26 GPIO High speed 26
GPIOHS27 GPIO High speed 27
GPIOHS28 GPIO High speed 28
GPIOHS29 GPIO High speed 29
GPIOHS30 GPIO High speed 30
GPIOHS31 GPIO High speed 31
GPIO0 GPIO pin 0
GPIO1 GPIO pin 1
GPIO2 GPIO pin 2
GPIO3 GPIO pin 3
GPIO4 GPIO pin 4
GPIO5 GPIO pin 5
GPIO6 GPIO pin 6
GPIO7 GPIO pin 7
UART1_RX UART1 Receiver
UART1_TX UART1 Transmitter
UART2_RX UART2 Receiver
UART2_TX UART2 Transmitter
UART3_RX UART3 Receiver
UART3_TX UART3 Transmitter
SPI1_D0 SPI1 Data 0
SPI1_D1 SPI1 Data 1
SPI1_D2 SPI1 Data 2
SPI1_D3 SPI1 Data 3
SPI1_D4 SPI1 Data 4
SPI1_D5 SPI1 Data 5
SPI1_D6 SPI1 Data 6
SPI1_D7 SPI1 Data 7
SPI1_SS0 SPI1 Chip Select 0
SPI1_SS1 SPI1 Chip Select 1
SPI1_SS2 SPI1 Chip Select 2
SPI1_SS3 SPI1 Chip Select 3
SPI1_ARB SPI1 Arbitration
SPI1_SCLK SPI1 Serial Clock
SPI_SLAVE_D0 SPI Slave Data 0
SPI_SLAVE_SS SPI Slave Select
SPI_SLAVE_SCLK SPI Slave Serial Clock
I2S0_MCLK I2S0 Master Clock
I2S0_SCLK I2S0 Serial Clock(BCLK)
I2S0_WS I2S0 Word Select(LRCLK)
I2S0_IN_D0 I2S0 Serial Data Input 0
I2S0_IN_D1 I2S0 Serial Data Input 1
I2S0_IN_D2 I2S0 Serial Data Input 2
I2S0_IN_D3 I2S0 Serial Data Input 3
I2S0_OUT_D0 I2S0 Serial Data Output 0
I2S0_OUT_D1 I2S0 Serial Data Output 1
I2S0_OUT_D2 I2S0 Serial Data Output 2
I2S0_OUT_D3 I2S0 Serial Data Output 3
I2S1_MCLK I2S1 Master Clock
I2S1_SCLK I2S1 Serial Clock(BCLK)
I2S1_WS I2S1 Word Select(LRCLK)
I2S1_IN_D0 I2S1 Serial Data Input 0
I2S1_IN_D1 I2S1 Serial Data Input 1
I2S1_IN_D2 I2S1 Serial Data Input 2
I2S1_IN_D3 I2S1 Serial Data Input 3
I2S1_OUT_D0 I2S1 Serial Data Output 0
I2S1_OUT_D1 I2S1 Serial Data Output 1
I2S1_OUT_D2 I2S1 Serial Data Output 2
I2S1_OUT_D3 I2S1 Serial Data Output 3
I2S2_MCLK I2S2 Master Clock
I2S2_SCLK I2S2 Serial Clock(BCLK)
I2S2_WS I2S2 Word Select(LRCLK)
I2S2_IN_D0 I2S2 Serial Data Input 0
I2S2_IN_D1 I2S2 Serial Data Input 1
I2S2_IN_D2 I2S2 Serial Data Input 2
I2S2_IN_D3 I2S2 Serial Data Input 3
I2S2_OUT_D0 I2S2 Serial Data Output 0
I2S2_OUT_D1 I2S2 Serial Data Output 1
I2S2_OUT_D2 I2S2 Serial Data Output 2
I2S2_OUT_D3 I2S2 Serial Data Output 3
RESV0 Reserved function
RESV1 Reserved function
RESV2 Reserved function
RESV3 Reserved function
RESV4 Reserved function
RESV5 Reserved function
I2C0_SCLK I2C0 Serial Clock
I2C0_SDA I2C0 Serial Data
I2C1_SCLK I2C1 Serial Clock
I2C1_SDA I2C1 Serial Data
I2C2_SCLK I2C2 Serial Clock
I2C2_SDA I2C2 Serial Data
CMOS_XCLK DVP System Clock
CMOS_RST DVP System Reset
CMOS_PWDN DVP Power Down Mode
CMOS_VSYNC DVP Vertical Sync
CMOS_HREF DVP Horizontal Reference output
CMOS_PCLK Pixel Clock
CMOS_D0 Data Bit 0
CMOS_D1 Data Bit 1
CMOS_D2 Data Bit 2
CMOS_D3 Data Bit 3
CMOS_D4 Data Bit 4
CMOS_D5 Data Bit 5
CMOS_D6 Data Bit 6
CMOS_D7 Data Bit 7
SCCB_SCLK SCCB Serial Clock
SCCB_SDA SCCB Serial Data
UART1_CTS UART1 Clear To Send
UART1_DSR UART1 Data Set Ready
UART1_DCD UART1 Data Carrier Detect
UART1_RI UART1 Ring Indicator
UART1_SIR_IN UART1 Serial Infrared Input
UART1_DTR UART1 Data Terminal Ready
UART1_RTS UART1 Request To Send
UART1_OUT2 UART1 User-designated Output 2
UART1_OUT1 UART1 User-designated Output 1
UART1_SIR_OUT UART1 Serial Infrared Output
UART1_BAUD UART1 Transmit Clock Output
UART1_RE UART1 Receiver Output Enable
UART1_DE UART1 Driver Output Enable
UART1_RS485_EN UART1 RS485 Enable
UART2_CTS UART2 Clear To Send
UART2_DSR UART2 Data Set Ready
UART2_DCD UART2 Data Carrier Detect
UART2_RI UART2 Ring Indicator
UART2_SIR_IN UART2 Serial Infrared Input
UART2_DTR UART2 Data Terminal Ready
UART2_RTS UART2 Request To Send
UART2_OUT2 UART2 User-designated Output 2
UART2_OUT1 UART2 User-designated Output 1
UART2_SIR_OUT UART2 Serial Infrared Output
UART2_BAUD UART2 Transmit Clock Output
UART2_RE UART2 Receiver Output Enable
UART2_DE UART2 Driver Output Enable
UART2_RS485_EN UART2 RS485 Enable
UART3_CTS UART3 Clear To Send
UART3_DSR UART3 Data Set Ready
UART3_DCD UART3 Data Carrier Detect
UART3_RI UART3 Ring Indicator
UART3_SIR_IN UART3 Serial Infrared Input
UART3_DTR UART3 Data Terminal Ready
UART3_RTS UART3 Request To Send
UART3_OUT2 UART3 User-designated Output 2
UART3_OUT1 UART3 User-designated Output 1
UART3_SIR_OUT UART3 Serial Infrared Output
UART3_BAUD UART3 Transmit Clock Output
UART3_RE UART3 Receiver Output Enable
UART3_DE UART3 Driver Output Enable
UART3_RS485_EN UART3 RS485 Enable
TIMER0_TOGGLE1 TIMER0 Toggle Output 1
TIMER0_TOGGLE2 TIMER0 Toggle Output 2
TIMER0_TOGGLE3 TIMER0 Toggle Output 3
TIMER0_TOGGLE4 TIMER0 Toggle Output 4
TIMER1_TOGGLE1 TIMER1 Toggle Output 1
TIMER1_TOGGLE2 TIMER1 Toggle Output 2
TIMER1_TOGGLE3 TIMER1 Toggle Output 3
TIMER1_TOGGLE4 TIMER1 Toggle Output 4
TIMER2_TOGGLE1 TIMER2 Toggle Output 1
TIMER2_TOGGLE2 TIMER2 Toggle Output 2
TIMER2_TOGGLE3 TIMER2 Toggle Output 3
TIMER2_TOGGLE4 TIMER2 Toggle Output 4
CLK_SPI2 Clock SPI2
CLK_I2C2 Clock I2C2