mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Compare commits
21 Commits
4be4aab79a
...
983f5d7f3f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
983f5d7f3f | ||
|
|
15070dd722 | ||
|
|
77c8cc5fe0 | ||
|
|
8890c80e3d | ||
|
|
c8fc7903f4 | ||
|
|
8403340257 | ||
|
|
55488e08a5 | ||
|
|
8ece14c1a7 | ||
|
|
5f766069ad | ||
|
|
21ceec4229 | ||
|
|
29e94ad8e8 | ||
|
|
4b1837f72e | ||
|
|
200c702d1d | ||
|
|
9d0e012b1c | ||
|
|
46961ffe0b | ||
|
|
643676c2bb | ||
|
|
7c3f3d58c8 | ||
|
|
bb8ed01a23 | ||
|
|
110a93695f | ||
|
|
310eeae048 | ||
|
|
9110a21450 |
29
.github/workflows/firmware.yml
vendored
29
.github/workflows/firmware.yml
vendored
@ -48,11 +48,26 @@ jobs:
|
||||
- ARDUINO_NICLA_VISION
|
||||
- ARDUINO_NANO_RP2040_CONNECT
|
||||
- ARDUINO_NANO_33_BLE_SENSE
|
||||
- DOCKER
|
||||
profile: [0] # default profile
|
||||
qemu: [false]
|
||||
profile: [0]
|
||||
artifacts: [true]
|
||||
include:
|
||||
- target: DOCKER
|
||||
qemu: false
|
||||
profile: 0
|
||||
artifacts: false
|
||||
- target: OPENMV_N6
|
||||
qemu: false
|
||||
profile: 1
|
||||
artifacts: false
|
||||
- target: MPS2_AN500
|
||||
qemu: true
|
||||
profile: 0
|
||||
artifacts: false
|
||||
- target: MPS3_AN547
|
||||
qemu: true
|
||||
profile: 0
|
||||
artifacts: false
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: '⏳ Checkout repository'
|
||||
@ -82,6 +97,8 @@ jobs:
|
||||
|
||||
- name: '🛠 Install dependencies'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y qemu-system-arm
|
||||
pip install -r .github/workflows/requirements.txt
|
||||
flake8 --version
|
||||
pytest --version
|
||||
@ -104,10 +121,14 @@ jobs:
|
||||
run: source tools/ci.sh && ci_install_stedgeai ${HOME}/cache/stedgeai
|
||||
|
||||
- name: '🏗 Build firmware'
|
||||
run: source tools/ci.sh && ci_build_target ${{ matrix.target }} ${{ matrix.profile }}
|
||||
run: source tools/ci.sh && ci_build_target ${{ matrix.target }} ${{ matrix.qemu }} ${{ matrix.profile }} ${{ matrix.artifacts }}
|
||||
|
||||
- name: '🧪 Run QEMU tests'
|
||||
if: matrix.qemu
|
||||
run: source tools/ci.sh && ci_run_qemu_tests ${{ matrix.target }}
|
||||
|
||||
- name: '⬆ Upload artifacts'
|
||||
if: matrix.target != 'DOCKER' && matrix.profile == 0
|
||||
if: matrix.artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
|
||||
1
.github/workflows/requirements.txt
vendored
1
.github/workflows/requirements.txt
vendored
@ -5,3 +5,4 @@ tabulate==0.9.0
|
||||
cryptography==44.0.1
|
||||
pyelftools==0.27
|
||||
colorama==0.4.6
|
||||
mpremote
|
||||
|
||||
164
boards/MPS2_AN500/imlib_config.h
Normal file
164
boards/MPS2_AN500/imlib_config.h
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2025 OpenMV, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Image library configuration.
|
||||
*/
|
||||
#ifndef __IMLIB_CONFIG_H__
|
||||
#define __IMLIB_CONFIG_H__
|
||||
|
||||
// Enable Image I/O
|
||||
#define IMLIB_ENABLE_IMAGE_IO
|
||||
|
||||
// Enable Image File I/O
|
||||
#define IMLIB_ENABLE_IMAGE_FILE_IO
|
||||
|
||||
// Enable LAB LUT
|
||||
#define IMLIB_ENABLE_LAB_LUT
|
||||
|
||||
// Enable YUV LUT
|
||||
//#define IMLIB_ENABLE_YUV_LUT
|
||||
|
||||
// Enable ISP ops
|
||||
#define IMLIB_ENABLE_ISP_OPS
|
||||
|
||||
// Enable binary ops
|
||||
#define IMLIB_ENABLE_BINARY_OPS
|
||||
|
||||
// Enable math ops
|
||||
#define IMLIB_ENABLE_MATH_OPS
|
||||
|
||||
// Enable flood_fill()
|
||||
#define IMLIB_ENABLE_FLOOD_FILL
|
||||
|
||||
// Enable mean()
|
||||
#define IMLIB_ENABLE_MEAN
|
||||
|
||||
// Enable median()
|
||||
#define IMLIB_ENABLE_MEDIAN
|
||||
|
||||
// Enable mode()
|
||||
#define IMLIB_ENABLE_MODE
|
||||
|
||||
// Enable midpoint()
|
||||
#define IMLIB_ENABLE_MIDPOINT
|
||||
|
||||
// Enable morph()
|
||||
#define IMLIB_ENABLE_MORPH
|
||||
|
||||
// Enable Gaussian
|
||||
#define IMLIB_ENABLE_GAUSSIAN
|
||||
|
||||
// Enable Laplacian
|
||||
#define IMLIB_ENABLE_LAPLACIAN
|
||||
|
||||
// Enable bilateral()
|
||||
#define IMLIB_ENABLE_BILATERAL
|
||||
|
||||
// Enable linpolar()
|
||||
#define IMLIB_ENABLE_LINPOLAR
|
||||
|
||||
// Enable logpolar()
|
||||
#define IMLIB_ENABLE_LOGPOLAR
|
||||
|
||||
// Enable lens_corr()
|
||||
#define IMLIB_ENABLE_LENS_CORR
|
||||
|
||||
// Enable rotation_corr()
|
||||
#define IMLIB_ENABLE_ROTATION_CORR
|
||||
|
||||
// Enable phasecorrelate()
|
||||
#if defined(IMLIB_ENABLE_ROTATION_CORR)
|
||||
#define IMLIB_ENABLE_FIND_DISPLACEMENT
|
||||
#endif
|
||||
|
||||
// Enable get_similarity()
|
||||
#define IMLIB_ENABLE_GET_SIMILARITY
|
||||
|
||||
// Enable find_lines()
|
||||
#define IMLIB_ENABLE_FIND_LINES
|
||||
|
||||
// Enable find_line_segments()
|
||||
#define IMLIB_ENABLE_FIND_LINE_SEGMENTS
|
||||
|
||||
// Enable find_circles()
|
||||
#define IMLIB_ENABLE_FIND_CIRCLES
|
||||
|
||||
// Enable find_rects()
|
||||
#define IMLIB_ENABLE_FIND_RECTS
|
||||
|
||||
// Enable find_qrcodes() (14 KB)
|
||||
#define IMLIB_ENABLE_QRCODES
|
||||
|
||||
// Enable find_apriltags() (64 KB)
|
||||
#define IMLIB_ENABLE_APRILTAGS
|
||||
#define IMLIB_ENABLE_APRILTAGS_TAG36H11
|
||||
|
||||
// Enable fine find_apriltags() - (8-way connectivity versus 4-way connectivity)
|
||||
// #define IMLIB_ENABLE_FINE_APRILTAGS
|
||||
|
||||
// Enable high res find_apriltags() - uses more RAM
|
||||
// #define IMLIB_ENABLE_HIGH_RES_APRILTAGS
|
||||
|
||||
// Enable find_datamatrices() (26 KB)
|
||||
#define IMLIB_ENABLE_DATAMATRICES
|
||||
|
||||
// Enable find_barcodes() (42 KB)
|
||||
#define IMLIB_ENABLE_BARCODES
|
||||
|
||||
// Enable find_features() and Haar cascades.
|
||||
#define IMLIB_ENABLE_FEATURES
|
||||
|
||||
// Enable Tensor Flow
|
||||
#define IMLIB_ENABLE_STAI
|
||||
|
||||
// Enable AGAST.
|
||||
#define IMLIB_ENABLE_AGAST
|
||||
|
||||
// Enable find_template()
|
||||
#define IMLIB_FIND_TEMPLATE
|
||||
|
||||
// Enable find_lbp()
|
||||
#define IMLIB_ENABLE_FIND_LBP
|
||||
|
||||
// Enable find_keypoints()
|
||||
#if defined(IMLIB_ENABLE_FAST) || defined(IMLIB_ENABLE_AGAST)
|
||||
#define IMLIB_ENABLE_FIND_KEYPOINTS
|
||||
#endif
|
||||
|
||||
// Enable load, save and match descriptor
|
||||
#define IMLIB_ENABLE_DESCRIPTOR
|
||||
|
||||
// Enable find_hog()
|
||||
#define IMLIB_ENABLE_HOG
|
||||
|
||||
// Enable selective_search()
|
||||
#define IMLIB_ENABLE_SELECTIVE_SEARCH
|
||||
|
||||
// Enable PNG encoder/decoder
|
||||
#define IMLIB_ENABLE_PNG_ENCODER
|
||||
#define IMLIB_ENABLE_PNG_DECODER
|
||||
|
||||
// Stereo Imaging
|
||||
#define IMLIB_ENABLE_STEREO_DISPARITY
|
||||
|
||||
#endif //__IMLIB_CONFIG_H__
|
||||
5
boards/MPS2_AN500/manifest.py
Normal file
5
boards/MPS2_AN500/manifest.py
Normal file
@ -0,0 +1,5 @@
|
||||
# OpenMV library
|
||||
add_library("openmv-lib", "$(OMV_LIB_DIR)")
|
||||
|
||||
# Libraries
|
||||
require("ml", library="openmv-lib")
|
||||
71
boards/MPS2_AN500/omv_boardconfig.h
Normal file
71
boards/MPS2_AN500/omv_boardconfig.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2025 OpenMV, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Board configuration and pin definitions.
|
||||
*/
|
||||
#ifndef __OMV_BOARDCONFIG_H__
|
||||
#define __OMV_BOARDCONFIG_H__
|
||||
|
||||
// Architecture info
|
||||
#define OMV_BOARD_ARCH "MPS2 AN500" // 33 chars max
|
||||
#define OMV_BOARD_TYPE "M7"
|
||||
|
||||
// JPEG configuration.
|
||||
#define OMV_JPEG_CODEC_ENABLE (0)
|
||||
#define OMV_JPEG_QUALITY_LOW (35)
|
||||
#define OMV_JPEG_QUALITY_HIGH (60)
|
||||
#define OMV_JPEG_QUALITY_THRESHOLD (160 * 120 * 2)
|
||||
|
||||
// UMM heap block size
|
||||
#define OMV_UMM_BLOCK_SIZE 16
|
||||
|
||||
#define OMV_MAIN_MEMORY SRAM1 // Data/BSS memory
|
||||
#define OMV_STACK_MEMORY SRAM1 // stack memory
|
||||
#define OMV_STACK_SIZE (64K)
|
||||
#define OMV_FB_MEMORY SRAM1 // Framebuffer, fb_alloc
|
||||
#define OMV_FB_SIZE (1M) // FB memory: header + QVGA/GS image
|
||||
#define OMV_FB_ALLOC_SIZE (1850K) // minimum fb alloc size
|
||||
#define OMV_GC_BLOCK0_MEMORY SRAM1 // Main GC block
|
||||
#define OMV_GC_BLOCK0_SIZE (1M)
|
||||
#define OMV_SB_SIZE (128K) // IDE JPEG buffer size (header + data).
|
||||
|
||||
// Memory map.
|
||||
#define OMV_SRAM1_ORIGIN 0x20000000
|
||||
#define OMV_SRAM1_LENGTH 4M
|
||||
|
||||
#define OMV_FLASH_ISR_ORIGIN 0x00000000
|
||||
#define OMV_FLASH_ISR_LENGTH 4K
|
||||
|
||||
#define OMV_FLASH_TXT_ORIGIN 0x00001000
|
||||
#define OMV_FLASH_TXT_LENGTH 4092M
|
||||
|
||||
// CSI configuration.
|
||||
#define OMV_CSI_I2C_ID (0)
|
||||
#define OMV_CSI_I2C_SPEED (OMV_I2C_SPEED_STANDARD)
|
||||
#define OMV_CSI_CLK_SOURCE (OMV_CSI_CLK_SOURCE_TIM)
|
||||
#define OMV_CSI_CLK_FREQUENCY (24000000)
|
||||
#define OMV_CSI_HW_CROP_ENABLE (0)
|
||||
#define OMV_CSI_MAX_DEVICES (3)
|
||||
|
||||
#define OMV_SOFTCSI_ENABLE (1)
|
||||
#endif //__OMV_BOARDCONFIG_H__
|
||||
24
boards/MPS2_AN500/omv_boardconfig.mk
Executable file
24
boards/MPS2_AN500/omv_boardconfig.mk
Executable file
@ -0,0 +1,24 @@
|
||||
CPU=cortex-m7
|
||||
FPU=fpv5-d16
|
||||
PORT=qemu
|
||||
QEMU_MACHINE=mps2-an500
|
||||
OMV_ENABLE_BL=0
|
||||
OMV_LEPTON_SDK_ENABLE=0
|
||||
OMV_BOARD_CFLAGS= -DQEMU_SOC_MPS2 \
|
||||
-DCPU_FREQ_HZ=25000000
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 0
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_WINC1500 = 0
|
||||
MICROPY_PY_DISPLAY = 0
|
||||
MICROPY_PY_TV = 0
|
||||
MICROPY_PY_ML = 1
|
||||
MICROPY_PY_ML_TFLM = 1
|
||||
MICROPY_PY_LWIP = 0
|
||||
MICROPY_PY_SSL = 0
|
||||
MICROPY_PY_SSL_ECDSA_SIGN_ALT = 0
|
||||
MICROPY_SSL_MBEDTLS = 0
|
||||
MICROPY_PY_NETWORK_CYW43 = 0
|
||||
MICROPY_PY_BLUETOOTH = 0
|
||||
MICROPY_BLUETOOTH_NIMBLE = 0
|
||||
6
boards/MPS2_AN500/romfs.json
Normal file
6
boards/MPS2_AN500/romfs.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"0": {
|
||||
"size": "0x20000",
|
||||
"entries": [ ]
|
||||
}
|
||||
}
|
||||
17
boards/MPS2_AN500/ulab_config.h
Normal file
17
boards/MPS2_AN500/ulab_config.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013-2016 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* Ulab config file.
|
||||
*
|
||||
*/
|
||||
#ifndef __ULAB_CONFIG_H__
|
||||
#define __ULAB_CONFIG_H__
|
||||
// Override ulab defaults here.
|
||||
#define ULAB_MAX_DIMS (4)
|
||||
#define ULAB_SUPPORTS_COMPLEX (0)
|
||||
#define ULAB_SCIPY_HAS_OPTIMIZE_MODULE (0)
|
||||
#define ULAB_SCIPY_HAS_SPECIAL_MODULE (0)
|
||||
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (0)
|
||||
#endif //__ULAB_CONFIG_H__
|
||||
164
boards/MPS3_AN547/imlib_config.h
Normal file
164
boards/MPS3_AN547/imlib_config.h
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2025 OpenMV, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Image library configuration.
|
||||
*/
|
||||
#ifndef __IMLIB_CONFIG_H__
|
||||
#define __IMLIB_CONFIG_H__
|
||||
|
||||
// Enable Image I/O
|
||||
#define IMLIB_ENABLE_IMAGE_IO
|
||||
|
||||
// Enable Image File I/O
|
||||
#define IMLIB_ENABLE_IMAGE_FILE_IO
|
||||
|
||||
// Enable LAB LUT
|
||||
#define IMLIB_ENABLE_LAB_LUT
|
||||
|
||||
// Enable YUV LUT
|
||||
//#define IMLIB_ENABLE_YUV_LUT
|
||||
|
||||
// Enable ISP ops
|
||||
#define IMLIB_ENABLE_ISP_OPS
|
||||
|
||||
// Enable binary ops
|
||||
#define IMLIB_ENABLE_BINARY_OPS
|
||||
|
||||
// Enable math ops
|
||||
#define IMLIB_ENABLE_MATH_OPS
|
||||
|
||||
// Enable flood_fill()
|
||||
#define IMLIB_ENABLE_FLOOD_FILL
|
||||
|
||||
// Enable mean()
|
||||
#define IMLIB_ENABLE_MEAN
|
||||
|
||||
// Enable median()
|
||||
#define IMLIB_ENABLE_MEDIAN
|
||||
|
||||
// Enable mode()
|
||||
#define IMLIB_ENABLE_MODE
|
||||
|
||||
// Enable midpoint()
|
||||
#define IMLIB_ENABLE_MIDPOINT
|
||||
|
||||
// Enable morph()
|
||||
#define IMLIB_ENABLE_MORPH
|
||||
|
||||
// Enable Gaussian
|
||||
#define IMLIB_ENABLE_GAUSSIAN
|
||||
|
||||
// Enable Laplacian
|
||||
#define IMLIB_ENABLE_LAPLACIAN
|
||||
|
||||
// Enable bilateral()
|
||||
#define IMLIB_ENABLE_BILATERAL
|
||||
|
||||
// Enable linpolar()
|
||||
#define IMLIB_ENABLE_LINPOLAR
|
||||
|
||||
// Enable logpolar()
|
||||
#define IMLIB_ENABLE_LOGPOLAR
|
||||
|
||||
// Enable lens_corr()
|
||||
#define IMLIB_ENABLE_LENS_CORR
|
||||
|
||||
// Enable rotation_corr()
|
||||
#define IMLIB_ENABLE_ROTATION_CORR
|
||||
|
||||
// Enable phasecorrelate()
|
||||
#if defined(IMLIB_ENABLE_ROTATION_CORR)
|
||||
#define IMLIB_ENABLE_FIND_DISPLACEMENT
|
||||
#endif
|
||||
|
||||
// Enable get_similarity()
|
||||
#define IMLIB_ENABLE_GET_SIMILARITY
|
||||
|
||||
// Enable find_lines()
|
||||
#define IMLIB_ENABLE_FIND_LINES
|
||||
|
||||
// Enable find_line_segments()
|
||||
#define IMLIB_ENABLE_FIND_LINE_SEGMENTS
|
||||
|
||||
// Enable find_circles()
|
||||
#define IMLIB_ENABLE_FIND_CIRCLES
|
||||
|
||||
// Enable find_rects()
|
||||
#define IMLIB_ENABLE_FIND_RECTS
|
||||
|
||||
// Enable find_qrcodes() (14 KB)
|
||||
#define IMLIB_ENABLE_QRCODES
|
||||
|
||||
// Enable find_apriltags() (64 KB)
|
||||
#define IMLIB_ENABLE_APRILTAGS
|
||||
#define IMLIB_ENABLE_APRILTAGS_TAG36H11
|
||||
|
||||
// Enable fine find_apriltags() - (8-way connectivity versus 4-way connectivity)
|
||||
// #define IMLIB_ENABLE_FINE_APRILTAGS
|
||||
|
||||
// Enable high res find_apriltags() - uses more RAM
|
||||
// #define IMLIB_ENABLE_HIGH_RES_APRILTAGS
|
||||
|
||||
// Enable find_datamatrices() (26 KB)
|
||||
#define IMLIB_ENABLE_DATAMATRICES
|
||||
|
||||
// Enable find_barcodes() (42 KB)
|
||||
#define IMLIB_ENABLE_BARCODES
|
||||
|
||||
// Enable find_features() and Haar cascades.
|
||||
#define IMLIB_ENABLE_FEATURES
|
||||
|
||||
// Enable Tensor Flow
|
||||
#define IMLIB_ENABLE_STAI
|
||||
|
||||
// Enable AGAST.
|
||||
#define IMLIB_ENABLE_AGAST
|
||||
|
||||
// Enable find_template()
|
||||
#define IMLIB_FIND_TEMPLATE
|
||||
|
||||
// Enable find_lbp()
|
||||
#define IMLIB_ENABLE_FIND_LBP
|
||||
|
||||
// Enable find_keypoints()
|
||||
#if defined(IMLIB_ENABLE_FAST) || defined(IMLIB_ENABLE_AGAST)
|
||||
#define IMLIB_ENABLE_FIND_KEYPOINTS
|
||||
#endif
|
||||
|
||||
// Enable load, save and match descriptor
|
||||
#define IMLIB_ENABLE_DESCRIPTOR
|
||||
|
||||
// Enable find_hog()
|
||||
#define IMLIB_ENABLE_HOG
|
||||
|
||||
// Enable selective_search()
|
||||
#define IMLIB_ENABLE_SELECTIVE_SEARCH
|
||||
|
||||
// Enable PNG encoder/decoder
|
||||
#define IMLIB_ENABLE_PNG_ENCODER
|
||||
#define IMLIB_ENABLE_PNG_DECODER
|
||||
|
||||
// Stereo Imaging
|
||||
#define IMLIB_ENABLE_STEREO_DISPARITY
|
||||
|
||||
#endif //__IMLIB_CONFIG_H__
|
||||
5
boards/MPS3_AN547/manifest.py
Normal file
5
boards/MPS3_AN547/manifest.py
Normal file
@ -0,0 +1,5 @@
|
||||
# OpenMV library
|
||||
add_library("openmv-lib", "$(OMV_LIB_DIR)")
|
||||
|
||||
# Libraries
|
||||
require("ml", library="openmv-lib")
|
||||
75
boards/MPS3_AN547/omv_boardconfig.h
Normal file
75
boards/MPS3_AN547/omv_boardconfig.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2025 OpenMV, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Board configuration and pin definitions.
|
||||
*/
|
||||
#ifndef __OMV_BOARDCONFIG_H__
|
||||
#define __OMV_BOARDCONFIG_H__
|
||||
|
||||
// Architecture info
|
||||
#define OMV_BOARD_ARCH "MPS3 AN547" // 33 chars max
|
||||
#define OMV_BOARD_TYPE "M55"
|
||||
|
||||
// JPEG configuration.
|
||||
#define OMV_JPEG_CODEC_ENABLE (0)
|
||||
#define OMV_JPEG_QUALITY_LOW (35)
|
||||
#define OMV_JPEG_QUALITY_HIGH (60)
|
||||
#define OMV_JPEG_QUALITY_THRESHOLD (160 * 120 * 2)
|
||||
|
||||
// UMM heap block size
|
||||
#define OMV_UMM_BLOCK_SIZE 16
|
||||
|
||||
#define OMV_MAIN_MEMORY SRAM1 // Data/BSS memory
|
||||
#define OMV_STACK_MEMORY SRAM1 // stack memory
|
||||
#define OMV_STACK_SIZE (64K)
|
||||
#define OMV_FB_MEMORY SRAM1 // Framebuffer, fb_alloc
|
||||
#define OMV_FB_SIZE (1M) // FB memory: header + QVGA/GS image
|
||||
#define OMV_FB_ALLOC_SIZE (1850K) // minimum fb alloc size
|
||||
#define OMV_GC_BLOCK0_MEMORY SRAM1 // Main GC block
|
||||
#define OMV_GC_BLOCK0_SIZE (1M)
|
||||
#define OMV_SB_SIZE (128K) // IDE JPEG buffer size (header + data).
|
||||
|
||||
// Memory map.
|
||||
#define OMV_ITCM_ORIGIN 0x00000000
|
||||
#define OMV_ITCM_LENGTH 512K
|
||||
|
||||
#define OMV_SRAM1_ORIGIN 0x21000000
|
||||
#define OMV_SRAM1_LENGTH 4M
|
||||
|
||||
#define OMV_FLASH_ISR_ORIGIN 0x00000000
|
||||
#define OMV_FLASH_ISR_LENGTH 4K
|
||||
|
||||
#define OMV_FLASH_TXT_ORIGIN 0x01000000
|
||||
#define OMV_FLASH_TXT_LENGTH 2M
|
||||
|
||||
// CSI configuration.
|
||||
#define OMV_CSI_I2C_ID (0)
|
||||
#define OMV_CSI_I2C_SPEED (OMV_I2C_SPEED_STANDARD)
|
||||
#define OMV_CSI_CLK_SOURCE (OMV_CSI_CLK_SOURCE_TIM)
|
||||
#define OMV_CSI_CLK_FREQUENCY (24000000)
|
||||
#define OMV_CSI_HW_CROP_ENABLE (0)
|
||||
#define OMV_CSI_MAX_DEVICES (3)
|
||||
|
||||
#define OMV_SOFTCSI_ENABLE (1)
|
||||
|
||||
#endif //__OMV_BOARDCONFIG_H__
|
||||
25
boards/MPS3_AN547/omv_boardconfig.mk
Normal file
25
boards/MPS3_AN547/omv_boardconfig.mk
Normal file
@ -0,0 +1,25 @@
|
||||
CPU=cortex-m55
|
||||
FPU=auto
|
||||
PORT=qemu
|
||||
QEMU_MACHINE=mps3-an547
|
||||
OMV_ENABLE_BL=0
|
||||
OMV_LEPTON_SDK_ENABLE=0
|
||||
OMV_BOARD_CFLAGS= -mcmse \
|
||||
-DQEMU_SOC_MPS3 \
|
||||
-DCPU_FREQ_HZ=32000000
|
||||
MICROPY_PY_CSI = 1
|
||||
MICROPY_PY_CSI_NG = 1
|
||||
MICROPY_PY_FIR = 0
|
||||
MICROPY_PY_ULAB = 1
|
||||
MICROPY_PY_WINC1500 = 0
|
||||
MICROPY_PY_DISPLAY = 0
|
||||
MICROPY_PY_TV = 0
|
||||
MICROPY_PY_ML = 1
|
||||
MICROPY_PY_ML_TFLM = 1
|
||||
MICROPY_PY_LWIP = 0
|
||||
MICROPY_PY_SSL = 0
|
||||
MICROPY_PY_SSL_ECDSA_SIGN_ALT = 0
|
||||
MICROPY_SSL_MBEDTLS = 0
|
||||
MICROPY_PY_NETWORK_CYW43 = 0
|
||||
MICROPY_PY_BLUETOOTH = 0
|
||||
MICROPY_BLUETOOTH_NIMBLE = 0
|
||||
6
boards/MPS3_AN547/romfs.json
Normal file
6
boards/MPS3_AN547/romfs.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"0": {
|
||||
"size": "0x20000",
|
||||
"entries": [ ]
|
||||
}
|
||||
}
|
||||
17
boards/MPS3_AN547/ulab_config.h
Normal file
17
boards/MPS3_AN547/ulab_config.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013-2016 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* Ulab config file.
|
||||
*
|
||||
*/
|
||||
#ifndef __ULAB_CONFIG_H__
|
||||
#define __ULAB_CONFIG_H__
|
||||
// Override ulab defaults here.
|
||||
#define ULAB_MAX_DIMS (4)
|
||||
#define ULAB_SUPPORTS_COMPLEX (0)
|
||||
#define ULAB_SCIPY_HAS_OPTIMIZE_MODULE (0)
|
||||
#define ULAB_SCIPY_HAS_SPECIAL_MODULE (0)
|
||||
#define ULAB_FFT_IS_NUMPY_COMPATIBLE (0)
|
||||
#endif //__ULAB_CONFIG_H__
|
||||
@ -38,6 +38,18 @@
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/palm_detection_full_192.tflite",
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/hand_landmarks_full_224.tflite",
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "haar",
|
||||
"path": "{TOP}/lib/haar/haarcascade_eye.xml",
|
||||
|
||||
@ -25,6 +25,18 @@
|
||||
"path": "{TOP}/lib/models/blazeface_front_128.tflite",
|
||||
"alignment": 32,
|
||||
"profile": "default"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/palm_detection_full_192.tflite",
|
||||
"alignment": 32,
|
||||
"profile": "default"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/hand_landmarks_full_224.tflite",
|
||||
"alignment": 32,
|
||||
"profile": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef QEMU_BUILD
|
||||
#include <stdlib.h>
|
||||
#include "py/mphal.h"
|
||||
#include "py/runtime.h"
|
||||
@ -192,3 +192,4 @@ __attribute__((naked)) void PendSV_Handler(void) {
|
||||
"nlr_jump_ptr: .word nlr_jump\n"
|
||||
);
|
||||
}
|
||||
#endif // QEMU_BUILD
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
*
|
||||
* USB debugger.
|
||||
*/
|
||||
#ifndef QEMU_BUILD
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "py/nlr.h"
|
||||
@ -452,3 +453,4 @@ void usbdbg_control(void *buffer, uint8_t request, uint32_t size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // QEMU_BUILD
|
||||
|
||||
@ -35,7 +35,10 @@
|
||||
#include "omv_i2c.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
static uint32_t step = 0;
|
||||
|
||||
static int reset(omv_csi_t *csi) {
|
||||
step = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -88,7 +91,6 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
framebuffer_to_image(fb, image);
|
||||
image->pixels = buffer->data;
|
||||
|
||||
static uint32_t step = 0;
|
||||
uint32_t offset = (step++ / 4);
|
||||
|
||||
for (size_t y = 0; y < image->h; y++) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
lib/models/hand_landmarks_full_224.tflite
Normal file
BIN
lib/models/hand_landmarks_full_224.tflite
Normal file
Binary file not shown.
BIN
lib/models/palm_detection_full_192.tflite
Normal file
BIN
lib/models/palm_detection_full_192.tflite
Normal file
Binary file not shown.
@ -53,7 +53,7 @@ image_t *py_helper_arg_to_image(const mp_obj_t arg, uint32_t flags) {
|
||||
img_read_settings_t rs;
|
||||
imlib_read_geometry(&fp, image, path, &rs);
|
||||
file_close(&fp);
|
||||
image->data = fb_alloc(image_size(image), FB_ALLOC_CACHE_ALIGN);
|
||||
image_alloc(image, image_size(image));
|
||||
imlib_load_image(image, path);
|
||||
#else
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Image I/O is not supported"));
|
||||
|
||||
@ -464,7 +464,12 @@ static mp_obj_t py_imageio_sync(mp_obj_t self) {
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(py_imageio_sync_obj, py_imageio_sync);
|
||||
|
||||
static mp_obj_t py_imageio_close(mp_obj_t self) {
|
||||
py_imageio_obj_t *stream = py_imageio_obj(self);
|
||||
py_imageio_obj_t *stream = MP_OBJ_TO_PTR(self);
|
||||
|
||||
if (stream->closed) {
|
||||
// Already closed
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
if (0) {
|
||||
#if defined(IMLIB_ENABLE_IMAGE_FILE_IO)
|
||||
@ -477,7 +482,7 @@ static mp_obj_t py_imageio_close(mp_obj_t self) {
|
||||
|
||||
stream->closed = true;
|
||||
|
||||
return self;
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(py_imageio_close_obj, py_imageio_close);
|
||||
|
||||
|
||||
@ -262,16 +262,17 @@ static mp_obj_t py_ml_model_predict(size_t n_args, const mp_obj_t *pos_args, mp_
|
||||
}
|
||||
|
||||
bool callback = args[ARG_callback].u_obj != mp_const_none;
|
||||
bool postprocess = model->postprocess != mp_const_none || callback;
|
||||
|
||||
py_ml_process_input(model, pos_args[1]);
|
||||
ml_backend_run_inference(model);
|
||||
|
||||
mp_obj_t output = py_ml_process_output(model, !callback);
|
||||
mp_obj_t output = py_ml_process_output(model, !postprocess);
|
||||
|
||||
if (callback) {
|
||||
if (postprocess) {
|
||||
// Pass model, inputs, outputs to the post-processing callback.
|
||||
mp_obj_t fargs[3] = { MP_OBJ_FROM_PTR(model), pos_args[1], output };
|
||||
output = mp_call_function_n_kw(args[ARG_callback].u_obj, 3, 0, fargs);
|
||||
output = mp_call_function_n_kw(callback ? args[ARG_callback].u_obj : model->postprocess, 3, 0, fargs);
|
||||
}
|
||||
|
||||
return output;
|
||||
@ -313,6 +314,9 @@ static void py_ml_model_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
case MP_QSTR_output_zero_point:
|
||||
dest[0] = MP_OBJ_FROM_PTR(self->output_zero_point);
|
||||
break;
|
||||
case MP_QSTR_postprocess:
|
||||
dest[0] = self->postprocess;
|
||||
break;
|
||||
default:
|
||||
// Continue lookup in locals_dict.
|
||||
dest[1] = MP_OBJ_SENTINEL;
|
||||
@ -322,10 +326,11 @@ static void py_ml_model_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
}
|
||||
|
||||
mp_obj_t py_ml_model_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_path, ARG_load_to_fb };
|
||||
enum { ARG_path, ARG_load_to_fb, ARG_postprocess };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_path, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_load_to_fb, MP_ARG_REQUIRED | MP_ARG_BOOL },
|
||||
{ MP_QSTR_postprocess, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
};
|
||||
|
||||
// Parse args.
|
||||
@ -334,6 +339,7 @@ mp_obj_t py_ml_model_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
|
||||
|
||||
//const char *path = mp_obj_str_get_str(args[ARG_path].u_obj);
|
||||
py_ml_model_obj_t *model = mp_obj_malloc_with_finaliser(py_ml_model_obj_t, &py_ml_model_type);
|
||||
model->postprocess = args[ARG_postprocess].u_obj;
|
||||
|
||||
#if MICROPY_VFS
|
||||
mp_obj_t file_args[2] = {
|
||||
|
||||
@ -51,6 +51,7 @@ typedef struct py_ml_model_obj {
|
||||
mp_obj_tuple_t *output_scale;
|
||||
mp_obj_tuple_t *output_zero_point;
|
||||
mp_obj_tuple_t *output_dtype;
|
||||
mp_obj_t postprocess; // Post-processing object.
|
||||
void *state; // Private context for the backend.
|
||||
} py_ml_model_obj_t;
|
||||
|
||||
|
||||
@ -55,7 +55,8 @@ static mp_obj_t py_omv_board_type() {
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_board_type_obj, py_omv_board_type);
|
||||
|
||||
static mp_obj_t py_omv_board_id() {
|
||||
char str[25];
|
||||
char str[25] = {0};
|
||||
#ifdef OMV_BOARD_UID_ADDR
|
||||
snprintf(str, 25, "%08X%08X%08X",
|
||||
#if (OMV_BOARD_UID_SIZE == 2)
|
||||
0U,
|
||||
@ -64,6 +65,7 @@ static mp_obj_t py_omv_board_id() {
|
||||
#endif
|
||||
*((unsigned int *) (OMV_BOARD_UID_ADDR + OMV_BOARD_UID_OFFSET * 1)),
|
||||
*((unsigned int *) (OMV_BOARD_UID_ADDR + OMV_BOARD_UID_OFFSET * 0)));
|
||||
#endif
|
||||
return mp_obj_new_str(str, strlen(str));
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_board_id_obj, py_omv_board_id);
|
||||
|
||||
@ -162,6 +162,29 @@ int alif_hal_i2c_init(uint32_t bus_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int alif_hal_i3c_init(uint32_t bus_id) {
|
||||
omv_gpio_t scl_pin = NULL;
|
||||
omv_gpio_t sda_pin = NULL;
|
||||
|
||||
switch (bus_id) {
|
||||
#if defined(OMV_I3C0_ID)
|
||||
case OMV_I3C0_ID: {
|
||||
enable_i3c_clock();
|
||||
scl_pin = OMV_I3C0_SCL_PIN;
|
||||
sda_pin = OMV_I3C0_SDA_PIN;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
omv_gpio_config(scl_pin, OMV_GPIO_MODE_ALT_OD, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_MED, -1);
|
||||
omv_gpio_config(sda_pin, OMV_GPIO_MODE_ALT_OD, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_MED, -1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int alif_hal_spi_init(uint32_t bus_id, bool nss_enable, uint32_t nss_pol) {
|
||||
typedef struct {
|
||||
omv_gpio_t sclk_pin;
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
|
||||
void alif_hal_init(void);
|
||||
int alif_hal_i2c_init(uint32_t bus_id);
|
||||
int alif_hal_i3c_init(uint32_t bus_id);
|
||||
int alif_hal_spi_init(uint32_t bus_id, bool nss_enable, uint32_t nss_pol);
|
||||
int alif_hal_spi_deinit(uint32_t bus_id);
|
||||
int alif_hal_pdm_init(uint32_t pdm_id);
|
||||
|
||||
@ -173,6 +173,10 @@ int omv_i2c_init(omv_i2c_t *i2c, uint32_t bus_id, uint32_t speed) {
|
||||
while (inst->I3C_RESET_CTRL & 0x1) {
|
||||
}
|
||||
|
||||
i3c_master_set_dynamic_addr(inst);
|
||||
|
||||
i3c_master_enable_interrupts(inst);
|
||||
|
||||
// Initialize I2C controller
|
||||
i3c_master_init(inst);
|
||||
|
||||
@ -243,13 +247,13 @@ int omv_i2c_enable(omv_i2c_t *i2c, bool enable) {
|
||||
//TODO: For I3C this causes a lockup.
|
||||
I3C_Type *inst = i2c->inst;
|
||||
if (enable) {
|
||||
inst->I3C_DEVICE_CTRL = inst->I3C_DEVICE_CTRL & ~DEV_CTRL_ENABLE;
|
||||
while (inst->I3C_DEVICE_CTRL & DEV_CTRL_ENABLE) {
|
||||
inst->I3C_DEVICE_CTRL = inst->I3C_DEVICE_CTRL & ~I3C_DEVICE_CTRL_ENABLE;
|
||||
while (inst->I3C_DEVICE_CTRL & I3C_DEVICE_CTRL_ENABLE) {
|
||||
}
|
||||
|
||||
} else {
|
||||
inst->I3C_DEVICE_CTRL = inst->I3C_DEVICE_CTRL | DEV_CTRL_ENABLE;
|
||||
while (!(inst->I3C_DEVICE_CTRL & DEV_CTRL_ENABLE)) {
|
||||
inst->I3C_DEVICE_CTRL = inst->I3C_DEVICE_CTRL | I3C_DEVICE_CTRL_ENABLE;
|
||||
while (!(inst->I3C_DEVICE_CTRL & I3C_DEVICE_CTRL_ENABLE)) {
|
||||
}
|
||||
i3c_resume(inst);
|
||||
}
|
||||
@ -397,22 +401,31 @@ static int omv_i2c_transfer_timeout(omv_i2c_t *i2c, i2c_transfer_t *xfer, uint32
|
||||
|
||||
static int omv_i3c_transfer_timeout(omv_i2c_t *i2c, i2c_transfer_t *xfer, uint32_t timeout) {
|
||||
int ret = 0;
|
||||
I3C_XFER i3c_xfer = {0};
|
||||
i3c_xfer_t i3c_xfer = {0};
|
||||
I3C_Type *base = i2c->inst;
|
||||
|
||||
i3c_add_slv_to_dat(base, I2C_DAT_INDEX, 0, xfer->address);
|
||||
|
||||
i3c_xfer.xfer_cmd.speed = I3C_SPEED_SDR0;
|
||||
// i3c_xfer.addr = xfer->address;
|
||||
|
||||
i3c_xfer.xfer_cmd.addr_index = I2C_DAT_INDEX;
|
||||
// i3c_xfer.xfer_cmd.addr_depth = 1U;
|
||||
i3c_xfer.xfer_cmd.data_len = xfer->size;
|
||||
|
||||
if (xfer->direction == I2C_TRANSFER_READ) {
|
||||
i3c_xfer.rx_buf = xfer->data;
|
||||
i3c_xfer.rx_len = xfer->size;
|
||||
i3c_master_rx(base, &i3c_xfer, I2C_DAT_INDEX, i3c_xfer.rx_len);
|
||||
i3c_master_rx(base, &i3c_xfer);
|
||||
} else {
|
||||
i3c_xfer.tx_buf = xfer->data;
|
||||
i3c_xfer.tx_len = xfer->size;
|
||||
i3c_master_tx(base, &i3c_xfer, I2C_DAT_INDEX, i3c_xfer.tx_len);
|
||||
i3c_master_tx(base, &i3c_xfer);
|
||||
}
|
||||
|
||||
// Wait for the transfer to finish.
|
||||
mp_uint_t tick_start = mp_hal_ticks_ms();
|
||||
/* Waits till some response received */
|
||||
while (base->I3C_INTR_STATUS == 0) {
|
||||
if ((mp_hal_ticks_ms() - tick_start) >= timeout) {
|
||||
// Should not raise exception as we're not always in nlr context.
|
||||
@ -426,7 +439,7 @@ static int omv_i3c_transfer_timeout(omv_i2c_t *i2c, i2c_transfer_t *xfer, uint32
|
||||
// See Table 15-81 Response Data Structure
|
||||
uint32_t resp = base->I3C_RESPONSE_QUEUE_PORT;
|
||||
|
||||
if ((status & INTR_TRANSFER_ERR_STAT) || RESPONSE_PORT_ERR_STATUS(resp)) {
|
||||
if ((status & I3C_INTR_STATUS_TRANSFER_ERR_STS) || I3C_RESPONSE_QUEUE_PORT_ERR_STATUS(resp)) {
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
542
ports/alif/omv_i3c.c
Normal file
542
ports/alif/omv_i3c.c
Normal file
@ -0,0 +1,542 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2024 OpenMV, LLC.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Any redistribution, use, or modification in source or binary form
|
||||
* is done solely for personal benefit and not for any commercial
|
||||
* purpose or for monetary gain. For commercial licensing options,
|
||||
* please contact openmv@openmv.io
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
|
||||
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Alif I3C driver.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "omv_portconfig.h"
|
||||
#include "omv_boardconfig.h"
|
||||
#include "alif_hal.h"
|
||||
#include "omv_gpio.h"
|
||||
#include "omv_common.h"
|
||||
#include "omv_i3c.h"
|
||||
#include "sys_ctrl_i3c.h"
|
||||
|
||||
#define I3C_SCAN_TIMEOUT (10)
|
||||
#define I3C_XFER_TIMEOUT (1000)
|
||||
|
||||
/* I3C CCC (Common Command Codes) related definitions */
|
||||
#define I3C_CCC_DIRECT BIT(7)
|
||||
|
||||
#define I3C_CCC_ID(id, broadcast) ((id) | ((broadcast) ? 0 : I3C_CCC_DIRECT))
|
||||
|
||||
/* Commands valid in both broadcast and unicast modes */
|
||||
#define I3C_CCC_ENEC(broadcast) I3C_CCC_ID(0x0, broadcast)
|
||||
#define I3C_CCC_DISEC(broadcast) I3C_CCC_ID(0x1, broadcast)
|
||||
#define I3C_CCC_ENTAS(as, broadcast) I3C_CCC_ID(0x2 + (as), broadcast)
|
||||
#define I3C_CCC_RSTDAA(broadcast) I3C_CCC_ID(0x6, broadcast)
|
||||
#define I3C_CCC_SETMWL(broadcast) I3C_CCC_ID(0x9, broadcast)
|
||||
#define I3C_CCC_SETMRL(broadcast) I3C_CCC_ID(0xa, broadcast)
|
||||
#define I3C_CCC_SETXTIME(broadcast) ((broadcast) ? 0x28 : 0x98)
|
||||
#define I3C_CCC_VENDOR(id, broadcast) ((id) + ((broadcast) ? 0x61 : 0xe0))
|
||||
|
||||
/* Broadcast-only commands */
|
||||
#define I3C_CCC_ENTDAA I3C_CCC_ID(0x7, true)
|
||||
#define I3C_CCC_DEFSLVS I3C_CCC_ID(0x8, true)
|
||||
#define I3C_CCC_ENTTM I3C_CCC_ID(0xb, true)
|
||||
#define I3C_CCC_ENTHDR(x) I3C_CCC_ID(0x20 + (x), true)
|
||||
#define I3C_CCC_SETAASA I3C_CCC_ID(0x29, true)
|
||||
|
||||
/* Unicast-only commands */
|
||||
#define I3C_CCC_SETDASA I3C_CCC_ID(0x7, false)
|
||||
#define I3C_CCC_SETNEWDA I3C_CCC_ID(0x8, false)
|
||||
#define I3C_CCC_GETMWL I3C_CCC_ID(0xb, false)
|
||||
#define I3C_CCC_GETMRL I3C_CCC_ID(0xc, false)
|
||||
#define I3C_CCC_GETPID I3C_CCC_ID(0xd, false)
|
||||
#define I3C_CCC_GETBCR I3C_CCC_ID(0xe, false)
|
||||
#define I3C_CCC_GETDCR I3C_CCC_ID(0xf, false)
|
||||
#define I3C_CCC_GETSTATUS I3C_CCC_ID(0x10, false)
|
||||
#define I3C_CCC_GETACCMST I3C_CCC_ID(0x11, false)
|
||||
#define I3C_CCC_SETBRGTGT I3C_CCC_ID(0x13, false)
|
||||
#define I3C_CCC_GETMXDS I3C_CCC_ID(0x14, false)
|
||||
#define I3C_CCC_GETHDRCAP I3C_CCC_ID(0x15, false)
|
||||
#define I3C_CCC_GETXTIME I3C_CCC_ID(0x19, false)
|
||||
|
||||
/* List of some Defining byte values */
|
||||
#define I3C_CCC_DEF_BYTE_SYNC_TICK 0x7F
|
||||
#define I3C_CCC_DEF_BYTE_DELAY_TIME 0xBF
|
||||
#define I3C_CCC_DEF_BYTE_ASYNC_MODE0 0xDF
|
||||
#define I3C_CCC_DEF_BYTE_ASYNC_MODE1 0xEF
|
||||
#define I3C_CCC_DEF_BYTE_ASYNC_MODE2 0xF7
|
||||
#define I3C_CCC_DEF_BYTE_ASYNC_MODE3 0xFB
|
||||
#define I3C_CCC_DEF_BYTE_ASYNC_TRIG 0xFD
|
||||
#define I3C_CCC_DEF_BYTE_TPH 0x3F
|
||||
#define I3C_CCC_DEF_BYTE_TU 0x9F
|
||||
#define I3C_CCC_DEF_BYTE_ODR 0x8F
|
||||
|
||||
int omv_i3c_init(omv_i2c_t *i3c, uint32_t bus_id, uint32_t speed) {
|
||||
i3c->id = bus_id;
|
||||
i3c->initialized = false;
|
||||
i3c->cw_size = 0;
|
||||
i3c->speed = speed;
|
||||
|
||||
switch (speed) {
|
||||
case OMV_I3C_SPEED_SDR:
|
||||
speed = I3C_BUS_SDR0_SCL_RATE; // 12.5 Mbit/s
|
||||
break;
|
||||
case OMV_I3C_SPEED_HDR:
|
||||
speed = I3C_BUS_MAX_I3C_SCL_RATE; // 12.9 kbit/s
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (bus_id) {
|
||||
#if defined(OMV_I3C0_ID)
|
||||
case OMV_I3C0_ID: {
|
||||
i3c->inst = (I3C_Type *) I3C_BASE;
|
||||
i3c->scl_pin = OMV_I3C0_SCL_PIN;
|
||||
i3c->sda_pin = OMV_I3C0_SDA_PIN;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
alif_hal_i3c_init(bus_id);
|
||||
|
||||
// I3C controller in non-legacy mode.
|
||||
I3C_Type *base = i3c->inst;
|
||||
|
||||
// Core soft-reset.
|
||||
base->I3C_RESET_CTRL = 0x1;
|
||||
while (base->I3C_RESET_CTRL & 0x1) {
|
||||
}
|
||||
|
||||
i3c_master_set_dynamic_addr(base);
|
||||
|
||||
i3c_master_enable_interrupts(base);
|
||||
|
||||
/* Sets Slave Interrupt Request acceptability at master side */
|
||||
i3c_master_setup_slv_intr_req_ctrl(base, false);
|
||||
|
||||
/* Sets Master Request acceptability at master side */
|
||||
i3c_master_setup_mst_req_ctrl(base, false);
|
||||
|
||||
/* Sets up HJ acceptability at master side */
|
||||
i3c_master_setup_hot_join_ctrl(base, false);
|
||||
|
||||
// Initialize I2C controller
|
||||
i3c_master_init(base);
|
||||
|
||||
// Initialize I3C controller
|
||||
i3c_master_init(base);
|
||||
|
||||
// Configure clock and speed.
|
||||
i3c_normal_bus_clk_cfg(base, speed);
|
||||
|
||||
i3c->initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i3c_deinit(omv_i2c_t *i3c) {
|
||||
if (i3c->initialized) {
|
||||
// TODO
|
||||
i3c->initialized = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omv_i3c_get_addr_pos(omv_i2c_t *i3c, uint8_t addr) {
|
||||
size_t pos;
|
||||
|
||||
for (pos = 0; pos < i3c->cw_size; pos++) {
|
||||
if (addr == i3c->cw_buf[pos]) {
|
||||
return (int) pos;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void omv_i3c_add_dyn_addr_parity(uint8_t *dyn_addr) {
|
||||
uint8_t bit_iter = 0U;
|
||||
uint8_t xor_value = 0U;
|
||||
|
||||
/* XOR the 1st 7 bits of dynamic address*/
|
||||
xor_value = ((*dyn_addr) & (1U << 0U));
|
||||
|
||||
for (bit_iter = 1U; bit_iter < 7U; bit_iter++) {
|
||||
xor_value ^= (((*dyn_addr) >> bit_iter) & 1U);
|
||||
}
|
||||
|
||||
/* Assign the negated XOR value to 7th
|
||||
* bit of dynamic address */
|
||||
*dyn_addr |= ((~xor_value) << 7U);
|
||||
}
|
||||
|
||||
static int32_t omv_i3c_gen_dyn_addr(omv_i2c_t *i3c, uint8_t *ref_dyn_addr) {
|
||||
if (i3c->cw_size >= I3C_MAX_DEVS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we start assigning addresses from 0x09 */
|
||||
*ref_dyn_addr = (uint8_t) i3c->cw_size + I3C_NEXT_SLAVE_ADDR_OFFSET;
|
||||
while (true) {
|
||||
/* Checks if new address is not a self address and
|
||||
* not already assigned to some slave. If true then,
|
||||
* assign it otherwise increment it */
|
||||
if ((omv_i3c_get_addr_pos(i3c, *ref_dyn_addr) < 0) && (i3c_get_dynamic_addr(i3c->inst) != *ref_dyn_addr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
(*ref_dyn_addr)++;
|
||||
}
|
||||
|
||||
i3c->cw_buf[i3c->cw_size] = *ref_dyn_addr;
|
||||
// i3c->cw_buf[i3c->cw_size] &= (~I3C_TARGET_SLAVE_TYPE_I2C);
|
||||
|
||||
return (int32_t) i3c->cw_size++;
|
||||
}
|
||||
|
||||
static int omv_i3c_transfer_wait(I3C_Type *base, i3c_xfer_t *xfer, uint32_t timeout) {
|
||||
// Wait for the transfer to finish.
|
||||
mp_uint_t tick_start = mp_hal_ticks_ms();
|
||||
/* Waits till some response received */
|
||||
while (!(base->I3C_QUEUE_STATUS_LEVEL & I3C_QUEUE_STATUS_LEVEL_RESP_BUF_BLR_Msk)) {
|
||||
if ((mp_hal_ticks_ms() - tick_start) >= timeout) {
|
||||
// Should not raise exception as we're not always in nlr context.
|
||||
return -1;
|
||||
}
|
||||
mp_event_handle_nowait();
|
||||
}
|
||||
|
||||
// See Table 15-81 Response Data Structure
|
||||
uint32_t resp = base->I3C_RESPONSE_QUEUE_PORT;
|
||||
|
||||
if ((I3C_RESPONSE_QUEUE_PORT_ERR_STATUS(resp)) ||
|
||||
(I3C_RESPONSE_QUEUE_PORT_TID(resp) != xfer->xfer_cmd.port_id)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omv_i3c_transfer_end(I3C_Type *base) {
|
||||
i3c_clear_xfer_error(base);
|
||||
i3c_resume(base);
|
||||
}
|
||||
|
||||
static int omv_i3c_send_command_assign(I3C_Type *base, uint8_t cmd_id, uint8_t addr_index, uint8_t addr_depth) {
|
||||
i3c_xfer_t xfer = {0};
|
||||
|
||||
xfer.xfer_cmd.cmd_id = cmd_id;
|
||||
xfer.xfer_cmd.addr_index = addr_index;
|
||||
xfer.xfer_cmd.addr_depth = addr_depth;
|
||||
|
||||
xfer.error = 0U;
|
||||
|
||||
xfer.rx_len = 0U;
|
||||
xfer.xfer_cmd.cmd_type = I3C_XFER_TYPE_ADDR_ASSIGN;
|
||||
xfer.xfer_cmd.def_byte = 0U;
|
||||
xfer.xfer_cmd.data_len = 0U;
|
||||
|
||||
i3c_send_xfer_cmd(base, &xfer);
|
||||
if (omv_i3c_transfer_wait(base, &xfer, I3C_SCAN_TIMEOUT) != 0) {
|
||||
omv_i3c_transfer_end(base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i3c_assign(omv_i2c_t *i3c, uint8_t static_addr, uint8_t *ref_dyn_addr) {
|
||||
int32_t pos = 0U;
|
||||
I3C_Type *base = i3c->inst;
|
||||
/*Returns error if slave static address is invalid */
|
||||
if (!static_addr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find the first unused index in freepos, note that this also
|
||||
* corresponds to the first unused location in the DAT
|
||||
*/
|
||||
pos = omv_i3c_gen_dyn_addr(i3c, ref_dyn_addr);
|
||||
|
||||
/* the dat is full */
|
||||
if (pos < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We have space in the dat,
|
||||
* program the dat in index pos */
|
||||
i3c_add_slv_to_dat(base, pos, *ref_dyn_addr, static_addr);
|
||||
|
||||
if (omv_i3c_send_command_assign(base, I3C_CCC_SETDASA, (uint8_t) pos, 1U) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i3c_scan_assign(omv_i2c_t *i3c, uint8_t *list, uint8_t size) {
|
||||
int32_t pos = 0U;
|
||||
uint8_t init_pos = 0xFFU;
|
||||
I3C_Type *base = i3c->inst;
|
||||
uint8_t dyn_addr = 0U;
|
||||
uint8_t iter = 0U;
|
||||
|
||||
for (iter = 0U; iter < size; iter++) {
|
||||
pos = omv_i3c_gen_dyn_addr(i3c, &dyn_addr);
|
||||
|
||||
/* the dat is full */
|
||||
if (pos < 0) {
|
||||
if (init_pos == 0xFFU) {
|
||||
return -1;
|
||||
} else{
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (pos >= size) {
|
||||
break;
|
||||
}
|
||||
|
||||
list[pos] = dyn_addr;
|
||||
|
||||
omv_i3c_add_dyn_addr_parity(&dyn_addr);
|
||||
|
||||
/* We have space in the dat,
|
||||
* program the dat in index pos */
|
||||
i3c_add_slv_to_dat(base, pos, dyn_addr, 0);
|
||||
}
|
||||
|
||||
/* Stores first found free address position in
|
||||
* init position*/
|
||||
if (init_pos == 0xFFU) {
|
||||
init_pos = (uint8_t) pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (omv_i3c_send_command_assign(base, I3C_CCC_ENTDAA, init_pos, pos + 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i3c_enable(omv_i2c_t *i3c, bool enable) {
|
||||
//TODO: For I3C this causes a lockup.
|
||||
I3C_Type *base = i3c->inst;
|
||||
if (enable) {
|
||||
base->I3C_DEVICE_CTRL = base->I3C_DEVICE_CTRL & ~I3C_DEVICE_CTRL_ENABLE;
|
||||
while (base->I3C_DEVICE_CTRL & I3C_DEVICE_CTRL_ENABLE) {
|
||||
}
|
||||
|
||||
} else {
|
||||
base->I3C_DEVICE_CTRL = base->I3C_DEVICE_CTRL | I3C_DEVICE_CTRL_ENABLE;
|
||||
while (!(base->I3C_DEVICE_CTRL & I3C_DEVICE_CTRL_ENABLE)) {
|
||||
}
|
||||
i3c_resume(base);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i3c_gencall(omv_i2c_t *i3c, uint8_t cmd) {
|
||||
int ret = 0;
|
||||
ret |= omv_i3c_write_bytes(i3c, 0, &cmd, 1, OMV_I2C_XFER_NO_FLAGS);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int omv_i3c_readb(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t reg_addr, uint8_t *reg_data) {
|
||||
int ret = 0;
|
||||
ret |= omv_i3c_write_bytes(i3c, tgt_addr, ®_addr, 1, OMV_I2C_XFER_NO_STOP);
|
||||
ret |= omv_i3c_read_bytes(i3c, tgt_addr, reg_data, 1, OMV_I2C_XFER_NO_FLAGS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int omv_i3c_writeb(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t reg_addr, uint8_t reg_data) {
|
||||
int ret = 0;
|
||||
uint8_t buf[] = {reg_addr, reg_data};
|
||||
ret |= omv_i3c_write_bytes(i3c, tgt_addr, buf, 2, OMV_I2C_XFER_NO_FLAGS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int omv_i3c_readb2(omv_i2c_t *i3c, uint8_t tgt_addr, uint16_t reg_addr, uint8_t *reg_data) {
|
||||
int ret = 0;
|
||||
uint8_t buf[] = {(reg_addr >> 8), reg_addr};
|
||||
ret |= omv_i3c_write_bytes(i3c, tgt_addr, buf, 2, OMV_I2C_XFER_NO_STOP);
|
||||
ret |= omv_i3c_read_bytes(i3c, tgt_addr, reg_data, 1, OMV_I2C_XFER_NO_FLAGS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int omv_i3c_writeb2(omv_i2c_t *i3c, uint8_t tgt_addr, uint16_t reg_addr, uint8_t reg_data) {
|
||||
int ret = 0;
|
||||
uint8_t buf[] = {(reg_addr >> 8), reg_addr, reg_data};
|
||||
ret |= omv_i3c_write_bytes(i3c, tgt_addr, buf, 3, OMV_I2C_XFER_NO_FLAGS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int omv_i3c_readw(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t reg_addr, uint16_t *reg_data) {
|
||||
int ret = 0;
|
||||
ret |= omv_i3c_write_bytes(i3c, tgt_addr, ®_addr, 1, OMV_I2C_XFER_NO_STOP);
|
||||
ret |= omv_i3c_read_bytes(i3c, tgt_addr, (uint8_t *) reg_data, 2, OMV_I2C_XFER_NO_FLAGS);
|
||||
*reg_data = (*reg_data << 8) | (*reg_data >> 8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int omv_i3c_writew(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t reg_addr, uint16_t reg_data) {
|
||||
int ret = 0;
|
||||
uint8_t buf[] = {reg_addr, (reg_data >> 8), reg_data};
|
||||
ret |= omv_i3c_write_bytes(i3c, tgt_addr, buf, 3, OMV_I2C_XFER_NO_FLAGS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int omv_i3c_readw2(omv_i2c_t *i3c, uint8_t tgt_addr, uint16_t reg_addr, uint16_t *reg_data) {
|
||||
int ret = 0;
|
||||
uint8_t buf[] = {(reg_addr >> 8), reg_addr};
|
||||
ret |= omv_i3c_write_bytes(i3c, tgt_addr, buf, 2, OMV_I2C_XFER_NO_STOP);
|
||||
ret |= omv_i3c_read_bytes(i3c, tgt_addr, (uint8_t *) reg_data, 2, OMV_I2C_XFER_NO_FLAGS);
|
||||
*reg_data = (*reg_data << 8) | (*reg_data >> 8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int omv_i3c_writew2(omv_i2c_t *i3c, uint8_t tgt_addr, uint16_t reg_addr, uint16_t reg_data) {
|
||||
int ret = 0;
|
||||
uint8_t buf[] = {(reg_addr >> 8), reg_addr, (reg_data >> 8), reg_data};
|
||||
ret |= omv_i3c_write_bytes(i3c, tgt_addr, buf, 4, OMV_I2C_XFER_NO_FLAGS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omv_i3c_check_transfer(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t *buf, int len) {
|
||||
uint8_t addr = tgt_addr >> 1;
|
||||
|
||||
if (!buf || !len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len > I3C_MAX_DATA_BUF_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return omv_i3c_get_addr_pos(i3c, addr);
|
||||
}
|
||||
|
||||
int omv_i3c_read_bytes(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t *buf, int len, uint32_t flags) {
|
||||
int32_t index;
|
||||
i3c_xfer_t xfer = {0};
|
||||
I3C_Type *base = (I3C_Type *) i3c->inst;
|
||||
|
||||
index = omv_i3c_check_transfer(i3c, tgt_addr, buf, len);
|
||||
if (index < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
xfer.error = 0U;
|
||||
xfer.tx_buf = NULL;
|
||||
xfer.tx_len = 0U;
|
||||
xfer.xfer_cmd.addr_index = index;
|
||||
xfer.xfer_cmd.data_len = len;
|
||||
xfer.rx_len = len;
|
||||
|
||||
xfer.rx_buf = buf;
|
||||
|
||||
/* Invoke master receive api */
|
||||
if (flags & (OMV_I2C_XFER_NO_STOP | OMV_I2C_XFER_SUSPEND)) {
|
||||
i3c_master_rx_blocking(base, &xfer);
|
||||
} else {
|
||||
i3c_master_rx(base, &xfer);
|
||||
}
|
||||
|
||||
if (omv_i3c_transfer_wait(base, &xfer, I3C_XFER_TIMEOUT) != 0) {
|
||||
omv_i3c_transfer_end(base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if (xfer.rx_buf) {
|
||||
// for (uint32_t i = 0, dr = 0; i < xfer.rx_len; i++, dr >>= 8) {
|
||||
// if ((i % 4) == 0) {
|
||||
// dr = base->I3C_RX_DATA_PORT;
|
||||
// }
|
||||
// xfer.rx_buf[i] = dr & 0xFF;
|
||||
// }
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i3c_write_bytes(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t *buf, int len, uint32_t flags) {
|
||||
int32_t index;
|
||||
i3c_xfer_t xfer = {0};
|
||||
I3C_Type *base = (I3C_Type *) i3c->inst;
|
||||
|
||||
index = omv_i3c_check_transfer(i3c, tgt_addr, buf, len);
|
||||
if (index < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
xfer.error = 0U;
|
||||
xfer.rx_buf = NULL;
|
||||
xfer.rx_len = 0U;
|
||||
xfer.xfer_cmd.addr_index = index;
|
||||
xfer.xfer_cmd.data_len = len;
|
||||
|
||||
xfer.tx_buf = buf;
|
||||
xfer.tx_len = len;
|
||||
|
||||
/* Invoke master send api */
|
||||
if (flags & (OMV_I2C_XFER_NO_STOP | OMV_I2C_XFER_SUSPEND)) {
|
||||
i3c_master_tx_blocking(base, &xfer);
|
||||
} else {
|
||||
i3c_master_tx(base, &xfer);
|
||||
}
|
||||
|
||||
if (omv_i3c_transfer_wait(base, &xfer, I3C_XFER_TIMEOUT) != 0) {
|
||||
omv_i3c_transfer_end(base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i3c_pulse_scl(omv_i2c_t *i3c) {
|
||||
if (i3c->initialized && i3c->scl_pin) {
|
||||
omv_i3c_deinit(i3c);
|
||||
omv_gpio_config(i3c->scl_pin, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
// Pulse SCL to recover stuck device.
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
omv_gpio_write(i3c->scl_pin, 1);
|
||||
mp_hal_delay_us(10);
|
||||
omv_gpio_write(i3c->scl_pin, 0);
|
||||
mp_hal_delay_us(10);
|
||||
}
|
||||
omv_i3c_init(i3c, i3c->id, i3c->speed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
55
ports/alif/omv_i3c.h
Normal file
55
ports/alif/omv_i3c.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2013-2024 OpenMV, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* I3C bus abstraction layer.
|
||||
*/
|
||||
#ifndef __OMV_I3C_H__
|
||||
#define __OMV_I3C_H__
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "omv_i2c.h"
|
||||
|
||||
// Transfer speeds
|
||||
typedef enum _omv_i3c_speed {
|
||||
OMV_I3C_SPEED_SDR = (0U),
|
||||
OMV_I3C_SPEED_HDR = (1U)
|
||||
} omv_i3c_speed_t;
|
||||
|
||||
int omv_i3c_init(omv_i2c_t *i3c, uint32_t bus_id, uint32_t speed);
|
||||
int omv_i3c_deinit(omv_i2c_t *i3c);
|
||||
int omv_i3c_assign(omv_i2c_t *i3c, uint8_t static_addr, uint8_t *dyn_addr);
|
||||
int omv_i3c_scan_assign(omv_i2c_t *i3c, uint8_t *list, uint8_t size);
|
||||
int omv_i3c_enable(omv_i2c_t *i3c, bool enable);
|
||||
int omv_i3c_gencall(omv_i2c_t *i3c, uint8_t cmd);
|
||||
int omv_i3c_pulse_scl(omv_i2c_t *i3c);
|
||||
int omv_i3c_readb(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t reg_addr, uint8_t *reg_data);
|
||||
int omv_i3c_writeb(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t reg_addr, uint8_t reg_data);
|
||||
int omv_i3c_readb2(omv_i2c_t *i3c, uint8_t tgt_addr, uint16_t reg_addr, uint8_t *reg_data);
|
||||
int omv_i3c_writeb2(omv_i2c_t *i3c, uint8_t tgt_addr, uint16_t reg_addr, uint8_t reg_data);
|
||||
int omv_i3c_readw(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t reg_addr, uint16_t *reg_data);
|
||||
int omv_i3c_writew(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t reg_addr, uint16_t reg_data);
|
||||
int omv_i3c_readw2(omv_i2c_t *i3c, uint8_t tgt_addr, uint16_t reg_addr, uint16_t *reg_data);
|
||||
int omv_i3c_writew2(omv_i2c_t *i3c, uint8_t tgt_addr, uint16_t reg_addr, uint16_t reg_data);
|
||||
int omv_i3c_read_bytes(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t *buf, int len, uint32_t flags);
|
||||
int omv_i3c_write_bytes(omv_i2c_t *i3c, uint8_t tgt_addr, uint8_t *buf, int len, uint32_t flags);
|
||||
#endif // __OMV_I3C_H__
|
||||
118
ports/qemu/main.c
Normal file
118
ports/qemu/main.c
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2024 OpenMV, LLC.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Any redistribution, use, or modification in source or binary form
|
||||
* is done solely for personal benefit and not for any commercial
|
||||
* purpose or for monetary gain. For commercial licensing options,
|
||||
* please contact openmv@openmv.io
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
|
||||
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "py/compile.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/stackctrl.h"
|
||||
#include "shared/readline/readline.h"
|
||||
#include "shared/runtime/gchelper.h"
|
||||
#include "shared/runtime/pyexec.h"
|
||||
|
||||
#include "omv_boardconfig.h"
|
||||
#include "mp_utils.h"
|
||||
#include "file_utils.h"
|
||||
#include "fb_alloc.h"
|
||||
#include "framebuffer.h"
|
||||
#include "omv_csi.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
bool first_soft_reset = true;
|
||||
|
||||
soft_reset:
|
||||
// Initialise stack extents and GC heap.
|
||||
extern uint8_t _estack, _sstack, _heap_start, _heap_end;
|
||||
mp_init_gc_stack(&_sstack, &_estack, &_heap_start, &_heap_end, 1024);
|
||||
|
||||
mp_init();
|
||||
|
||||
fb_alloc_init0();
|
||||
framebuffer_init0();
|
||||
|
||||
#ifdef IMLIB_ENABLE_IMAGE_FILE_IO
|
||||
file_buffer_init0();
|
||||
#endif
|
||||
|
||||
imlib_init_all();
|
||||
|
||||
#if MICROPY_PY_CSI
|
||||
omv_csi_init0();
|
||||
|
||||
if (first_soft_reset) {
|
||||
omv_csi_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
|
||||
if (pyexec_raw_repl() != 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (pyexec_friendly_repl() != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mp_printf(&mp_plat_print, "MPY: soft reboot\n");
|
||||
gc_sweep_all();
|
||||
mp_deinit();
|
||||
first_soft_reset = false;
|
||||
goto soft_reset;
|
||||
}
|
||||
|
||||
void gc_collect(void) {
|
||||
gc_collect_start();
|
||||
gc_helper_collect_regs_and_stack();
|
||||
gc_collect_end();
|
||||
}
|
||||
|
||||
void nlr_jump_fail(void *val) {
|
||||
mp_printf(&mp_plat_print, "FATAL: uncaught exception %p\n", val);
|
||||
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(val));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void __fatal_error(const char *msg) {
|
||||
mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14);
|
||||
mp_hal_stdout_tx_strn(msg, strlen(msg));
|
||||
for (;;) {
|
||||
*(volatile uint32_t *) 0x4900C000 ^= 9;
|
||||
for (volatile uint delay = 0; delay < 10000000; delay++) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void usbdbg_set_irq_enabled(bool enabled) {
|
||||
|
||||
}
|
||||
85
ports/qemu/omv_csi.c
Normal file
85
ports/qemu/omv_csi.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 OpenMV, LLC.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Any redistribution, use, or modification in source or binary form
|
||||
* is done solely for personal benefit and not for any commercial
|
||||
* purpose or for monetary gain. For commercial licensing options,
|
||||
* please contact openmv@openmv.io
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
|
||||
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* QEMU dummy CSI driver.
|
||||
*/
|
||||
#if MICROPY_PY_CSI
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "omv_boardconfig.h"
|
||||
#include "omv_i2c.h"
|
||||
#include "omv_csi.h"
|
||||
|
||||
static int qemu_csi_config(omv_csi_t *csi, omv_csi_config_t config) {
|
||||
// Dummy implementation - accept all configurations
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qemu_csi_abort(omv_csi_t *csi, bool fifo_flush, bool in_irq) {
|
||||
// Dummy implementation
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t qemu_clk_get_frequency(omv_clk_t *clk) {
|
||||
// Dummy implementation - return nominal frequency
|
||||
return clk->freq;
|
||||
}
|
||||
|
||||
static int qemu_clk_set_frequency(omv_clk_t *clk, uint32_t frequency) {
|
||||
// Dummy implementation - accept any frequency
|
||||
clk->freq = frequency;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qemu_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
|
||||
// Dummy implementation - return timeout error since there's no camera
|
||||
return OMV_CSI_ERROR_CAPTURE_TIMEOUT;
|
||||
}
|
||||
|
||||
int omv_csi_ops_init(omv_csi_t *csi) {
|
||||
// Set CSI ops to dummy implementations
|
||||
csi->abort = qemu_csi_abort;
|
||||
csi->config = qemu_csi_config;
|
||||
csi->snapshot = qemu_csi_snapshot;
|
||||
|
||||
// Set CSI clock ops
|
||||
csi->clk->freq = OMV_CSI_CLK_FREQUENCY;
|
||||
csi->clk->set_freq = qemu_clk_set_frequency;
|
||||
csi->clk->get_freq = qemu_clk_get_frequency;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif // MICROPY_PY_CSI
|
||||
68
ports/qemu/omv_i2c.c
Normal file
68
ports/qemu/omv_i2c.c
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 OpenMV, LLC.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Any redistribution, use, or modification in source or binary form
|
||||
* is done solely for personal benefit and not for any commercial
|
||||
* purpose or for monetary gain. For commercial licensing options,
|
||||
* please contact openmv@openmv.io
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
|
||||
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* QEMU dummy I2C driver.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "omv_portconfig.h"
|
||||
#include "omv_i2c.h"
|
||||
|
||||
int omv_i2c_init(omv_i2c_t *i2c, uint32_t bus_id, uint32_t speed) {
|
||||
i2c->id = bus_id;
|
||||
i2c->speed = speed;
|
||||
i2c->initialized = true;
|
||||
i2c->scl_pin = NULL;
|
||||
i2c->sda_pin = NULL;
|
||||
i2c->inst = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i2c_deinit(omv_i2c_t *i2c) {
|
||||
if (i2c->initialized) {
|
||||
i2c->initialized = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i2c_scan(omv_i2c_t *i2c, uint8_t *list, uint8_t size) {
|
||||
// Dummy implementation - no devices found
|
||||
return 0;
|
||||
}
|
||||
|
||||
int omv_i2c_enable(omv_i2c_t *i2c, bool enable) {
|
||||
// Dummy implementation
|
||||
return 0;
|
||||
}
|
||||
50
ports/qemu/omv_mpconfigport.h
Normal file
50
ports/qemu/omv_mpconfigport.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2024 OpenMV, LLC.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Any redistribution, use, or modification in source or binary form
|
||||
* is done solely for personal benefit and not for any commercial
|
||||
* purpose or for monetary gain. For commercial licensing options,
|
||||
* please contact openmv@openmv.io
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
|
||||
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* MicroPython port configuration.
|
||||
*/
|
||||
#define MICROPY_NLR_RAISE_HOOK \
|
||||
do { \
|
||||
extern void fb_alloc_free_till_mark(); \
|
||||
fb_alloc_free_till_mark(); \
|
||||
} while (0);
|
||||
|
||||
#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C)
|
||||
typedef int mp_int_t; // must be pointer size
|
||||
typedef unsigned int mp_uint_t; // must be pointer size
|
||||
|
||||
#define MICROPY_PY_TIME (1)
|
||||
#define MICROPY_GC_SPLIT_HEAP (1)
|
||||
#define MICROPY_BANNER_NAME_AND_VERSION "OpenMV " OPENMV_GIT_TAG "; MicroPython " MICROPY_GIT_TAG
|
||||
|
||||
#include <mpconfigport.h>
|
||||
|
||||
#undef MICROPY_MEM_STATS
|
||||
#undef MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
59
ports/qemu/omv_portconfig.h
Normal file
59
ports/qemu/omv_portconfig.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 OpenMV, LLC.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Any redistribution, use, or modification in source or binary form
|
||||
* is done solely for personal benefit and not for any commercial
|
||||
* purpose or for monetary gain. For commercial licensing options,
|
||||
* please contact openmv@openmv.io
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE LICENSOR AND COPYRIGHT OWNER "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR OR COPYRIGHT
|
||||
* OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* OpenMV QEMU port abstraction layer.
|
||||
*/
|
||||
#ifndef __OMV_PORTCONFIG_H__
|
||||
#define __OMV_PORTCONFIG_H__
|
||||
#include <stdlib.h>
|
||||
|
||||
// omv_gpio_t definitions
|
||||
typedef void *omv_gpio_t;
|
||||
|
||||
// GPIO modes, speeds, and pull configurations (dummy values for QEMU)
|
||||
#define OMV_GPIO_MODE_INPUT (0)
|
||||
#define OMV_GPIO_MODE_OUTPUT (1)
|
||||
#define OMV_GPIO_MODE_OUTPUT_OD (2)
|
||||
#define OMV_GPIO_MODE_ALT (3)
|
||||
#define OMV_GPIO_MODE_ALT_OD (4)
|
||||
|
||||
#define OMV_GPIO_PULL_NONE (0)
|
||||
#define OMV_GPIO_PULL_UP (1)
|
||||
#define OMV_GPIO_PULL_DOWN (2)
|
||||
|
||||
#define OMV_GPIO_SPEED_LOW (0)
|
||||
#define OMV_GPIO_SPEED_MED (1)
|
||||
#define OMV_GPIO_SPEED_HIGH (2)
|
||||
#define OMV_GPIO_SPEED_MAX (3)
|
||||
|
||||
// omv_i2c_t definitions
|
||||
typedef void *omv_i2c_dev_t;
|
||||
|
||||
#endif // __OMV_PORTCONFIG_H__
|
||||
170
ports/qemu/omv_portconfig.mk
Normal file
170
ports/qemu/omv_portconfig.mk
Normal file
@ -0,0 +1,170 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Copyright (C) 2025 OpenMV, LLC.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
# Set startup and system files for CMSIS Makefile.
|
||||
LDSCRIPT ?= qemu
|
||||
ROMFS_IMAGE := $(FW_DIR)/romfs.stamp
|
||||
ROMFS_CONFIG := $(OMV_BOARD_CONFIG_DIR)/romfs.json
|
||||
|
||||
# Compiler Flags
|
||||
CFLAGS += -std=gnu11 \
|
||||
-Wall \
|
||||
-Werror \
|
||||
-Warray-bounds \
|
||||
-nostartfiles \
|
||||
-fdata-sections \
|
||||
-ffunction-sections \
|
||||
-fno-inline-small-functions \
|
||||
-mthumb \
|
||||
-mcpu=$(CPU) \
|
||||
-mfpu=$(FPU) \
|
||||
-mfloat-abi=hard
|
||||
|
||||
CFLAGS += -D$(TARGET) \
|
||||
-DQEMU_BUILD \
|
||||
-D__FPU_PRESENT=1 \
|
||||
-D__FPU_USED=1 \
|
||||
-D__VFP_FP__ \
|
||||
-DARM_NN_TRUNCATE=1 \
|
||||
-DCMSIS_MCU_H='<string.h>' \
|
||||
$(OMV_BOARD_CFLAGS)
|
||||
|
||||
# Linker Flags
|
||||
LDFLAGS = -mthumb \
|
||||
-nostartfiles \
|
||||
-mcpu=$(CPU) \
|
||||
-mfpu=$(FPU) \
|
||||
-mfloat-abi=hard \
|
||||
-mabi=aapcs-linux \
|
||||
-Wl,--print-memory-usage \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,-T$(BUILD)/$(LDSCRIPT).lds \
|
||||
-Wl,-Map=$(BUILD)/$(FIRMWARE).map
|
||||
|
||||
OMV_CFLAGS += -I$(TOP_DIR)
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/$(COMMON_DIR)
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/modules
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/ports/$(PORT)
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/ports/$(PORT)/modules
|
||||
OMV_CFLAGS += -I$(OMV_BOARD_CONFIG_DIR)
|
||||
|
||||
MPY_CFLAGS += -I$(MP_BOARD_CONFIG_DIR)
|
||||
MPY_CFLAGS += -I$(BUILD)/$(MICROPY_DIR)
|
||||
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)
|
||||
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/py
|
||||
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/lib/oofatfs
|
||||
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/lib/tinyusb/src
|
||||
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/lib/lwip/src/include
|
||||
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/shared/tinyusb
|
||||
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/ports/qemu
|
||||
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/ports/qemu/lwip_inc
|
||||
MPY_CFLAGS += -I$(TOP_DIR)/$(MICROPY_DIR)/shared/runtime
|
||||
|
||||
MPY_CFLAGS += -DMICROPY_PY_LWIP=$(MICROPY_PY_LWIP)
|
||||
MPY_CFLAGS += -DMICROPY_PY_SSL=$(MICROPY_PY_SSL)
|
||||
MPY_CFLAGS += -DMICROPY_PY_SSL_ECDSA_SIGN_ALT=$(MICROPY_PY_SSL_ECDSA_SIGN_ALT)
|
||||
MPY_CFLAGS += -DMICROPY_SSL_MBEDTLS=$(MICROPY_SSL_MBEDTLS)
|
||||
MPY_CFLAGS += -DMICROPY_PY_NETWORK_CYW43=$(MICROPY_PY_NETWORK_CYW43)
|
||||
MPY_CFLAGS += -DMICROPY_PY_BLUETOOTH=$(MICROPY_PY_BLUETOOTH)
|
||||
MPY_CFLAGS += -DMICROPY_BLUETOOTH_NIMBLE=$(MICROPY_BLUETOOTH_NIMBLE)
|
||||
MPY_CFLAGS += -DMICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS=1
|
||||
MPY_CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT
|
||||
|
||||
MPY_MKARGS += CMSIS_DIR=$(TOP_DIR)/$(CMSIS_DIR)
|
||||
MPY_MKARGS += MICROPY_VFS_LFS2=0
|
||||
MPY_MKARGS += CFLAGS_EXTRA="-std=gnu11"
|
||||
MPY_MKARGS += MICROPY_PY_LWIP=$(MICROPY_PY_LWIP)
|
||||
MPY_MKARGS += MICROPY_PY_SSL=$(MICROPY_PY_SSL)
|
||||
MPY_MKARGS += MICROPY_PY_SSL_ECDSA_SIGN_ALT=$(MICROPY_PY_SSL_ECDSA_SIGN_ALT)
|
||||
MPY_MKARGS += MICROPY_SSL_MBEDTLS=$(MICROPY_SSL_MBEDTLS)
|
||||
MPY_MKARGS += MICROPY_PY_NETWORK_CYW43=$(MICROPY_PY_NETWORK_CYW43)
|
||||
MPY_MKARGS += MICROPY_PY_BLUETOOTH=$(MICROPY_PY_BLUETOOTH)
|
||||
MPY_MKARGS += MICROPY_BLUETOOTH_NIMBLE=$(MICROPY_BLUETOOTH_NIMBLE)
|
||||
MPY_MKARGS += MICROPY_FLOAT_IMPL=float
|
||||
MPY_MKARGS += SUPPORTS_HARDWARE_FP_SINGLE=1
|
||||
|
||||
CFLAGS += $(HAL_CFLAGS) $(MPY_CFLAGS) $(OMV_CFLAGS)
|
||||
|
||||
# Firmware objects from .mk files.
|
||||
include lib/cmsis/cmsis.mk
|
||||
include common/common.mk
|
||||
include drivers/drivers.mk
|
||||
include lib/imlib/imlib.mk
|
||||
include lib/tflm/tflm.mk
|
||||
include ports/ports.mk
|
||||
include common/micropy.mk
|
||||
|
||||
# Firmware objects from port.
|
||||
MPY_FIRM_OBJ += $(addprefix $(BUILD)/$(MICROPY_DIR)/,\
|
||||
uart.o \
|
||||
mphalport.o \
|
||||
frozen_content.o \
|
||||
mcu/arm/errorhandler.o \
|
||||
mcu/arm/startup.o \
|
||||
mcu/arm/systick.o \
|
||||
)
|
||||
|
||||
ifeq ($(MICROPY_PY_ML_TFLM), 1)
|
||||
ifeq ($(CPU),cortex-m55)
|
||||
LIBS += $(TOP_DIR)/$(TENSORFLOW_DIR)/libtflm/lib/libtflm-cortex-m55-release.a
|
||||
else
|
||||
LIBS += $(TOP_DIR)/$(TENSORFLOW_DIR)/libtflm/lib/libtflm-$(CPU)+fp-release.a
|
||||
endif
|
||||
endif
|
||||
|
||||
###################################################
|
||||
all: $(ROMFS_IMAGE)
|
||||
$(SIZE) $(FW_DIR)/$(FIRMWARE).elf
|
||||
|
||||
# This target builds MicroPython.
|
||||
MICROPYTHON: | FIRM_DIRS
|
||||
$(MAKE) -C $(MICROPY_DIR)/ports/$(PORT) BUILD=$(BUILD)/$(MICROPY_DIR) $(MPY_MKARGS)
|
||||
|
||||
$(OMV_FIRM_OBJ): | MICROPYTHON
|
||||
|
||||
# This target builds the firmware.
|
||||
$(FIRMWARE): $(OMV_FIRM_OBJ)
|
||||
$(CPP) -P -E -DLINKER_SCRIPT -I$(COMMON_DIR) -I$(OMV_BOARD_CONFIG_DIR) \
|
||||
ports/$(PORT)/$(LDSCRIPT).ld.S > $(BUILD)/$(LDSCRIPT).lds
|
||||
$(CC) $(LDFLAGS) $(OMV_FIRM_OBJ) $(MPY_FIRM_OBJ) -o $(FW_DIR)/$(FIRMWARE).elf $(LIBS) -lm
|
||||
$(OBJCOPY) -Obinary $(FW_DIR)/$(FIRMWARE).elf $(FW_DIR)/$(FIRMWARE).bin
|
||||
|
||||
$(ROMFS_IMAGE): $(ROMFS_CONFIG) | $(FIRMWARE)
|
||||
$(ECHO) "GEN romfs image"
|
||||
$(PYTHON) $(TOOLS_DIR)/$(MKROMFS) \
|
||||
--top-dir $(TOP_DIR) \
|
||||
--out-dir $(FW_DIR) \
|
||||
--build-dir $(BUILD)/lib/models \
|
||||
$(STEDGE_ARGS) --config $(ROMFS_CONFIG)
|
||||
touch $@
|
||||
|
||||
QEMU_SYSTEM = qemu-system-arm
|
||||
QEMU_ARGS += -machine $(QEMU_MACHINE) -nographic -monitor null -semihosting
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
QEMU_ARGS += -s -S
|
||||
endif
|
||||
|
||||
run: $(ROMFS_IMAGE)
|
||||
$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel $(FW_DIR)/$(FIRMWARE).elf
|
||||
|
||||
include common/mkrules.mk
|
||||
73
ports/qemu/qemu.ld.S
Normal file
73
ports/qemu/qemu.ld.S
Normal file
@ -0,0 +1,73 @@
|
||||
/* This file is part of the MicroPython project, http://micropython.org/
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2018 Damien P. George
|
||||
*/
|
||||
|
||||
#include "omv_boardconfig.h"
|
||||
|
||||
MEMORY
|
||||
{
|
||||
#if defined(OMV_ITCM_ORIGIN)
|
||||
ITCM (xrw) : ORIGIN = OMV_ITCM_ORIGIN, LENGTH = OMV_ITCM_LENGTH
|
||||
#endif
|
||||
#if defined(OMV_SRAM1_ORIGIN)
|
||||
SRAM1 (xrw) : ORIGIN = OMV_SRAM1_ORIGIN, LENGTH = OMV_SRAM1_LENGTH
|
||||
#endif
|
||||
#if defined(OMV_SRAM2_ORIGIN)
|
||||
SRAM2 (xrw) : ORIGIN = OMV_SRAM2_ORIGIN, LENGTH = OMV_SRAM2_LENGTH
|
||||
#endif
|
||||
FLASH_ISR (rx) : ORIGIN = OMV_FLASH_ISR_ORIGIN, LENGTH = OMV_FLASH_ISR_LENGTH
|
||||
FLASH_TEXT (rx) : ORIGIN = OMV_FLASH_TXT_ORIGIN, LENGTH = OMV_FLASH_TXT_LENGTH
|
||||
}
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
.isr_vector : ALIGN(4)
|
||||
{
|
||||
KEEP(*(.isr_vector))
|
||||
. = ALIGN(4);
|
||||
} >FLASH_ISR
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text : ALIGN(4)
|
||||
{
|
||||
*(.text*) // .text* sections (code)
|
||||
. = ALIGN(4);
|
||||
*(.rodata*) // .rodata* sections (constants, strings, etc.)
|
||||
. = ALIGN(4);
|
||||
*(.ARM.exidx*)
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
} >FLASH_TEXT
|
||||
|
||||
/* The address used as the source for copying the initialized data section. */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections */
|
||||
.data : ALIGN(4)
|
||||
{
|
||||
_sdata = .; // Used by the startup to initialize the data section
|
||||
*(.data) // .data sections
|
||||
. = ALIGN(4);
|
||||
*(.data*) // .data* sections
|
||||
. = ALIGN(4);
|
||||
_edata = .; // Define a global symbol at data end
|
||||
} >OMV_MAIN_MEMORY AT> FLASH_TEXT
|
||||
|
||||
/* Uninitialized data section */
|
||||
.bss (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
_sbss = .; // Used by the startup to initialize the .bss section
|
||||
__bss_start__ = .;
|
||||
. = ALIGN(4);
|
||||
*(.bss*)
|
||||
. = ALIGN(4);
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .; // Define a global symbol at bss end
|
||||
__bss_end__ = .;
|
||||
} >OMV_MAIN_MEMORY
|
||||
|
||||
#include "common.ld.S"
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2025 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# This example shows off Google's MediaPipe Palm Detection model.
|
||||
#
|
||||
# NOTE: This exaxmple requires an OpenMV Cam with an NPU like the AE3 or N6 to run real-time.
|
||||
|
||||
import csi
|
||||
import time
|
||||
import ml
|
||||
from ml.postprocessing.mediapipe import BlazePalm
|
||||
|
||||
# Initialize the sensor.
|
||||
csi0 = csi.CSI()
|
||||
csi0.reset()
|
||||
csi0.pixformat(csi.RGB565)
|
||||
csi0.framesize(csi.VGA)
|
||||
csi0.window((400, 400))
|
||||
|
||||
# Load built-in palm detection model
|
||||
model = ml.Model("/rom/palm_detection_full_192.tflite", postprocess=BlazePalm(threshold=0.4))
|
||||
print(model)
|
||||
|
||||
# Line connections between hand joints for drawing the hand skeleton.
|
||||
palm_lines = ((0, 1), (1, 2), (2, 3), (3, 4), (4, 0), (0, 5), (5, 6))
|
||||
|
||||
# Visualization parameters.
|
||||
palm_labels = ["palm"]
|
||||
palm_colors = [(0, 0, 255)]
|
||||
kp_color = (255, 0, 0)
|
||||
line_color = (0, 255, 0)
|
||||
|
||||
clock = time.clock()
|
||||
while True:
|
||||
clock.tick()
|
||||
img = csi0.snapshot()
|
||||
|
||||
# palms is a list of ((x, y, w, h), score, keypoints) tuples
|
||||
palms = model.predict([img])
|
||||
|
||||
# Draw bounding boxes around the detected palms and keypoints.
|
||||
if palms:
|
||||
for r, score, keypoints in palms[0]:
|
||||
ml.utils.draw_predictions(img, [r], palm_labels, palm_colors, format=None)
|
||||
|
||||
# keypoints is a ndarray of shape (7, 2)
|
||||
# 0 - wrist (x, y)
|
||||
# 1 - index finger mcp (x, y)
|
||||
# 2 - middle finger mcp (x, y)
|
||||
# 3 - ring finger mcp (x, y)
|
||||
# 4 - pinky mcp (x, y)
|
||||
# 5 - thumb cmc (x, y)
|
||||
# 6 - thumb mcp (x, y)
|
||||
#
|
||||
# mcp = Metacarpophalangeal Joint - the knuckle
|
||||
# cmc = Carpometacarpal Joint - the base of the thumb
|
||||
ml.utils.draw_skeleton(img, keypoints, palm_lines, kp_color=kp_color, line_color=line_color)
|
||||
|
||||
print(clock.fps(), "fps")
|
||||
@ -0,0 +1,77 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2025 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# This example shows off Google's MediaPipe Hand Landmarks Detection model for multiple hands.
|
||||
#
|
||||
# NOTE: This exaxmple requires an OpenMV Cam with an NPU like the AE3 or N6 to run real-time.
|
||||
|
||||
import csi
|
||||
import time
|
||||
import ml
|
||||
from ml.preprocessing import Normalization
|
||||
from ml.postprocessing.mediapipe import BlazePalm
|
||||
from ml.postprocessing.mediapipe import HandLandmarks
|
||||
|
||||
# Initialize the sensor.
|
||||
csi0 = csi.CSI()
|
||||
csi0.reset()
|
||||
csi0.pixformat(csi.RGB565)
|
||||
csi0.framesize(csi.VGA)
|
||||
csi0.window((400, 400))
|
||||
|
||||
# Load built-in palm detection model
|
||||
palm_detection = ml.Model("/rom/palm_detection_full_192.tflite", postprocess=BlazePalm(threshold=0.4))
|
||||
print(palm_detection)
|
||||
|
||||
# Load built-in hand landmark model
|
||||
hand_landmarks = ml.Model("/rom/hand_landmarks_full_224.tflite", postprocess=HandLandmarks(threshold=0.4))
|
||||
print(hand_landmarks)
|
||||
|
||||
# Line connections between hand joints for drawing the hand skeleton.
|
||||
hand_lines = ((0, 1), (1, 2), (2, 3), (3, 4), (0, 5), (5, 6), (6, 7), (7, 8),
|
||||
(5, 9), (9, 10), (10, 11), (11, 12), (9, 13), (13, 14), (14, 15), (15, 16),
|
||||
(13, 17), (17, 18), (18, 19), (19, 20), (0, 17))
|
||||
|
||||
# Visualization parameters.
|
||||
palm_colors = [(0, 0, 255)]
|
||||
kp_color = (255, 0, 0)
|
||||
line_color = (0, 255, 0)
|
||||
|
||||
clock = time.clock()
|
||||
while True:
|
||||
clock.tick()
|
||||
img = csi0.snapshot()
|
||||
|
||||
# palms is a list of ((x, y, w, h), score, keypoints) tuples
|
||||
palms = palm_detection.predict([img])
|
||||
|
||||
if palms:
|
||||
for r, score, keypoints in palms[0]:
|
||||
# rect is (x, y, w, h) - enlarge by 3x for hand landmarks model
|
||||
wider_rect = (r[0] - r[2], r[1] - r[3], r[2] * 3, r[3] * 3)
|
||||
# Operate on just the ROI of the detected palm
|
||||
n = Normalization(roi=wider_rect)
|
||||
|
||||
# hands is a list of ((x, y, w, h), score, keypoints) tuples
|
||||
# index 0 (if present) is left hand
|
||||
# index 1 (if present) is right hand
|
||||
hands = hand_landmarks.predict([n(img)])
|
||||
|
||||
# Draw bounding boxes around the detected hands and keypoints.
|
||||
for i, detections in enumerate(hands):
|
||||
for r, score, keypoints in detections:
|
||||
ml.utils.draw_predictions(img, [r], ["right" if i else "left"], palm_colors, format=None)
|
||||
|
||||
# keypoints: ndarray (21, 3) of hand joints (x, y, z)
|
||||
# Indices follow MediaPipe convention:
|
||||
# 0: wrist
|
||||
# Thumb: 1 cmc, 2 mcp, 3 ip, 4 tip
|
||||
# Index: 5 mcp, 6 pip, 7 dip, 8 tip
|
||||
# Middle: 9 mcp, 10 pip, 11 dip, 12 tip
|
||||
# Ring: 13 mcp, 14 pip, 15 dip, 16 tip
|
||||
# Pinky: 17 mcp, 18 pip, 19 dip, 20 tip
|
||||
# (cmc=base, mcp=knuckle, pip=mid, dip=distal, ip=thumb joint, tip=fingertip)
|
||||
ml.utils.draw_skeleton(img, keypoints, hand_lines, kp_color=kp_color, line_color=line_color)
|
||||
|
||||
print(clock.fps(), "fps")
|
||||
@ -0,0 +1,95 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2025 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# This example shows off Google's MediaPipe Hand Landmarks Detection model for a single hand.
|
||||
#
|
||||
# NOTE: This exaxmple requires an OpenMV Cam with an NPU like the AE3 or N6 to run real-time.
|
||||
|
||||
import csi
|
||||
import time
|
||||
import ml
|
||||
from ml.preprocessing import Normalization
|
||||
from ml.postprocessing.mediapipe import BlazePalm
|
||||
from ml.postprocessing.mediapipe import HandLandmarks
|
||||
|
||||
# Initialize the sensor.
|
||||
csi0 = csi.CSI()
|
||||
csi0.reset()
|
||||
csi0.pixformat(csi.RGB565)
|
||||
csi0.framesize(csi.VGA)
|
||||
csi0.window((400, 400))
|
||||
|
||||
# Load built-in palm detection model
|
||||
palm_detection = ml.Model("/rom/palm_detection_full_192.tflite", postprocess=BlazePalm(threshold=0.4))
|
||||
print(palm_detection)
|
||||
|
||||
# Load built-in hand landmark model
|
||||
hand_landmarks = ml.Model("/rom/hand_landmarks_full_224.tflite", postprocess=HandLandmarks(threshold=0.4))
|
||||
print(hand_landmarks)
|
||||
|
||||
# Line connections between hand joints for drawing the hand skeleton.
|
||||
hand_lines = ((0, 1), (1, 2), (2, 3), (3, 4), (0, 5), (5, 6), (6, 7), (7, 8),
|
||||
(5, 9), (9, 10), (10, 11), (11, 12), (9, 13), (13, 14), (14, 15), (15, 16),
|
||||
(13, 17), (17, 18), (18, 19), (19, 20), (0, 17))
|
||||
|
||||
# Visualization parameters.
|
||||
palm_colors = [(0, 0, 255)]
|
||||
kp_color = (255, 0, 0)
|
||||
line_color = (0, 255, 0)
|
||||
|
||||
# Tracking vars.
|
||||
n = None
|
||||
|
||||
clock = time.clock()
|
||||
while True:
|
||||
clock.tick()
|
||||
img = csi0.snapshot()
|
||||
|
||||
if n is None:
|
||||
# palms is a list of ((x, y, w, h), score, keypoints) tuples
|
||||
palms = palm_detection.predict([img])
|
||||
|
||||
if palms:
|
||||
for r, score, keypoints in palms[0]:
|
||||
# rect is (x, y, w, h) - enlarge by 3x for hand landmarks model
|
||||
wider_rect = (r[0] - r[2], r[1] - r[3], r[2] * 3, r[3] * 3)
|
||||
# Operate on just the ROI of the detected palm
|
||||
n = Normalization(roi=wider_rect)
|
||||
|
||||
else:
|
||||
# hands is a list of ((x, y, w, h), score, keypoints) tuples
|
||||
# index 0 (if present) is left hand
|
||||
# index 1 (if present) is right hand
|
||||
hands = hand_landmarks.predict([n(img)])
|
||||
|
||||
# No hands detected, reset the tracker.
|
||||
if not hands:
|
||||
n = None
|
||||
continue
|
||||
|
||||
# Draw bounding boxes around the detected hands and keypoints.
|
||||
for i, detections in enumerate(hands):
|
||||
for r, score, keypoints in detections:
|
||||
ml.utils.draw_predictions(img, [r], ["right" if i else "left"], [(0, 0, 255)], format=None)
|
||||
|
||||
# keypoints: ndarray (21, 3) of hand joints (x, y, z)
|
||||
# Indices follow MediaPipe convention:
|
||||
# 0: wrist
|
||||
# Thumb: 1 cmc, 2 mcp, 3 ip, 4 tip
|
||||
# Index: 5 mcp, 6 pip, 7 dip, 8 tip
|
||||
# Middle: 9 mcp, 10 pip, 11 dip, 12 tip
|
||||
# Ring: 13 mcp, 14 pip, 15 dip, 16 tip
|
||||
# Pinky: 17 mcp, 18 pip, 19 dip, 20 tip
|
||||
# (cmc=base, mcp=knuckle, pip=mid, dip=distal, ip=thumb joint, tip=fingertip)
|
||||
ml.utils.draw_skeleton(img, keypoints, hand_lines, kp_color=kp_color, line_color=line_color)
|
||||
|
||||
# Center new_wider_rect on hand for tracking
|
||||
new_wider_rect = (r[0] + (r[2] // 2) - (wider_rect[2] // 2),
|
||||
r[1] + (r[3] // 2) - (wider_rect[3] // 2),
|
||||
wider_rect[2],
|
||||
wider_rect[3])
|
||||
# Operate on just the ROI of the detected hand
|
||||
n = Normalization(roi=new_wider_rect)
|
||||
|
||||
print(clock.fps(), "fps")
|
||||
@ -1,63 +0,0 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2023 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# Colorbar Test Example
|
||||
#
|
||||
# This example is the color bar test run by each OpenMV Cam before being allowed
|
||||
# out of the factory. The OMV sensors can output a color bar image which you
|
||||
# can threshold to check the the camera bus is connected correctly.
|
||||
|
||||
import sensor
|
||||
|
||||
sensor.reset()
|
||||
# Set sensor settings
|
||||
sensor.set_brightness(0)
|
||||
sensor.set_saturation(3)
|
||||
sensor.set_gainceiling(8)
|
||||
sensor.set_contrast(2)
|
||||
|
||||
# Set sensor pixel format
|
||||
sensor.set_framesize(sensor.QVGA)
|
||||
sensor.set_pixformat(sensor.RGB565)
|
||||
|
||||
# Enable colorbar test mode
|
||||
sensor.set_colorbar(True)
|
||||
|
||||
# Skip a few frames to allow the sensor settle down
|
||||
for i in range(0, 30):
|
||||
image = sensor.snapshot()
|
||||
|
||||
# Color bars thresholds
|
||||
t = [
|
||||
lambda r, g, b: r < 70 and g < 70 and b < 70, # Black
|
||||
lambda r, g, b: r < 70 and g < 70 and b > 200, # Blue
|
||||
lambda r, g, b: r > 200 and g < 70 and b < 70, # Red
|
||||
lambda r, g, b: r > 200 and g < 70 and b > 200, # Purple
|
||||
lambda r, g, b: r < 70 and g > 200 and b < 70, # Green
|
||||
lambda r, g, b: r < 70 and g > 200 and b > 200, # Aqua
|
||||
lambda r, g, b: r > 200 and g > 200 and b < 70, # Yellow
|
||||
lambda r, g, b: r > 200 and g > 200 and b > 200,
|
||||
] # White
|
||||
|
||||
# color bars are inverted for OV7725
|
||||
if sensor.get_id() == sensor.OV7725:
|
||||
t = t[::-1]
|
||||
|
||||
# 320x240 image with 8 color bars each one is approx 40 pixels.
|
||||
# we start from the center of the frame buffer, and average the
|
||||
# values of 10 sample pixels from the center of each color bar.
|
||||
for i in range(0, 8):
|
||||
avg = (0, 0, 0)
|
||||
idx = 40 * i + 20 # center of colorbars
|
||||
for off in range(0, 10): # avg 10 pixels
|
||||
rgb = image.get_pixel(idx + off, 120)
|
||||
avg = tuple(map(sum, zip(avg, rgb)))
|
||||
|
||||
if not t[i](avg[0] / 10, avg[1] / 10, avg[2] / 10):
|
||||
raise Exception(
|
||||
"COLOR BARS TEST FAILED. "
|
||||
"BAR#(%d): RGB(%d,%d,%d)" % (i + 1, avg[0] / 10, avg[1] / 10, avg[2] / 10)
|
||||
)
|
||||
|
||||
print("COLOR BARS TEST PASSED...")
|
||||
@ -1,19 +0,0 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2023 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# FPS Test Script.
|
||||
import sensor
|
||||
import time
|
||||
|
||||
sensor.reset() # Initialize the camera sensor.
|
||||
sensor.set_framesize(sensor.QQVGA) # or sensor.QQVGA (or others)
|
||||
sensor.set_pixformat(sensor.RGB565) # or sensor.GRAYSCALE
|
||||
sensor.set_colorbar(True) # Enable colorbars output
|
||||
|
||||
clock = time.clock() # Tracks FPS.
|
||||
for i in range(0, 600):
|
||||
clock.tick() # Track elapsed milliseconds between snapshots().
|
||||
sensor.snapshot() # Capture snapshot.
|
||||
|
||||
print("FPS:", clock.fps())
|
||||
@ -1,89 +0,0 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2023 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# Self Test Example
|
||||
#
|
||||
# This example shows how your OpenMV Cam tests itself before being allowed out
|
||||
# of the factory. Every OpenMV Cam should pass this test.
|
||||
|
||||
import sensor
|
||||
import pyb
|
||||
|
||||
|
||||
def test_int_adc():
|
||||
adc = pyb.ADCAll(12)
|
||||
# Test VBAT
|
||||
vbat = adc.read_core_vbat()
|
||||
vbat_diff = abs(vbat - 3.3)
|
||||
if vbat_diff > 0.1:
|
||||
raise Exception("INTERNAL ADC TEST FAILED VBAT=%fv" % vbat)
|
||||
|
||||
# Test VREF
|
||||
vref = adc.read_core_vref()
|
||||
vref_diff = abs(vref - 1.2)
|
||||
if vref_diff > 0.1:
|
||||
raise Exception("INTERNAL ADC TEST FAILED VREF=%fv" % vref)
|
||||
adc = None
|
||||
print("INTERNAL ADC TEST PASSED...")
|
||||
|
||||
|
||||
def test_color_bars():
|
||||
sensor.reset()
|
||||
# Set sensor settings
|
||||
sensor.set_brightness(0)
|
||||
sensor.set_saturation(3)
|
||||
sensor.set_gainceiling(8)
|
||||
sensor.set_contrast(2)
|
||||
|
||||
# Set sensor pixel format
|
||||
sensor.set_framesize(sensor.QVGA)
|
||||
sensor.set_pixformat(sensor.RGB565)
|
||||
|
||||
# Enable colorbar test mode
|
||||
sensor.set_colorbar(True)
|
||||
|
||||
# Skip a few frames to allow the sensor settle down
|
||||
for i in range(0, 100):
|
||||
image = sensor.snapshot()
|
||||
|
||||
# color bars thresholds
|
||||
t = [
|
||||
lambda r, g, b: r < 70 and g < 70 and b < 70, # Black
|
||||
lambda r, g, b: r < 70 and g < 70 and b > 200, # Blue
|
||||
lambda r, g, b: r > 200 and g < 70 and b < 70, # Red
|
||||
lambda r, g, b: r > 200 and g < 70 and b > 200, # Purple
|
||||
lambda r, g, b: r < 70 and g > 200 and b < 70, # Green
|
||||
lambda r, g, b: r < 70 and g > 200 and b > 200, # Aqua
|
||||
lambda r, g, b: r > 200 and g > 200 and b < 70, # Yellow
|
||||
lambda r, g, b: r > 200 and g > 200 and b > 200,
|
||||
] # White
|
||||
|
||||
# color bars are inverted for OV7725
|
||||
if sensor.get_id() == sensor.OV7725:
|
||||
t = t[::-1]
|
||||
|
||||
# 320x240 image with 8 color bars each one is approx 40 pixels.
|
||||
# we start from the center of the frame buffer, and average the
|
||||
# values of 10 sample pixels from the center of each color bar.
|
||||
for i in range(0, 8):
|
||||
avg = (0, 0, 0)
|
||||
idx = 40 * i + 20 # center of colorbars
|
||||
for off in range(0, 10): # avg 10 pixels
|
||||
rgb = image.get_pixel(idx + off, 120)
|
||||
avg = tuple(map(sum, zip(avg, rgb)))
|
||||
|
||||
if not t[i](avg[0] / 10, avg[1] / 10, avg[2] / 10):
|
||||
raise Exception(
|
||||
"COLOR BARS TEST FAILED."
|
||||
"BAR#(%d): RGB(%d,%d,%d)"
|
||||
% (i + 1, avg[0] / 10, avg[1] / 10, avg[2] / 10)
|
||||
)
|
||||
|
||||
print("COLOR BARS TEST PASSED...")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("")
|
||||
test_int_adc()
|
||||
test_color_bars()
|
||||
@ -1,48 +0,0 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2023 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# OpenMV Unit Tests.
|
||||
#
|
||||
import os
|
||||
import gc
|
||||
|
||||
TEST_DIR = "unittest"
|
||||
TEMP_DIR = "unittest/temp"
|
||||
DATA_DIR = "unittest/data"
|
||||
SCRIPT_DIR = "unittest/script"
|
||||
|
||||
if not (TEST_DIR in os.listdir("")):
|
||||
raise Exception("Unittest dir not found!")
|
||||
|
||||
print("")
|
||||
test_failed = False
|
||||
|
||||
|
||||
def print_result(test, result):
|
||||
s = "Unittest (%s)" % (test)
|
||||
padding = "." * (60 - len(s))
|
||||
print(s + padding + result)
|
||||
|
||||
|
||||
for test in sorted(os.listdir(SCRIPT_DIR)):
|
||||
if test.endswith(".py"):
|
||||
test_result = "PASSED"
|
||||
test_path = "/".join((SCRIPT_DIR, test))
|
||||
try:
|
||||
exec(open(test_path).read())
|
||||
gc.collect()
|
||||
if unittest(DATA_DIR, TEMP_DIR) is False:
|
||||
raise Exception()
|
||||
except Exception as e:
|
||||
if "unavailable" in str(e):
|
||||
test_result = "DISABLED"
|
||||
else:
|
||||
test_failed = True
|
||||
test_result = "FAILED"
|
||||
print_result(test, test_result)
|
||||
|
||||
if test_failed:
|
||||
print("\nSome tests have FAILED!!!\n\n")
|
||||
else:
|
||||
print("\nAll tests PASSED.\n\n")
|
||||
@ -33,7 +33,7 @@ from ml.preprocessing import Normalization
|
||||
|
||||
class Model(uml.Model):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, kwargs.get("load_to_fb", False))
|
||||
super().__init__(*args, kwargs.get("load_to_fb", False), kwargs.get("postprocess", False))
|
||||
try:
|
||||
path = args[0].split(".")[0] + ".txt"
|
||||
self.labels = [line.rstrip('\n') for line in open(path, "r")]
|
||||
|
||||
@ -195,3 +195,39 @@ def draw_predictions(
|
||||
color=box_color,
|
||||
)
|
||||
image.draw_string(x, y - font_height, label.upper(), text_color)
|
||||
|
||||
|
||||
def draw_keypoints(
|
||||
image,
|
||||
keypoints,
|
||||
radius=4,
|
||||
color=(255, 0, 0),
|
||||
thickness=1,
|
||||
fill=False,
|
||||
):
|
||||
|
||||
if radius > 0:
|
||||
for kp in keypoints:
|
||||
image.draw_circle(int(kp[0]), int(kp[1]), radius, color=color, thickness=thickness, fill=fill)
|
||||
elif radius == 0:
|
||||
for kp in keypoints:
|
||||
image.set_pixel(int(kp[0]), int(kp[1]), color)
|
||||
|
||||
|
||||
def draw_skeleton(
|
||||
image,
|
||||
keypoints,
|
||||
lines,
|
||||
kp_radius=4,
|
||||
kp_color=(255, 0, 0),
|
||||
kp_thickness=1,
|
||||
kp_fill=False,
|
||||
line_color=(0, 255, 0),
|
||||
line_thickness=1,
|
||||
):
|
||||
draw_keypoints(image, keypoints, radius=kp_radius, color=kp_color, thickness=kp_thickness, fill=kp_fill)
|
||||
|
||||
for line in lines:
|
||||
image.draw_line(int(keypoints[line[0]][0]), int(keypoints[line[0]][1]),
|
||||
int(keypoints[line[1]][0]), int(keypoints[line[1]][1]),
|
||||
color=line_color, thickness=line_thickness)
|
||||
|
||||
@ -34,24 +34,26 @@ from ulab import numpy as np
|
||||
_NO_DETECTION = const(())
|
||||
|
||||
|
||||
class BlazeFace:
|
||||
_BLAZEFACE_CX = const(0)
|
||||
_BLAZEFACE_CY = const(1)
|
||||
_BLAZEFACE_CW = const(2)
|
||||
_BLAZEFACE_CH = const(3)
|
||||
_BLAZEFACE_KP = const(4)
|
||||
class mediapipe_detection_postprocess:
|
||||
_MEDIAPIPE_CX = const(0)
|
||||
_MEDIAPIPE_CY = const(1)
|
||||
_MEDIAPIPE_CW = const(2)
|
||||
_MEDIAPIPE_CH = const(3)
|
||||
_MEDIAPIPE_KP = const(4)
|
||||
|
||||
def __init__(self, threshold=0.6, anchors=None, nms_threshold=0.1, nms_sigma=0.1):
|
||||
def __init__(self, threshold=0.6, anchors=None, anchor_grid=None, scores=[], cords=[],
|
||||
nms_threshold=0.1, nms_sigma=0.1):
|
||||
self.threshold = threshold
|
||||
self.anchors = anchors
|
||||
self.scores = scores
|
||||
self.cords = cords
|
||||
|
||||
if self.anchors is None:
|
||||
self.anchors = np.empty((896, 2))
|
||||
anchor_count = sum((g * g) * d for g, d in anchor_grid)
|
||||
self.anchors = np.empty((anchor_count, 2))
|
||||
idx = 0
|
||||
|
||||
# Generate anchors for 16x16 grid with 2 duplicates and
|
||||
# 8x8 grid with 6 duplicates to match the model output size.
|
||||
for grid_size, scales in [(16, 2), (8, 6)]:
|
||||
for grid_size, scales in anchor_grid:
|
||||
for gy in range(grid_size):
|
||||
cy = (gy + 0.5) / grid_size
|
||||
for gx in range(grid_size):
|
||||
@ -69,16 +71,16 @@ class BlazeFace:
|
||||
nms = NMS(iw, ih, inputs[0].roi)
|
||||
output_len = outputs[0].shape[1]
|
||||
|
||||
self.blazeface_post_process(ih, iw, nms, model, inputs, outputs, 1, 0,
|
||||
self.detection_post_process(ih, iw, nms, model, inputs, outputs, self.scores[0], self.cords[0],
|
||||
self.threshold, self.anchors[:output_len])
|
||||
|
||||
if output_len < len(self.anchors):
|
||||
self.blazeface_post_process(ih, iw, nms, model, inputs, outputs, 2, 3,
|
||||
self.detection_post_process(ih, iw, nms, model, inputs, outputs, self.scores[1], self.cords[1],
|
||||
self.threshold, self.anchors[output_len:])
|
||||
|
||||
return nms.get_bounding_boxes(threshold=self.nms_threshold, sigma=self.nms_sigma)
|
||||
|
||||
def blazeface_post_process(self, ih, iw, nms, model, inputs, outputs, score_idx, cords_idx, t, anchors):
|
||||
def detection_post_process(self, ih, iw, nms, model, inputs, outputs, score_idx, cords_idx, t, anchors):
|
||||
s_oh, s_ow, s_oc = model.output_shape[score_idx]
|
||||
scale = model.output_scale[score_idx]
|
||||
t = quantize(model, logit(t), index=score_idx)
|
||||
@ -102,18 +104,18 @@ class BlazeFace:
|
||||
bb_a_array = np.take(anchors, score_indices, axis=0)
|
||||
|
||||
# Compute the bounding box information
|
||||
ax = bb_a_array[:, _BLAZEFACE_CX]
|
||||
ay = bb_a_array[:, _BLAZEFACE_CY]
|
||||
x_center = bb[:, _BLAZEFACE_CX] / iw + ax
|
||||
y_center = bb[:, _BLAZEFACE_CY] / ih + ay
|
||||
w_rel = bb[:, _BLAZEFACE_CW] / iw * 0.5
|
||||
h_rel = bb[:, _BLAZEFACE_CH] / ih * 0.5
|
||||
ax = bb_a_array[:, _MEDIAPIPE_CX]
|
||||
ay = bb_a_array[:, _MEDIAPIPE_CY]
|
||||
x_center = bb[:, _MEDIAPIPE_CX] / iw + ax
|
||||
y_center = bb[:, _MEDIAPIPE_CY] / ih + ay
|
||||
w_rel = bb[:, _MEDIAPIPE_CW] / iw * 0.5
|
||||
h_rel = bb[:, _MEDIAPIPE_CH] / ih * 0.5
|
||||
|
||||
# Get the keypoint information
|
||||
row_count = bb.shape[0]
|
||||
keypoints = np.empty((row_count, (c_oc - _BLAZEFACE_KP) // 2, 2))
|
||||
keypoints[:, :, 0] = (bb[:, _BLAZEFACE_KP::2] / iw + ax.reshape((row_count, 1))) * iw
|
||||
keypoints[:, :, 1] = (bb[:, _BLAZEFACE_KP + 1::2] / ih + ay.reshape((row_count, 1))) * ih
|
||||
keypoints = np.empty((row_count, (c_oc - _MEDIAPIPE_KP) // 2, 2))
|
||||
keypoints[:, :, 0] = (bb[:, _MEDIAPIPE_KP::2] / iw + ax.reshape((row_count, 1))) * iw
|
||||
keypoints[:, :, 1] = (bb[:, _MEDIAPIPE_KP + 1::2] / ih + ay.reshape((row_count, 1))) * ih
|
||||
|
||||
# Scale the bounding boxes to have enough integer precision for NMS
|
||||
xmin = (x_center - w_rel) * iw
|
||||
@ -123,3 +125,51 @@ class BlazeFace:
|
||||
|
||||
for i in range(bb.shape[0]):
|
||||
nms.add_bounding_box(xmin[i], ymin[i], xmax[i], ymax[i], bb_scores[i], 0, keypoints=keypoints[i])
|
||||
|
||||
|
||||
class BlazeFace(mediapipe_detection_postprocess):
|
||||
def __init__(self, threshold=0.6, anchors=None, nms_threshold=0.1, nms_sigma=0.1):
|
||||
super().__init__(threshold=threshold, anchors=anchors,
|
||||
anchor_grid=[(16, 2), (8, 6)], scores=[1, 2], cords=[0, 3],
|
||||
nms_threshold=nms_threshold, nms_sigma=nms_sigma)
|
||||
|
||||
|
||||
class BlazePalm(mediapipe_detection_postprocess):
|
||||
def __init__(self, threshold=0.6, anchors=None, nms_threshold=0.1, nms_sigma=0.1):
|
||||
super().__init__(threshold=threshold, anchors=anchors,
|
||||
anchor_grid=[(24, 2), (12, 6)], scores=[0], cords=[1],
|
||||
nms_threshold=nms_threshold, nms_sigma=nms_sigma)
|
||||
|
||||
|
||||
class HandLandmarks:
|
||||
def __init__(self, threshold=0.6, nms_threshold=0.1, nms_sigma=0.1):
|
||||
self.threshold = threshold
|
||||
self.nms_threshold = nms_threshold
|
||||
self.nms_sigma = nms_sigma
|
||||
|
||||
def __call__(self, model, inputs, outputs):
|
||||
ib, ih, iw, ic = model.input_shape[0]
|
||||
nms = NMS(iw, ih, inputs[0].roi)
|
||||
|
||||
score = outputs[2][0, 0]
|
||||
if score < self.threshold:
|
||||
return _NO_DETECTION
|
||||
|
||||
cords = outputs[3][0, :]
|
||||
|
||||
# Get the keypoint information
|
||||
keypoints = np.empty((len(cords) // 3, 3))
|
||||
keypoints[:, 0] = cords[0::3]
|
||||
keypoints[:, 1] = cords[1::3]
|
||||
keypoints[:, 2] = cords[2::3]
|
||||
|
||||
# Get bounding box information
|
||||
xmin = np.min(keypoints[:, 0])
|
||||
ymin = np.min(keypoints[:, 1])
|
||||
xmax = np.max(keypoints[:, 0])
|
||||
ymax = np.max(keypoints[:, 1])
|
||||
|
||||
left_right = outputs[0][0, 0] > 0.5
|
||||
|
||||
nms.add_bounding_box(xmin, ymin, xmax, ymax, score, left_right, keypoints=keypoints)
|
||||
return nms.get_bounding_boxes(threshold=self.nms_threshold, sigma=self.nms_sigma)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
scripts/unittest/data/compressed.jpeg
Normal file
BIN
scripts/unittest/data/compressed.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
scripts/unittest/data/csi.pgm
Normal file
BIN
scripts/unittest/data/csi.pgm
Normal file
Binary file not shown.
BIN
scripts/unittest/data/csi.ppm
Normal file
BIN
scripts/unittest/data/csi.ppm
Normal file
Binary file not shown.
Binary file not shown.
BIN
scripts/unittest/data/edges1.pgm
Normal file
BIN
scripts/unittest/data/edges1.pgm
Normal file
Binary file not shown.
BIN
scripts/unittest/data/edges2.pgm
Normal file
BIN
scripts/unittest/data/edges2.pgm
Normal file
Binary file not shown.
BIN
scripts/unittest/data/faces.bmp
Normal file
BIN
scripts/unittest/data/faces.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 213 KiB |
BIN
scripts/unittest/data/fomo_face_detection.tflite
Normal file
BIN
scripts/unittest/data/fomo_face_detection.tflite
Normal file
Binary file not shown.
BIN
scripts/unittest/data/graffiti.bmp
Normal file
BIN
scripts/unittest/data/graffiti.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
90
scripts/unittest/run.py
Normal file
90
scripts/unittest/run.py
Normal file
@ -0,0 +1,90 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2023 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# OpenMV Unit Tests.
|
||||
#
|
||||
import os
|
||||
import gc
|
||||
import time
|
||||
|
||||
|
||||
TEMP_PATH = "/remote/temp"
|
||||
DATA_PATH = "/remote/data"
|
||||
TEST_PATH = "/remote/tests"
|
||||
|
||||
# Terminal color codes
|
||||
COLOR_GREEN = "\033[92m" # Bright green
|
||||
COLOR_RED = "\033[91m" # Bright red
|
||||
COLOR_YELLOW = "\033[33m"
|
||||
COLOR_RESET = "\033[0m"
|
||||
|
||||
|
||||
def print_result(test, result, time_ms):
|
||||
s = "Unittest (%s)" % (test)
|
||||
padding = "." * (60 - len(s))
|
||||
|
||||
# Color the result based on status
|
||||
if result == "PASSED":
|
||||
colored_result = COLOR_GREEN + result + COLOR_RESET
|
||||
elif result == "FAILED":
|
||||
colored_result = COLOR_RED + result + COLOR_RESET
|
||||
else: # DISABLED
|
||||
colored_result = COLOR_YELLOW + result + COLOR_RESET
|
||||
|
||||
print(s + padding + colored_result + " (%dms)" % time_ms)
|
||||
|
||||
|
||||
def main():
|
||||
passed_count = 0
|
||||
failed_count = 0
|
||||
skipped_count = 0
|
||||
tests = sorted(os.listdir(TEST_PATH))
|
||||
|
||||
if not "temp" in os.listdir():
|
||||
os.mkdir("temp") # Make a temp directory
|
||||
|
||||
for test in tests:
|
||||
result = "PASSED"
|
||||
path = "/".join((TEST_PATH, test))
|
||||
start_time = time.ticks_ms()
|
||||
try:
|
||||
with open(path) as f:
|
||||
buf = f.read()
|
||||
exec(buf)
|
||||
if unittest(DATA_PATH, TEMP_PATH) is False:
|
||||
raise Exception()
|
||||
except Exception as e:
|
||||
if "unavailable" in str(e):
|
||||
result = "DISABLED"
|
||||
else:
|
||||
result = "FAILED"
|
||||
|
||||
# Update counters
|
||||
if result == "PASSED":
|
||||
passed_count += 1
|
||||
elif result == "FAILED":
|
||||
failed_count += 1
|
||||
else: # DISABLED
|
||||
skipped_count += 1
|
||||
|
||||
time_ms = time.ticks_diff(time.ticks_ms(), start_time)
|
||||
print_result(test, result, time_ms)
|
||||
gc.collect()
|
||||
|
||||
# Print summary
|
||||
total_tests = passed_count + failed_count + skipped_count
|
||||
print("\n" + "=" * 60)
|
||||
print("Total: %d | " % total_tests +
|
||||
COLOR_GREEN + "Passed: %d" % passed_count + COLOR_RESET + " | " +
|
||||
COLOR_RED + "Failed: %d" % failed_count + COLOR_RESET + " | " +
|
||||
COLOR_YELLOW + "Skipped: %d" % skipped_count + COLOR_RESET)
|
||||
print("=" * 60)
|
||||
|
||||
if failed_count > 0:
|
||||
print(COLOR_RED + "Some tests FAILED." + COLOR_RESET)
|
||||
else:
|
||||
print(COLOR_GREEN + "All tests PASSED." + COLOR_RESET)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1,15 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
# Load image and find keypoints
|
||||
img = image.Image(data_path+"/graffiti.pgm", copy_to_fb=True)
|
||||
kpts1 = img.find_keypoints(max_keypoints=150, threshold=20, normalized=False)
|
||||
|
||||
# Load descriptor
|
||||
kpts2 = image.load_descriptor(data_path+"/graffiti.orb")
|
||||
|
||||
# Match keypoints
|
||||
match = image.match_descriptor(kpts1, kpts2, threshold=85)
|
||||
return (match.cx() == 138 and match.cy() == 117 and \
|
||||
match.x() == 36 and match.y() == 34 and \
|
||||
match.w() == 251 and match.h() == 167 and \
|
||||
match.count() == 150 and match.theta() == 0)
|
||||
@ -1,18 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image, os
|
||||
# Load image and find keypoints
|
||||
img = image.Image(data_path+"/graffiti.pgm", copy_to_fb=True)
|
||||
kpts1 = img.find_keypoints(max_keypoints=150, threshold=20, normalized=False)
|
||||
|
||||
# Save descriptor
|
||||
image.save_descriptor(kpts1, temp_path+"/graffiti2.orb")
|
||||
|
||||
# Load descriptor
|
||||
kpts2 = image.load_descriptor(temp_path+"/graffiti2.orb")
|
||||
|
||||
# Match keypoints
|
||||
match = image.match_descriptor(kpts1, kpts2, threshold=85)
|
||||
return (match.cx() == 138 and match.cy() == 117 and \
|
||||
match.x() == 36 and match.y() == 34 and \
|
||||
match.w() == 251 and match.h() == 167 and \
|
||||
match.count() == 150 and match.theta() == 0)
|
||||
@ -1,15 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
# Load image and find keypoints
|
||||
img = image.Image(data_path+"/graffiti.pgm", copy_to_fb=True)
|
||||
kpts1 = img.find_keypoints(max_keypoints=150, threshold=20, normalized=False)
|
||||
|
||||
# Load descriptor
|
||||
kpts2 = image.load_descriptor(data_path+"/graffiti.orb")
|
||||
|
||||
# Match keypoints
|
||||
match = image.match_descriptor(kpts1, kpts2, threshold=85)
|
||||
return (match.cx() == 138 and match.cy() == 117 and \
|
||||
match.x() == 36 and match.y() == 34 and \
|
||||
match.w() == 251 and match.h() == 167 and \
|
||||
match.count() == 150 and match.theta() == 0)
|
||||
@ -1,11 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
# Load Haar Cascade
|
||||
cascade = image.HaarCascade(data_path+"/frontalface.cascade")
|
||||
|
||||
# Load image and find keypoints
|
||||
img = image.Image(data_path+"/dennis.pgm", copy_to_fb=True)
|
||||
|
||||
# Find objects
|
||||
objects = img.find_features(cascade, threshold=0.75, scale_factor=1.25)
|
||||
return (objects and objects[0] == (189, 53, 88, 88) and objects[1] == (12, 11, 107, 107))
|
||||
@ -1,20 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
e = 0.0000001
|
||||
# Load image
|
||||
img = image.Image("unittest/data/cat.pgm", copy_to_fb=True)
|
||||
|
||||
# Load histogram
|
||||
with open("unittest/data/cat.csv", "r") as f:
|
||||
hist1 = [float(x) for x in f.read().split(',')]
|
||||
|
||||
# Get histogram
|
||||
hist2 = img.get_histogram()
|
||||
|
||||
# Compare
|
||||
for a, b in zip(hist1, hist2[0]):
|
||||
if abs(a-b) > e:
|
||||
return False
|
||||
|
||||
return hist2.get_percentile(0.5)[0] == 96 and hist2.get_statistics()[0:] ==\
|
||||
(81, 96, 0, 59, 0, 255, 13, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
@ -1,12 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
thresholds = [(0, 100, 56, 95, 41, 74), # generic_red_thresholds
|
||||
(0, 100, -128, -22, -128, 99), # generic_green_thresholds
|
||||
(0, 100, -128, 98, -128, -16)] # generic_blue_thresholds
|
||||
# Load image
|
||||
img = image.Image("unittest/data/blobs.ppm", copy_to_fb=True)
|
||||
|
||||
blobs = img.find_blobs(thresholds, pixels_threshold=2000, area_threshold=200)
|
||||
return [int(x) for x in blobs[0][0:-5]] == [122, 41, 98, 82, 6260, 168, 82] and\
|
||||
[int(x) for x in blobs[1][0:-5]] == [44, 42, 78, 88, 5158, 80, 84] and\
|
||||
[int(x) for x in blobs[2][0:-5]] == [210, 40, 72, 82, 3986, 248, 77]
|
||||
@ -1,5 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
img = image.Image("unittest/data/shapes.ppm", copy_to_fb=True)
|
||||
circles = img.find_circles(threshold = 5000, x_margin = 30, y_margin = 30, r_margin = 30)
|
||||
return len(circles) == 1 and circles[0][0:] == (118, 56, 22, 5856)
|
||||
@ -1,9 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
img = image.Image("unittest/data/shapes.ppm", copy_to_fb=True)
|
||||
lines = img.find_lines(threshold = 5000, theta_margin = 25, rho_margin = 25)
|
||||
return len(lines) == 4 and\
|
||||
lines[0][0:] == (22, 0, 22, 119, 119, 8670, 0, 22) and\
|
||||
lines[1][0:] == (0, 39, 159, 39, 159, 8670, 90, 39) and\
|
||||
lines[2][0:] == (57, 0, 57, 119, 119, 8670, 0, 57) and\
|
||||
lines[3][0:] == (0, 75, 159, 75, 159, 10710, 90, 75)
|
||||
@ -1,12 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
img = image.Image("unittest/data/shapes.ppm", copy_to_fb=True)
|
||||
lines = img.find_line_segments()
|
||||
return len(lines) == 7 and\
|
||||
lines[0][0:] == (23, 58, 22, 58, 1, 16, 90, 58) and\
|
||||
lines[1][0:] == (24, 74, 56, 74, 32, 19, 90, 74) and\
|
||||
lines[2][0:] == (54, 38, 26, 38, 28, 14, 90, 38) and\
|
||||
lines[3][0:] == (104, 70, 114, 76, 12, 2, 121, 6) and\
|
||||
lines[4][0:] == (139, 51, 133, 41, 12, 3, 149, -93) and\
|
||||
lines[5][0:] == (109, 37, 100, 46, 13, 14, 45, 103) and\
|
||||
lines[6][0:] == (129, 73, 137, 64, 12, 8, 42, 145)
|
||||
@ -1,5 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
img = image.Image("unittest/data/qrcode.pgm", copy_to_fb=True)
|
||||
codes = img.find_qrcodes()
|
||||
return len(codes) == 1 and codes[0][0:] == (76, 36, 168, 168, 'https://openmv.io', 1, 1, 3, 4, 0)
|
||||
@ -1,5 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
img = image.Image("unittest/data/apriltags.pgm", copy_to_fb=True)
|
||||
tags = img.find_apriltags()
|
||||
return len(tags) == 1 and tags[0][0:8] == (45, 27, 69, 69, 255, 16, 80, 61)
|
||||
@ -1,5 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
img = image.Image("unittest/data/datamatrix.pgm", copy_to_fb=True)
|
||||
matrices = img.find_datamatrices()
|
||||
return len(matrices) == 1 and matrices[0][0:] == (34, 15, 90, 89, 'https://openmv.io/', 0.0, 18, 18, 18, 0)
|
||||
@ -1,5 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
img = image.Image("unittest/data/barcode.pgm", copy_to_fb=True)
|
||||
codes = img.find_barcodes()
|
||||
return len(codes) == 1 and codes[0][0:] == (61, 46, 514, 39, 'https://openmv.io/', 15, 0.0, 40)
|
||||
@ -1,17 +0,0 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import sensor
|
||||
sensor.reset()
|
||||
sensor.set_framesize(sensor.QVGA)
|
||||
sensor.set_pixformat(sensor.GRAYSCALE)
|
||||
img = sensor.snapshot().clear()
|
||||
img.set_pixel(img.width()//2+50, 120, 255)
|
||||
img.set_pixel(img.width()//2-50, 120, 255)
|
||||
img.draw_line([img.width()//2-50, 50, img.width()//2+50, 50])
|
||||
img.draw_rectangle([img.width()//2-25, img.height()//2-25, 50, 50])
|
||||
img.draw_circle(img.width()//2, img.height()//2, 40)
|
||||
img.draw_string(11, 10, "HelloWorld!")
|
||||
img.draw_cross(img.width()//2, img.height()//2)
|
||||
sensor.flush()
|
||||
img.difference(data_path+"/drawing.pgm")
|
||||
stats = img.get_statistics()
|
||||
return (stats.max() == 0) and (stats.min() == 0)
|
||||
25
scripts/unittest/tests/adaptive_threshold_median.py
Normal file
25
scripts/unittest/tests/adaptive_threshold_median.py
Normal file
@ -0,0 +1,25 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
|
||||
# Create grayscale image with varying brightness
|
||||
img = image.Image(64, 64, image.GRAYSCALE, copy_to_fb=True)
|
||||
|
||||
# Create gradient - dark on left, bright on right
|
||||
for y in range(64):
|
||||
for x in range(64):
|
||||
img.set_pixel(x, y, int(x * 4))
|
||||
|
||||
# Apply median adaptive threshold
|
||||
img.median(2, threshold=True, offset=5, invert=False)
|
||||
|
||||
# After adaptive thresholding, should be binary
|
||||
# Verify format is still grayscale (binary thresholding in place)
|
||||
if img.format() != image.GRAYSCALE:
|
||||
return False
|
||||
|
||||
# Verify output contains only 0 or 255 values (binary)
|
||||
test_pixel = img.get_pixel(32, 32)
|
||||
if test_pixel != 0 and test_pixel != 255:
|
||||
return False
|
||||
|
||||
return True
|
||||
25
scripts/unittest/tests/adaptive_threshold_mode.py
Normal file
25
scripts/unittest/tests/adaptive_threshold_mode.py
Normal file
@ -0,0 +1,25 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
|
||||
# Create grayscale image with varying brightness
|
||||
img = image.Image(64, 64, image.GRAYSCALE, copy_to_fb=True)
|
||||
|
||||
# Create gradient - dark on left, bright on right
|
||||
for y in range(64):
|
||||
for x in range(64):
|
||||
img.set_pixel(x, y, int(x * 4))
|
||||
|
||||
# Apply mode adaptive threshold
|
||||
img.mode(2, threshold=True, offset=5, invert=False)
|
||||
|
||||
# After adaptive thresholding, should be binary
|
||||
# Verify format is still grayscale (binary thresholding in place)
|
||||
if img.format() != image.GRAYSCALE:
|
||||
return False
|
||||
|
||||
# Verify output contains only 0 or 255 values (binary)
|
||||
test_pixel = img.get_pixel(32, 32)
|
||||
if test_pixel != 0 and test_pixel != 255:
|
||||
return False
|
||||
|
||||
return True
|
||||
19
scripts/unittest/tests/apriltags.py
Normal file
19
scripts/unittest/tests/apriltags.py
Normal file
@ -0,0 +1,19 @@
|
||||
def unittest(data_path, temp_path):
|
||||
import image
|
||||
|
||||
img = image.Image(data_path + "/apriltags.pgm", copy_to_fb=True)
|
||||
tags = img.find_apriltags()
|
||||
|
||||
if len(tags) != 1:
|
||||
return False
|
||||
|
||||
return (
|
||||
tags[0].x == 45
|
||||
and tags[0].y == 27
|
||||
and tags[0].w == 69
|
||||
and tags[0].h == 69
|
||||
and tags[0].id == 255
|
||||
and tags[0].family == 16
|
||||
and tags[0].cx == 79
|
||||
and tags[0].cy == 61
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user