MaixCubeのハードウェア調査

# MaixCubeのハードウェアについて

回路図 https://dl.sipeed.com/MAIX/HDK/Sipeed-Maix-Cube/Maix-Cube-2757/Maix-Cube-2757(Schematic).pdf

# 前面のLED

２個ついているが、同時に点灯する。レバースイッチの位置でR、G、Bそれぞれに点灯させるサンプル

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

LED_R = 13
LED_G = 12
LED_B = 14

fm.register(LED_R, fm.fpioa.GPIOHS0, force=True)
fm.register(LED_G, fm.fpioa.GPIOHS1, force=True)
fm.register(LED_B, fm.fpioa.GPIOHS2, force=True)
fm.register(board_info.PIN11, fm.fpioa.GPIO0, force=True)
fm.register(board_info.PIN10, fm.fpioa.GPIO1, force=True)
fm.register(board_info.BOOT_KEY, fm.fpioa.GPIO2, force=True)

led_r = GPIO(GPIO.GPIOHS0, GPIO.OUT)
led_g = GPIO(GPIO.GPIOHS1, GPIO.OUT)
led_b = GPIO(GPIO.GPIOHS2, GPIO.OUT)
input0 = GPIO(GPIO.GPIO0, GPIO.IN)
input1 = GPIO(GPIO.GPIO1, GPIO.IN)
input2 = GPIO(GPIO.GPIO2, GPIO.IN)

i = 0
status = 0
while i<20:
    print("-----input0:", input0.value())
    print("-----input1:", input1.value())
    print("-----input2:", input2.value())
    led_r.value(input0.value())
    led_g.value(input1.value())
    led_b.value(input2.value())
    i+=1
    utime.sleep_ms(500)

fm.unregister(board_info.LED_R, fm.fpioa.GPIOHS0)
fm.unregister(board_info.LED_G, fm.fpioa.GPIOHS1)
fm.unregister(board_info.LED_B, fm.fpioa.GPIOHS2)
fm.unregister(board_info.PIN11, fm.fpioa.GPIO0)
fm.unregister(board_info.PIN10, fm.fpioa.GPIO1)
fm.unregister(board_info.BOOT_KEY, fm.fpioa.GPIO2)

```

# 背面のフラッシュLED

かなり明るい

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

WLED_EN = 32

fm.register(WLED_EN, fm.fpioa.GPIOHS0, force=True)
fm.register(board_info.PIN10, fm.fpioa.GPIO0, force=True)

wled_en = GPIO(GPIO.GPIOHS0, GPIO.OUT)
input = GPIO(GPIO.GPIO0, GPIO.IN)

i = 0
status = 0
while i<20:
    print("-----input:", input.value())
    wled_en.value(input.value())
    i+=1
    utime.sleep_ms(500)

fm.unregister(WLED_EN, fm.fpioa.GPIOHS0)
fm.unregister(board_info.PIN10, fm.fpioa.GPIO0)

```

# ３軸加速度センサー (MSA301)

```python
import time
from machine import I2C

MSA301_CMD  = 0
MSA301_DATA = 1
MSA301_ADDR = 0x26

def acc_init(i2c):
    partID = i2c.readfrom_mem(MSA301_ADDR, 0x01, 1, mem_size=8)
    if (partID != b'\x13'):
        print("MSA301 not found")
    i2c.writeto_mem(MSA301_ADDR, 0x11, 0x0E, mem_size=8) # power_mode: normal

def acc_read(i2c):
    accX = i2c.readfrom_mem(MSA301_ADDR, 0x02, 2, mem_size=8)
    accY = i2c.readfrom_mem(MSA301_ADDR, 0x04, 2, mem_size=8)
    accZ = i2c.readfrom_mem(MSA301_ADDR, 0x06, 2, mem_size=8)
    print("x=", int.from_bytes(accX, 'little') / 4,
        "y=", int.from_bytes(accY, 'little') / 4,
        "z=", int.from_bytes(accZ, 'little') / 4)


i2c = I2C(I2C.I2C0, mode=I2C.MODE_MASTER, freq=400000, scl=30, sda=31, addr_size=7)

time.sleep(1)
acc_init(i2c)
while True:
    acc_read(i2c)
    time.sleep(1)

```

