MicroPython(ESP32)でLS027B4DH01にMPU9250のデータを表示する

以前作った、LCDに表示するプログラムセンサの値を取得するプログラムを使って、LCDにセンサのデータを表示するプログラムを作成してみました。

センサの値を取得

mpu9250.pyを使用してセンサの値を取得します。値はfloatで返ってきます。

accel = mpu9250.read_accel()
gyro = mpu9250.read_gyro()
mag = mpu9250.read_magnet()

値をLCDに表示

値は下記の手順で表示しました。

lcd.data_reset()
lcd.font_size(24)
lcd.string(5, 5, 'MPU9250 data')
lcd.font_size(16)
lcd.string(5, 100, 'x:'+'{:.2f}'.format(accel['x']) )
lcd.line(0, 58, 400, 58)
lcd.update_all_line()

動作の様子

ソースコード

フォントデータはfont_24.pyとfont_16.pyというファイル名で、PCで作成したフォントデータが書き込まれているものを使用しました。

main.py

import utime
from machine import Pin, SPI, I2C
from LS027B4DH01 import LS027B4DH01
from mpu9250 import MPU9250

def main():
    
    # LCD
    lcd = LS027B4DH01()
    lcd.spi = SPI(
        2, #vspi = id = 2
        baudrate=10_000_000, #1MHz
        polarity=0, phase=0, bits=8, firstbit=SPI.LSB,
        sck=Pin(18), mosi=Pin(23), miso=Pin(19)
    )
    lcd.scs      = Pin(32, Pin.OUT)
    lcd.extcomin = Pin(33, Pin.OUT)
    lcd.disp     = Pin(25, Pin.OUT)
    lcd.initialize()
    lcd.disp.on()
    lcd.font_size(16)

    # Sensor
    i2c = I2C(scl=Pin(21), sda=Pin(22), freq=100000)
    mpu9250 = MPU9250(i2c)
    mpu9250.setting(mpu9250.GFS_1000, mpu9250.AFS_16G)

    lcd.string(50, 50, 'hello')
    lcd.update_all_line()
    utime.sleep(3)

    print('initialized')

    while True:
        
        # get sensor value
        accel = mpu9250.read_accel()
        gyro = mpu9250.read_gyro()
        mag = mpu9250.read_magnet()

        # display reset
        lcd.data_reset()

        # display title
        lcd.font_size(24)
        lcd.string(5, 5, 'MPU9250 data')

        # display sensor
        lcd.font_size(16)
        lcd.string(  5,  60, 'Accel')
        lcd.string(  5, 100, 'x:'+'{:.2f}'.format(accel['x']) )
        lcd.string(  5, 140, 'y:'+'{:.2f}'.format(accel['y']) )
        lcd.string(  5, 180, 'z:'+'{:.2f}'.format(accel['z']) )
        
        lcd.string(140,  60, 'GYRO')
        lcd.string(140, 100, 'x:'+'{:.2f}'.format(gyro['x']) )
        lcd.string(140, 140, 'y:'+'{:.2f}'.format(gyro['y']) )
        lcd.string(140, 180, 'z:'+'{:.2f}'.format(gyro['z']) )
        
        lcd.string(260,  60, 'MAG')
        lcd.string(260, 100, 'x:'+'{:.2f}'.format(mag['x']) )
        lcd.string(260, 140, 'y:'+'{:.2f}'.format(mag['y']) )
        lcd.string(260, 180, 'z:'+'{:.2f}'.format(mag['z']) )
        
        # display line
        lcd.line(  0,  58, 400,  58)
        lcd.line(130,  58, 130, 240)
        lcd.line(250,  58, 250, 240)

        # display update
        lcd.update_all_line()

if __name__ == "__main__":
    main()

LS027B4DH01.py

import utime
from font_24 import font_24
from font_16 import font_16

