You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
matouch_esp32_s3_2_8_spi work
- Natural orientation: camera on top, USB ports on bottom - OV3660 camera works (but colors are off and it breaks the touch input) - Improve board detection
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
|
||||
print("matouch_esp32_s3_2_8.py initialization")
|
||||
# Hardware initialization for Makerfabs MaTouch ESP32-S3 SPI 2.8" with Camera
|
||||
# Manufacturer's website: https://www.makerfabs.com/matouch-esp32-s3.html
|
||||
# Manufacturer's website: https://www.makerfabs.com/matouch-esp32-s3-spi-ips-2-8-with-camera-ov3660.html
|
||||
# Hardware Specifications:
|
||||
# - MCU: ESP32-S3 with 16MB Flash, 8MB Octal PSRAM
|
||||
# - Display: 2.8" IPS LCD, 320x240 resolution, ST7789 driver, SPI interface
|
||||
@@ -77,45 +77,25 @@ mpos.ui.main_display.set_power(True)
|
||||
mpos.ui.main_display.set_backlight(100)
|
||||
|
||||
# Touch handling
|
||||
# Often times, a "ghost" device seems to show up on the I2C bus at 0x14.
|
||||
# Initializing it, although it fails, seems to bring up the "proper" GT911 at address 0x5D (gt911.I2C_ADDR).
|
||||
try:
|
||||
import i2c
|
||||
import gt911
|
||||
i2c_bus = i2c.I2C.Bus(host=0, scl=38, sda=39, freq=400000, use_locks=False)
|
||||
touch_dev = i2c.I2C.Device(bus=i2c_bus, dev_id=0x14, reg_bits=gt911.BITS)
|
||||
indev = gt911.GT911(touch_dev, reset_pin=1, interrupt_pin=40, debug=True)
|
||||
except Exception as e:
|
||||
print(f"Touch init phase 1 got exception: {e}")
|
||||
try:
|
||||
import pointer_framework
|
||||
i2c_bus = i2c.I2C.Bus(host=0, scl=38, sda=39)
|
||||
import mpos.indev.gt911 as gt911
|
||||
touch_dev = i2c.I2C.Device(bus=i2c_bus, dev_id=gt911.I2C_ADDR, reg_bits=gt911.BITS)
|
||||
indev = gt911.GT911(touch_dev, reset_pin=1, interrupt_pin=40, startup_rotation=pointer_framework.lv.DISPLAY_ROTATION._180, debug=True)
|
||||
indev = gt911.GT911(touch_dev, reset_pin=1, interrupt_pin=40, debug=True) # remove debug because it's slower
|
||||
except Exception as e:
|
||||
print(f"Touch init phase 2 got exception: {e}")
|
||||
print(f"Touch init got exception: {e}")
|
||||
|
||||
# Initialize LVGL
|
||||
lv.init()
|
||||
mpos.ui.main_display.set_rotation(lv.DISPLAY_ROTATION._90) # must be done after initializing display and creating the touch drivers, to ensure proper handling
|
||||
|
||||
# === BATTERY VOLTAGE MONITORING ===
|
||||
# Note: MaTouch ESP32-S3 battery monitoring configuration may vary
|
||||
# This is a placeholder - adjust ADC pin and conversion formula based on actual hardware
|
||||
from mpos import BatteryManager
|
||||
# TODO: initialize SDIO (instead of SPI) SD card with:
|
||||
# CMD = 2
|
||||
# SCLK = 42
|
||||
# D0 = 41
|
||||
#import mpos.sdcard
|
||||
#mpos.sdcard.init(spi_bus, cs_pin=14)
|
||||
|
||||
def adc_to_voltage(adc_value):
|
||||
"""
|
||||
Convert raw ADC value to battery voltage.
|
||||
Currently uses simple linear scaling: voltage = adc * 0.00262
|
||||
|
||||
This should be calibrated with actual battery voltages and ADC readings.
|
||||
To calibrate: measure actual battery voltages and corresponding ADC readings,
|
||||
then fit a linear or polynomial function.
|
||||
"""
|
||||
return adc_value * 0.00262
|
||||
|
||||
# Note: Adjust ADC pin number based on actual hardware schematic
|
||||
# BatteryManager.init_adc(5, adc_to_voltage)
|
||||
|
||||
# === AUDIO HARDWARE ===
|
||||
# Note: MaTouch ESP32-S3 has no buzzer or I2S audio hardware
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
# Copyright (c) 2024 - 2025 Kevin G. Schlosser
|
||||
|
||||
# this driver uses a special i2c bus implimentation I have written.
|
||||
# This implimentation takes into consideration the ESP32 and it having
|
||||
# threading available. It also has some convience methods built into it
|
||||
# that figure out what is wanting to be done automatically.
|
||||
# read more about it's use in the stub files.
|
||||
|
||||
from micropython import const # NOQA
|
||||
import pointer_framework
|
||||
import machine # NOQA
|
||||
import time
|
||||
|
||||
|
||||
_CMD_REG = const(0x8040)
|
||||
_CMD_CHECK_REG = const(0x8046)
|
||||
_CMD_READ_DATA = const(0x01)
|
||||
|
||||
_ESD_CHECK_REG = const(0x8041)
|
||||
|
||||
_STATUS_REG = const(0x814E)
|
||||
_POINT_1_REG = const(0x8150)
|
||||
|
||||
_PRODUCT_ID_REG = const(0x8140)
|
||||
_FIRMWARE_VERSION_REG = const(0x8144)
|
||||
_VENDOR_ID_REG = const(0x814A)
|
||||
|
||||
_X_CORD_RES_REG = const(0x8146)
|
||||
_Y_CORD_RES_REG = const(0x8148)
|
||||
|
||||
I2C_ADDR = 0x5D
|
||||
BITS = 16
|
||||
|
||||
_ADDR2 = const(0x14)
|
||||
|
||||
|
||||
class GT911(pointer_framework.PointerDriver):
|
||||
|
||||
def _read_reg(self, reg, num_bytes=None, buf=None):
|
||||
self._tx_buf[0] = reg >> 8
|
||||
self._tx_buf[1] = reg & 0xFF
|
||||
try:
|
||||
if num_bytes is not None:
|
||||
self._device.write_readinto(self._tx_mv[:2], self._rx_mv[:num_bytes])
|
||||
else:
|
||||
self._device.write_readinto(self._tx_mv[:2], buf)
|
||||
except Exception as e:
|
||||
print(f"GT911 _read_reg got exception: {e}")
|
||||
|
||||
def _write_reg(self, reg, value=None, buf=None):
|
||||
try:
|
||||
if value is not None:
|
||||
self._tx_buf[0] = value
|
||||
self._device.write_mem(reg, self._tx_mv[:1])
|
||||
elif buf is not None:
|
||||
self._device.write_mem(reg, buf)
|
||||
except Exception as e:
|
||||
print(f"GT911 _write_reg got exception: {e}")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
device,
|
||||
reset_pin=None,
|
||||
interrupt_pin=None,
|
||||
touch_cal=None,
|
||||
startup_rotation=pointer_framework.lv.DISPLAY_ROTATION._0, # NOQA
|
||||
debug=False
|
||||
):
|
||||
self._tx_buf = bytearray(3)
|
||||
self._tx_mv = memoryview(self._tx_buf)
|
||||
self._rx_buf = bytearray(6)
|
||||
self._rx_mv = memoryview(self._rx_buf)
|
||||
|
||||
self._device = device
|
||||
|
||||
self.__x = 0
|
||||
self.__y = 0
|
||||
self.__last_state = self.RELEASED
|
||||
|
||||
if isinstance(reset_pin, int):
|
||||
reset_pin = machine.Pin(reset_pin, machine.Pin.OUT)
|
||||
|
||||
if isinstance(interrupt_pin, int):
|
||||
interrupt_pin = machine.Pin(interrupt_pin, machine.Pin.OUT)
|
||||
|
||||
self._reset_pin = reset_pin
|
||||
self._interrupt_pin = interrupt_pin
|
||||
|
||||
self.hw_reset()
|
||||
super().__init__(
|
||||
touch_cal=touch_cal, startup_rotation=startup_rotation, debug=debug
|
||||
)
|
||||
|
||||
def hw_reset(self):
|
||||
if self._interrupt_pin and self._reset_pin:
|
||||
self._interrupt_pin.init(self._interrupt_pin.OUT)
|
||||
self._interrupt_pin(0)
|
||||
self._reset_pin(0)
|
||||
time.sleep_ms(10) # NOQA
|
||||
self._interrupt_pin(0)
|
||||
time.sleep_ms(1) # NOQA
|
||||
self._reset_pin(1)
|
||||
time.sleep_ms(5) # NOQA
|
||||
self._interrupt_pin(0)
|
||||
time.sleep_ms(50) # NOQA
|
||||
self._interrupt_pin.init(self._interrupt_pin.IN)
|
||||
time.sleep_ms(50) # NOQA
|
||||
|
||||
self._write_reg(_ESD_CHECK_REG, 0x00)
|
||||
self._write_reg(_CMD_CHECK_REG, _CMD_READ_DATA)
|
||||
self._write_reg(_CMD_REG, _CMD_READ_DATA)
|
||||
|
||||
self._read_reg(_PRODUCT_ID_REG, 4)
|
||||
|
||||
product_id = ''
|
||||
for item in self._rx_buf[:4]:
|
||||
try:
|
||||
product_id += chr(item)
|
||||
except: # NOQA
|
||||
break
|
||||
|
||||
print('Touch Product id:', product_id)
|
||||
|
||||
self._read_reg(_FIRMWARE_VERSION_REG, 2)
|
||||
print(
|
||||
'Touch Firmware version:',
|
||||
hex(self._rx_buf[0] + (self._rx_buf[1] << 8))
|
||||
)
|
||||
|
||||
self._read_reg(_VENDOR_ID_REG, 1)
|
||||
print(f'Touch Vendor id: 0x{hex(self._rx_buf[0])[2:].upper()}')
|
||||
x, y = self.hw_size
|
||||
print(f'Touch resolution: width={x}, height={y}')
|
||||
|
||||
@property
|
||||
def hw_size(self):
|
||||
self._read_reg(_X_CORD_RES_REG, 2)
|
||||
x = self._rx_buf[0] + (self._rx_buf[1] << 8)
|
||||
|
||||
self._read_reg(_Y_CORD_RES_REG, 2)
|
||||
y = self._rx_buf[0] + (self._rx_buf[1] << 8)
|
||||
|
||||
return x, y
|
||||
|
||||
@property
|
||||
def firmware_config(self):
|
||||
try:
|
||||
import gt911_extension
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
'you need to upload the gt911_extension.py file to the MCU'
|
||||
)
|
||||
return gt911_extension.GT911Extension(self, self._device)
|
||||
|
||||
def _get_coords(self):
|
||||
self._read_reg(_STATUS_REG, 1)
|
||||
touch_cnt = self._rx_buf[0] & 0x0F
|
||||
status = self._rx_buf[0] & 0x80
|
||||
|
||||
if status:
|
||||
if touch_cnt == 1:
|
||||
self._read_reg(_POINT_1_REG, 6)
|
||||
|
||||
x = self._rx_buf[0] + (self._rx_buf[1] << 8)
|
||||
y = self._rx_buf[2] + (self._rx_buf[3] << 8)
|
||||
|
||||
self._write_reg(_STATUS_REG, 0x00)
|
||||
|
||||
self.__x = x
|
||||
self.__y = y
|
||||
self.__last_state = self.PRESSED
|
||||
|
||||
elif touch_cnt == 0:
|
||||
self.__last_state = self.RELEASED
|
||||
|
||||
self._write_reg(_STATUS_REG, 0x00)
|
||||
|
||||
return self.__last_state, self.__x, self.__y
|
||||
@@ -0,0 +1,157 @@
|
||||
# Copyright (c) 2024 - 2025 Kevin G. Schlosser
|
||||
|
||||
from micropython import const # NOQA
|
||||
|
||||
|
||||
_CONFIG_START_REG = const(0x8047)
|
||||
_CONFIG_VERSION_POS = const(0x00)
|
||||
_X_OUTPUT_MAX_LOW_POS = const(0x01)
|
||||
_X_OUTPUT_MAX_HIGH_POS = const(0x02)
|
||||
_Y_OUTPUT_MAX_LOW_POS = const(0x03)
|
||||
_Y_OUTPUT_MAX_HIGH_POS = const(0x04)
|
||||
# Touch Number 0x05
|
||||
# Module_Switch1 0x06
|
||||
# Module_Switch2 0x07
|
||||
# Shake_Count 0x08
|
||||
# Filter 0x09
|
||||
# Large_Touch 0x0A
|
||||
_NOISE_REDUCTION_POS = const(0x0B)
|
||||
_TOUCH_PRESS_LEVEL_POS = const(0x0C)
|
||||
_TOUCH_LEAVE_LEVEL_POS = const(0x0D)
|
||||
# Low_Power_Control 0x0E
|
||||
# Refresh_Rate 0x0F
|
||||
# x_threshold 0x10
|
||||
# y_threshold 0x11
|
||||
# X_Speed_Limit 0x12
|
||||
# y_Speed_Limit 0x13
|
||||
_VER_SPACE_POS = const(0x14) # const(0x805B) # low 4 bits are bottom and hight is top
|
||||
_HOR_SPACE_POS = const(0x15) # const(0x805C) # low 4 bits is right and high is left
|
||||
|
||||
_CONFIG_CHKSUM_REG = const(0x80FF)
|
||||
_CONFIG_FRESH_REG = const(0x8100)
|
||||
# 0-15 * 32
|
||||
|
||||
|
||||
class GT911Extension(object):
|
||||
|
||||
def _read_reg(self, reg, num_bytes=None, buf=None):
|
||||
self._tx_buf[0] = reg >> 8
|
||||
self._tx_buf[1] = reg & 0xFF
|
||||
if num_bytes is not None:
|
||||
self._i2c.write_readinto(self._tx_mv[:2], self._rx_mv[:num_bytes])
|
||||
else:
|
||||
self._i2c.write_readinto(self._tx_mv[:2], buf)
|
||||
|
||||
def _write_reg(self, reg, value=None, buf=None):
|
||||
if value is not None:
|
||||
self._tx_buf[0] = value
|
||||
self._i2c.write_mem(reg, self._tx_mv[:1])
|
||||
elif buf is not None:
|
||||
self._i2c.write_mem(reg, buf)
|
||||
|
||||
def __init__(self, indev, i2c):
|
||||
self._indev = indev
|
||||
self._i2c = i2c
|
||||
|
||||
self._tx_buf = bytearray(3)
|
||||
self._tx_mv = memoryview(self._tx_buf)
|
||||
self._rx_buf = bytearray(6)
|
||||
self._rx_mv = memoryview(self._rx_buf)
|
||||
|
||||
self._config_data = bytearray(_CONFIG_FRESH_REG - _CONFIG_START_REG + 1)
|
||||
self._config_mv = memoryview(self._config_data)
|
||||
|
||||
self._read_reg(_CONFIG_START_REG, buf=self._config_mv[:-2])
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
return (
|
||||
(self._config_data[_X_OUTPUT_MAX_HIGH_POS] << 8) |
|
||||
self._config_data[_X_OUTPUT_MAX_LOW_POS]
|
||||
)
|
||||
|
||||
@width.setter
|
||||
def width(self, value):
|
||||
self._config_data[_X_OUTPUT_MAX_LOW_POS] = value & 0xFF
|
||||
self._config_data[_X_OUTPUT_MAX_HIGH_POS] = (value >> 8) & 0xFF
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
return (
|
||||
(self._config_data[_Y_OUTPUT_MAX_HIGH_POS] << 8) |
|
||||
self._config_data[_Y_OUTPUT_MAX_LOW_POS]
|
||||
)
|
||||
|
||||
@height.setter
|
||||
def height(self, value):
|
||||
self._config_data[_Y_OUTPUT_MAX_LOW_POS] = value & 0xFF
|
||||
self._config_data[_Y_OUTPUT_MAX_HIGH_POS] = (value >> 8) & 0xFF
|
||||
|
||||
@property
|
||||
def noise_reduction(self):
|
||||
return self._config_data[_NOISE_REDUCTION_POS] & 0x0F
|
||||
|
||||
@noise_reduction.setter
|
||||
def noise_reduction(self, value):
|
||||
upper_val = self._config_data[_NOISE_REDUCTION_POS] >> 4
|
||||
self._config_data[_NOISE_REDUCTION_POS + 2] = (upper_val << 4) | (value & 0x0F)
|
||||
|
||||
@property
|
||||
def touch_press_level(self):
|
||||
return self._config_data[_TOUCH_PRESS_LEVEL_POS]
|
||||
|
||||
@touch_press_level.setter
|
||||
def touch_press_level(self, value):
|
||||
self._config_data[_TOUCH_PRESS_LEVEL_POS] = value & 0xFF
|
||||
|
||||
@property
|
||||
def touch_leave_level(self):
|
||||
return self._config_data[_TOUCH_LEAVE_LEVEL_POS]
|
||||
|
||||
@touch_leave_level.setter
|
||||
def touch_leave_level(self, value):
|
||||
self._config_data[_TOUCH_LEAVE_LEVEL_POS] = value & 0xFF
|
||||
|
||||
@property
|
||||
def pad_left(self):
|
||||
return self._config_data[_HOR_SPACE_POS] >> 4
|
||||
|
||||
@pad_left.setter
|
||||
def pad_left(self, value):
|
||||
self._config_data[_HOR_SPACE_POS] = (value << 4) | self.pad_right
|
||||
|
||||
@property
|
||||
def pad_right(self):
|
||||
return self._config_data[_HOR_SPACE_POS] & 0xF
|
||||
|
||||
@pad_right.setter
|
||||
def pad_right(self, value):
|
||||
self._config_data[_HOR_SPACE_POS] = (self.pad_left << 4) | (value & 0xF)
|
||||
|
||||
@property
|
||||
def pad_top(self):
|
||||
return self._config_data[_VER_SPACE_POS] >> 4
|
||||
|
||||
@pad_top.setter
|
||||
def pad_top(self, value):
|
||||
self._config_data[_VER_SPACE_POS] = (value << 4) | self.pad_bottom
|
||||
|
||||
@property
|
||||
def pad_bottom(self):
|
||||
return self._config_data[_VER_SPACE_POS] & 0xF
|
||||
|
||||
@pad_bottom.setter
|
||||
def pad_bottom(self, value):
|
||||
self._config_data[_VER_SPACE_POS] = (self.pad_top << 4) | (value & 0xF)
|
||||
|
||||
def save(self):
|
||||
# calculate the checksum
|
||||
self._config_data[-2] = ((~sum(self._config_data[:-2])) + 1) & 0xFF
|
||||
|
||||
# set the flag to save the data the data
|
||||
self._config_data[-1] = 0x01 # _CONFIG_FRESH_REG
|
||||
|
||||
# write all config data to the touch IC
|
||||
self._write_reg(_CONFIG_START_REG, buf=self._config_mv)
|
||||
|
||||
self._indev.hw_reset()
|
||||
@@ -0,0 +1,4 @@
|
||||
# Copyright (c) 2024 - 2025 Kevin G. Schlosser
|
||||
|
||||
import lvgl as lv # NOQA
|
||||
|
||||
@@ -32,9 +32,11 @@ def detect_board():
|
||||
return "linux"
|
||||
elif sys.platform == "esp32":
|
||||
from machine import Pin, I2C
|
||||
return "matouch_esp32_s3_2_8" # i2c scan confuses the camera so hard-code for now
|
||||
|
||||
i2c0 = I2C(0, sda=Pin(39), scl=Pin(38), freq=400000)
|
||||
if {0x14} <= set(i2c0.scan()): # GT911 touch initial "ghost" device
|
||||
devices = set(i2c0.scan()) # causes a "ghost" device to appear on 0x20 and breaks the camera
|
||||
if {0x14} <= devices or {0x5D} <= devices: # "ghost" device or real GT911
|
||||
return "matouch_esp32_s3_2_8"
|
||||
|
||||
i2c0 = I2C(0, sda=Pin(48), scl=Pin(47)) # on matouch_esp32_s3_2_8, this "finds" devices at all addresses 8-119 so only do this after matouch_esp32_s3_2_8
|
||||
|
||||
@@ -414,13 +414,13 @@ class CameraActivity(Activity):
|
||||
for attempt in range(max_attempts):
|
||||
try:
|
||||
cam = Camera(
|
||||
data_pins=[12,13,15,11,14,10,7,2],
|
||||
vsync_pin=6,
|
||||
href_pin=4,
|
||||
sda_pin=21,
|
||||
scl_pin=16,
|
||||
pclk_pin=9,
|
||||
xclk_pin=8,
|
||||
data_pins=[7,5,4,6,16,8,3,46],
|
||||
vsync_pin=11,
|
||||
href_pin=10,
|
||||
sda_pin=39,
|
||||
scl_pin=38,
|
||||
pclk_pin=17,
|
||||
xclk_pin=9,
|
||||
xclk_freq=20000000,
|
||||
powerdown_pin=-1,
|
||||
reset_pin=-1,
|
||||
|
||||
@@ -111,7 +111,7 @@ if [ "$target" == "esp32" ]; then
|
||||
# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
||||
pushd "$codebasedir"/lvgl_micropython/
|
||||
rm -rf lib/micropython/ports/esp32/build-ESP32_GENERIC_S3-SPIRAM_OCT/
|
||||
python3 make.py --ota --partition-size=4194304 --flash-size=16 esp32 BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=st7789 INDEV=gt911 USER_C_MODULE="$codebasedir"/micropython-camera-API/src/micropython.cmake USER_C_MODULE="$codebasedir"/secp256k1-embedded-ecdh/micropython.cmake USER_C_MODULE="$codebasedir"/c_mpos/micropython.cmake CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y "$frozenmanifest"
|
||||
python3 make.py --ota --partition-size=4194304 --flash-size=16 esp32 BOARD=ESP32_GENERIC_S3 BOARD_VARIANT=SPIRAM_OCT DISPLAY=st7789 INDEV=cst816s USER_C_MODULE="$codebasedir"/micropython-camera-API/src/micropython.cmake USER_C_MODULE="$codebasedir"/secp256k1-embedded-ecdh/micropython.cmake USER_C_MODULE="$codebasedir"/c_mpos/micropython.cmake CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y "$frozenmanifest"
|
||||
popd
|
||||
echo "Grepping..."
|
||||
pwd
|
||||
|
||||
Reference in New Issue
Block a user