# 電源管理 (AXP173)

takeruさんの[コード](https://gist.github.com/takeru/5fe5e6666e922f0917f371aee8aefd5e)を参考にさせて頂きました。


```python
# M5StickVの電源管理方法
# https://github.com/sipeed/MaixPy/blob/master/components/boards/m5stick/src/m5stick.c
# https://github.com/sipeed/MaixPy/blob/master/projects/maixpy_m5stickv/builtin_py/pmu.py
# https://pdf1.alldatasheet.com/datasheet-pdf/view/757469/ETC2/AXP173.html

# 参考: M5StickC https://github.com/m5stack/M5StickC/blob/master/src/AXP173.cpp

from machine import I2C, Timer
import machine
import lcd
#import pmu

class PMUError(Exception):
    pass
 
class NotFoundError(PMUError):
    pass

class OutOfRange(PMUError):
    pass

def __chkPwrKeyWaitForSleep__(timer):
    global __pmuI2CDEV__, __preButPressed__ #<- Do not do this :(

    __pmuI2CDEV__.writeto(52, bytes([0x46]))
    pek_stu = (__pmuI2CDEV__.readfrom(52, 1))[0]
    __pmuI2CDEV__.writeto_mem(52, 0x46, 0xFF, mem_size=8) #Clear IRQ

    #Prevent loop in restart, wait for release
    if __preButPressed__ == -1 and ((pek_stu & (0x01 << 1)) or (pek_stu & 0x01)):
        return
    
    if __preButPressed__ == -1 and  ((pek_stu & (0x01 << 1)) == False and (pek_stu & 0x01) == False):
        __preButPressed__ = 0

    if (pek_stu & 0x01):
        __pmuI2CDEV__.writeto_mem(52, 0x31, 0x0F, mem_size=8)  #Enable Sleep Mode
        __pmuI2CDEV__.writeto_mem(52, 0x12, 0x00, mem_size=8)  #Turn off other power source
    
    if (pek_stu & (0x01 << 1)):
        machine.reset()

class axp173():
    def __init__(self, i2cDev=None):
        if i2cDev == None:
            try:
                self.i2cDev = I2C(I2C.I2C0, freq=400000, scl=30, sda=31)
            except:
                raise PMUError("Unable to init I2C0 as Master")
        else:
            self.i2cDev = i2cDev
        
        self.axp173Addr = 52

        global __pmuI2CDEV__, __preButPressed__
        __pmuI2CDEV__ = self.i2cDev
        __preButPressed__ = -1
        
        scanList = self.i2cDev.scan()
        if self.axp173Addr not in scanList:
            raise NotFoundError

    def __writeReg(self, regAddr, value):
        self.i2cDev.writeto_mem(self.axp173Addr, regAddr, value, mem_size=8)

    def __readReg(self, regAddr):
        self.i2cDev.writeto(self.axp173Addr, bytes([regAddr]))
        return (self.i2cDev.readfrom(self.axp173Addr, 1))[0]

    def enableADCs(self, enable):
        if enable == True:
            self.__writeReg(0x82, 0xFF)
        else:
            self.__writeReg(0x82, 0x00)

    def enableCoulombCounter(self, enable):
        if enable == True:
            self.__writeReg(0xB8, 0x80)
        else:
            self.__writeReg(0xB8, 0x00)
    
    def stopCoulombCounter(self):
        self.__writeReg(0xB8, 0xC0)
    
    def clearCoulombCounter(self):
        self.__writeReg(0xB8, 0xA0)

    def __getCoulombChargeData(self):
        CoulombCounter_LSB = self.__readReg(0xB0)
        CoulombCounter_B1 = self.__readReg(0xB1)
        CoulombCounter_B2 = self.__readReg(0xB2)
        CoulombCounter_MSB = self.__readReg(0xB3)

        return ((CoulombCounter_LSB << 24) + (CoulombCounter_B1 << 16) + \
            (CoulombCounter_B2 << 8) + CoulombCounter_MSB)
    
    def __getCoulombDischargeData(self):
        CoulombCounter_LSB = self.__readReg(0xB4)
        CoulombCounter_B1 = self.__readReg(0xB5)
        CoulombCounter_B2 = self.__readReg(0xB6)
        CoulombCounter_MSB = self.__readReg(0xB7)

        return ((CoulombCounter_LSB << 24) + (CoulombCounter_B1 << 16) + \
            (CoulombCounter_B2 << 8) + CoulombCounter_MSB)
    
    def getCoulombCounterData(self):
        return 65536 * 0.5 * (self.__getCoulombChargeData() -\
             self.__getCoulombDischargeData) / 3600.0 / 25.0
    
    def getVbatVoltage(self):
        Vbat_LSB = self.__readReg(0x78)
        Vbat_MSB = self.__readReg(0x79)

        return ((Vbat_LSB << 4) + Vbat_MSB) * 1.1 #AXP173-DS PG26 1.1mV/div
        
    def getUSBVoltage(self):
        Vin_LSB = self.__readReg(0x56)
        Vin_MSB = self.__readReg(0x57)

        return ((Vin_LSB << 4) + Vin_MSB) * 1.7 #AXP173-DS PG26 1.7mV/div
    
    def getUSBInputCurrent(self):
        Iin_LSB = self.__readReg(0x58)
        Iin_MSB = self.__readReg(0x59)

        return ((Iin_LSB << 4) + Iin_MSB) * 0.625 #AXP173-DS PG26 0.625mA/div
    
    def getConnextVoltage(self):
        Vcnx_LSB = self.__readReg(0x5A)
        Vcnx_MSB = self.__readReg(0x5B)

        return ((Vcnx_LSB << 4) + Vcnx_MSB) * 1.7 #AXP173-DS PG26 1.7mV/div
    
    def getConnextInputCurrent(self):
        IinCnx_LSB = self.__readReg(0x5C)
        IinCnx_MSB = self.__readReg(0x5D)

        return ((IinCnx_LSB << 4) + IinCnx_MSB) * 0.625 #AXP173-DS PG26 0.625mA/div

    def getBatteryChargeCurrent(self):
        Ichg_LSB = self.__readReg(0x7A)
        Ichg_MSB = self.__readReg(0x7B)

        return ((Ichg_LSB << 5) + Ichg_MSB) * 0.5 #AXP173-DS PG27 0.5mA/div

    def getBatteryDischargeCurrent(self):
        Idcg_LSB = self.__readReg(0x7C)
        Idcg_MSB = self.__readReg(0x7D)

        return ((Idcg_LSB << 5) + Idcg_MSB) * 0.5 #AXP173-DS PG27 0.5mA/div

    def getBatteryInstantWatts(self):
        Iinswat_LSB = self.__readReg(0x70)
        Iinswat_B2 = self.__readReg(0x71)
        Iinswat_MSB = self.__readReg(0x72)

        #AXP173-DS PG32 0.5mA*1.1mV/1000/mW
        return ((Iinswat_LSB << 16) + (Iinswat_B2 << 8) + Iinswat_MSB) * 1.1 * 0.5 / 1000 
 
    def getTemperature(self):
        Temp_LSB = self.__readReg(0x5E)
        Temp_MSB = self.__readReg(0x5F)

        #AXP173-DS PG26 0.1degC/div -144.7degC Biased
        return (((Temp_LSB << 4) + Temp_MSB) * 0.1) - 144.7 
    
    def setK210Vcore(self, vol):
        if vol > 1.05 or vol < 0.8:
            raise OutOfRange("Voltage is invaild for K210")
        DCDC2Steps = int((vol - 0.7) * 1000 / 25)
        self.__writeReg(0x23, DCDC2Steps)
    
    def getKeyStuatus(self): # -1: NoPress, 1: ShortPress, 2:LongPress
        but_stu = self.__readReg(0x46)
        if (but_stu & (0x1 << 1)):
            return 1
        else:
            if (but_stu & (0x1 << 0)):
                return 2
            else:
                return -1
    
    def setEnterSleepMode(self):
        self.__writeReg(0x31, 0x0F)  #Enable Sleep Mode
        self.__writeReg(0x12, 0x00)  #Turn off other power source

    def enablePMICSleepMode(self, enable):
        if enable == True:
            self.__writeReg(0x36, 0x27) #Turnoff PEK Overtime Shutdown
            self.__writeReg(0x46, 0xFF) #Clear the interrupts

            self.butChkTimer = Timer(Timer.TIMER2, Timer.CHANNEL0, mode=Timer.MODE_PERIODIC, period=500, callback=__chkPwrKeyWaitForSleep__)
        else:
            self.__writeReg(0x36, 0x6C) #Set to default
            try:
                self.butChkTimer.stop()
                del self.butChkTimer
            except:
                pass


class App():
    def main(self):
        self.setup()
        while(True):
            self.loop()

    def setup(self):
        lcd.init(freq=15000000, type=2)
        lcd.rotation(2)

        self.counter = 0
        self.axp173 = axp173()

        self.axp173.enableADCs(True)
        self.axp173.enableCoulombCounter(True)

        self.printRegs()

        # LCDの明るさ
#        self.axp173.__writeReg(0x91, 0x80) # 上位4bit 7から15
#        # self.axp173.setScreenBrightness(7) # <- 0x28じゃなくて0x91が正しい https://github.com/sipeed/MaixPy/pull/153

        # バッテリ充電ON/OFF https://twitter.com/michan06/status/1168104180445106180
        reg0x33 = self.axp173.__readReg(0x33)
        reg0x33 |= (1<<7) # ON
        #reg0x33 &= ~(1<<7) # OFF
        self.axp173.__writeReg(0x33, reg0x33)



        # REG 33H：充電制御1
        # デフォルト：C8H
        #
        # bit7    充電機能により、内部および外部チャネルを含む制御ビットが可能 0：オフ、1：オン
        # bit6:5  充電目標電圧設定
        #           00：4.1V;
        #           01：4.15V;
        #           10：4.2V;
        #           11：4.36V
        # bit4    充電終了電流設定
        #           0：充電電流が設定値の10％未満になったら充電を終了
        #           1：充電電流が設定値の15％未満になったら充電を終了します
        # bit3-0  内部パス充電電流設定 default=1000(780mA)
        #           0000： 100 mA;  0001： 190 mA;  0010： 280 mA;  0011： 360 mA;
        #           0100： 450 mA;  0101： 550 mA;  0110： 630 mA;  0111： 700 mA;
        #           1000： 780 mA;  1001： 880 mA;  1010： 960 mA;  1011：1000 mA;
        #           1100：1080 mA;  1101：1160 mA;  1110：1240 mA;  1111：1320 mA;

        # 0xC0(0b11000000) なら「充電オン、充電目標電圧=4.2V、充電終了電流=10%、充電電流=100mA」
        reg0x33 = self.axp173.__readReg(0x33)
        reg0x33 = (reg0x33 & 0xF0) | 0x01 # 200mA
        self.axp173.__writeReg(0x33, reg0x33)

        # REG 34H：充電制御2
        # デフォルト：41H
        #  7    プリチャージタイムアウト設定Bit1
        #         00：30分; 01：40分;
        #         10：50分; 11：60分
        #  6    プリチャージタイムアウト設定Bit0
        #  5-3  外部パス充電電流設定範囲300-1000mA、100mA /ステップ、デフォルト300mA
        #  2    充電中の外部パスイネーブル設定0：オフ; 1：オープン
        #  1    定電流モードでのタイムアウト設定Bit1   00：7時間; 01：8時間;  10：9時間;   11：10時間RW0
        #  0    定電流モードでのタイムアウト設定Bit0


        # REG 01H：電源装置の動作モードと充電状態の表示
        # 0x70なら「温度超過なし、充電中、バッテリーあり、バッテリーアクティベーションモードに入っていない、実際の充電電流は予想電流と等しい、モードA」
        #
        # bit7    AXP173が過熱しているかどうかを示します    0：温度超過なし; 1：温度超過
        # bit6    充電表示    0：充電していない、または充電が完了している、   1：充電中
        # bit5    バッテリーの存在表示   0：バッテリーはAXP173に接続されていません;    1：バッテリーはAXP173に接続されていま
        # bit4    予約済み、変更不可
        # bit3    バッテリーがアクティブモードかどうかを示します   0：バッテリーアクティベーションモードに入っていない;    1：バッテリーアクティベーションモードに入っている
        # bit2    充電電流が目的の電流よりも小さいかどうかを示します   0：実際の充電電流は予想電流と等しい;    1：実際の充電電流は予想電流よりも小さい
        # bit1    AXP173スイッチモード表示   0：モードA; 1：モードB
        # bit0    予約済み、変更不可

        # self.axp173.setK210Vcore(0.8)

    def loop(self):
        self.printRegs()

        self.counter += 1
        print("counter=%04d vbat=%6.1fmV USB=%6.1fmV,%5.1fmA EX=%6.1fmV,%5.1fmA Bat=+%5.1fmA,-%5.1fmA,%5.1fmW %4.1fC" % (
            self.counter,
            self.axp173.getVbatVoltage(),
            self.axp173.getUSBVoltage(),
            self.axp173.getUSBInputCurrent(),
            self.axp173.getConnextVoltage(),
            self.axp173.getConnextInputCurrent(),
            self.axp173.getBatteryChargeCurrent(),
            self.axp173.getBatteryDischargeCurrent(),
            self.axp173.getBatteryInstantWatts(),
            self.axp173.getTemperature()
        ))

        lcd.clear(lcd.BLACK)
        lcd.draw_string(2,  5, "hello maixpy %d" % (self.counter), lcd.WHITE, lcd.RED)
        lcd.draw_string(2, 20, "vbat=%.1fmV" % (self.axp173.getVbatVoltage()), lcd.WHITE, lcd.BLUE)
        lcd.draw_string(2, 35, "USB=%.1fmV,%.1fmA" % (self.axp173.getUSBVoltage(), self.axp173.getUSBInputCurrent()), lcd.GREEN, lcd.BLACK)
        lcd.draw_string(2, 50, "EX=%.1fmV,%.1fmA" % (self.axp173.getConnextVoltage(), self.axp173.getConnextInputCurrent()), lcd.WHITE, lcd.BLACK)
        lcd.draw_string(2, 65, "Bat=+%.1fmA,-%.1fmA,%.1fmW" % (
            self.axp173.getBatteryChargeCurrent(),
            self.axp173.getBatteryDischargeCurrent(),
            self.axp173.getBatteryInstantWatts()),
            lcd.BLACK, lcd.YELLOW)
        lcd.draw_string(2, 80, "%.1fC" % (self.axp173.getTemperature()), lcd.RED, lcd.WHITE)
        time.sleep(1)

        #if self.counter % 5 == 0 and self.axp173.getBatteryChargeCurrent() < 15.0:
        #    self.resetCharge()

        # VBatの計算あってる？
        #reg0x78 = self.axp173.__readReg(0x78)
        #reg0x79 = self.axp173.__readReg(0x79)
        #print("reg0x78=%02X reg0x79=%02X" % (reg0x78, reg0x79))
        #vbat = (reg0x78 << 4) + reg0x79
        #print(vbat, vbat*1.1)

    def printRegs(self):
        s = ""
        for addr in [0x28, 0x12, 0x33, 0x34, 0x01]:
          value = self.axp173.__readReg(addr)
          s += 'REG{:02X}H=0x{:02X} ({:08b}) '.format(addr, value, value)
        print(s)

    def resetCharge(self):
        print("*** resetCharge ***")
        reg0x33 = self.axp173.__readReg(0x33)
        reg0x33 = (reg0x33 & 0xF0) | 0x01 # 200mA
        reg0x33 &= ~(1<<7) # OFF
        self.axp173.__writeReg(0x33, reg0x33)

        reg0x33 = self.axp173.__readReg(0x33)
        print("reg0x33=%02X" % (reg0x33))

        time.sleep(1)

        reg0x33 = self.axp173.__readReg(0x33)
        reg0x33 |= (1<<7) # ON
        self.axp173.__writeReg(0x33, reg0x33)

        reg0x33 = self.axp173.__readReg(0x33)
        print("reg0x33=%02X" % (reg0x33))

App().main()


```