class LS027B4DH01():
    def __init__(self):
        self.scs = None
        self.extcomin = None
        self.disp = None
        self.spi = None
        self.data = bytearray( (1+1+50) * 241 + 2 )
        self.font_24 = font_24
        self.font_16 = font_16
        self.font = self.font_24
        self.byte_length = 3

        self.data_reset()

    def set_one_byte(self, x, y, byte):
        self.data[2 + x + y * 52] = byte

    def font_size(self, font_size):
        if font_size == 24:
            self.font = self.font_24
            self.byte_length = 3
        if font_size == 16:
            self.font = self.font_16
            self.byte_length = 2

    def update_one_line(self, line, data_array):
        self.scs.on()
        # send mode
        self.spi.write(b'\x01')
        # send gate line address
        self.spi.write( bytearray([line]) )
        # send data
        self.spi.write(data_array)
        # dummy data
        self.spi.write(b'\x00\x00')
        self.scs.off()

    def data_reset(self):
        for i in range(len(self.data)):
            self.data[i] = 0xFF
        self.data[0] = 0x01
        self.data[1] = 0x01
        for i in range(2, 241):
            self.data[i * 52 + 1] = i
        
    def update_all_line(self):
        self.scs.on()
        utime.sleep_us(4)
        self.spi.write(self.data)
        utime.sleep_us(4)
        self.scs.off()
        utime.sleep_us(4)

    def character(self, x, y, character):
        font_bytearray = self.font[character]

        for index, byte in enumerate(font_bytearray):
            byte_number = index % self.byte_length
            line_number = int(index / self.byte_length)
            x2 = int(x / 8) + byte_number
            y2 = (y + line_number) * 52
            data_index = 2 + y2 + x2
            self.data[data_index] = byte

    def string(self, x, y, string):
        for x2, character in enumerate(string):
            self.character(x + x2 * self.byte_length * 8, y, character)

    def line(self, x1, y1, x2, y2):
        delta_x = abs(x2 - x1)
        delta_y = abs(y2 - y1)

        if delta_x == 0:
            for y in range(delta_y):
                self.dot(x1, y + y1)
            return
        
        a = (y2 - y1) / (x2 - x1)

        if delta_x > delta_y:
            x_array = [ i for i in range(x1, x2)]
            y_array = [ int(a * x + y1) for x in x_array ]
        else:
            y_array = [ i for i in range(y1, y2)]
            x_array = [ int( (y - y1) / a ) for y in y_array ]

        for x, y in zip(x_array, y_array):
            self.dot(x, y)

    def dot(self, x, y):
        index = 2 + int(x / 8) + y * 52
        byte = ~self.data[index] | (1 << (x % 8))
        byte = ~byte
        self.data[index] = byte

    def clear_all(self):
        self.disp.off()
        self.scs.on()
        utime.sleep_us(3)
        self.spi.write(b'\x04\x00')
        utime.sleep_us(3)
        self.scs.off()
        utime.sleep_us(5)
        self.disp.on()

    def initialize(self):
        self.disp.off()
        utime.sleep_us(500)
        self.scs.off()
        self.clear_all()
        utime.sleep_ms(5)
        self.disp.on()

mpu9250.py

from micropython import const
import utime

class MPU9250():
    MPU9250_ADDRESS = const(0x68)
    DEVICE_ID       = const(0x71)

    SMPLRT_DIV     = const(0x19)
    CONFIG         = const(0x1A)
    GYRO_CONFIG    = const(0x1B)
    ACCEL_CONFIG   = const(0x1C)
    ACCEL_CONFIG_2 = const(0x1D)
    LP_ACCEL_ODR   = const(0x1E)
    WOM_THR        = const(0x1F)
    FIFO_EN        = const(0x23)
    I2C_MST_CTRL   = const(0x24)
    I2C_MST_STATUS = const(0x36)
    INT_PIN_CFG    = const(0x37)
    INT_ENABLE     = const(0x38)
    INT_STATUS     = const(0x3A)
    ACCEL_OUT      = const(0x3B)
    TEMP_OUT       = const(0x41)
    GYRO_OUT       = const(0x43)

    I2C_MST_DELAY_CTRL = const(0x67)
    SIGNAL_PATH_RESET  = const(0x68)
    MOT_DETECT_CTRL    = const(0x69)
    USER_CTRL          = const(0x6A)
    PWR_MGMT_1         = const(0x6B)
    PWR_MGMT_2         = const(0x6C)
    FIFO_R_W           = const(0x74)
    WHO_AM_I           = const(0x75)

    GFS_250  = const(0x00)
    GFS_500  = const(0x01)
    GFS_1000 = const(0x02)
    GFS_2000 = const(0x03)
    AFS_2G   = const(0x00)
    AFS_4G   = const(0x01)
    AFS_8G   = const(0x02)
    AFS_16G  = const(0x03)

    def __init__(self, i2c, address = MPU9250_ADDRESS):
        self.i2c = i2c
        self.address = address
        self.setting(self.GFS_250, self.AFS_2G)
        self.ak8963 = AK8963(self.i2c)

    def searchDevice(self):
        who_am_i = self.i2c.readfrom(self.address, self.WHO_AM_I)
        if(who_am_i == self.DEVICE_ID):
            return True
        else:
            return False

    def setting(self, gfs, afs):
        if gfs == self.GFS_250:
            self.gres = 250.0/32768.0
        elif gfs == self.GFS_500:
            self.gres = 500.0/32768.0
        elif gfs == self.GFS_1000:
            self.gres = 1000.0/32768.0
        else:  # gfs == GFS_2000
            self.gres = 2000.0/32768.0

        if afs == self.AFS_2G:
            self.ares = 2.0/32768.0
        elif afs == self.AFS_4G:
            self.ares = 4.0/32768.0
        elif afs == self.AFS_8G:
            self.ares = 8.0/32768.0
        else: # afs == AFS_16G:
            self.ares = 16.0/32768.0

        buffer = bytearray(1)
        self.i2c.writeto_mem(self.address, self.PWR_MGMT_1, b'\x00') # sleep off
        utime.sleep_ms(100)
        self.i2c.writeto_mem(self.address, self.PWR_MGMT_1, b'\x01') # auto select clock source
        utime.sleep_ms(100)
        self.i2c.writeto_mem(self.address, self.CONFIG, b'\x03') # DLPF_CFG
        self.i2c.writeto_mem(self.address, self.SMPLRT_DIV, b'\x04') # sample rate divider
        buffer[0] = gfs << 3
        self.i2c.writeto_mem(self.address, self.GYRO_CONFIG, buffer) # gyro full scale select
        buffer[0] = afs << 3
        self.i2c.writeto_mem(self.address, self.ACCEL_CONFIG, buffer) # accel full scale select
        self.i2c.writeto_mem(self.address, self.ACCEL_CONFIG_2, b'\x03') # A_DLPFCFG
        self.i2c.writeto_mem(self.address, self.INT_PIN_CFG, b'\x02') # BYPASS_EN
        utime.sleep_ms(100)

    def check_data_ready(self):
        drdy = self.i2c.readfrom(self.address, self.INT_STATUS)
        if drdy & 0x01:
            return True
        else:
            return False
            
    def read_accel(self):
        data = self.i2c.readfrom_mem(self.address, self.ACCEL_OUT, 6)
        x = self.data_convert(data[1], data[0])
        y = self.data_convert(data[3], data[2])
        z = self.data_convert(data[5], data[4])

        x = round(x*self.ares, 3)
        y = round(y*self.ares, 3)
        z = round(z*self.ares, 3)

        return {"x":x, "y":y, "z":z}

    def read_gyro(self):
        data = self.i2c.readfrom_mem(self.address, self.GYRO_OUT, 6)

        x = self.data_convert(data[1], data[0])
        y = self.data_convert(data[3], data[2])
        z = self.data_convert(data[5], data[4])

        x = round(x*self.gres, 3)
        y = round(y*self.gres, 3)
        z = round(z*self.gres, 3)

        return {"x":x, "y":y, "z":z}

    def read_magnet(self):
        return self.ak8963.read_magnet()

    def data_convert(self, data1, data2):
        value = data1 | (data2 << 8)
        if(value & (1 << 16 - 1)):
            value -= (1<<16)
        return value

class AK8963():
    AK8963_SLAVE_ADDRESS = const(0x0C)
    AK8963_ST1           = const(0x02)
    AK8963_MAGNET_OUT    = const(0x03)
    AK8963_CNTL1         = const(0x0A)
    AK8963_CNTL2         = const(0x0B)
    AK8963_ASAX          = const(0x10)
    AK8963_MODE_DOWN     = const(0x00)
    AK8963_MODE_ONE      = const(0x01)
    AK8963_MODE_C8HZ     = const(0x02)
    AK8963_MODE_C100HZ   = const(0x06)
    AK8963_BIT_14        = const(0x00)
    AK8963_BIT_16        = const(0x01)

    def __init__(self, i2c, address=0x76):
        self.i2c = i2c
        self.address = address
        self.setting(self.AK8963_MODE_C8HZ, self.AK8963_BIT_16)

    def setting(self, mode, mfs):
        if mfs == self.AK8963_BIT_14:
            self.mres = 4912.0/8190.0
        else: #  mfs == AK8963_BIT_16:
            self.mres = 4912.0/32760.0

        self.i2c.writeto_mem(self.AK8963_SLAVE_ADDRESS, self.AK8963_CNTL1, b'\x00')
        utime.sleep_ms(10)
        self.i2c.writeto_mem(self.AK8963_SLAVE_ADDRESS, self.AK8963_CNTL1, b'\x0F') # set read FuseROM mode
        utime.sleep_ms(10)
        data = self.i2c.readfrom_mem(self.AK8963_SLAVE_ADDRESS, self.AK8963_ASAX, 3) # read coef data

        self.magXcoef = (data[0] - 128) / 256.0 + 1.0
        self.magYcoef = (data[1] - 128) / 256.0 + 1.0
        self.magZcoef = (data[2] - 128) / 256.0 + 1.0

        self.i2c.writeto_mem(self.AK8963_SLAVE_ADDRESS, self.AK8963_CNTL1, b'\x00') # set power down mode
        utime.sleep_ms(10)
        buffer = bytearray(1)
        buffer[0] = (mfs << 4 | mode)
        self.i2c.writeto_mem(self.AK8963_SLAVE_ADDRESS, self.AK8963_CNTL1, buffer) # set scale&continous mode
        utime.sleep_ms(10)

    def read_magnet(self):
        x, y, z=0, 0, 0
        
        data = self.i2c.readfrom_mem(self.AK8963_SLAVE_ADDRESS, self.AK8963_MAGNET_OUT, 7)

        # check overflow
        if (data[6] & 0x08)!=0x08:
            x = self.data_convert(data[0], data[1])
            y = self.data_convert(data[2], data[3])
            z = self.data_convert(data[4], data[5])

            x = round(x * self.mres * self.magXcoef, 3)
            y = round(y * self.mres * self.magYcoef, 3)
            z = round(z * self.mres * self.magZcoef, 3)

        return {"x":x, "y":y, "z":z}

    def data_convert(self, data1, data2):
        value = data1 | (data2 << 8)
        if(value & (1 << 16 - 1)):
            value -= (1<<16)
        return value

記事の共有

関連記事

コメント

comments powered by Disqus