mirror of
https://github.com/openmv/openmv.git
synced 2025-09-26 23:09:13 +08:00
Compare commits
87 Commits
7edf86bd71
...
d09c6c303c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d09c6c303c | ||
![]() |
87b01fac51 | ||
![]() |
552f4ada8e | ||
![]() |
7a7788a03a | ||
![]() |
16dfe5f5cb | ||
![]() |
4257464982 | ||
![]() |
daa681dfe6 | ||
![]() |
9310becba0 | ||
![]() |
304a2d36e8 | ||
![]() |
a19d832f1e | ||
![]() |
9beb565882 | ||
![]() |
6a1c9b8ab9 | ||
![]() |
72e04221c7 | ||
![]() |
c9e42a4e45 | ||
![]() |
3667b54319 | ||
![]() |
f6f39f924b | ||
![]() |
65829023f0 | ||
![]() |
7124b5134d | ||
![]() |
eccb90699d | ||
![]() |
14d277c95b | ||
![]() |
3a79080713 | ||
![]() |
f16f566141 | ||
![]() |
aaabe05565 | ||
![]() |
2205f177f3 | ||
![]() |
0de60ff3d5 | ||
![]() |
75c7a4fb82 | ||
![]() |
04145abea8 | ||
![]() |
1dab51addf | ||
![]() |
a559f03553 | ||
![]() |
3946a19cd2 | ||
![]() |
f994fe1d06 | ||
![]() |
01113ebee9 | ||
![]() |
28f0ad2d7d | ||
![]() |
e60750b5cc | ||
![]() |
1b59e95162 | ||
![]() |
085f282e4b | ||
![]() |
ce4922c1bc | ||
![]() |
bcb20f45ac | ||
![]() |
bb52288561 | ||
![]() |
4f40ae487e | ||
![]() |
050d750afd | ||
![]() |
9937b9cc1e | ||
![]() |
8b19717c21 | ||
![]() |
a4ce426308 | ||
![]() |
616ce71b96 | ||
![]() |
1dd9b33acc | ||
![]() |
facd715c12 | ||
![]() |
384c3e734d | ||
![]() |
1308129fdb | ||
![]() |
be25f116ab | ||
![]() |
22981bdb61 | ||
![]() |
51e87a4e40 | ||
![]() |
960ae86545 | ||
![]() |
23808f81cf | ||
![]() |
f177d221e9 | ||
![]() |
30a62c2c05 | ||
![]() |
cd2a4d4f95 | ||
![]() |
b1f81f7cdd | ||
![]() |
fbc8d14cda | ||
![]() |
66c8e7e8d5 | ||
![]() |
4d69dd396c | ||
![]() |
d05d5f1d3c | ||
![]() |
4c2c59d0e5 | ||
![]() |
bc39969581 | ||
![]() |
0e124a215d | ||
![]() |
49f330f2dc | ||
![]() |
67a014ebdc | ||
![]() |
657c9a6325 | ||
![]() |
e64877bbea | ||
![]() |
8e72debf11 | ||
![]() |
de7a761d3b | ||
![]() |
84e6ee6507 | ||
![]() |
7cbf85cd1c | ||
![]() |
0dbb5c8aa7 | ||
![]() |
d4ce288ae2 | ||
![]() |
d057f85f0b | ||
![]() |
927a2c7b54 | ||
![]() |
872735212f | ||
![]() |
e5cdccc72e | ||
![]() |
cf7b84d682 | ||
![]() |
e6d693cabf | ||
![]() |
90bd11e937 | ||
![]() |
29f07312f8 | ||
![]() |
0d5c5bc6eb | ||
![]() |
f45c0181fc | ||
![]() |
89fc302812 | ||
![]() |
447e782304 |
63
.github/workflows/codeformat.yml
vendored
63
.github/workflows/codeformat.yml
vendored
@ -5,7 +5,9 @@ on:
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- '**/*.{c,cc,h}'
|
||||
- '**/*.c'
|
||||
- '**/*.cc'
|
||||
- '**/*.h'
|
||||
|
||||
pull_request:
|
||||
types:
|
||||
@ -16,7 +18,9 @@ on:
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- '**/*.{c,cc,h}'
|
||||
- '**/*.c'
|
||||
- '**/*.cc'
|
||||
- '**/*.h'
|
||||
|
||||
jobs:
|
||||
formatting-check:
|
||||
@ -27,42 +31,23 @@ jobs:
|
||||
with:
|
||||
submodules: false
|
||||
persist-credentials: false
|
||||
|
||||
- name: '♻ Caching dependencies'
|
||||
uses: actions/cache@v4.2.0
|
||||
id: cache
|
||||
with:
|
||||
path: ~/cache/deps/bin
|
||||
key: 'uncrustify'
|
||||
|
||||
- name: '🛠 Install dependencies'
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: source tools/ci.sh && ci_install_code_format_deps
|
||||
|
||||
- name: '📜 Get list of changed files'
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v46
|
||||
with:
|
||||
files: |
|
||||
**/*.{c,cc,h}
|
||||
!cubeai/**
|
||||
!docker/**
|
||||
!docs/**
|
||||
!drivers/**
|
||||
drivers/sensors/*.{c,cc,h}
|
||||
!lib/**
|
||||
lib/imlib/*.{c,cc,h}
|
||||
lib/tflm/*.{c,cc,h}
|
||||
!scripts/**
|
||||
!tools/**
|
||||
|
||||
- name: '📜 Show list of changed files'
|
||||
run: |
|
||||
echo "${{ toJSON(steps.changed-files.outputs) }}"
|
||||
shell:
|
||||
bash
|
||||
fetch-depth: 50
|
||||
|
||||
- name: '🔎 Check code formatting'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: |
|
||||
source tools/ci.sh && ci_run_code_format_check ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
uses: openmv/uncrustify-action@v1
|
||||
with:
|
||||
config-path: 'tools/uncrustify.cfg'
|
||||
extensions: 'c,cc,h'
|
||||
exclude-patterns: |
|
||||
cubeai/**
|
||||
docker/**
|
||||
docs/**
|
||||
drivers/**
|
||||
!drivers/sensors/**
|
||||
lib/**
|
||||
!lib/imlib/**
|
||||
!lib/tflm/**
|
||||
scripts/**
|
||||
tools/**
|
||||
uncrustify-version: '0.75.0'
|
||||
fail-on-error: true
|
||||
|
16
.github/workflows/firmware.yml
vendored
16
.github/workflows/firmware.yml
vendored
@ -49,6 +49,10 @@ jobs:
|
||||
- ARDUINO_NANO_RP2040_CONNECT
|
||||
- ARDUINO_NANO_33_BLE_SENSE
|
||||
- DOCKER
|
||||
profile: [0] # default profile
|
||||
include:
|
||||
- target: OPENMV_N6
|
||||
profile: 1
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: '⏳ Checkout repository'
|
||||
@ -71,7 +75,7 @@ jobs:
|
||||
key: 'gcc-14.3.rel1_llvm-18.1.3_make-4.4.1_stedgeai-2.1'
|
||||
|
||||
- name: '🐍 Install Python'
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
cache: 'pip'
|
||||
python-version: "3.12.4"
|
||||
@ -100,10 +104,10 @@ 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 }}
|
||||
run: source tools/ci.sh && ci_build_target ${{ matrix.target }} ${{ matrix.profile }}
|
||||
|
||||
- name: '⬆ Upload artifacts'
|
||||
if: matrix.target != 'DOCKER'
|
||||
if: matrix.target != 'DOCKER' && matrix.profile == 0
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
@ -132,7 +136,7 @@ jobs:
|
||||
key: 'gcc-14.3.rel1_llvm-18.1.3_make-4.4.1_stedgeai-2.1'
|
||||
|
||||
- name: '🐍 Install Python'
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
cache: 'pip'
|
||||
python-version: "3.12.4"
|
||||
@ -198,7 +202,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: '🔥 Create stable release'
|
||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
|
||||
with:
|
||||
draft: true
|
||||
files: firmware_*.zip
|
||||
@ -250,7 +254,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: '🔥 Create development release'
|
||||
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
|
||||
with:
|
||||
draft: false
|
||||
name: Development Release
|
||||
|
2
.github/workflows/python-linter.yml
vendored
2
.github/workflows/python-linter.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: '🐍 Set up Python ${{ matrix.python-version }}'
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
cache: 'pip'
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
33
Makefile
33
Makefile
@ -107,10 +107,24 @@ CFLAGS += -DFB_ALLOC_STATS
|
||||
endif
|
||||
|
||||
# Enable timing for some functions.
|
||||
ifeq ($(PROFILE), 1)
|
||||
CFLAGS += -DOMV_PROFILE_ENABLE=1
|
||||
ifeq ($(PROFILE_ENABLE), 1)
|
||||
$(info ===================================)
|
||||
$(info ======= Profiling Enabled =======)
|
||||
$(info ===================================)
|
||||
|
||||
# Enable profiling in IRQ context (default: enabled)
|
||||
PROFILE_IRQ ?= 0
|
||||
|
||||
# Default profiler hash table size (must be power of 2)
|
||||
ifeq ($(PROFILE_HASH),)
|
||||
PROFILE_HASH=256
|
||||
endif
|
||||
|
||||
CFLAGS += -DOMV_PROFILER_ENABLE=1
|
||||
CFLAGS += -DOMV_PROFILER_HASH_SIZE=$(PROFILE_HASH)
|
||||
CFLAGS += -DOMV_PROFILER_IRQ_ENABLE=$(PROFILE_IRQ)
|
||||
CFLAGS += -finstrument-functions-exclude-file-list=lib/cmsis,lib/stm32,/lib/mimxrt,lib/alif,simd.h
|
||||
endif
|
||||
|
||||
# Include OpenMV board config first to set the port.
|
||||
include $(OMV_BOARD_CONFIG_DIR)/omv_boardconfig.mk
|
||||
@ -239,9 +253,20 @@ endif
|
||||
$(SIZE) --format=SysV $(FW_DIR)/$(FIRMWARE).elf
|
||||
|
||||
jlink:
|
||||
${JLINK_GDB_SERVER} -speed ${JLINK_SPEED} -nogui \
|
||||
setsid ${JLINK_GDB_SERVER} -speed ${JLINK_SPEED} -nogui \
|
||||
-if ${JLINK_INTERFACE} -halt -cpu cortex-m \
|
||||
-device ${JLINK_DEVICE} -novd ${JLINK_SCRIPT}
|
||||
-device ${JLINK_DEVICE} -novd ${JLINK_SCRIPT} \
|
||||
>/dev/null 2>&1 & \
|
||||
trap "pkill -f JLinkGDBServer 2>/dev/null" EXIT; \
|
||||
for i in $$(seq 1 3); do \
|
||||
if ss -ln | grep -q ":2331 "; then break; fi; \
|
||||
sleep 1; \
|
||||
done; \
|
||||
if ! ss -ln | grep -q ":2331 "; then \
|
||||
echo "J-Link GDB server failed to start after 3 seconds"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
jlink-gdb $(FW_DIR)/$(FIRMWARE).elf
|
||||
|
||||
submodules:
|
||||
$(MAKE) -C $(MICROPY_DIR)/ports/$(PORT) BOARD=$(TARGET) submodules
|
||||
|
@ -139,8 +139,8 @@
|
||||
#define OMV_FB_ALLOC_SIZE (1M) // minimum fb alloc size
|
||||
#define OMV_FB_OVERLAY_MEMORY SRAM0 // Fast fb_alloc memory.
|
||||
#define OMV_FB_OVERLAY_SIZE (448K) // Fast fb_alloc memory size.
|
||||
#define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer.
|
||||
#define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_MEMORY DRAM // Streaming buffer memory.
|
||||
#define OMV_SB_SIZE (1M) // Streaming buffer size.
|
||||
#define OMV_DMA_MEMORY SRAM3 // Misc DMA buffers memory.
|
||||
#define OMV_DMA_MEMORY_D1 SRAM0 // Domain 1 DMA buffers.
|
||||
#define OMV_DMA_MEMORY_D2 SRAM3 // Domain 2 DMA buffers.
|
||||
|
@ -38,6 +38,12 @@
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/blazeface_front_128.tflite",
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "haar",
|
||||
"path": "{TOP}/lib/haar/haarcascade_eye.xml",
|
||||
|
@ -70,7 +70,7 @@
|
||||
#define OMV_FB_ALLOC_SIZE (16K) // minimum fb alloc size
|
||||
#define OMV_GC_BLOCK0_MEMORY SRAM // Main GC block.
|
||||
#define OMV_GC_BLOCK0_SIZE (64K)
|
||||
#define OMV_JPEG_SIZE (16K) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_SIZE (16K) // Streaming buffer size.
|
||||
|
||||
#define OMV_TEXT_ORIGIN 0x00026000
|
||||
#define OMV_FFS_LENGTH 64K
|
||||
|
@ -69,7 +69,7 @@ extern unsigned char *OMV_BOARD_UID_ADDR; // Unique address.
|
||||
#define OMV_FB_ALLOC_SIZE (16K) // minimum fb alloc size
|
||||
#define OMV_GC_BLOCK0_MEMORY RAM // Main GC block
|
||||
#define OMV_GC_BLOCK0_SIZE (60 * 1024) // MicroPython's heap
|
||||
#define OMV_JPEG_SIZE (20K) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_SIZE (20K) // Streaming buffer size.
|
||||
|
||||
// GP LED
|
||||
#define OMV_LED_PIN (6)
|
||||
|
@ -123,8 +123,8 @@
|
||||
#define OMV_FB_MEMORY SRAM0 // Framebuffer, fb_alloc
|
||||
#define OMV_FB_SIZE (400K) // FB memory: header + VGA/GS image
|
||||
#define OMV_FB_ALLOC_SIZE (76K) // minimum fb alloc size
|
||||
#define OMV_JPEG_MEMORY SRAM0 // JPEG buffer memory buffer.
|
||||
#define OMV_JPEG_SIZE (32K) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_MEMORY SRAM0 // Streaming buffer memory
|
||||
#define OMV_SB_SIZE (32K) // Streaming buffer size
|
||||
#define OMV_DMA_MEMORY SRAM2 // DMA buffers memory.
|
||||
#define OMV_DMA_MEMORY_D1 SRAM0 // Domain 1 DMA buffers.
|
||||
#define OMV_DMA_MEMORY_D2 SRAM2 // Domain 2 DMA buffers.
|
||||
|
@ -32,6 +32,12 @@
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/blazeface_front_128.tflite",
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "haar",
|
||||
"path": "{TOP}/lib/haar/haarcascade_eye.xml",
|
||||
|
@ -132,8 +132,8 @@
|
||||
#define OMV_FB_ALLOC_SIZE (1M) // minimum fb alloc size
|
||||
#define OMV_FB_OVERLAY_MEMORY SRAM0 // Fast fb_alloc memory.
|
||||
#define OMV_FB_OVERLAY_SIZE (480K) // Fast fb_alloc memory size.
|
||||
#define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer.
|
||||
#define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_MEMORY DRAM // Streaming buffer memory.
|
||||
#define OMV_SB_SIZE (1M) // Streaming buffer size.
|
||||
#define OMV_DMA_MEMORY SRAM3 // Misc DMA buffers memory.
|
||||
#define OMV_DMA_MEMORY_D1 SRAM0 // Domain 1 DMA buffers.
|
||||
#define OMV_DMA_MEMORY_D2 SRAM3 // Domain 2 DMA buffers.
|
||||
|
@ -38,6 +38,12 @@
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/blazeface_front_128.tflite",
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "haar",
|
||||
"path": "{TOP}/lib/haar/haarcascade_eye.xml",
|
||||
|
@ -88,7 +88,7 @@
|
||||
#define OMV_DMA_MEMORY SRAM2 // Misc DMA buffers
|
||||
#define OMV_GC_BLOCK0_MEMORY DTCM // Main GC block
|
||||
#define OMV_GC_BLOCK0_SIZE (46K)
|
||||
#define OMV_JPEG_SIZE (8K) // IDE JPEG buffer size (header + data).
|
||||
#define OMV_SB_SIZE (8K) // IDE JPEG buffer size (header + data).
|
||||
#define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data
|
||||
#define OMV_FFS_BUF_SIZE (16K) // Flash filesystem cache
|
||||
#define OMV_LINE_BUF_SIZE (2 * 1024) // Image line buffer round(320 * 2BPP * 2 buffers).
|
||||
|
@ -88,7 +88,7 @@
|
||||
#define OMV_DMA_MEMORY DTCM // Misc DMA buffers
|
||||
#define OMV_GC_BLOCK0_MEMORY DTCM // Main GC block
|
||||
#define OMV_GC_BLOCK0_SIZE (53K)
|
||||
#define OMV_JPEG_SIZE (22K) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_SIZE (22K) // IDE JPEG buffer (header + data).
|
||||
#define OMV_MSC_BUF_SIZE (2K) // USB MSC bot data
|
||||
#define OMV_FFS_BUF_SIZE (32K) // Flash filesystem cache
|
||||
#define OMV_LINE_BUF_SIZE (3 * 1024) // Image line buffer round(640 * 2BPP * 2 buffers).
|
||||
|
@ -131,8 +131,8 @@
|
||||
#define OMV_FB_MEMORY SRAM0 // Framebuffer, fb_alloc
|
||||
#define OMV_FB_SIZE (400K) // FB memory: header + VGA/GS image
|
||||
#define OMV_FB_ALLOC_SIZE (80K) // minimum fb alloc size
|
||||
#define OMV_JPEG_MEMORY SRAM0 // JPEG buffer memory.
|
||||
#define OMV_JPEG_SIZE (32K) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_MEMORY SRAM0 // Streaming buffer memory.
|
||||
#define OMV_SB_SIZE (32K) // Streaming buffer size.
|
||||
#define OMV_DMA_MEMORY SRAM2 // Misc DMA buffers memory.
|
||||
#define OMV_GC_BLOCK0_MEMORY SRAM4 // Main GC block.
|
||||
#define OMV_GC_BLOCK0_SIZE (64K)
|
||||
|
@ -118,8 +118,8 @@
|
||||
#define OMV_FB_ALLOC_SIZE (11M) // minimum fb alloc size
|
||||
#define OMV_FB_OVERLAY_MEMORY SRAM0 // Fast fb_alloc memory.
|
||||
#define OMV_FB_OVERLAY_SIZE (496K) // Fast fb_alloc memory size.
|
||||
#define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer.
|
||||
#define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_MEMORY DRAM // Streaming buffer memory.
|
||||
#define OMV_SB_SIZE (1M) // Streaming buffer size.
|
||||
#define OMV_DMA_MEMORY SRAM3 // Misc DMA buffers memory.
|
||||
#define OMV_DMA_MEMORY_D1 SRAM0 // Domain 1 DMA buffers.
|
||||
#define OMV_DMA_MEMORY_D2 SRAM3 // Domain 2 DMA buffers.
|
||||
|
@ -38,6 +38,12 @@
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/blazeface_front_128.tflite",
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "haar",
|
||||
"path": "{TOP}/lib/haar/haarcascade_eye.xml",
|
||||
|
@ -111,8 +111,8 @@
|
||||
#define OMV_FB_ALLOC_SIZE (23M) // minimum fb alloc size
|
||||
#define OMV_FB_OVERLAY_MEMORY SRAM0 // Fast fb_alloc memory.
|
||||
#define OMV_FB_OVERLAY_SIZE (496K) // Fast fb_alloc memory size.
|
||||
#define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer.
|
||||
#define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_MEMORY DRAM // Streaming buffer memory.
|
||||
#define OMV_SB_SIZE (1M) // Streaming buffer size.
|
||||
#define OMV_DMA_MEMORY SRAM3 // DMA buffers memory.
|
||||
#define OMV_DMA_MEMORY_D1 SRAM0 // Domain 1 DMA buffers.
|
||||
#define OMV_DMA_MEMORY_D2 SRAM3 // Domain 2 DMA buffers.
|
||||
|
@ -38,6 +38,12 @@
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/blazeface_front_128.tflite",
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "haar",
|
||||
"path": "{TOP}/lib/haar/haarcascade_eye.xml",
|
||||
|
@ -103,8 +103,8 @@ extern unsigned char OMV_BOARD_UID_ADDR[12]; // Unique address.
|
||||
#define OMV_FB_ALLOC_SIZE (464K) // Minimum fb alloc size
|
||||
#define OMV_FB_OVERLAY_MEMORY DTCM // Fast fb_alloc memory.
|
||||
#define OMV_FB_OVERLAY_SIZE (256K) // Fast fb_alloc memory size.
|
||||
#define OMV_JPEG_MEMORY SRAM6_A // JPEG buffer.
|
||||
#define OMV_JPEG_SIZE (1M)
|
||||
#define OMV_SB_MEMORY SRAM6_A // Streaming buffer memory.
|
||||
#define OMV_SB_SIZE (1M) // Streaming buffer size.
|
||||
#define OMV_DMA_MEMORY DTCM // Misc DMA buffers memory.
|
||||
#define OMV_GPU_MEMORY SRAM9_B // GPU heap.
|
||||
#define OMV_GPU_SIZE (256K)
|
||||
@ -123,8 +123,8 @@ extern unsigned char OMV_BOARD_UID_ADDR[12]; // Unique address.
|
||||
#define OMV_FB_MEMORY SRAM6_B // Main Frame buffer, fb_alloc
|
||||
#define OMV_FB_SIZE (256K)
|
||||
#define OMV_FB_ALLOC_SIZE (256K) // Minimum fb alloc size
|
||||
#define OMV_JPEG_MEMORY SRAM6_B // JPEG buffer.
|
||||
#define OMV_JPEG_SIZE (500K)
|
||||
#define OMV_SB_MEMORY SRAM6_B // JPEG buffer.
|
||||
#define OMV_SB_SIZE (500K)
|
||||
#define OMV_DMA_MEMORY DTCM // Misc DMA buffers memory.
|
||||
#define OMV_OPENAMP_MEMORY SRAM9_A // Open-AMP SHM
|
||||
#define OMV_OPENAMP_SIZE (64K)
|
||||
|
@ -32,6 +32,12 @@
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/blazeface_front_128.tflite",
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "haar",
|
||||
"path": "{TOP}/lib/haar/haarcascade_eye.xml",
|
||||
|
@ -101,9 +101,9 @@
|
||||
#define OMV_RCC_IC10_SOURCE (RCC_ICCLKSOURCE_PLL1)
|
||||
#define OMV_RCC_IC10_CLKDIV (8)
|
||||
|
||||
// Used by MicroPython UART and SPI
|
||||
#define OMV_RCC_IC9_SOURCE (RCC_ICCLKSOURCE_PLL1)
|
||||
#define OMV_RCC_IC9_CLKDIV (8)
|
||||
// Used by MicroPython for slow peripherals.
|
||||
#define OMV_RCC_IC14_SOURCE (RCC_ICCLKSOURCE_PLL1)
|
||||
#define OMV_RCC_IC14_CLKDIV (8)
|
||||
|
||||
#define OMV_RCC_IC15_SOURCE (RCC_ICCLKSOURCE_PLL1)
|
||||
#define OMV_RCC_IC15_CLKDIV (16)
|
||||
@ -115,8 +115,8 @@
|
||||
#define OMV_RCC_IC18_CLKDIV (60)
|
||||
|
||||
#define OMV_OSC_I2C3_SOURCE (RCC_I2C3CLKSOURCE_IC10)
|
||||
#define OMV_OSC_SPI5_SOURCE (RCC_SPI5CLKSOURCE_IC9)
|
||||
#define OMV_OSC_USART2_SOURCE (RCC_USART2CLKSOURCE_IC9)
|
||||
#define OMV_OSC_SPI5_SOURCE (RCC_SPI5CLKSOURCE_IC14)
|
||||
#define OMV_OSC_USART2_SOURCE (RCC_USART2CLKSOURCE_IC14)
|
||||
|
||||
#define OMV_OSC_DCMIPP_SOURCE (RCC_DCMIPPCLKSOURCE_IC17)
|
||||
#define OMV_OSC_CSI_SOURCE (0) // has one clock source IC18
|
||||
@ -142,15 +142,15 @@
|
||||
#define OMV_HEAP_MEMORY SRAM1 // libc/sbrk heap memory
|
||||
#define OMV_HEAP_SIZE (256K)
|
||||
#define OMV_FB_MEMORY DRAM // Framebuffer, fb_alloc
|
||||
#define OMV_FB_SIZE (20M) // FB memory.
|
||||
#define OMV_FB_ALLOC_SIZE (11M) // minimum fb_alloc size
|
||||
#define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer.
|
||||
#define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data).
|
||||
#define OMV_FB_SIZE (20M) // FB memory.
|
||||
#define OMV_FB_ALLOC_SIZE (11M) // minimum fb_alloc size
|
||||
#define OMV_SB_MEMORY DRAM // Streaming buffer memory.
|
||||
#define OMV_SB_SIZE (1M) // Streaming buffer size.
|
||||
#define OMV_DMA_MEMORY SRAM1 // Misc DMA buffers memory.
|
||||
#define OMV_DMA_MEMORY_D2 SRAM7 // Domain 2 DMA buffers.
|
||||
#define OMV_GC_BLOCK0_MEMORY SRAM2 // Main GC block
|
||||
#define OMV_GC_BLOCK0_SIZE (1M)
|
||||
#define OMV_GC_BLOCK1_MEMORY DRAM // Main GC block
|
||||
#define OMV_GC_BLOCK1_MEMORY DRAM // Main GC block
|
||||
#define OMV_GC_BLOCK1_SIZE (24M)
|
||||
#define OMV_MSC_BUF_SIZE (4K) // USB MSC bot data
|
||||
#define OMV_VOSPI_DMA_BUFFER ".d2_dma_buffer"
|
||||
|
@ -19,6 +19,12 @@
|
||||
"path": "{TOP}/lib/models/yolo_v5_224_nano.tflite",
|
||||
"alignment": 32,
|
||||
"profile": "default"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/blazeface_front_128.tflite",
|
||||
"alignment": 32,
|
||||
"profile": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -70,8 +70,8 @@
|
||||
#define OMV_MAIN_MEMORY DTCM // Data/BSS memory
|
||||
#define OMV_STACK_MEMORY ITCM1 // stack memory
|
||||
#define OMV_STACK_SIZE (32K)
|
||||
#define OMV_JPEG_MEMORY DRAM // JPEG buffer memory buffer.
|
||||
#define OMV_JPEG_SIZE (1M) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_MEMORY DRAM // Streaming buffer memory.
|
||||
#define OMV_SB_SIZE (1M) // Streaming buffer size.
|
||||
#define OMV_FB_MEMORY DRAM // Framebuffer, fb_alloc
|
||||
#define OMV_FB_SIZE (13M) // FB memory: header + VGA/GS image
|
||||
#define OMV_FB_ALLOC_SIZE (10M) // minimum fb alloc size
|
||||
|
@ -38,6 +38,12 @@
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "tflite",
|
||||
"path": "{TOP}/lib/models/blazeface_front_128.tflite",
|
||||
"alignment": 16,
|
||||
"optimize": "Performance"
|
||||
},
|
||||
{
|
||||
"type": "haar",
|
||||
"path": "{TOP}/lib/haar/haarcascade_eye.xml",
|
||||
|
@ -53,7 +53,7 @@ extern unsigned char *OMV_BOARD_UID_ADDR; // Unique ID address.
|
||||
#define OMV_FB_MEMORY RAM // Framebuffer, fb_alloc
|
||||
#define OMV_FB_SIZE (151K) // FB memory: header + QVGA/GS image
|
||||
#define OMV_FB_ALLOC_SIZE (12K) // minimum fb alloc size
|
||||
#define OMV_JPEG_SIZE (20K) // IDE JPEG buffer (header + data).
|
||||
#define OMV_SB_SIZE (20K) // IDE JPEG buffer (header + data).
|
||||
|
||||
// GP LED
|
||||
#define OMV_LED_PIN (25)
|
||||
|
39
boot/include/version.h
Normal file
39
boot/include/version.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*
|
||||
* Bootloader Version.
|
||||
*/
|
||||
#ifndef __OMV_BOOT_VERSION_H__
|
||||
#define __OMV_BOOT_VERSION_H__
|
||||
#define OMV_BOOTLOADER_VERSION_MAJOR (1)
|
||||
#define OMV_BOOTLOADER_VERSION_MINOR (0)
|
||||
#define OMV_BOOTLOADER_VERSION_PATCH (0)
|
||||
#endif // __OMV_BOOT_VERSION_H__
|
||||
|
@ -121,16 +121,16 @@ OMV_GC_BLOCK_NEW(4)
|
||||
PROVIDE(__stack = __StackTop);
|
||||
#endif
|
||||
|
||||
/* JPEG framebuffer memory */
|
||||
#if defined(OMV_JPEG_MEMORY)
|
||||
.jpeg_memory (NOLOAD) :
|
||||
/* Streaming buffer memory */
|
||||
#if defined(OMV_SB_MEMORY)
|
||||
.sb_memory (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_jpeg_memory_start = .;
|
||||
. = . + OMV_JPEG_SIZE;
|
||||
_sb_memory_start = .;
|
||||
. = . + OMV_SB_SIZE;
|
||||
. = ALIGN(4);
|
||||
_jpeg_memory_end = .;
|
||||
} >OMV_JPEG_MEMORY
|
||||
_sb_memory_end = .;
|
||||
} >OMV_SB_MEMORY
|
||||
#endif
|
||||
|
||||
/* GPU memory */
|
||||
@ -211,11 +211,11 @@ PROVIDE(__stack = __StackTop);
|
||||
. = . + OMV_MSC_BUF_SIZE;
|
||||
#endif
|
||||
|
||||
#if defined(OMV_JPEG_SIZE) && !defined(OMV_JPEG_MEMORY)
|
||||
#if defined(OMV_SB_SIZE) && !defined(OMV_SB_MEMORY)
|
||||
. = ALIGN(16);
|
||||
_jpeg_memory_start = .;
|
||||
. = . + OMV_JPEG_SIZE;
|
||||
_jpeg_memory_end = .;
|
||||
_sb_memory_start = .;
|
||||
. = . + OMV_SB_SIZE;
|
||||
_sb_memory_end = .;
|
||||
#endif
|
||||
|
||||
/* FFS (internal) filesystem buffer */
|
||||
|
@ -31,6 +31,7 @@ COMMON_SRC_C += \
|
||||
mutex.c \
|
||||
nosys_stubs.c \
|
||||
omv_csi.c \
|
||||
omv_crc.c \
|
||||
pendsv.c \
|
||||
tinyusb_debug.c \
|
||||
trace.c \
|
||||
@ -39,6 +40,7 @@ COMMON_SRC_C += \
|
||||
usbdbg.c \
|
||||
vospi.c \
|
||||
queue.c \
|
||||
omv_profiler.c \
|
||||
|
||||
CFLAGS += -I$(TOP_DIR)/common
|
||||
OMV_FIRM_OBJ += $(addprefix $(BUILD)/common/, $(COMMON_SRC_C:.c=.o))
|
||||
|
@ -64,13 +64,13 @@ void fb_alloc_init0() {
|
||||
}
|
||||
|
||||
uint32_t fb_avail() {
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
uint32_t temp = pointer - framebuffer_pool_end(fb) - sizeof(uint32_t);
|
||||
return (temp < sizeof(uint32_t)) ? 0 : temp;
|
||||
}
|
||||
|
||||
void fb_alloc_mark() {
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
char *new_pointer = pointer - sizeof(uint32_t);
|
||||
|
||||
// Check if allocation overwrites the framebuffer pixels
|
||||
@ -137,7 +137,7 @@ void fb_alloc_free_till_mark_past_mark_permanent() {
|
||||
|
||||
// returns null pointer without error if size==0
|
||||
void *fb_alloc(uint32_t size, int hints) {
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
|
||||
if (!size) {
|
||||
return NULL;
|
||||
@ -198,7 +198,7 @@ void *fb_alloc0(uint32_t size, int hints) {
|
||||
}
|
||||
|
||||
void *fb_alloc_all(uint32_t *size, int hints) {
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
uint32_t temp = pointer - framebuffer_pool_end(fb) - sizeof(uint32_t);
|
||||
|
||||
if (temp < sizeof(uint32_t)) {
|
||||
|
@ -31,7 +31,9 @@ void mutex_init0(mutex_t *m) {
|
||||
}
|
||||
|
||||
void mutex_lock(mutex_t *m, size_t tid) {
|
||||
while (atomic_flag_test_and_set_explicit(&m->lock, memory_order_acquire));
|
||||
while (atomic_flag_test_and_set_explicit(&m->lock, memory_order_acquire)) {
|
||||
|
||||
}
|
||||
atomic_store_explicit(&m->tid, tid, memory_order_release);
|
||||
}
|
||||
|
||||
@ -53,8 +55,10 @@ bool mutex_try_lock_fair(mutex_t *m, size_t tid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void mutex_unlock(mutex_t *m, size_t tid) {
|
||||
bool mutex_unlock(mutex_t *m, size_t tid) {
|
||||
if (atomic_load_explicit(&m->tid, memory_order_acquire) == tid) {
|
||||
atomic_flag_clear_explicit(&m->lock, memory_order_release);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -48,5 +48,5 @@ void mutex_lock(mutex_t *mutex, size_t tid);
|
||||
bool mutex_try_lock(mutex_t *mutex, size_t tid);
|
||||
// Prevents a thread from locking twice in a row.
|
||||
bool mutex_try_lock_fair(mutex_t *mutex, size_t tid);
|
||||
void mutex_unlock(mutex_t *mutex, size_t tid);
|
||||
bool mutex_unlock(mutex_t *mutex, size_t tid);
|
||||
#endif // __MUTEX_H__
|
||||
|
@ -31,6 +31,7 @@
|
||||
#define OMV_ATTR_ALWAYS_INLINE inline __attribute__((always_inline))
|
||||
#define OMV_ATTR_OPTIMIZE(o) __attribute__((optimize(o)))
|
||||
#define OMV_ATTR_SEC_ALIGN(x, s, a) x __attribute__((section(s), aligned(a)))
|
||||
#define OMV_ATTR_NO_INSTRUMENT __attribute__((no_instrument_function))
|
||||
#define OMV_DEBUG_BREAKPOINT() __asm__ volatile ("BKPT")
|
||||
|
||||
#ifndef __DCACHE_PRESENT
|
||||
@ -49,6 +50,12 @@
|
||||
#define OMV_ALIGN_DOWN(x, alignment) \
|
||||
((uintptr_t)(x) & ~((uintptr_t)(alignment) - 1))
|
||||
|
||||
#define check_timeout_ms(start_ms, timeout) \
|
||||
((mp_hal_ticks_ms() - start_ms) > timeout)
|
||||
|
||||
#define check_timeout_us(start_us, timeout) \
|
||||
((mp_hal_ticks_us() - start_us) > timeout)
|
||||
|
||||
#ifdef OMV_DEBUG_PRINTF
|
||||
#define debug_printf(fmt, ...) \
|
||||
do { printf("%s(): " fmt, __func__, ##__VA_ARGS__);} while (0)
|
||||
@ -70,17 +77,11 @@
|
||||
|
||||
#define OMV_ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
#if OMV_PROFILE_ENABLE
|
||||
#include <stdio.h>
|
||||
#include "py/mphal.h"
|
||||
#define OMV_CONCATENATE_DETAIL(x, y) x##y
|
||||
#define OMV_CONCATENATE(x, y) OMV_CONCATENATE_DETAIL(x, y)
|
||||
#define OMV_PROFILE_START(F) mp_uint_t OMV_CONCATENATE(_ticks_start_, F) = mp_hal_ticks_us()
|
||||
#define OMV_PROFILE_PRINT(F) printf("%s:%s %u us\n", __FUNCTION__, #F, mp_hal_ticks_us() - OMV_CONCATENATE(_ticks_start_, F))
|
||||
#else
|
||||
#define OMV_PROFILE_START(F)
|
||||
#define OMV_PROFILE_PRINT(F)
|
||||
#endif
|
||||
// Token concatenation macros
|
||||
// OMV_CONCAT expands its arguments before pasting, while
|
||||
// OMV_CONCAT_HELPER performs the raw token pasting (x##y).
|
||||
#define OMV_CONCAT_HELPER(x, y) x##y
|
||||
#define OMV_CONCAT_STR(x, y) OMV_CONCAT_HELPER(x, y)
|
||||
|
||||
// Returns a pointer to the containing structure
|
||||
// ptr: Pointer to the member within the structure
|
||||
|
86
common/omv_crc.c
Normal file
86
common/omv_crc.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include "omv_crc.h"
|
||||
|
||||
static const uint16_t crc16_table[256] = {
|
||||
0x0000, 0xBAAD, 0xCFF7, 0x755A, 0x2543, 0x9FEE, 0xEAB4, 0x5019,
|
||||
0x4A86, 0xF02B, 0x8571, 0x3FDC, 0x6FC5, 0xD568, 0xA032, 0x1A9F,
|
||||
0x950C, 0x2FA1, 0x5AFB, 0xE056, 0xB04F, 0x0AE2, 0x7FB8, 0xC515,
|
||||
0xDF8A, 0x6527, 0x107D, 0xAAD0, 0xFAC9, 0x4064, 0x353E, 0x8F93,
|
||||
0x90B5, 0x2A18, 0x5F42, 0xE5EF, 0xB5F6, 0x0F5B, 0x7A01, 0xC0AC,
|
||||
0xDA33, 0x609E, 0x15C4, 0xAF69, 0xFF70, 0x45DD, 0x3087, 0x8A2A,
|
||||
0x05B9, 0xBF14, 0xCA4E, 0x70E3, 0x20FA, 0x9A57, 0xEF0D, 0x55A0,
|
||||
0x4F3F, 0xF592, 0x80C8, 0x3A65, 0x6A7C, 0xD0D1, 0xA58B, 0x1F26,
|
||||
0x9BC7, 0x216A, 0x5430, 0xEE9D, 0xBE84, 0x0429, 0x7173, 0xCBDE,
|
||||
0xD141, 0x6BEC, 0x1EB6, 0xA41B, 0xF402, 0x4EAF, 0x3BF5, 0x8158,
|
||||
0x0ECB, 0xB466, 0xC13C, 0x7B91, 0x2B88, 0x9125, 0xE47F, 0x5ED2,
|
||||
0x444D, 0xFEE0, 0x8BBA, 0x3117, 0x610E, 0xDBA3, 0xAEF9, 0x1454,
|
||||
0x0B72, 0xB1DF, 0xC485, 0x7E28, 0x2E31, 0x949C, 0xE1C6, 0x5B6B,
|
||||
0x41F4, 0xFB59, 0x8E03, 0x34AE, 0x64B7, 0xDE1A, 0xAB40, 0x11ED,
|
||||
0x9E7E, 0x24D3, 0x5189, 0xEB24, 0xBB3D, 0x0190, 0x74CA, 0xCE67,
|
||||
0xD4F8, 0x6E55, 0x1B0F, 0xA1A2, 0xF1BB, 0x4B16, 0x3E4C, 0x84E1,
|
||||
0x8D23, 0x378E, 0x42D4, 0xF879, 0xA860, 0x12CD, 0x6797, 0xDD3A,
|
||||
0xC7A5, 0x7D08, 0x0852, 0xB2FF, 0xE2E6, 0x584B, 0x2D11, 0x97BC,
|
||||
0x182F, 0xA282, 0xD7D8, 0x6D75, 0x3D6C, 0x87C1, 0xF29B, 0x4836,
|
||||
0x52A9, 0xE804, 0x9D5E, 0x27F3, 0x77EA, 0xCD47, 0xB81D, 0x02B0,
|
||||
0x1D96, 0xA73B, 0xD261, 0x68CC, 0x38D5, 0x8278, 0xF722, 0x4D8F,
|
||||
0x5710, 0xEDBD, 0x98E7, 0x224A, 0x7253, 0xC8FE, 0xBDA4, 0x0709,
|
||||
0x889A, 0x3237, 0x476D, 0xFDC0, 0xADD9, 0x1774, 0x622E, 0xD883,
|
||||
0xC21C, 0x78B1, 0x0DEB, 0xB746, 0xE75F, 0x5DF2, 0x28A8, 0x9205,
|
||||
0x16E4, 0xAC49, 0xD913, 0x63BE, 0x33A7, 0x890A, 0xFC50, 0x46FD,
|
||||
0x5C62, 0xE6CF, 0x9395, 0x2938, 0x7921, 0xC38C, 0xB6D6, 0x0C7B,
|
||||
0x83E8, 0x3945, 0x4C1F, 0xF6B2, 0xA6AB, 0x1C06, 0x695C, 0xD3F1,
|
||||
0xC96E, 0x73C3, 0x0699, 0xBC34, 0xEC2D, 0x5680, 0x23DA, 0x9977,
|
||||
0x8651, 0x3CFC, 0x49A6, 0xF30B, 0xA312, 0x19BF, 0x6CE5, 0xD648,
|
||||
0xCCD7, 0x767A, 0x0320, 0xB98D, 0xE994, 0x5339, 0x2663, 0x9CCE,
|
||||
0x135D, 0xA9F0, 0xDCAA, 0x6607, 0x361E, 0x8CB3, 0xF9E9, 0x4344,
|
||||
0x59DB, 0xE376, 0x962C, 0x2C81, 0x7C98, 0xC635, 0xB36F, 0x09C2
|
||||
};
|
||||
|
||||
__attribute__((weak)) void omv_crc_init(void) {
|
||||
|
||||
}
|
||||
|
||||
__attribute__((weak)) omv_crc_t omv_crc_start(const void *buf, size_t size) {
|
||||
const uint8_t *data = (const uint8_t *)buf;
|
||||
omv_crc_t crc = OMV_CRC_INIT;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
uint8_t index = (crc >> 8) ^ data[i];
|
||||
crc = (crc << 8) ^ crc16_table[index];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
__attribute__((weak)) omv_crc_t omv_crc_update(omv_crc_t crc, const void *buf, size_t size) {
|
||||
const uint8_t *data = (const uint8_t *)buf;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
uint8_t index = (crc >> 8) ^ data[i];
|
||||
crc = (crc << 8) ^ crc16_table[index];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
63
common/omv_crc.h
Normal file
63
common/omv_crc.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef __OMV_CRC_H__
|
||||
#define __OMV_CRC_H__
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// 0xBAAD is ironically good - HD=5 up to 108 bits, HD=4 up to 7985 bits
|
||||
#define OMV_CRC_POLY (0xBAAD)
|
||||
#define OMV_CRC_INIT (0xFFFF)
|
||||
#define OMV_CRC_SIZE (16)
|
||||
typedef uint16_t omv_crc_t;
|
||||
|
||||
// Initialize CRC hardware (if available)
|
||||
void omv_crc_init(void);
|
||||
|
||||
// Start CRC calculation for buffer data
|
||||
omv_crc_t omv_crc_start(const void *buf, size_t size);
|
||||
|
||||
// Update CRC with additional buffer data
|
||||
omv_crc_t omv_crc_update(omv_crc_t crc, const void *buf, size_t size);
|
||||
|
||||
// Read CRC value from the last 2 bytes of buffer
|
||||
static inline omv_crc_t omv_crc_read(const void *buf, size_t size) {
|
||||
const uint8_t *crc_buf = (const uint8_t *)buf + size - 2;
|
||||
return *((omv_crc_t*) crc_buf);
|
||||
}
|
||||
|
||||
// Calculate CRC and write it to the last 2 bytes of buffer
|
||||
static inline void omv_crc_write(void *buf, size_t size) {
|
||||
omv_crc_t crc = omv_crc_start(buf, size - 2);
|
||||
uint8_t *crc_buf = (uint8_t *)buf + size - 2;
|
||||
crc_buf[0] = crc & 0xFF;
|
||||
crc_buf[1] = (crc >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
// Calculate and check if CRC matches the one stored in buffer
|
||||
static inline bool omv_crc_check(const void *buf, size_t size) {
|
||||
return omv_crc_read(buf, size) == omv_crc_start(buf, size - 2);
|
||||
}
|
||||
#endif // __OMV_CRC_H__
|
@ -185,7 +185,7 @@ __weak int omv_csi_init() {
|
||||
memset(csi, 0, sizeof(omv_csi_t));
|
||||
csi->i2c = &csi_i2c;
|
||||
csi->clk = &csi_clk;
|
||||
csi->fb = framebuffer_get(-1);
|
||||
csi->fb = framebuffer_get(FB_MAINFB_ID);
|
||||
csi->color_palette = rainbow_table;
|
||||
memcpy(csi->resolution, csi_resolution, sizeof(csi_resolution));
|
||||
omv_csi_ops_init(csi);
|
||||
@ -223,7 +223,7 @@ __weak int omv_csi_init() {
|
||||
}
|
||||
|
||||
// Clear fb_enabled flag.
|
||||
JPEG_FB()->enabled = 0;
|
||||
framebuffer_set_enabled(framebuffer_get(FB_STREAM_ID), false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1282,24 +1282,25 @@ __weak int omv_csi_set_framebuffers(omv_csi_t *csi, size_t count, bool expand) {
|
||||
return OMV_CSI_ERROR_INVALID_FRAMESIZE;
|
||||
}
|
||||
|
||||
// TODO pass this to resize.
|
||||
#if OMV_CSI_HW_CROP_ENABLE
|
||||
// If hardware cropping is supported, use window size.
|
||||
csi->fb->frame_size = csi->fb->u * csi->fb->v * 2;
|
||||
size_t frame_size = csi->fb->u * csi->fb->v * 2;
|
||||
#else
|
||||
// Otherwise, use the real frame size.
|
||||
csi->fb->frame_size = csi->resolution[csi->framesize][0] * csi->resolution[csi->framesize][1] * 2;
|
||||
size_t frame_size = csi->resolution[csi->framesize][0] * csi->resolution[csi->framesize][1] * 2;
|
||||
#endif
|
||||
|
||||
if (count == -1) {
|
||||
for (size_t i=3; i>0; i--) {
|
||||
if (!framebuffer_resize(csi->fb, i, expand)) {
|
||||
if (!framebuffer_resize(csi->fb, i, frame_size, expand)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return framebuffer_resize(csi->fb, count, expand);
|
||||
return framebuffer_resize(csi->fb, count, frame_size, expand);
|
||||
}
|
||||
|
||||
__weak int omv_csi_set_special_effect(omv_csi_t *csi, omv_csi_sde_t sde) {
|
||||
@ -1583,8 +1584,8 @@ __weak int omv_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
if (buffer && (buffer->flags & VB_FLAG_USED)) {
|
||||
if (flags & OMV_CSI_FLAG_UPDATE_FB) {
|
||||
image_t tmp;
|
||||
framebuffer_init_image(csi->fb, &tmp);
|
||||
framebuffer_update_jpeg_buffer(&tmp);
|
||||
framebuffer_to_image(csi->fb, &tmp);
|
||||
framebuffer_update_preview(&tmp);
|
||||
}
|
||||
|
||||
// Release the previous buffer from used queue -> free queue.
|
||||
|
368
common/omv_profiler.c
Normal file
368
common/omv_profiler.c
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* OpenMV code profiler.
|
||||
*/
|
||||
#if OMV_PROFILER_ENABLE
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "omv_profiler.h"
|
||||
|
||||
#if __PMU_PRESENT
|
||||
#define OMV_GET_CYCLE_COUNT() ARM_PMU_Get_CCNTR()
|
||||
#define OMV_GET_EVENT_COUNT(i) ARM_PMU_Get_EVCNTR(i)
|
||||
#else
|
||||
#define OMV_GET_CYCLE_COUNT() 0
|
||||
#define OMV_GET_EVENT_COUNT(i) 0
|
||||
#endif // __PMU_PRESENT
|
||||
#define OMV_GET_TICKS_COUNT() ticks_us_monotonic()
|
||||
|
||||
// Call stack entry for tracking nested calls
|
||||
typedef struct {
|
||||
void *func_addr; // Function address
|
||||
void *call_addr; // Call site address
|
||||
|
||||
uint32_t enter_ticks; // Timestamp on entry
|
||||
uint32_t enter_cycles; // Cycle count on entry
|
||||
|
||||
uint64_t child_ticks; // Total child execution time
|
||||
uint64_t child_cycles; // Total child cycle count
|
||||
|
||||
#if __PMU_PRESENT
|
||||
uint16_t enter_events[__PMU_NUM_EVENTCNT]; // PMU events on entry
|
||||
uint64_t child_events[__PMU_NUM_EVENTCNT]; // Total child PMU events
|
||||
#endif
|
||||
} omv_stack_entry_t;
|
||||
|
||||
typedef struct {
|
||||
bool initialized; // Profiler state.
|
||||
mutex_t mutex; // Protects profile data
|
||||
bool reset_pending; // Reset requested flag
|
||||
omv_profiler_mode_t mode; // Inclusive/exclusive mode
|
||||
|
||||
uint32_t collisions; // Hash table collision count
|
||||
omv_profiler_data_t *hash[OMV_PROFILER_HASH_SIZE]; // Hash table buckets
|
||||
|
||||
uint32_t pool_index; // Next free pool entry
|
||||
omv_profiler_data_t pool[OMV_PROFILER_HASH_SIZE]; // Entry pool
|
||||
|
||||
int32_t stack_top; // Current stack position
|
||||
uint32_t stack_depth; // Max stack depth reached
|
||||
omv_stack_entry_t stack[OMV_PROFILER_STACK_DEPTH]; // Call stack
|
||||
} omv_profiler_state_t;
|
||||
|
||||
static omv_profiler_state_t profiler;
|
||||
|
||||
#define profiler_stack_top(x) profiler.stack[profiler.stack_top].x
|
||||
#define profiler_stack_prv(x) profiler.stack[profiler.stack_top - 1].x
|
||||
|
||||
static OMV_ATTR_NO_INSTRUMENT mp_uint_t ticks_us_monotonic(void) {
|
||||
static mp_uint_t last_timestamp = 0;
|
||||
mp_uint_t current = mp_hal_ticks_us();
|
||||
|
||||
// Ensure monotonic behavior
|
||||
if (current > last_timestamp) {
|
||||
last_timestamp = current;
|
||||
}
|
||||
|
||||
return last_timestamp;
|
||||
}
|
||||
|
||||
static inline uint32_t OMV_ATTR_NO_INSTRUMENT hash_address(void *addr) {
|
||||
uint32_t x = (uint32_t) addr >> 2; // drop thumb bit + alignment bits
|
||||
// mix (Murmur finalizer style)
|
||||
x ^= x >> 16;
|
||||
x *= 0x7feb352dU;
|
||||
x ^= x >> 15;
|
||||
x *= 0x846ca68bU;
|
||||
x ^= x >> 16;
|
||||
return x & (OMV_PROFILER_HASH_SIZE - 1);
|
||||
}
|
||||
|
||||
// Initialize profiling system
|
||||
void omv_profiler_init(void) {
|
||||
if (!profiler.initialized) {
|
||||
// Reset state
|
||||
memset(&profiler, 0, sizeof(profiler));
|
||||
#if __PMU_PRESENT
|
||||
// Enable PMU
|
||||
ARM_PMU_Enable();
|
||||
// Disable all event counters
|
||||
ARM_PMU_CNTR_Disable(((1U << __PMU_NUM_EVENTCNT) - 1));
|
||||
// Enable cycles counter.
|
||||
ARM_PMU_CNTR_Enable(PMU_CNTENSET_CCNTR_ENABLE_Msk);
|
||||
// Enable Trace
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Initialize mutex
|
||||
mutex_init0(&profiler.mutex);
|
||||
profiler.initialized = true;
|
||||
}
|
||||
|
||||
void omv_profiler_reset(void) {
|
||||
if (!mutex_try_lock(&profiler.mutex, MUTEX_TID_OMV)) {
|
||||
// Set a pending reset if the data is locked.
|
||||
profiler.reset_pending = true;
|
||||
} else {
|
||||
// Reset state
|
||||
profiler.pool_index = 0;
|
||||
profiler.stack_top = -1;
|
||||
profiler.stack_depth = 0;
|
||||
profiler.collisions = 0;
|
||||
profiler.reset_pending = false;
|
||||
|
||||
// Clear hash table
|
||||
memset(profiler.hash, 0, sizeof(profiler.hash));
|
||||
memset(profiler.pool, 0, sizeof(profiler.pool));
|
||||
memset(profiler.stack, 0, sizeof(profiler.stack));
|
||||
|
||||
mutex_unlock(&profiler.mutex, MUTEX_TID_OMV);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_t *omv_profiler_lock(void) {
|
||||
return &profiler.mutex;
|
||||
}
|
||||
|
||||
size_t omv_profiler_get_size(void) {
|
||||
return profiler.pool_index * sizeof(omv_profiler_data_t);
|
||||
}
|
||||
|
||||
const void *omv_profiler_get_data(void) {
|
||||
return profiler.pool;
|
||||
}
|
||||
|
||||
void omv_profiler_set_mode(uint32_t mode) {
|
||||
profiler.mode = mode;
|
||||
profiler.reset_pending = true;
|
||||
}
|
||||
|
||||
void omv_profiler_set_event(uint32_t num, uint32_t type) {
|
||||
#if __PMU_PRESENT
|
||||
if (num < __PMU_NUM_EVENTCNT) {
|
||||
ARM_PMU_Disable();
|
||||
|
||||
// Configure and enable event counter.
|
||||
ARM_PMU_Set_EVTYPER(num, type);
|
||||
ARM_PMU_CNTR_Enable(1 << num);
|
||||
|
||||
// Reset all event counters
|
||||
ARM_PMU_EVCNTR_ALL_Reset();
|
||||
|
||||
// Enable PMU
|
||||
ARM_PMU_Enable();
|
||||
}
|
||||
#endif
|
||||
profiler.reset_pending = true;
|
||||
}
|
||||
|
||||
static omv_profiler_data_t *omv_profiler_get_entry(void *func_addr) {
|
||||
uint32_t hash = hash_address(func_addr);
|
||||
omv_profiler_data_t *entry = profiler.hash[hash];
|
||||
|
||||
// Search existing entries in collision chain
|
||||
while (entry != NULL) {
|
||||
if (entry->func_addr == func_addr) {
|
||||
return entry;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
// Create new entry if not found
|
||||
if (profiler.pool_index >= OMV_PROFILER_HASH_SIZE) {
|
||||
// Pool exhausted
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry = &profiler.pool[profiler.pool_index++];
|
||||
|
||||
memset(entry, 0, sizeof(omv_profiler_data_t));
|
||||
entry->min_ticks = UINT32_MAX;
|
||||
|
||||
// Insert at head of collision chain
|
||||
if (profiler.hash[hash] != NULL) {
|
||||
profiler.collisions++;
|
||||
}
|
||||
|
||||
entry->next = profiler.hash[hash];
|
||||
profiler.hash[hash] = entry;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void OMV_ATTR_NO_INSTRUMENT __cyg_profile_func_enter(void *func_addr, void *call_addr) {
|
||||
uint32_t enter_ticks = OMV_GET_TICKS_COUNT();
|
||||
uint32_t enter_cycles = OMV_GET_CYCLE_COUNT();
|
||||
|
||||
if (func_addr == NULL || call_addr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip profiling if called from IRQ context
|
||||
#if !OMV_PROFILER_IRQ_ENABLE
|
||||
if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Restart profiler if needed
|
||||
if (profiler.reset_pending) {
|
||||
return omv_profiler_reset();
|
||||
}
|
||||
|
||||
// Update call stack
|
||||
profiler.stack_top++;
|
||||
if (profiler.stack_top >= OMV_PROFILER_STACK_DEPTH) {
|
||||
profiler.stack_top = OMV_PROFILER_STACK_DEPTH - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (profiler.stack_top > (int32_t)profiler.stack_depth) {
|
||||
profiler.stack_depth = profiler.stack_top;
|
||||
}
|
||||
|
||||
profiler_stack_top(func_addr) = func_addr;
|
||||
profiler_stack_top(call_addr) = call_addr;
|
||||
|
||||
profiler_stack_top(child_ticks) = 0;
|
||||
profiler_stack_top(enter_ticks) = enter_ticks;
|
||||
|
||||
profiler_stack_top(child_cycles) = 0;
|
||||
profiler_stack_top(enter_cycles) = enter_cycles;
|
||||
|
||||
#if __PMU_PRESENT
|
||||
for (size_t i = 0; i < __PMU_NUM_EVENTCNT; i++) {
|
||||
profiler_stack_top(child_events[i]) = 0;
|
||||
profiler_stack_top(enter_events[i]) = OMV_GET_EVENT_COUNT(i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void OMV_ATTR_NO_INSTRUMENT __cyg_profile_func_exit(void *func_addr, void *call_addr) {
|
||||
uint32_t exit_ticks = OMV_GET_TICKS_COUNT();
|
||||
uint32_t exit_cycles = OMV_GET_CYCLE_COUNT();
|
||||
|
||||
if (func_addr == NULL || call_addr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip profiling if called from IRQ context
|
||||
#if !OMV_PROFILER_IRQ_ENABLE
|
||||
if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Restart profiler if needed
|
||||
if (profiler.reset_pending) {
|
||||
return omv_profiler_reset();
|
||||
}
|
||||
|
||||
// Update events
|
||||
#if __PMU_PRESENT
|
||||
uint16_t exit_events[__PMU_NUM_EVENTCNT];
|
||||
for (size_t i = 0; i < __PMU_NUM_EVENTCNT; i++) {
|
||||
exit_events[i] = OMV_GET_EVENT_COUNT(i);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (profiler.stack_top < 0 || profiler.stack_top >= OMV_PROFILER_STACK_DEPTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Function address should match the stack's top.
|
||||
if (func_addr != profiler_stack_top(func_addr) ||
|
||||
call_addr != profiler_stack_top(call_addr)) {
|
||||
goto exit_cleanup;
|
||||
}
|
||||
|
||||
// Protect profile data update via mutex.
|
||||
if (!mutex_try_lock(&profiler.mutex, MUTEX_TID_OMV)) {
|
||||
goto exit_cleanup;
|
||||
}
|
||||
|
||||
// Calculate inclusive ticks and cycles
|
||||
uint32_t incl_ticks = exit_ticks - profiler_stack_top(enter_ticks);
|
||||
uint32_t incl_cycles = exit_cycles - profiler_stack_top(enter_cycles);
|
||||
|
||||
// Calculate exclusive ticks and cycles
|
||||
uint32_t excl_ticks = incl_ticks - profiler_stack_top(child_ticks);
|
||||
uint32_t excl_cycles = incl_cycles - profiler_stack_top(child_cycles);
|
||||
|
||||
// Check if this entry makes sense.
|
||||
if (incl_ticks < excl_ticks || incl_cycles < excl_cycles) {
|
||||
goto mutex_unlock;
|
||||
}
|
||||
|
||||
// Get or create a new entry
|
||||
omv_profiler_data_t *entry = omv_profiler_get_entry(func_addr);
|
||||
if (entry == NULL) {
|
||||
goto mutex_unlock;
|
||||
}
|
||||
|
||||
entry->call_count++;
|
||||
entry->func_addr = func_addr;
|
||||
entry->call_addr = call_addr;
|
||||
|
||||
entry->total_ticks += excl_ticks;
|
||||
entry->total_cycles += excl_cycles;
|
||||
|
||||
if (excl_ticks > entry->max_ticks) {
|
||||
entry->max_ticks = excl_ticks;
|
||||
}
|
||||
|
||||
if (excl_ticks < entry->min_ticks) {
|
||||
entry->min_ticks = excl_ticks;
|
||||
}
|
||||
|
||||
// In exclusive mode, updated the parent's child counters.
|
||||
if (profiler.stack_top && profiler.mode == OMV_PROFILER_EXCLUSIVE) {
|
||||
profiler_stack_prv(child_ticks) += incl_ticks;
|
||||
profiler_stack_prv(child_cycles) += incl_cycles;
|
||||
}
|
||||
|
||||
#if __PMU_PRESENT
|
||||
for (size_t i = 0; i < __PMU_NUM_EVENTCNT; i++) {
|
||||
uint16_t incl_events = exit_events[i] - profiler_stack_top(enter_events[i]);
|
||||
uint16_t excl_events = incl_events - profiler_stack_top(child_events[i]);
|
||||
|
||||
entry->total_events[i] += excl_events;
|
||||
|
||||
// In exclusive mode, updated the parent's child counters.
|
||||
if (profiler.stack_top && profiler.mode == OMV_PROFILER_EXCLUSIVE) {
|
||||
profiler_stack_prv(child_events[i]) += incl_events;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mutex_unlock:
|
||||
mutex_unlock(&profiler.mutex, MUTEX_TID_OMV);
|
||||
exit_cleanup:
|
||||
profiler.stack_top--;
|
||||
}
|
||||
#endif // OMV_PROFILER_ENABLE
|
103
common/omv_profiler.h
Normal file
103
common/omv_profiler.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* OpenMV code profiler.
|
||||
*/
|
||||
#ifndef __OMV_PROFILER_H__
|
||||
#define __OMV_PROFILER_H__
|
||||
|
||||
#if OMV_PROFILER_ENABLE
|
||||
#include <stdio.h>
|
||||
#include "py/mphal.h"
|
||||
#if __PMU_PRESENT
|
||||
#include "pmu_armv8.h"
|
||||
#endif
|
||||
#ifndef __cplusplus
|
||||
#include "common/mutex.h"
|
||||
#endif
|
||||
#include "common/omv_common.h"
|
||||
|
||||
#ifndef __PMU_NUM_EVENTCNT
|
||||
#define __PMU_NUM_EVENTCNT 0
|
||||
#endif
|
||||
|
||||
// Must be a power of 2
|
||||
#ifndef OMV_PROFILER_HASH_SIZE
|
||||
#define OMV_PROFILER_HASH_SIZE 256
|
||||
#endif
|
||||
|
||||
#ifndef OMV_PROFILER_STACK_DEPTH
|
||||
#define OMV_PROFILER_STACK_DEPTH 32
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
OMV_PROFILER_INCLUSIVE = 0,
|
||||
OMV_PROFILER_EXCLUSIVE = 1,
|
||||
} omv_profiler_mode_t;
|
||||
|
||||
// Profile record structure
|
||||
typedef struct __attribute__((packed)) _prof_data {
|
||||
void *func_addr; // Function address
|
||||
void *call_addr; // Caller address
|
||||
uint32_t call_count; // Number of times called
|
||||
uint32_t min_ticks; // Minimum execution time
|
||||
uint32_t max_ticks; // Maximum execution time
|
||||
uint64_t total_ticks; // Total time in ticks
|
||||
uint64_t total_cycles; // Total CPU cycles
|
||||
#if __PMU_PRESENT
|
||||
uint64_t total_events[__PMU_NUM_EVENTCNT];
|
||||
#endif
|
||||
struct _prof_data *next; // Next in hash collision chain
|
||||
} omv_profiler_data_t;
|
||||
|
||||
OMV_ATTR_NO_INSTRUMENT void omv_profiler_init(void);
|
||||
OMV_ATTR_NO_INSTRUMENT void omv_profiler_reset(void);
|
||||
OMV_ATTR_NO_INSTRUMENT size_t omv_profiler_get_size(void);
|
||||
OMV_ATTR_NO_INSTRUMENT const void *omv_profiler_get_data(void);
|
||||
OMV_ATTR_NO_INSTRUMENT void omv_profiler_set_mode(uint32_t mode);
|
||||
OMV_ATTR_NO_INSTRUMENT void omv_profiler_set_event(uint32_t num, uint32_t type);
|
||||
#ifndef __cplusplus
|
||||
OMV_ATTR_NO_INSTRUMENT mutex_t *omv_profiler_lock(void);
|
||||
#endif
|
||||
|
||||
// Manual instrumentation macros
|
||||
#define OMV_PROFILER_ENTER(func) \
|
||||
do { \
|
||||
void *func_addr = (void*)(func); \
|
||||
void *call_addr = __builtin_return_address(0); \
|
||||
__cyg_profile_func_enter(func_addr, call_addr); \
|
||||
} while(0)
|
||||
|
||||
#define OMV_PROFILER_EXIT(func) \
|
||||
do { \
|
||||
void *func_addr = (void*)(func); \
|
||||
void *call_addr = __builtin_return_address(0); \
|
||||
__cyg_profile_func_exit(func_addr, call_addr); \
|
||||
} while(0)
|
||||
|
||||
#else
|
||||
// Disabled - empty macros
|
||||
#define OMV_PROFILER_ENTER(func) do {} while(0)
|
||||
#define OMV_PROFILER_EXIT(func) do {} while(0)
|
||||
#endif // OMV_PROFILER_ENABLE
|
||||
#endif // __OMV_PROFILER_H__
|
@ -25,7 +25,8 @@
|
||||
*/
|
||||
|
||||
#include "omv_boardconfig.h"
|
||||
#if (OMV_TUSBDBG_ENABLE == 1)
|
||||
|
||||
#if OMV_TUSBDBG_ENABLE
|
||||
#include "py/runtime.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/mphal.h"
|
||||
@ -36,159 +37,175 @@
|
||||
#include "usbdbg.h"
|
||||
#include "tinyusb_debug.h"
|
||||
#include "omv_common.h"
|
||||
#include "cmsis_gcc.h"
|
||||
|
||||
#define USBDBG_BAUDRATE_SLOW (921600)
|
||||
#define USBDBG_BAUDRATE_FAST (12000000)
|
||||
#define USBDBG_DATA_TIMEOUT (1000)
|
||||
#define DEBUG_EP_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
void NORETURN __fatal_error(const char *msg);
|
||||
static mp_sched_node_t tusb_debug_node;
|
||||
typedef struct {
|
||||
uint32_t timestamp;
|
||||
uint8_t opcode;
|
||||
uint32_t length;
|
||||
bool debug_mode;
|
||||
mp_sched_node_t sched_node;
|
||||
ringbuf_t ringbuf;
|
||||
uint8_t rawbuf[OMV_TUSBDBG_BUFFER];
|
||||
} debug_context_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t cmd;
|
||||
uint8_t request;
|
||||
uint32_t xfer_length;
|
||||
}
|
||||
usbdbg_cmd_t;
|
||||
|
||||
static uint8_t tx_array[OMV_TUSBDBG_BUFFER];
|
||||
static ringbuf_t tx_ringbuf = { tx_array, sizeof(tx_array) };
|
||||
static volatile bool tinyusb_debug_mode = false;
|
||||
static debug_context_t ctx;
|
||||
|
||||
uint32_t usb_cdc_buf_len() {
|
||||
return ringbuf_avail(&tx_ringbuf);
|
||||
return ringbuf_avail(&ctx.ringbuf);
|
||||
}
|
||||
|
||||
uint32_t usb_cdc_get_buf(uint8_t *buf, uint32_t len) {
|
||||
// Read all of the request bytes.
|
||||
if (ringbuf_get_bytes(&tx_ringbuf, buf, len) == 0) {
|
||||
if (ringbuf_get_bytes(&ctx.ringbuf, buf, len) == 0) {
|
||||
return len;
|
||||
}
|
||||
|
||||
// Try to read as much data as possible.
|
||||
uint32_t bytes = 0;
|
||||
for (; bytes < len; bytes++) {
|
||||
int c = ringbuf_get(&tx_ringbuf);
|
||||
int c = ringbuf_get(&ctx.ringbuf);
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
buf[bytes] = c;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void usb_cdc_reset_buffers(void) {
|
||||
tx_ringbuf.iget = 0;
|
||||
tx_ringbuf.iput = 0;
|
||||
ctx.ringbuf.iget = 0;
|
||||
ctx.ringbuf.iput = 0;
|
||||
}
|
||||
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *coding) {
|
||||
tx_ringbuf.iget = 0;
|
||||
tx_ringbuf.iput = 0;
|
||||
usb_cdc_reset_buffers();
|
||||
|
||||
if (0) {
|
||||
#if defined(MICROPY_BOARD_ENTER_BOOTLOADER)
|
||||
} else if (coding->bit_rate == 1200) {
|
||||
#if defined(MICROPY_BOARD_ENTER_BOOTLOADER)
|
||||
if (coding->bit_rate == 1200) {
|
||||
MICROPY_BOARD_ENTER_BOOTLOADER(0, 0);
|
||||
#endif
|
||||
} else if (coding->bit_rate == USBDBG_BAUDRATE_SLOW ||
|
||||
coding->bit_rate == USBDBG_BAUDRATE_FAST) {
|
||||
tinyusb_debug_mode = true;
|
||||
} else {
|
||||
tinyusb_debug_mode = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx.debug_mode = (coding->bit_rate == USBDBG_BAUDRATE_SLOW ||
|
||||
coding->bit_rate == USBDBG_BAUDRATE_FAST);
|
||||
}
|
||||
|
||||
bool tinyusb_debug_enabled(void) {
|
||||
return tinyusb_debug_mode;
|
||||
}
|
||||
|
||||
extern void __real_mp_usbd_task(void);
|
||||
// Wrapped MicroPython functions.
|
||||
void __wrap_mp_usbd_task(void) {
|
||||
extern void __real_mp_usbd_task(void);
|
||||
|
||||
if (!tinyusb_debug_enabled()) {
|
||||
__real_mp_usbd_task();
|
||||
}
|
||||
}
|
||||
|
||||
extern void __real_tud_cdc_rx_cb(uint8_t itf);
|
||||
void __wrap_tud_cdc_rx_cb(uint8_t itf) {
|
||||
extern void __real_tud_cdc_rx_cb(uint8_t itf);
|
||||
|
||||
if (!tinyusb_debug_enabled()) {
|
||||
__real_tud_cdc_rx_cb(itf);
|
||||
}
|
||||
}
|
||||
|
||||
extern uintptr_t __real_mp_hal_stdio_poll(uintptr_t poll_flags);
|
||||
uintptr_t __wrap_mp_hal_stdio_poll(uintptr_t poll_flags) {
|
||||
extern uintptr_t __real_mp_hal_stdio_poll(uintptr_t poll_flags);
|
||||
|
||||
if (!tinyusb_debug_enabled()) {
|
||||
return __real_mp_hal_stdio_poll(poll_flags);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern mp_uint_t __real_mp_hal_stdout_tx_strn(const char *str, mp_uint_t len);
|
||||
mp_uint_t __wrap_mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
if (tinyusb_debug_enabled()) {
|
||||
if (tud_cdc_connected()) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
// The ring buffer overflows occasionally, espcially when using a slow poll
|
||||
// rate and fast print rate. When this happens, reset the buffer and start
|
||||
// over, if this string fits entirely in the buffer. This helps the ring buffer
|
||||
// self-recover from broken strings.
|
||||
if (ringbuf_put(&tx_ringbuf, str[i]) == -1 && len <= tx_ringbuf.size) {
|
||||
tx_ringbuf.iget = 0;
|
||||
tx_ringbuf.iput = 0;
|
||||
ringbuf_put(&tx_ringbuf, str[i]);
|
||||
}
|
||||
extern mp_uint_t __real_mp_hal_stdout_tx_strn(const char *str, mp_uint_t len);
|
||||
|
||||
if (!tinyusb_debug_enabled()) {
|
||||
return __real_mp_hal_stdout_tx_strn(str, len);
|
||||
} else if (tud_cdc_connected()) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
// On overflow, reset the ring buffer, if this string fits
|
||||
// entirely in the buffer, to recover from broken strings.
|
||||
if (ringbuf_put(&ctx.ringbuf, str[i]) == -1 && len <= ctx.ringbuf.size) {
|
||||
usb_cdc_reset_buffers();
|
||||
ringbuf_put(&ctx.ringbuf, str[i]);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
} else {
|
||||
return __real_mp_hal_stdout_tx_strn(str, len);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int tinyusb_debug_init(void) {
|
||||
ctx.opcode = 0;
|
||||
ctx.length = 0;
|
||||
if (!ctx.ringbuf.buf) {
|
||||
ctx.ringbuf = (ringbuf_t) { ctx.rawbuf, sizeof(ctx.rawbuf), 0, 0 };
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool tinyusb_debug_enabled(void) {
|
||||
return ctx.debug_mode;
|
||||
}
|
||||
|
||||
void tinyusb_debug_task(mp_sched_node_t *node) {
|
||||
tud_task_ext(0, false);
|
||||
|
||||
if (!tinyusb_debug_enabled() || !tud_cdc_connected() || tud_cdc_available() < 6) {
|
||||
if (!tinyusb_debug_enabled() || !tud_cdc_connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
usbdbg_cmd_t cmd;
|
||||
tud_cdc_read(&cmd, sizeof(cmd));
|
||||
if (!ctx.length && tud_cdc_available() >= USBDBG_HEADER_SIZE) {
|
||||
uint8_t cmdbuf[USBDBG_HEADER_SIZE];
|
||||
tud_cdc_read(cmdbuf, sizeof(cmdbuf));
|
||||
|
||||
if (cmd.cmd != 0x30) {
|
||||
// TODO: Try to recover from this state but for now, call __fatal_error.
|
||||
__fatal_error("Invalid USB CMD received.");
|
||||
if (cmdbuf[0] == 0x30) {
|
||||
ctx.opcode = cmdbuf[1];
|
||||
ctx.length = *((uint32_t*)(cmdbuf+2));
|
||||
usbdbg_control(NULL, ctx.opcode, ctx.length);
|
||||
}
|
||||
|
||||
if (ctx.length > 0) {
|
||||
ctx.timestamp = mp_hal_ticks_ms();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t request = cmd.request;
|
||||
uint32_t xfer_length = cmd.xfer_length;
|
||||
usbdbg_control(NULL, request, xfer_length);
|
||||
while (ctx.length) {
|
||||
uint32_t bytes = 0;
|
||||
|
||||
while (xfer_length) {
|
||||
// && tud_cdc_connected())
|
||||
if (tud_task_event_ready()) {
|
||||
tud_task_ext(0, false);
|
||||
}
|
||||
if (request & 0x80) {
|
||||
// Device-to-host data phase
|
||||
int size = OMV_MIN(xfer_length, tud_cdc_write_available());
|
||||
if (size) {
|
||||
xfer_length -= size;
|
||||
usbdbg_data_in(size, tud_cdc_write);
|
||||
|
||||
if (ctx.opcode & 0x80) {
|
||||
bytes = OMV_MIN(ctx.length, tud_cdc_write_available());
|
||||
if (bytes) {
|
||||
ctx.length -= bytes;
|
||||
usbdbg_data_in(bytes, tud_cdc_write);
|
||||
}
|
||||
if (size > 0 && size < DEBUG_EP_SIZE) {
|
||||
if (bytes > 0 && bytes < DEBUG_EP_SIZE) {
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
} else {
|
||||
// Host-to-device data phase
|
||||
int size = OMV_MIN(xfer_length, tud_cdc_available());
|
||||
if (size) {
|
||||
xfer_length -= size;
|
||||
usbdbg_data_out(size, tud_cdc_read);
|
||||
bytes = OMV_MIN(ctx.length, tud_cdc_available());
|
||||
if (bytes) {
|
||||
ctx.length -= bytes;
|
||||
usbdbg_data_out(bytes, tud_cdc_read);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes) {
|
||||
ctx.timestamp = mp_hal_ticks_ms();
|
||||
} else if (__get_PRIMASK() & 1) {
|
||||
break;
|
||||
} else if (check_timeout_ms(ctx.timestamp, USBDBG_DATA_TIMEOUT)) {
|
||||
tinyusb_debug_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For the mimxrt, and nrf ports this replaces the weak USB IRQ handlers.
|
||||
@ -197,7 +214,7 @@ void OMV_USB1_IRQ_HANDLER(void) {
|
||||
dcd_int_handler(0);
|
||||
// If there are any event to process, schedule a call to cdc loop.
|
||||
if (tud_task_event_ready()) {
|
||||
mp_sched_schedule_node(&tusb_debug_node, tinyusb_debug_task);
|
||||
mp_sched_schedule_node(&ctx.sched_node, tinyusb_debug_task);
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,7 +223,7 @@ void OMV_USB2_IRQ_HANDLER(void) {
|
||||
dcd_int_handler(1);
|
||||
// If there are any event to process, schedule a call to cdc loop.
|
||||
if (tud_task_event_ready()) {
|
||||
mp_sched_schedule_node(&tusb_debug_node, tinyusb_debug_task);
|
||||
mp_sched_schedule_node(&ctx.sched_node, tinyusb_debug_task);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -27,5 +27,4 @@
|
||||
void USBD_IRQHandler(void);
|
||||
int tinyusb_debug_init(void);
|
||||
bool tinyusb_debug_enabled(void);
|
||||
void tinyusb_debug_tx_strn(const char *str, mp_uint_t len);
|
||||
#endif // __TUSBDBG_H__
|
||||
|
291
common/usbdbg.c
291
common/usbdbg.c
@ -39,6 +39,7 @@
|
||||
#endif
|
||||
#include "framebuffer.h"
|
||||
#include "usbdbg.h"
|
||||
#include "tinyusb_debug.h"
|
||||
#include "omv_boardconfig.h"
|
||||
#include "py_image.h"
|
||||
|
||||
@ -56,14 +57,18 @@ extern uint32_t usb_cdc_buf_len();
|
||||
extern uint32_t usb_cdc_get_buf(uint8_t *buf, uint32_t len);
|
||||
extern void usb_cdc_reset_buffers(void);
|
||||
|
||||
|
||||
void usbdbg_init() {
|
||||
cmd = USBDBG_NONE;
|
||||
script_ready = false;
|
||||
script_running = false;
|
||||
irq_enabled = false;
|
||||
|
||||
vstr_init(&script_buf, 32);
|
||||
#if OMV_TUSBDBG_ENABLE
|
||||
tinyusb_debug_init();
|
||||
#endif
|
||||
#if OMV_PROFILER_ENABLE
|
||||
omv_profiler_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool usbdbg_script_ready() {
|
||||
@ -113,16 +118,6 @@ bool usbdbg_get_irq_enabled() {
|
||||
return irq_enabled;
|
||||
}
|
||||
|
||||
uint32_t ticks_diff_ms(uint32_t start_ms) {
|
||||
uint32_t current_ms = mp_hal_ticks_ms();
|
||||
if (current_ms >= start_ms) {
|
||||
return current_ms - start_ms;
|
||||
} else {
|
||||
// Handle wraparound
|
||||
return (UINT32_MAX - start_ms) + current_ms + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback) {
|
||||
switch (cmd) {
|
||||
case USBDBG_FW_VERSION: {
|
||||
@ -173,16 +168,17 @@ void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback) {
|
||||
// Return 0 if FB is locked or not ready.
|
||||
uint32_t buffer[3] = { 0 };
|
||||
// Try to lock FB. If header size == 0 frame is not ready
|
||||
if (mutex_try_lock_fair(&JPEG_FB()->lock, MUTEX_TID_IDE)) {
|
||||
framebuffer_t *fb = framebuffer_get(FB_STREAM_ID);
|
||||
if (mutex_try_lock(&fb->lock, MUTEX_TID_IDE)) {
|
||||
// If header size == 0 frame is not ready
|
||||
if (JPEG_FB()->size == 0) {
|
||||
if (fb->size == 0) {
|
||||
// unlock FB
|
||||
mutex_unlock(&JPEG_FB()->lock, MUTEX_TID_IDE);
|
||||
mutex_unlock(&fb->lock, MUTEX_TID_IDE);
|
||||
} else {
|
||||
// Return header w, h and size/bpp
|
||||
buffer[0] = JPEG_FB()->w;
|
||||
buffer[1] = JPEG_FB()->h;
|
||||
buffer[2] = JPEG_FB()->size;
|
||||
buffer[0] = fb->w;
|
||||
buffer[1] = fb->h;
|
||||
buffer[2] = fb->size;
|
||||
}
|
||||
}
|
||||
cmd = USBDBG_NONE;
|
||||
@ -192,12 +188,13 @@ void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback) {
|
||||
|
||||
case USBDBG_FRAME_DUMP:
|
||||
if (xfer_offs < xfer_size) {
|
||||
write_callback(JPEG_FB()->pixels + xfer_offs, size);
|
||||
framebuffer_t *fb = framebuffer_get(FB_STREAM_ID);
|
||||
write_callback(fb->raw_base + sizeof(framebuffer_header_t) + xfer_offs, size);
|
||||
xfer_offs += size;
|
||||
if (xfer_offs == xfer_size) {
|
||||
cmd = USBDBG_NONE;
|
||||
JPEG_FB()->w = 0; JPEG_FB()->h = 0; JPEG_FB()->size = 0;
|
||||
mutex_unlock(&JPEG_FB()->lock, MUTEX_TID_IDE);
|
||||
fb->w = 0; fb->h = 0; fb->size = 0;
|
||||
mutex_unlock(&fb->lock, MUTEX_TID_IDE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -238,30 +235,39 @@ void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback) {
|
||||
|
||||
// Set script running flag
|
||||
if (script_running) {
|
||||
buffer[0] |= USBDBG_STATE_FLAGS_SCRIPT;
|
||||
buffer[0] |= USBDBG_FLAG_SCRIPT_RUNNING;
|
||||
}
|
||||
|
||||
// Set text buf valid flag.
|
||||
uint32_t tx_buf_len = usb_cdc_buf_len();
|
||||
if (tx_buf_len) {
|
||||
buffer[0] |= USBDBG_STATE_FLAGS_TEXT;
|
||||
buffer[0] |= USBDBG_FLAG_TEXTBUF_NOTEMPTY;
|
||||
}
|
||||
|
||||
// Set code profiling flags
|
||||
#if OMV_PROFILER_ENABLE
|
||||
buffer[0] |= USBDBG_FLAG_PROFILE_ENABLED;
|
||||
#if __PMU_PRESENT
|
||||
buffer[0] |= USBDBG_FLAG_PROFILE_HAS_PMU;
|
||||
#endif // __PMU_PRESENT
|
||||
#endif // OMV_PROFILER_ENABLE
|
||||
|
||||
// Limit the frames sent over USB to 20Hz.
|
||||
if (ticks_diff_ms(last_update_ms) > 50 &&
|
||||
mutex_try_lock_fair(&JPEG_FB()->lock, MUTEX_TID_IDE)) {
|
||||
framebuffer_t *fb = framebuffer_get(FB_STREAM_ID);
|
||||
if (check_timeout_ms(last_update_ms, 50) &&
|
||||
mutex_try_lock_fair(&fb->lock, MUTEX_TID_IDE)) {
|
||||
// If header size == 0 frame is not ready
|
||||
if (JPEG_FB()->size == 0) {
|
||||
if (fb->size == 0) {
|
||||
// unlock FB
|
||||
mutex_unlock(&JPEG_FB()->lock, MUTEX_TID_IDE);
|
||||
mutex_unlock(&fb->lock, MUTEX_TID_IDE);
|
||||
} else {
|
||||
// Set valid frame flag.
|
||||
buffer[0] |= USBDBG_STATE_FLAGS_FRAME;
|
||||
buffer[0] |= USBDBG_FLAG_FRAMEBUF_LOCKED;
|
||||
|
||||
// Set frame width, height and size/bpp
|
||||
buffer[1] = JPEG_FB()->w;
|
||||
buffer[2] = JPEG_FB()->h;
|
||||
buffer[3] = JPEG_FB()->size;
|
||||
buffer[1] = fb->w;
|
||||
buffer[2] = fb->h;
|
||||
buffer[3] = fb->size;
|
||||
last_update_ms = mp_hal_ticks_ms();
|
||||
}
|
||||
}
|
||||
@ -279,6 +285,36 @@ void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback) {
|
||||
break;
|
||||
}
|
||||
|
||||
case USBDBG_PROFILE_SIZE: {
|
||||
// Return 0 if the profiling data is locked.
|
||||
uint32_t buffer[3] = { 0 };
|
||||
#if OMV_PROFILER_ENABLE
|
||||
if (mutex_try_lock(omv_profiler_lock(), MUTEX_TID_IDE)) {
|
||||
size_t size = omv_profiler_get_size();
|
||||
buffer[0] = size / sizeof(omv_profiler_data_t);
|
||||
buffer[1] = sizeof(omv_profiler_data_t);
|
||||
buffer[2] = __PMU_NUM_EVENTCNT;
|
||||
}
|
||||
#endif
|
||||
cmd = USBDBG_NONE;
|
||||
write_callback(&buffer, sizeof(buffer));
|
||||
break;
|
||||
}
|
||||
|
||||
#if OMV_PROFILER_ENABLE
|
||||
case USBDBG_PROFILE_DUMP:
|
||||
if (xfer_offs < xfer_size) {
|
||||
const char *data = omv_profiler_get_data();
|
||||
write_callback(data + xfer_offs, size);
|
||||
xfer_offs += size;
|
||||
if (xfer_offs == xfer_size) {
|
||||
cmd = USBDBG_NONE;
|
||||
mutex_unlock(omv_profiler_lock(), MUTEX_TID_IDE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: /* error */
|
||||
break;
|
||||
}
|
||||
@ -287,11 +323,14 @@ void usbdbg_data_in(uint32_t size, usbdbg_write_callback_t write_callback) {
|
||||
void usbdbg_data_out(uint32_t size, usbdbg_read_callback_t read_callback) {
|
||||
switch (cmd) {
|
||||
case USBDBG_FB_ENABLE: {
|
||||
read_callback(&(JPEG_FB()->enabled), 4);
|
||||
if (JPEG_FB()->enabled == 0) {
|
||||
uint32_t enabled = 0;
|
||||
framebuffer_t *fb = framebuffer_get(FB_STREAM_ID);
|
||||
read_callback(&enabled, 4);
|
||||
framebuffer_set_enabled(fb, enabled);
|
||||
if (fb->enabled == 0) {
|
||||
// When disabling framebuffer, the IDE might still be holding FB lock.
|
||||
// If the IDE is not the current lock owner, this operation is ignored.
|
||||
mutex_unlock(&JPEG_FB()->lock, MUTEX_TID_IDE);
|
||||
mutex_unlock(&fb->lock, MUTEX_TID_IDE);
|
||||
}
|
||||
cmd = USBDBG_NONE;
|
||||
break;
|
||||
@ -317,94 +356,30 @@ void usbdbg_data_out(uint32_t size, usbdbg_read_callback_t read_callback) {
|
||||
}
|
||||
break;
|
||||
|
||||
case USBDBG_TEMPLATE_SAVE: {
|
||||
#if defined(IMLIB_ENABLE_IMAGE_FILE_IO)
|
||||
image_t image;
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
|
||||
framebuffer_init_image(fb, &image);
|
||||
|
||||
size = MIN(128, size);
|
||||
char buffer[size];
|
||||
read_callback(buffer, size);
|
||||
buffer[size - 1] = 0;
|
||||
|
||||
rectangle_t *roi = (rectangle_t *) buffer;
|
||||
char *path = (char *) buffer + sizeof(rectangle_t);
|
||||
|
||||
imlib_save_image(&image, path, roi, 50);
|
||||
#endif //IMLIB_ENABLE_IMAGE_FILE_IO
|
||||
break;
|
||||
}
|
||||
|
||||
case USBDBG_DESCRIPTOR_SAVE: {
|
||||
#if defined(IMLIB_ENABLE_IMAGE_FILE_IO) \
|
||||
&& defined(IMLIB_ENABLE_KEYPOINTS)
|
||||
image_t image;
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
|
||||
framebuffer_init_image(fb, &image);
|
||||
|
||||
size = MIN(128, size);
|
||||
char buffer[size];
|
||||
read_callback(buffer, size);
|
||||
buffer[size - 1] = 0;
|
||||
|
||||
rectangle_t *roi = (rectangle_t *) buffer;
|
||||
char *path = (char *) buffer + sizeof(rectangle_t);
|
||||
|
||||
py_image_descriptor_from_roi(&image, path, roi);
|
||||
#endif //IMLIB_ENABLE_IMAGE_FILE_IO && IMLIB_ENABLE_KEYPOINTS
|
||||
cmd = USBDBG_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case USBDBG_ATTR_WRITE: {
|
||||
#if MICROPY_PY_CSI
|
||||
struct {
|
||||
int32_t name;
|
||||
int32_t value;
|
||||
} attr;
|
||||
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
|
||||
read_callback(&attr, sizeof(attr));
|
||||
|
||||
switch (attr.name) {
|
||||
case OMV_CSI_ATTR_CONTRAST:
|
||||
omv_csi_set_contrast(csi, attr.value);
|
||||
break;
|
||||
case OMV_CSI_ATTR_BRIGHTNESS:
|
||||
omv_csi_set_brightness(csi, attr.value);
|
||||
break;
|
||||
case OMV_CSI_ATTR_SATURATION:
|
||||
omv_csi_set_saturation(csi, attr.value);
|
||||
break;
|
||||
case OMV_CSI_ATTR_GAINCEILING:
|
||||
omv_csi_set_gainceiling(csi, attr.value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case USBDBG_PROFILE_MODE: {
|
||||
uint32_t buffer[1];
|
||||
read_callback(&buffer, sizeof(buffer));
|
||||
#if OMV_PROFILER_ENABLE
|
||||
omv_profiler_set_mode(buffer[0]);
|
||||
#endif
|
||||
cmd = USBDBG_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case USBDBG_SET_TIME: {
|
||||
// TODO implement
|
||||
uint8_t buffer[32];
|
||||
case USBDBG_PROFILE_EVENT: {
|
||||
uint32_t buffer[2];
|
||||
read_callback(&buffer, sizeof(buffer));
|
||||
#if OMV_PROFILER_ENABLE
|
||||
omv_profiler_set_event(buffer[0], buffer[1]);
|
||||
#endif
|
||||
cmd = USBDBG_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case USBDBG_TX_INPUT: {
|
||||
// TODO implement
|
||||
cmd = USBDBG_NONE;
|
||||
break;
|
||||
}
|
||||
default: /* error */
|
||||
size = OMV_MIN(size, 512);
|
||||
uint8_t byte_buffer[size];
|
||||
read_callback(&byte_buffer, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -413,31 +388,37 @@ void usbdbg_control(void *buffer, uint8_t request, uint32_t size) {
|
||||
cmd = (enum usbdbg_cmd) request;
|
||||
switch (cmd) {
|
||||
case USBDBG_FW_VERSION:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_FRAME_SIZE:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_FRAME_DUMP:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_ARCH_STR:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_FB_ENABLE:
|
||||
case USBDBG_TX_BUF:
|
||||
case USBDBG_TX_BUF_LEN:
|
||||
case USBDBG_SENSOR_ID:
|
||||
case USBDBG_GET_STATE:
|
||||
case USBDBG_PROFILE_SIZE:
|
||||
case USBDBG_PROFILE_DUMP:
|
||||
case USBDBG_PROFILE_MODE:
|
||||
case USBDBG_PROFILE_EVENT:
|
||||
case USBDBG_SCRIPT_EXEC:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
vstr_reset(&script_buf);
|
||||
break;
|
||||
|
||||
case USBDBG_PROFILE_RESET: {
|
||||
#if OMV_PROFILER_ENABLE
|
||||
omv_profiler_reset();
|
||||
#endif
|
||||
cmd = USBDBG_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
case USBDBG_SCRIPT_RUNNING:
|
||||
vstr_reset(&script_buf);
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_SCRIPT_STOP:
|
||||
if (script_running) {
|
||||
// Reset CDC buffers.
|
||||
@ -449,28 +430,6 @@ void usbdbg_control(void *buffer, uint8_t request, uint32_t size) {
|
||||
cmd = USBDBG_NONE;
|
||||
break;
|
||||
|
||||
case USBDBG_SCRIPT_SAVE:
|
||||
// TODO: save running script
|
||||
cmd = USBDBG_NONE;
|
||||
break;
|
||||
|
||||
case USBDBG_SCRIPT_RUNNING:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_TEMPLATE_SAVE:
|
||||
case USBDBG_DESCRIPTOR_SAVE:
|
||||
/* save template */
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_ATTR_WRITE:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_SYS_RESET:
|
||||
#if defined(OMV_BOARD_RESET)
|
||||
OMV_BOARD_RESET();
|
||||
@ -488,38 +447,6 @@ void usbdbg_control(void *buffer, uint8_t request, uint32_t size) {
|
||||
break;
|
||||
}
|
||||
|
||||
case USBDBG_FB_ENABLE: {
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
}
|
||||
|
||||
case USBDBG_TX_BUF:
|
||||
case USBDBG_TX_BUF_LEN:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_SENSOR_ID:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_SET_TIME:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_TX_INPUT:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
case USBDBG_GET_STATE:
|
||||
xfer_offs = 0;
|
||||
xfer_size = size;
|
||||
break;
|
||||
|
||||
default: /* error */
|
||||
cmd = USBDBG_NONE;
|
||||
break;
|
||||
|
@ -35,6 +35,7 @@
|
||||
|
||||
#define USBDBG_BAUDRATE_SLOW (921600)
|
||||
#define USBDBG_BAUDRATE_FAST (12000000)
|
||||
#define USBDBG_HEADER_SIZE (6)
|
||||
|
||||
/**
|
||||
* To add a new debugging command, increment the last command value used.
|
||||
@ -52,28 +53,28 @@ enum usbdbg_cmd {
|
||||
USBDBG_ARCH_STR = 0x83,
|
||||
USBDBG_SCRIPT_EXEC = 0x05,
|
||||
USBDBG_SCRIPT_STOP = 0x06,
|
||||
USBDBG_SCRIPT_SAVE = 0x07,
|
||||
USBDBG_SCRIPT_RUNNING = 0x87,
|
||||
USBDBG_TEMPLATE_SAVE = 0x08,
|
||||
USBDBG_DESCRIPTOR_SAVE = 0x09,
|
||||
USBDBG_ATTR_READ = 0x8A,
|
||||
USBDBG_ATTR_WRITE = 0x0B,
|
||||
USBDBG_SYS_RESET = 0x0C,
|
||||
USBDBG_SYS_RESET_TO_BL = 0x0E,
|
||||
USBDBG_FB_ENABLE = 0x0D,
|
||||
USBDBG_TX_BUF_LEN = 0x8E,
|
||||
USBDBG_TX_BUF = 0x8F,
|
||||
USBDBG_SENSOR_ID = 0x90,
|
||||
USBDBG_TX_INPUT = 0x11,
|
||||
USBDBG_SET_TIME = 0x12,
|
||||
USBDBG_GET_STATE = 0x93,
|
||||
USBDBG_PROFILE_SIZE = 0x94,
|
||||
USBDBG_PROFILE_DUMP = 0x95,
|
||||
USBDBG_PROFILE_MODE = 0x16,
|
||||
USBDBG_PROFILE_EVENT = 0x17,
|
||||
USBDBG_PROFILE_RESET = 0x18,
|
||||
};
|
||||
|
||||
enum usbdbg_state_flags {
|
||||
USBDBG_STATE_FLAGS_SCRIPT = (1 << 0),
|
||||
USBDBG_STATE_FLAGS_TEXT = (1 << 1),
|
||||
USBDBG_STATE_FLAGS_FRAME = (1 << 2),
|
||||
};
|
||||
typedef enum {
|
||||
USBDBG_FLAG_SCRIPT_RUNNING = (1 << 0),
|
||||
USBDBG_FLAG_TEXTBUF_NOTEMPTY = (1 << 1),
|
||||
USBDBG_FLAG_FRAMEBUF_LOCKED = (1 << 2),
|
||||
USBDBG_FLAG_PROFILE_ENABLED = (1 << 3),
|
||||
USBDBG_FLAG_PROFILE_HAS_PMU = (1 << 4),
|
||||
} usbdbg_flags_t;
|
||||
|
||||
typedef uint32_t (*usbdbg_read_callback_t) (void *buf, uint32_t len);
|
||||
typedef uint32_t (*usbdbg_write_callback_t) (const void *buf, uint32_t len);
|
||||
|
@ -736,6 +736,7 @@ int genx320_init(omv_csi_t *csi) {
|
||||
csi->set_hmirror = set_hmirror;
|
||||
csi->set_vflip = set_vflip;
|
||||
csi->ioctl = ioctl;
|
||||
csi->auxiliary = 1;
|
||||
|
||||
// Set csi flags
|
||||
csi->mono_bpp = sizeof(uint8_t);
|
||||
|
@ -440,7 +440,7 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
fb->pixfmt = csi->pixformat;
|
||||
|
||||
image_t fb_image;
|
||||
framebuffer_init_image(fb, &fb_image);
|
||||
framebuffer_to_image(fb, &fb_image);
|
||||
|
||||
LEP_SYS_FPA_TEMPERATURE_KELVIN_T kelvin;
|
||||
if (lepton.measurement_mode && (!lepton.radiometry)) {
|
||||
@ -481,7 +481,7 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
NULL, NULL, NULL, NULL);
|
||||
|
||||
fb_alloc_free_till_mark();
|
||||
framebuffer_init_image(fb, image);
|
||||
framebuffer_to_image(fb, image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -84,8 +84,8 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
fb->h = csi->transpose ? fb->u : fb->v;
|
||||
|
||||
// The new buffer hasn't been released yet, so the data pointer
|
||||
// has to be set manually after calling framebuffer_init_image.
|
||||
framebuffer_init_image(fb, image);
|
||||
// has to be set manually after calling framebuffer_to_image.
|
||||
framebuffer_to_image(fb, image);
|
||||
image->pixels = buffer->data;
|
||||
|
||||
static uint32_t step = 0;
|
||||
@ -129,7 +129,7 @@ static int snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
|
||||
// Move the buffer from free queue -> used queue.
|
||||
framebuffer_release(fb, FB_FLAG_FREE);
|
||||
framebuffer_init_image(fb, image);
|
||||
framebuffer_to_image(fb, image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
HAL_SRC_C += \
|
||||
drivers/source/adc.c \
|
||||
drivers/source/crc.c \
|
||||
drivers/source/dma_ctrl.c \
|
||||
drivers/source/dma_op.c \
|
||||
drivers/source/i2c.c \
|
||||
|
@ -192,11 +192,23 @@ __STATIC_FORCEINLINE uint32_t __UXTB(uint32_t op1)
|
||||
return(result);
|
||||
}
|
||||
|
||||
__STATIC_FORCEINLINE uint32_t __UXTAB(uint32_t op1, uint32_t op2)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
__ASM volatile ("uxtab %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2));
|
||||
return(result);
|
||||
}
|
||||
|
||||
__STATIC_FORCEINLINE uint32_t __UXTB_RORn(uint32_t op1, uint32_t rotate)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
__ASM volatile ("uxtb %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) );
|
||||
if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) {
|
||||
__ASM volatile ("uxtb %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) );
|
||||
} else {
|
||||
result = __UXTB(__ROR(op1, rotate));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -204,7 +216,11 @@ __STATIC_FORCEINLINE uint32_t __UXTB16_RORn(uint32_t op1, uint32_t rotate)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
__ASM volatile ("uxtb16 %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) );
|
||||
if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) {
|
||||
__ASM volatile ("uxtb16 %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) );
|
||||
} else {
|
||||
result = __UXTB16(__ROR(op1, rotate));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -212,7 +228,11 @@ __STATIC_FORCEINLINE uint32_t __UXTAB_RORn(uint32_t op1, uint32_t op2, uint32_t
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
__ASM volatile ("uxtab %0, %1, %2, ROR %3" : "=r" (result) : "r" (op1), "r" (op2), "i" (rotate) );
|
||||
if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) {
|
||||
__ASM volatile ("uxtab %0, %1, %2, ROR %3" : "=r" (result) : "r" (op1), "r" (op2), "i" (rotate) );
|
||||
} else {
|
||||
result = __UXTAB(op1, __ROR(op2, rotate));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -228,7 +248,11 @@ __STATIC_FORCEINLINE uint32_t __SXTB_RORn(uint32_t op1, uint32_t rotate)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
__ASM volatile ("sxtb %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) );
|
||||
if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) {
|
||||
__ASM volatile ("sxtb %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) );
|
||||
} else {
|
||||
result = __SXTB(__ROR(op1, rotate));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1160,7 +1160,6 @@ void imlib_debayer_line(int x_start, int x_end, int y_row, void *dst_row_ptr, pi
|
||||
// assumes dst->h == src->h
|
||||
// src and dst may not overlap, but, faster than imlib_debayer_image_awb
|
||||
void imlib_debayer_image(image_t *dst, image_t *src) {
|
||||
OMV_PROFILE_START();
|
||||
rectangle_t roi = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
@ -1168,7 +1167,6 @@ void imlib_debayer_image(image_t *dst, image_t *src) {
|
||||
.h = src->h,
|
||||
};
|
||||
vdebayer(src, &roi, 0, dst);
|
||||
OMV_PROFILE_PRINT();
|
||||
}
|
||||
|
||||
#if defined(IMLIB_ENABLE_DEBAYER_OPTIMIZATION)
|
||||
@ -2294,8 +2292,6 @@ static void vdebayer_rggb_to_rgb565_awb_quarter(image_t *src, image_t *dst, uint
|
||||
// RGB565: src->data == dst->data + image_size(src)
|
||||
// YUV422: Not supported
|
||||
void imlib_debayer_image_awb(image_t *dst, image_t *src, bool fast, uint32_t r_out, uint32_t g_out, uint32_t b_out) {
|
||||
OMV_PROFILE_START();
|
||||
|
||||
uint32_t red_gain = IM_DIV(g_out * 32, r_out);
|
||||
red_gain = IM_MIN(red_gain, 128U);
|
||||
|
||||
@ -2459,6 +2455,4 @@ void imlib_debayer_image_awb(image_t *dst, image_t *src, bool fast, uint32_t r_o
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OMV_PROFILE_PRINT();
|
||||
}
|
||||
|
@ -2838,7 +2838,6 @@ void imlib_draw_image(image_t *dst_img,
|
||||
imlib_draw_row_callback_t callback,
|
||||
void *callback_arg,
|
||||
void *dst_row_override) {
|
||||
OMV_PROFILE_START();
|
||||
int dst_delta_x = 1; // positive direction
|
||||
if (x_scale < 0.f) {
|
||||
// flip X
|
||||
@ -5404,7 +5403,6 @@ exit_cleanup:
|
||||
if (&new_src_img == src_img) {
|
||||
fb_free();
|
||||
}
|
||||
OMV_PROFILE_PRINT();
|
||||
}
|
||||
|
||||
#ifdef IMLIB_ENABLE_FLOOD_FILL
|
||||
|
@ -30,73 +30,95 @@
|
||||
#include "framebuffer.h"
|
||||
#include "omv_boardconfig.h"
|
||||
|
||||
// Main framebuffer memory
|
||||
extern char _fb_memory_start;
|
||||
extern char _fb_memory_end;
|
||||
static framebuffer_t framebuffer;
|
||||
|
||||
extern char _jpeg_memory_start;
|
||||
extern char _jpeg_memory_end;
|
||||
jpegbuffer_t jpegbuffer;
|
||||
// Streaming buffer memory
|
||||
extern char _sb_memory_start;
|
||||
extern char _sb_memory_end;
|
||||
|
||||
// Framebuffers array.
|
||||
static framebuffer_t framebuffers[2];
|
||||
|
||||
void framebuffer_init0() {
|
||||
// Save enable flag before resetting the state.
|
||||
int fb_enabled = jpegbuffer.enabled;
|
||||
// Reuse the last enabled flag after resetting the state.
|
||||
bool enabled = framebuffer_get(FB_STREAM_ID)->enabled;
|
||||
|
||||
// Initialize the static frame buffer.
|
||||
framebuffer_init(&framebuffer, &_fb_memory_start, &_fb_memory_end - &_fb_memory_start, false);
|
||||
// Initialize the main framebuffer.
|
||||
framebuffer_init(framebuffer_get(FB_MAINFB_ID), &_fb_memory_start,
|
||||
&_fb_memory_end - &_fb_memory_start, false, true);
|
||||
|
||||
// Initialize jpeg buffer.
|
||||
memset(&jpegbuffer, 0, sizeof(jpegbuffer_t));
|
||||
mutex_init0(&jpegbuffer.lock);
|
||||
jpegbuffer.enabled = fb_enabled;
|
||||
jpegbuffer.pixels = (uint8_t *) &_jpeg_memory_start;
|
||||
jpegbuffer.quality = ((OMV_JPEG_QUALITY_HIGH - OMV_JPEG_QUALITY_LOW) / 2) + OMV_JPEG_QUALITY_LOW;
|
||||
// Initialize the streaming buffer.
|
||||
framebuffer_init(framebuffer_get(FB_STREAM_ID), &_sb_memory_start,
|
||||
&_sb_memory_end - &_sb_memory_start, false, enabled);
|
||||
|
||||
// Reset embedded header
|
||||
framebuffer_t *fb = framebuffer_get(FB_STREAM_ID);
|
||||
memset(fb->raw_base, 0, sizeof(framebuffer_header_t));
|
||||
}
|
||||
|
||||
void framebuffer_init(framebuffer_t *fb, void *buff, size_t size, bool dynamic) {
|
||||
void framebuffer_init(framebuffer_t *fb, void *buff, size_t size, bool dynamic, bool enabled) {
|
||||
// Clear framebuffers
|
||||
memset(fb, 0, sizeof(framebuffer_t));
|
||||
|
||||
fb->raw_size = size;
|
||||
fb->raw_base = buff;
|
||||
fb->dynamic = dynamic;
|
||||
fb->enabled = enabled;
|
||||
#if OMV_RAW_PREVIEW_ENABLE
|
||||
fb->raw_w = OMV_RAW_PREVIEW_WIDTH;
|
||||
fb->raw_h = OMV_RAW_PREVIEW_HEIGHT;
|
||||
fb->raw_enabled = true;
|
||||
#else
|
||||
fb->raw_w = 0;
|
||||
fb->raw_h = 0;
|
||||
fb->raw_enabled = false;
|
||||
#endif
|
||||
fb->quality = ((OMV_JPEG_QUALITY_HIGH - OMV_JPEG_QUALITY_LOW) / 2) + OMV_JPEG_QUALITY_LOW;
|
||||
mutex_init0(&fb->lock);
|
||||
}
|
||||
|
||||
void framebuffer_init_image(framebuffer_t *fb, image_t *img) {
|
||||
void framebuffer_to_image(framebuffer_t *fb, image_t *img) {
|
||||
if (img != NULL) {
|
||||
vbuffer_t *buffer = framebuffer_acquire(fb, FB_FLAG_USED | FB_FLAG_PEEK);
|
||||
|
||||
img->w = fb->w;
|
||||
img->h = fb->h;
|
||||
img->size = fb->size;
|
||||
img->pixfmt = fb->pixfmt;
|
||||
img->pixels = (buffer == NULL) ? NULL : buffer->data;
|
||||
|
||||
// For streaming buffers (no queues), use raw_base directly
|
||||
if (fb->used_queue == NULL) {
|
||||
img->pixels = (uint8_t *) fb->raw_base;
|
||||
} else {
|
||||
vbuffer_t *buffer = framebuffer_acquire(fb, FB_FLAG_USED | FB_FLAG_PEEK);
|
||||
img->pixels = (buffer == NULL) ? NULL : buffer->data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void framebuffer_init_from_image(framebuffer_t *fb, image_t *img) {
|
||||
fb->w = img->w;
|
||||
fb->h = img->h;
|
||||
fb->size = img->size;
|
||||
fb->pixfmt = img->pixfmt;
|
||||
}
|
||||
|
||||
static void jpegbuffer_init_from_image(image_t *img) {
|
||||
void framebuffer_from_image(framebuffer_t *fb, image_t *img) {
|
||||
if (img == NULL) {
|
||||
jpegbuffer.w = 0;
|
||||
jpegbuffer.h = 0;
|
||||
jpegbuffer.size = 0;
|
||||
fb->w = 0;
|
||||
fb->h = 0;
|
||||
fb->size = 0;
|
||||
fb->pixfmt = PIXFORMAT_INVALID;
|
||||
} else {
|
||||
jpegbuffer.w = img->w;
|
||||
jpegbuffer.h = img->h;
|
||||
jpegbuffer.size = img->size;
|
||||
fb->w = img->w;
|
||||
fb->h = img->h;
|
||||
fb->size = img->size;
|
||||
fb->pixfmt = img->pixfmt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
framebuffer_t *framebuffer_get(size_t id) {
|
||||
return &framebuffer;
|
||||
if (id >= FB_MAX_ID) {
|
||||
return NULL;
|
||||
}
|
||||
return &framebuffers[id];
|
||||
}
|
||||
|
||||
|
||||
char *framebuffer_pool_start(framebuffer_t *fb, size_t buf_count) {
|
||||
size_t qsize = (buf_count <= 3) ? 0 : queue_calc_size(buf_count);
|
||||
return fb->raw_base + OMV_ALIGN_TO(qsize * 2, FRAMEBUFFER_ALIGNMENT);
|
||||
@ -122,7 +144,7 @@ void framebuffer_flush(framebuffer_t *fb) {
|
||||
queue_flush(fb->used_queue);
|
||||
}
|
||||
|
||||
for (size_t i=0; i<fb->buf_count; i++) {
|
||||
for (size_t i = 0; i < fb->buf_count; i++) {
|
||||
vbuffer_t *buffer = framebuffer_pool_get(fb, i);
|
||||
|
||||
// Reset the buffer's state.
|
||||
@ -138,14 +160,14 @@ void framebuffer_flush(framebuffer_t *fb) {
|
||||
}
|
||||
}
|
||||
|
||||
int framebuffer_resize(framebuffer_t *fb, size_t count, bool expand) {
|
||||
int framebuffer_resize(framebuffer_t *fb, size_t count, size_t frame_size, bool expand) {
|
||||
size_t buf_size = 0;
|
||||
// Queue size given the requested buffer count.
|
||||
size_t queue_size = queue_calc_size(count);
|
||||
|
||||
// Maximum usable memory size without queues.
|
||||
size_t min_size = frame_size + sizeof(vbuffer_t);
|
||||
size_t max_size = fb->raw_size - queue_size * 2;
|
||||
size_t min_size = fb->frame_size + sizeof(vbuffer_t);
|
||||
|
||||
// Use the frame buffer memory for big queues.
|
||||
char *queue_memory = (count > 3) ? fb->raw_base : fb->raw_static;
|
||||
@ -238,99 +260,106 @@ vbuffer_t *framebuffer_release(framebuffer_t *fb, uint32_t flags) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void framebuffer_update_jpeg_buffer(image_t *src) {
|
||||
void framebuffer_update_preview(image_t *src) {
|
||||
static int overflow_count = 0;
|
||||
const size_t max_size = (&_jpeg_memory_end - &_jpeg_memory_start) - sizeof(jpegbuffer_t);
|
||||
framebuffer_t *fb = framebuffer_get(FB_STREAM_ID);
|
||||
|
||||
// Check if JPEG buffer is disabled, image is NULL or format is not set.
|
||||
if (!jpegbuffer.enabled || !src->data || src->pixfmt == PIXFORMAT_INVALID) {
|
||||
// Check if the streaming buffer is disabled, image is NULL or format is not set.
|
||||
if (!fb->enabled || !src->data || src->pixfmt == PIXFORMAT_INVALID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock the JPEG buffer.
|
||||
if (!mutex_try_lock_fair(&jpegbuffer.lock, MUTEX_TID_OMV)) {
|
||||
// Lock the streaming buffer.
|
||||
if (!mutex_try_lock_fair(&fb->lock, MUTEX_TID_OMV)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reserve space for header at the beginning
|
||||
framebuffer_header_t *header = (framebuffer_header_t *) fb->raw_base;
|
||||
uint8_t *frame_data = (uint8_t *) fb->raw_base + sizeof(framebuffer_header_t);
|
||||
size_t available_size = fb->raw_size - sizeof(framebuffer_header_t);
|
||||
|
||||
if (src->is_compressed) {
|
||||
if (max_size < src->size) {
|
||||
jpegbuffer_init_from_image(NULL);
|
||||
if (src->size > available_size) {
|
||||
framebuffer_from_image(fb, NULL);
|
||||
mp_printf(MP_PYTHON_PRINTER, "\x1b[40O\n");
|
||||
} else {
|
||||
jpegbuffer_init_from_image(src);
|
||||
memcpy(jpegbuffer.pixels, src->pixels, src->size);
|
||||
framebuffer_from_image(fb, src);
|
||||
memcpy(frame_data, src->pixels, src->size);
|
||||
}
|
||||
} else {
|
||||
image_t dst = {
|
||||
.w = src->w,
|
||||
.h = src->h,
|
||||
.pixfmt = PIXFORMAT_JPEG,
|
||||
.size = max_size,
|
||||
.pixels = jpegbuffer.pixels
|
||||
};
|
||||
goto exit_cleanup;
|
||||
}
|
||||
|
||||
bool compress = true;
|
||||
bool overflow = false;
|
||||
image_t dst = {
|
||||
.w = src->w,
|
||||
.h = src->h,
|
||||
.pixfmt = PIXFORMAT_JPEG,
|
||||
.size = available_size,
|
||||
.pixels = frame_data
|
||||
};
|
||||
|
||||
#if OMV_RAW_PREVIEW_ENABLE
|
||||
if (src->is_mutable) {
|
||||
// Down-scale the frame (if necessary) and send the raw frame.
|
||||
dst.size = src->bpp;
|
||||
dst.pixfmt = src->pixfmt;
|
||||
if (src->w <= OMV_RAW_PREVIEW_WIDTH && src->h <= OMV_RAW_PREVIEW_HEIGHT) {
|
||||
if (image_size(&dst) <= max_size) {
|
||||
memcpy(dst.pixels, src->pixels, image_size(src));
|
||||
compress = false;
|
||||
}
|
||||
} else {
|
||||
float x_scale = OMV_RAW_PREVIEW_WIDTH / (float) src->w;
|
||||
float y_scale = OMV_RAW_PREVIEW_HEIGHT / (float) src->h;
|
||||
float scale = IM_MIN(x_scale, y_scale);
|
||||
dst.w = fast_floorf(src->w * scale);
|
||||
dst.h = fast_floorf(src->h * scale);
|
||||
if (image_size(&dst) <= max_size) {
|
||||
imlib_draw_image(&dst, src, 0, 0, scale, scale, NULL, -1, 255, NULL, NULL,
|
||||
IMAGE_HINT_BILINEAR | IMAGE_HINT_BLACK_BACKGROUND,
|
||||
NULL, NULL, NULL, NULL);
|
||||
compress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bool overflow = false;
|
||||
bool raw_stream = src->is_mutable && fb->raw_enabled && fb->raw_w && fb->raw_h;
|
||||
|
||||
if (compress) {
|
||||
// For all other formats, send a compressed frame.
|
||||
overflow = jpeg_compress(src, &dst, jpegbuffer.quality, false, JPEG_SUBSAMPLING_AUTO);
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
// JPEG buffer overflowed, reduce JPEG quality for the next frame
|
||||
// and skip the current frame. The IDE doesn't receive this frame.
|
||||
if (jpegbuffer.quality > 1) {
|
||||
// Keep this quality for the next n frames
|
||||
overflow_count = 60;
|
||||
jpegbuffer.quality = IM_MAX(1, (jpegbuffer.quality / 2));
|
||||
}
|
||||
|
||||
jpegbuffer_init_from_image(NULL);
|
||||
if (raw_stream) {
|
||||
// Down-scale the frame (if necessary) and send the raw frame.
|
||||
dst.size = src->bpp;
|
||||
dst.pixfmt = src->pixfmt;
|
||||
if (src->w <= fb->raw_w && src->h <= fb->raw_h && image_size(&dst) <= available_size) {
|
||||
memcpy(dst.pixels, src->pixels, image_size(src));
|
||||
} else {
|
||||
if (overflow_count) {
|
||||
overflow_count--;
|
||||
float scale = IM_MIN((fb->raw_w / (float) src->w),
|
||||
(fb->raw_h / (float) src->h));
|
||||
dst.w = fast_floorf(src->w * scale);
|
||||
dst.h = fast_floorf(src->h * scale);
|
||||
if (image_size(&dst) <= available_size) {
|
||||
imlib_draw_image(&dst, src, 0, 0, scale, scale, NULL, -1, 255, NULL, NULL,
|
||||
IMAGE_HINT_BILINEAR | IMAGE_HINT_BLACK_BACKGROUND,
|
||||
NULL, NULL, NULL, NULL);
|
||||
} else {
|
||||
raw_stream = false;
|
||||
}
|
||||
|
||||
// Dynamically adjust our quality if the image is huge.
|
||||
bool big_frame = image_size(src) > OMV_JPEG_QUALITY_THRESHOLD;
|
||||
int quality_max = big_frame ? OMV_JPEG_QUALITY_LOW : OMV_JPEG_QUALITY_HIGH;
|
||||
|
||||
// No buffer overflow, increase quality up to max quality based on frame size...
|
||||
if ((!overflow_count) && (jpegbuffer.quality < quality_max)) {
|
||||
jpegbuffer.quality++;
|
||||
}
|
||||
|
||||
jpegbuffer_init_from_image(&dst);
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock the JPEG buffer.
|
||||
mutex_unlock(&jpegbuffer.lock, MUTEX_TID_OMV);
|
||||
|
||||
// Compress the frame if the raw image didn't fit or the format is non-mutable.
|
||||
if (!raw_stream) {
|
||||
overflow = jpeg_compress(src, &dst, fb->quality, false, JPEG_SUBSAMPLING_AUTO);
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
// JPEG buffer overflowed, reduce JPEG quality for the next frame
|
||||
// and skip the current frame. The IDE doesn't receive this frame.
|
||||
if (fb->quality > 1) {
|
||||
// Keep this quality for the next n frames
|
||||
overflow_count = 60;
|
||||
fb->quality = IM_MAX(1, (fb->quality / 2));
|
||||
}
|
||||
|
||||
framebuffer_from_image(fb, NULL);
|
||||
} else {
|
||||
if (overflow_count) {
|
||||
overflow_count--;
|
||||
}
|
||||
|
||||
// Dynamically adjust our quality if the image is huge.
|
||||
bool big_frame = image_size(src) > OMV_JPEG_QUALITY_THRESHOLD;
|
||||
int quality_max = big_frame ? OMV_JPEG_QUALITY_LOW : OMV_JPEG_QUALITY_HIGH;
|
||||
|
||||
// No buffer overflow, increase quality up to max quality based on frame size...
|
||||
if ((!overflow_count) && (fb->quality < quality_max)) {
|
||||
fb->quality++;
|
||||
}
|
||||
|
||||
framebuffer_from_image(fb, &dst);
|
||||
}
|
||||
|
||||
exit_cleanup:
|
||||
header->width = fb->w;
|
||||
header->height = fb->h;
|
||||
header->pixfmt = fb->pixfmt;
|
||||
header->size = fb->is_compressed ? fb->size : fb->bpp;
|
||||
|
||||
// Unlock the streaming buffer.
|
||||
mutex_unlock(&fb->lock, MUTEX_TID_OMV);
|
||||
}
|
||||
|
@ -35,11 +35,12 @@
|
||||
#define FRAMEBUFFER_ALIGNMENT OMV_CACHE_LINE_SIZE
|
||||
#endif
|
||||
|
||||
// TODO these should just be removed.
|
||||
#define framebuffer_get_width(fb) (fb->w)
|
||||
#define framebuffer_get_height(fb) (fb->h)
|
||||
#define framebuffer_get_depth(fb) (fb->bpp)
|
||||
#define framebuffer_get_buffer_size(fb) (fb->buf_size)
|
||||
// Framebuffer ID
|
||||
typedef enum {
|
||||
FB_MAINFB_ID = 0,
|
||||
FB_STREAM_ID = 1,
|
||||
FB_MAX_ID = 2,
|
||||
} fb_id_t;
|
||||
|
||||
// If FB_FLAG_CHECK_LAST is set and this is the last buffer in
|
||||
// the free queue the release logic depends on the buffer mode:
|
||||
@ -85,19 +86,25 @@ typedef enum {
|
||||
// ² Unused frame buffer space can be used to expand buffers up
|
||||
// to the maximum available size (raw size minus queue size).
|
||||
typedef struct framebuffer {
|
||||
int32_t x, y, w, h, u, v;
|
||||
PIXFORMAT_STRUCT;
|
||||
bool dynamic; // Dynamically allocated or not.
|
||||
bool expanded; // True if buffers were expanded.
|
||||
size_t raw_size; // Raw buffer size and address.
|
||||
char *raw_base;
|
||||
size_t buf_size; // Buffers size and count
|
||||
size_t buf_count;
|
||||
size_t frame_size; // Actual frame size
|
||||
queue_t *used_queue;
|
||||
queue_t *free_queue;
|
||||
// Static memory for small queues.
|
||||
char raw_static[queue_calc_size(3) * 2];
|
||||
int32_t x, y; // Framebuffer offset
|
||||
int32_t w, h; // Framebuffer dimensions
|
||||
int32_t u, v; // Backup dimensions
|
||||
int16_t raw_w; // Raw streaming width
|
||||
int16_t raw_h; // Raw streaming height
|
||||
PIXFORMAT_STRUCT; // Pixel format struct.
|
||||
uint8_t dynamic; // Dynamically allocated or not.
|
||||
uint8_t expanded; // True if buffers were expanded.
|
||||
uint8_t enabled; // Enable/disable framebuffer
|
||||
uint8_t quality; // JPEG compression quality (1-100)
|
||||
uint8_t raw_enabled; // Enable raw streaming
|
||||
size_t raw_size; // Raw buffer size.
|
||||
char *raw_base; // Raw buffer address.
|
||||
size_t buf_size; // Vbuffer size.
|
||||
size_t buf_count; // Vbuffer count
|
||||
mutex_t lock; // Thread-safe access
|
||||
queue_t *used_queue; // Vbuffer used/read queue.
|
||||
queue_t *free_queue; // Vbuffer free/write queue.
|
||||
char raw_static[queue_calc_size(3) * 2]; // Static memory for small queues.
|
||||
} framebuffer_t;
|
||||
|
||||
// Drivers can add more flags:
|
||||
@ -116,29 +123,37 @@ typedef struct vbuffer {
|
||||
OMV_ATTR_ALIGNED(uint8_t data[], FRAMEBUFFER_ALIGNMENT); // Data.
|
||||
} vbuffer_t;
|
||||
|
||||
typedef struct jpegbuffer {
|
||||
int32_t w, h;
|
||||
int32_t size;
|
||||
int32_t enabled;
|
||||
int32_t quality;
|
||||
uint8_t *pixels;
|
||||
mutex_t lock;
|
||||
} jpegbuffer_t;
|
||||
// Frame header embedded in frame data for streaming protocol.
|
||||
typedef struct __attribute__((packed)) framebuffer_header {
|
||||
uint32_t width; // Frame width
|
||||
uint32_t height; // Frame height
|
||||
PIXFORMAT_STRUCT; // Pixel format
|
||||
} framebuffer_header_t;
|
||||
|
||||
void framebuffer_init0();
|
||||
|
||||
// Initializes a frame buffer instance.
|
||||
void framebuffer_init(framebuffer_t *fb, void *buff, size_t size, bool dynamic);
|
||||
void framebuffer_init(framebuffer_t *fb, void *buff, size_t size, bool dynamic, bool enabled);
|
||||
|
||||
// Initializes an image from the frame buffer.
|
||||
void framebuffer_init_image(framebuffer_t *fb, image_t *img);
|
||||
void framebuffer_to_image(framebuffer_t *fb, image_t *img);
|
||||
|
||||
// Sets the frame buffer from an image.
|
||||
void framebuffer_init_from_image(framebuffer_t *fb, image_t *img);
|
||||
void framebuffer_from_image(framebuffer_t *fb, image_t *img);
|
||||
|
||||
// Return the static frame buffer instance.
|
||||
framebuffer_t *framebuffer_get(size_t id);
|
||||
|
||||
// Return the current buffer size
|
||||
static inline size_t framebuffer_get_buffer_size(framebuffer_t *fb) {
|
||||
return fb->buf_size;
|
||||
}
|
||||
|
||||
// Enable/disable framebuffer.
|
||||
static inline void framebuffer_set_enabled(framebuffer_t *fb, bool enabled) {
|
||||
fb->enabled = enabled;
|
||||
}
|
||||
|
||||
// Returns a pointer to the end of the framebuffer(s).
|
||||
char *framebuffer_pool_end(framebuffer_t *fb);
|
||||
|
||||
@ -148,7 +163,7 @@ void framebuffer_flush(framebuffer_t *fb);
|
||||
// Change the number of buffers in the frame buffer.
|
||||
// If expand is true, the buffer size will expand to use all of the
|
||||
// available memory, otherwise it will equal the current frame size.
|
||||
int framebuffer_resize(framebuffer_t *fb, size_t count, bool expand);
|
||||
int framebuffer_resize(framebuffer_t *fb, size_t count, size_t frame_size, bool expand);
|
||||
|
||||
// Return true if free queue is not empty.
|
||||
bool framebuffer_writable(framebuffer_t *fb);
|
||||
@ -170,12 +185,13 @@ static inline void framebuffer_reset(vbuffer_t *buffer) {
|
||||
memset(buffer, 0, offsetof(vbuffer_t, data));
|
||||
}
|
||||
|
||||
// Compress src image to the JPEG buffer if src is mutable,
|
||||
// otherwise copy src to the JPEG buffer.
|
||||
void framebuffer_update_jpeg_buffer(image_t *src);
|
||||
|
||||
// Use this macro to get a pointer to the JPEG buffer.
|
||||
extern jpegbuffer_t jpegbuffer;
|
||||
#define JPEG_FB() (&jpegbuffer)
|
||||
// Set raw preview dimensions.
|
||||
static inline void framebuffer_set_preview(framebuffer_t *fb, uint16_t width, uint16_t height) {
|
||||
fb->raw_w = width;
|
||||
fb->raw_h = height;
|
||||
}
|
||||
|
||||
// Compress the source image into the streaming buffer if it is mutable
|
||||
// and raw preview is disabled, or copy it directly if already compressed.
|
||||
void framebuffer_update_preview(image_t *src);
|
||||
#endif /* __FRAMEBUFFER_H__ */
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "imlib_config.h"
|
||||
#include "omv_boardconfig.h"
|
||||
#include "omv_common.h"
|
||||
#include "omv_profiler.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
// Enables 38 TensorFlow Lite operators.
|
||||
|
@ -85,4 +85,11 @@ ifeq ($(CLANG_ENABLE),1)
|
||||
OMV_CLANG_OBJ = $(BUILD)/lib/imlib/bayer.o
|
||||
endif
|
||||
|
||||
# Enable instrumentation.
|
||||
ifeq ($(PROFILE_ENABLE), 1)
|
||||
$(BUILD)/lib/imlib/%.o: override CFLAGS += -finstrument-functions
|
||||
# Clang does not support -finstrument-functions-exclude-file-list.
|
||||
$(OMV_CLANG_OBJ): override CFLAGS := $(filter-out -finstrument-functions-exclude-file-list=%,$(CFLAGS))
|
||||
endif
|
||||
|
||||
OMV_FIRM_OBJ += $(addprefix $(BUILD)/lib/imlib/, $(IMLIB_SRC_C:.c=.o))
|
||||
|
@ -2858,7 +2858,6 @@ static int DecodeJPEG(JPEGIMAGE *pJPEG) {
|
||||
}
|
||||
|
||||
void jpeg_decompress(image_t *dst, image_t *src) {
|
||||
OMV_PROFILE_START();
|
||||
JPEGIMAGE jpg;
|
||||
|
||||
// Supports decoding baseline JPEGs only.
|
||||
@ -2898,7 +2897,5 @@ void jpeg_decompress(image_t *dst, image_t *src) {
|
||||
if (JPEG_decode(&jpg, 0, 0, 0) == 0) {
|
||||
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("JPEG decoder failed."));
|
||||
}
|
||||
|
||||
OMV_PROFILE_PRINT();
|
||||
}
|
||||
#endif
|
||||
|
@ -931,8 +931,6 @@ static void jpeg_write_headers(jpeg_buf_t *jpeg_buf, int w, int h, int bpp, jpeg
|
||||
}
|
||||
|
||||
bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc, jpeg_subsampling_t subsampling) {
|
||||
OMV_PROFILE_START();
|
||||
|
||||
if (!dst->data) {
|
||||
uint32_t size = 0;
|
||||
dst->data = fb_alloc_all(&size, FB_ALLOC_PREFER_SIZE | FB_ALLOC_CACHE_ALIGN);
|
||||
@ -1261,8 +1259,6 @@ bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc, jpeg_s
|
||||
|
||||
dst->size = jpeg_buf.idx;
|
||||
dst->data = jpeg_buf.buf;
|
||||
|
||||
OMV_PROFILE_PRINT();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -120,8 +120,6 @@ unsigned lodepng_convert_cb(unsigned char *out, const unsigned char *in,
|
||||
|
||||
#if defined(IMLIB_ENABLE_PNG_ENCODER)
|
||||
bool png_compress(image_t *src, image_t *dst) {
|
||||
OMV_PROFILE_START();
|
||||
|
||||
if (src->is_compressed) {
|
||||
return true;
|
||||
}
|
||||
@ -192,14 +190,12 @@ bool png_compress(image_t *src, image_t *dst) {
|
||||
// free fb_alloc() memory used for umm_init_x().
|
||||
fb_free(); // umm_init_x();
|
||||
}
|
||||
OMV_PROFILE_PRINT();
|
||||
return false;
|
||||
}
|
||||
#endif // IMLIB_ENABLE_PNG_ENCODER
|
||||
|
||||
#if defined(IMLIB_ENABLE_PNG_DECODER)
|
||||
void png_decompress(image_t *dst, image_t *src) {
|
||||
OMV_PROFILE_START();
|
||||
umm_init_x(fb_avail());
|
||||
|
||||
LodePNGState state;
|
||||
@ -241,7 +237,6 @@ void png_decompress(image_t *dst, image_t *src) {
|
||||
|
||||
// free fb_alloc() memory used for umm_init_x().
|
||||
fb_free(); // umm_init_x();
|
||||
OMV_PROFILE_PRINT();
|
||||
}
|
||||
#endif // IMLIB_ENABLE_PNG_DECODER
|
||||
#endif // IMLIB_ENABLE_PNG_ENCODER || IMLIB_ENABLE_PNG_DECODER
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit b806ed73202ee19d0187ab1094b251a8405c40ff
|
||||
Subproject commit b7efb956cfb21afda9b71162eeefec8c31d3dff9
|
BIN
lib/models/blazeface_front_128.tflite
Normal file
BIN
lib/models/blazeface_front_128.tflite
Normal file
Binary file not shown.
@ -78,6 +78,11 @@ $(BUILD)/lib/stai/libstai/ll_aton/%.o: override CFLAGS += \
|
||||
-Wno-double-promotion \
|
||||
$(STAI_CFLAGS) \
|
||||
|
||||
# Enable instrumentation.
|
||||
ifeq ($(PROFILE_ENABLE), 1)
|
||||
$(BUILD)/lib/stai/stai_backend.o: override CFLAGS += -finstrument-functions
|
||||
endif
|
||||
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/lib/stai/libstai/include
|
||||
OMV_FIRM_OBJ += $(addprefix $(BUILD)/lib/stai/, $(STAI_SRC_C:.c=.o))
|
||||
endif
|
||||
|
@ -178,7 +178,7 @@ int ml_backend_init_model(py_ml_model_obj_t *model) {
|
||||
const LL_Buffer_InfoTypeDef *model_outputs = ll_aton_reloc_get_output_buffers_info(&state->nn_inst, -1);
|
||||
|
||||
// Initialize the model's inputs.
|
||||
model->inputs_size = 1;
|
||||
for (model->inputs_size = 0; model_inputs[model->inputs_size].name != NULL; model->inputs_size++);
|
||||
model->input_shape = (mp_obj_tuple_t *) MP_OBJ_TO_PTR(mp_obj_new_tuple(model->inputs_size, NULL));
|
||||
model->input_scale = (mp_obj_tuple_t *) MP_OBJ_TO_PTR(mp_obj_new_tuple(model->inputs_size, NULL));
|
||||
model->input_zero_point = (mp_obj_tuple_t *) MP_OBJ_TO_PTR(mp_obj_new_tuple(model->inputs_size, NULL));
|
||||
@ -205,7 +205,7 @@ int ml_backend_init_model(py_ml_model_obj_t *model) {
|
||||
}
|
||||
|
||||
// Initialize the model's outputs.
|
||||
model->outputs_size = 1;
|
||||
for (model->outputs_size = 0; model_outputs[model->outputs_size].name != NULL; model->outputs_size++);
|
||||
model->output_shape = (mp_obj_tuple_t *) MP_OBJ_TO_PTR(mp_obj_new_tuple(model->outputs_size, NULL));
|
||||
model->output_scale = (mp_obj_tuple_t *) MP_OBJ_TO_PTR(mp_obj_new_tuple(model->outputs_size, NULL));
|
||||
model->output_zero_point = (mp_obj_tuple_t *) MP_OBJ_TO_PTR(mp_obj_new_tuple(model->outputs_size, NULL));
|
||||
|
@ -50,6 +50,5 @@ $(BUILD)/lib/tflm/tflm_backend.o: CXXFLAGS = \
|
||||
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/lib/tflm/libtflm/include
|
||||
OMV_CFLAGS += -I$(TOP_DIR)/lib/tflm/libtflm/include/third_party/ethos_u_core_driver/include
|
||||
|
||||
OMV_FIRM_OBJ += $(addprefix $(BUILD)/lib/tflm/, $(TFLM_SRC_CC:.cc=.o))
|
||||
endif
|
||||
|
@ -51,6 +51,7 @@ extern "C" {
|
||||
#include "py/binary.h"
|
||||
#include "py/gc.h"
|
||||
#include "py_ml.h"
|
||||
#include "common/omv_profiler.h"
|
||||
|
||||
using namespace tflite;
|
||||
#define TF_ARENA_EXTRA (512)
|
||||
@ -319,6 +320,8 @@ int ml_backend_init_model(py_ml_model_obj_t *model) {
|
||||
}
|
||||
|
||||
int ml_backend_run_inference(py_ml_model_obj_t *model) {
|
||||
OMV_PROFILER_ENTER(ml_backend_run_inference);
|
||||
|
||||
RegisterDebugLogCallback(ml_backend_log_handler);
|
||||
ml_backend_state_t *state = (ml_backend_state_t *) model->state;
|
||||
|
||||
@ -326,6 +329,7 @@ int ml_backend_run_inference(py_ml_model_obj_t *model) {
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invoke failed"));
|
||||
}
|
||||
|
||||
OMV_PROFILER_EXIT(ml_backend_run_inference);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -66,3 +66,10 @@ ifeq ($(DEBUG), 0)
|
||||
# Use a higher optimization level for user C modules.
|
||||
$(BUILD)/modules/%.o: override CFLAGS += $(USERMOD_OPT)
|
||||
endif
|
||||
|
||||
ifeq ($(PROFILE_ENABLE), 1)
|
||||
$(BUILD)/modules/py_ml.o: override CFLAGS += -finstrument-functions
|
||||
$(BUILD)/modules/py_image.o: override CFLAGS += -finstrument-functions
|
||||
$(BUILD)/modules/ulab/%.o: override CFLAGS += -finstrument-functions
|
||||
endif
|
||||
|
||||
|
@ -138,8 +138,8 @@ static MP_DEFINE_CONST_FUN_OBJ_1(py_omv_csi_shutdown_obj, py_omv_csi_shutdown);
|
||||
static mp_obj_t py_omv_csi_flush() {
|
||||
omv_csi_t *csi = omv_csi_get(-1);
|
||||
image_t tmp;
|
||||
framebuffer_init_image(csi->fb, &tmp);
|
||||
framebuffer_update_jpeg_buffer(&tmp);
|
||||
framebuffer_to_image(csi->fb, &tmp);
|
||||
framebuffer_update_preview(&tmp);
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_flush_obj, py_omv_csi_flush);
|
||||
@ -215,7 +215,7 @@ static mp_obj_t py_omv_csi_get_fb() {
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
framebuffer_init_image(csi->fb, &image);
|
||||
framebuffer_to_image(csi->fb, &image);
|
||||
return py_image_from_struct(&image);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_0(py_omv_csi_get_fb_obj, py_omv_csi_get_fb);
|
||||
|
@ -179,8 +179,8 @@ static MP_DEFINE_CONST_FUN_OBJ_2(py_csi_sleep_obj, py_csi_sleep);
|
||||
static mp_obj_t py_csi_flush(mp_obj_t self_in) {
|
||||
py_csi_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
image_t tmp;
|
||||
framebuffer_init_image(self->csi->fb, &tmp);
|
||||
framebuffer_update_jpeg_buffer(&tmp);
|
||||
framebuffer_to_image(self->csi->fb, &tmp);
|
||||
framebuffer_update_preview(&tmp);
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(py_csi_flush_obj, py_csi_flush);
|
||||
@ -1350,7 +1350,7 @@ mp_obj_t py_csi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
|
||||
if (csi->fb == NULL) {
|
||||
size_t fb_size = args[ARG_fb_size].u_int;
|
||||
csi->fb = (framebuffer_t *) m_malloc(sizeof(framebuffer_t));
|
||||
framebuffer_init(csi->fb, m_malloc(fb_size), fb_size, true);
|
||||
framebuffer_init(csi->fb, m_malloc(fb_size), fb_size, true, true);
|
||||
}
|
||||
|
||||
#if MICROPY_PY_IMU
|
||||
@ -1383,7 +1383,6 @@ static void py_csi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin
|
||||
mp_printf(print, " Expanded : %s\n", csi->fb->expanded ? "true" : "false");
|
||||
mp_printf(print, " Raw Buffer Size : %u\n", (unsigned) csi->fb->raw_size);
|
||||
mp_printf(print, " Raw Buffer Addr : 0x%p\n", csi->fb->raw_base);
|
||||
mp_printf(print, " Frame Size : %u\n", (unsigned) csi->fb->frame_size);
|
||||
mp_printf(print, " Vbuffer Size : %u\n", (unsigned) csi->fb->buf_size);
|
||||
mp_printf(print, " Vbuffer Count : %u\n", (unsigned) csi->fb->buf_count);
|
||||
mp_printf(print, " Used Queue Size : %u\n", queue_size(csi->fb->used_queue));
|
||||
|
@ -977,7 +977,7 @@ mp_obj_t py_fir_snapshot(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
|
||||
fb_alloc_free_till_mark();
|
||||
|
||||
if (args[ARG_copy_to_fb].u_bool) {
|
||||
framebuffer_update_jpeg_buffer(&dst_img);
|
||||
framebuffer_update_preview(&dst_img);
|
||||
}
|
||||
return py_image_from_struct(&dst_img);
|
||||
}
|
||||
|
@ -129,12 +129,12 @@ static mp_obj_t py_gif_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
py_gif_obj_t *gif = mp_obj_malloc_with_finaliser(py_gif_obj_t, &py_gif_type);
|
||||
|
||||
gif->width = (args[ARG_width].u_int == -1) ? framebuffer_get_width(fb) : args[ARG_width].u_int;
|
||||
gif->height = (args[ARG_height].u_int == -1) ? framebuffer_get_height(fb) : args[ARG_height].u_int;
|
||||
gif->color = (args[ARG_color].u_int == -1) ? (framebuffer_get_depth(fb) >= 2) : args[ARG_color].u_bool;
|
||||
gif->width = (args[ARG_width].u_int == -1) ? fb->w : args[ARG_width].u_int;
|
||||
gif->height = (args[ARG_height].u_int == -1) ? fb->h : args[ARG_height].u_int;
|
||||
gif->color = (args[ARG_color].u_int == -1) ? (fb->bpp >= 2) : args[ARG_color].u_bool;
|
||||
gif->loop = args[ARG_loop].u_bool;
|
||||
|
||||
file_open(&gif->fp, path, false, FA_WRITE | FA_CREATE_ALWAYS);
|
||||
|
@ -584,7 +584,7 @@ const uint8_t *py_helper_keyword_alpha_palette(size_t n_args, const mp_obj_t *ar
|
||||
}
|
||||
|
||||
bool py_helper_is_equal_to_framebuffer(image_t *img) {
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
vbuffer_t *buffer = framebuffer_acquire(fb, FB_FLAG_USED | FB_FLAG_PEEK);
|
||||
|
||||
return (buffer != NULL) && (img->data == buffer->data);
|
||||
@ -592,7 +592,7 @@ bool py_helper_is_equal_to_framebuffer(image_t *img) {
|
||||
|
||||
void py_helper_update_framebuffer(image_t *img) {
|
||||
if (py_helper_is_equal_to_framebuffer(img)) {
|
||||
framebuffer_init_from_image(framebuffer_get(0), img);
|
||||
framebuffer_from_image(framebuffer_get(FB_MAINFB_ID), img);
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,11 +604,11 @@ void py_helper_set_to_framebuffer(image_t *img) {
|
||||
|
||||
omv_csi_abort(csi, true, false);
|
||||
#else
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
#endif
|
||||
|
||||
// Resize the frame buffer to use all memory for one buffer.
|
||||
framebuffer_resize(fb, 1, true);
|
||||
framebuffer_resize(fb, 1, 0, true);
|
||||
|
||||
// This should never be NULL after resizing the frame buffer.
|
||||
vbuffer_t *buffer = framebuffer_acquire(fb, FB_FLAG_FREE | FB_FLAG_PEEK);
|
||||
@ -617,6 +617,6 @@ void py_helper_set_to_framebuffer(image_t *img) {
|
||||
PY_ASSERT_TRUE_MSG((image_size(img) <= framebuffer_get_buffer_size(fb)),
|
||||
"The image doesn't fit in the frame buffer!");
|
||||
|
||||
framebuffer_init_from_image(fb, img);
|
||||
framebuffer_from_image(fb, img);
|
||||
img->data = buffer->data;
|
||||
}
|
||||
|
@ -731,7 +731,6 @@ static MP_DEFINE_CONST_FUN_OBJ_1(py_image_bytearray_obj, py_image_bytearray);
|
||||
|
||||
#if defined(MODULE_ULAB_ENABLED) && (ULAB_MAX_DIMS == 4)
|
||||
static mp_obj_t py_image_to_ndarray(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
OMV_PROFILE_START();
|
||||
enum { ARG_dtype, ARG_buffer };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_dtype, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
|
||||
@ -906,8 +905,6 @@ static mp_obj_t py_image_to_ndarray(size_t n_args, const mp_obj_t *pos_args, mp_
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OMV_PROFILE_PRINT();
|
||||
return MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_ndarray_obj, 1, py_image_to_ndarray);
|
||||
@ -1085,10 +1082,10 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, mp_rom_obj_t default_color_palet
|
||||
float *transform = py_helper_arg_to_transform(args[ARG_transform].u_obj);
|
||||
|
||||
if (args[ARG_copy_to_fb].u_bool) {
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
image_t tmp;
|
||||
framebuffer_init_image(fb, &tmp);
|
||||
framebuffer_update_jpeg_buffer(&tmp);
|
||||
framebuffer_to_image(fb, &tmp);
|
||||
framebuffer_update_preview(&tmp);
|
||||
}
|
||||
|
||||
image_t dst_img = {
|
||||
@ -1188,7 +1185,7 @@ static mp_obj_t py_image_to(pixformat_t pixfmt, mp_rom_obj_t default_color_palet
|
||||
image_alloc(&dst_img, size);
|
||||
} else {
|
||||
// Convert in place.
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
bool is_fb = py_helper_is_equal_to_framebuffer(src_img);
|
||||
size_t buf_size = is_fb ? framebuffer_get_buffer_size(fb) : image_size(src_img);
|
||||
PY_ASSERT_TRUE_MSG((size <= buf_size), "The image doesn't fit in the frame buffer!");
|
||||
@ -1300,7 +1297,7 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(py_image_save_obj, 2, py_image_save);
|
||||
#endif //IMLIB_ENABLE_IMAGE_FILE_IO
|
||||
|
||||
static mp_obj_t py_image_flush(mp_obj_t img_obj) {
|
||||
framebuffer_update_jpeg_buffer(py_image_cobj(img_obj));
|
||||
framebuffer_update_preview(py_image_cobj(img_obj));
|
||||
return mp_const_none;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(py_image_flush_obj, py_image_flush);
|
||||
@ -6308,7 +6305,7 @@ mp_obj_t py_image_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw
|
||||
}
|
||||
|
||||
if (args[ARG_copy_to_fb].u_bool) {
|
||||
framebuffer_update_jpeg_buffer(&image);
|
||||
framebuffer_update_preview(&image);
|
||||
}
|
||||
return py_image_from_struct(&image);
|
||||
}
|
||||
|
@ -407,7 +407,7 @@ static mp_obj_t py_imageio_read(size_t n_args, const mp_obj_t *pos_args, mp_map_
|
||||
py_helper_update_framebuffer(&image);
|
||||
|
||||
if (args[ARG_copy_to_fb].u_bool) {
|
||||
framebuffer_update_jpeg_buffer(&image);
|
||||
framebuffer_update_preview(&image);
|
||||
}
|
||||
return py_image_from_struct(&image);
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ static mp_obj_t py_mjpeg_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
framebuffer_t *fb = framebuffer_get(0);
|
||||
framebuffer_t *fb = framebuffer_get(FB_MAINFB_ID);
|
||||
py_mjpeg_obj_t *mjpeg = mp_obj_malloc_with_finaliser(py_mjpeg_obj_t, &py_mjpeg_type);
|
||||
|
||||
mjpeg->frames = 0;
|
||||
@ -196,8 +196,8 @@ static mp_obj_t py_mjpeg_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t
|
||||
mjpeg->us_old = 0;
|
||||
mjpeg->us_avg = 0;
|
||||
mjpeg->closed = 0;
|
||||
mjpeg->width = (args[ARG_width].u_int == -1) ? framebuffer_get_width(fb) : args[ARG_width].u_int;
|
||||
mjpeg->height = (args[ARG_height].u_int == -1) ? framebuffer_get_height(fb) : args[ARG_height].u_int;
|
||||
mjpeg->width = (args[ARG_width].u_int == -1) ? fb->w : args[ARG_width].u_int;
|
||||
mjpeg->height = (args[ARG_height].u_int == -1) ? fb->h : args[ARG_height].u_int;
|
||||
|
||||
file_open(&mjpeg->fp, path, false, FA_WRITE | FA_CREATE_ALWAYS);
|
||||
mjpeg_open(&mjpeg->fp, mjpeg->width, mjpeg->height);
|
||||
|
@ -65,7 +65,7 @@ static size_t py_ml_tuple_sum(mp_obj_tuple_t *o) {
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t pl_ml_dtype_size(char dtype) {
|
||||
static size_t py_ml_dtype_size(char dtype) {
|
||||
switch (dtype) {
|
||||
case 'f':
|
||||
return 4;
|
||||
@ -92,7 +92,7 @@ static void py_ml_process_input(py_ml_model_obj_t *model, mp_obj_t arg) {
|
||||
if (mp_obj_is_callable(input_arg)) {
|
||||
// Input is a callable. Call the object and pass the tensor buffer and dtype.
|
||||
mp_obj_t fargs[3] = {
|
||||
mp_obj_new_bytearray_by_ref(input_size * pl_ml_dtype_size(input_dtype), input_buffer),
|
||||
mp_obj_new_bytearray_by_ref(input_size * py_ml_dtype_size(input_dtype), input_buffer),
|
||||
MP_OBJ_FROM_PTR(input_shape),
|
||||
mp_obj_new_int(input_dtype)
|
||||
};
|
||||
@ -151,7 +151,7 @@ static void py_ml_process_input(py_ml_model_obj_t *model, mp_obj_t arg) {
|
||||
}
|
||||
}
|
||||
|
||||
static mp_obj_t py_ml_process_output(py_ml_model_obj_t *model) {
|
||||
static mp_obj_t py_ml_process_output(py_ml_model_obj_t *model, bool deep_copy) {
|
||||
mp_obj_list_t *output_list = MP_OBJ_TO_PTR(mp_obj_new_list(model->outputs_size, NULL));
|
||||
for (size_t i = 0; i < model->outputs_size; i++) {
|
||||
void *model_output = ml_backend_get_output(model, i);
|
||||
@ -172,31 +172,38 @@ static mp_obj_t py_ml_process_output(py_ml_model_obj_t *model) {
|
||||
shape[ulab_offset + j] = mp_obj_get_int(output_shape->items[j]);
|
||||
}
|
||||
|
||||
ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(output_shape->len, shape, NDARRAY_FLOAT);
|
||||
ndarray_obj_t *ndarray;
|
||||
|
||||
if (output_dtype == 'f') {
|
||||
memcpy(ndarray->array, model_output, size * sizeof(float));
|
||||
} else if (output_dtype == 'b') {
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
float v = (((int8_t *) model_output)[j] - output_zero_point);
|
||||
((float *) ndarray->array)[j] = v * output_scale;
|
||||
}
|
||||
} else if (output_dtype == 'B') {
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
float v = (((uint8_t *) model_output)[j] - output_zero_point);
|
||||
((float *) ndarray->array)[j] = v * output_scale;
|
||||
}
|
||||
} else if (output_dtype == 'h') {
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
float v = (((int16_t *) model_output)[j] - output_zero_point);
|
||||
((float *) ndarray->array)[j] = v * output_scale;
|
||||
}
|
||||
} else if (output_dtype == 'H') {
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
float v = (((uint16_t *) model_output)[j] - output_zero_point);
|
||||
((float *) ndarray->array)[j] = v * output_scale;
|
||||
if (deep_copy) {
|
||||
ndarray = ndarray_new_dense_ndarray(output_shape->len, shape, NDARRAY_FLOAT);
|
||||
|
||||
if (output_dtype == 'f') {
|
||||
memcpy(ndarray->array, model_output, size * sizeof(float));
|
||||
} else if (output_dtype == 'b') {
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
float v = (((int8_t *) model_output)[j] - output_zero_point);
|
||||
((float *) ndarray->array)[j] = v * output_scale;
|
||||
}
|
||||
} else if (output_dtype == 'B') {
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
float v = (((uint8_t *) model_output)[j] - output_zero_point);
|
||||
((float *) ndarray->array)[j] = v * output_scale;
|
||||
}
|
||||
} else if (output_dtype == 'h') {
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
float v = (((int16_t *) model_output)[j] - output_zero_point);
|
||||
((float *) ndarray->array)[j] = v * output_scale;
|
||||
}
|
||||
} else if (output_dtype == 'H') {
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
float v = (((uint16_t *) model_output)[j] - output_zero_point);
|
||||
((float *) ndarray->array)[j] = v * output_scale;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ndarray = ndarray_new_ndarray(output_shape->len, shape, NULL, output_dtype, model_output);
|
||||
}
|
||||
|
||||
output_list->items[i] = MP_OBJ_FROM_PTR(ndarray);
|
||||
}
|
||||
|
||||
@ -254,17 +261,14 @@ static mp_obj_t py_ml_model_predict(size_t n_args, const mp_obj_t *pos_args, mp_
|
||||
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Unsupported input type. Expected a list"));
|
||||
}
|
||||
|
||||
OMV_PROFILE_START(preprocess);
|
||||
bool callback = args[ARG_callback].u_obj != mp_const_none;
|
||||
|
||||
py_ml_process_input(model, pos_args[1]);
|
||||
OMV_PROFILE_PRINT(preprocess);
|
||||
|
||||
OMV_PROFILE_START(inference);
|
||||
ml_backend_run_inference(model);
|
||||
OMV_PROFILE_PRINT(inference);
|
||||
|
||||
mp_obj_t output = py_ml_process_output(model);
|
||||
mp_obj_t output = py_ml_process_output(model, !callback);
|
||||
|
||||
if (args[ARG_callback].u_obj != mp_const_none) {
|
||||
if (callback) {
|
||||
// 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);
|
||||
|
@ -590,7 +590,7 @@ mp_obj_t py_tof_snapshot(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
|
||||
fb_alloc_free_till_mark();
|
||||
|
||||
if (args[ARG_copy_to_fb].u_bool) {
|
||||
framebuffer_update_jpeg_buffer(&dst_img);
|
||||
framebuffer_update_preview(&dst_img);
|
||||
}
|
||||
return py_image_from_struct(&dst_img);
|
||||
}
|
||||
|
70
ports/alif/omv_crc.c
Normal file
70
ports/alif/omv_crc.c
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2023-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.
|
||||
*/
|
||||
#include "crc.h"
|
||||
#include "global_map.h"
|
||||
#include "omv_crc.h"
|
||||
|
||||
#define CRC0 ((CRC_Type *)CRC0_BASE)
|
||||
|
||||
static bool crc_initialized = false;
|
||||
|
||||
void omv_crc_init(void) {
|
||||
crc_clear_config(CRC0);
|
||||
crc_enable_16bit(CRC0);
|
||||
crc_enable_custom_poly(CRC0);
|
||||
crc_set_custom_poly(CRC0, OMV_CRC_POLY);
|
||||
crc_set_seed(CRC0, OMV_CRC_INIT);
|
||||
crc_enable(CRC0);
|
||||
crc_initialized = true;
|
||||
}
|
||||
|
||||
omv_crc_t omv_crc_start(const void *buf, size_t size) {
|
||||
if (!crc_initialized) {
|
||||
omv_crc_init();
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return OMV_CRC_INIT;
|
||||
}
|
||||
|
||||
uint32_t result = 0;
|
||||
crc_calculate_16bit(CRC0, buf, size, &result);
|
||||
return (omv_crc_t)result;
|
||||
}
|
||||
|
||||
omv_crc_t omv_crc_update(omv_crc_t crc, const void *buf, size_t size) {
|
||||
if (!crc_initialized) {
|
||||
omv_crc_init();
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return crc;
|
||||
}
|
||||
|
||||
// Set current CRC value and calculate incrementally
|
||||
crc_set_seed(CRC0, crc);
|
||||
uint32_t result = 0;
|
||||
crc_calculate_16bit(CRC0, buf, size, &result);
|
||||
return (omv_crc_t)result;
|
||||
}
|
@ -348,7 +348,7 @@ int alif_csi_snapshot(omv_csi_t *csi, image_t *dst_image, uint32_t flags) {
|
||||
}
|
||||
|
||||
// Initialize a frame using the frame buffer.
|
||||
framebuffer_init_image(fb, dst_image);
|
||||
framebuffer_to_image(fb, dst_image);
|
||||
|
||||
// Set the frame's pixel format to bayer for raw sensors.
|
||||
if (csi->raw_output && csi->pixformat != PIXFORMAT_BAYER) {
|
||||
|
@ -135,7 +135,6 @@ int omv_gpu_draw_image(image_t *src_img,
|
||||
if (color_palette || alpha_palette || transform) {
|
||||
return -1;
|
||||
}
|
||||
OMV_PROFILE_START();
|
||||
d2_s32 err;
|
||||
d2_u32 blit_flags = 0;
|
||||
|
||||
@ -183,7 +182,6 @@ int omv_gpu_draw_image(image_t *src_img,
|
||||
|
||||
// Invalidate the framebuffer image.
|
||||
SCB_InvalidateDCache_by_Addr(dst_img->data, image_size(dst_img));
|
||||
OMV_PROFILE_PRINT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -470,7 +470,7 @@ int imx_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
}
|
||||
|
||||
// Set the user image.
|
||||
framebuffer_init_image(fb, image);
|
||||
framebuffer_to_image(fb, image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ static int nrf_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
framebuffer_release(fb, FB_FLAG_FREE);
|
||||
|
||||
// Set the user image.
|
||||
framebuffer_init_image(fb, image);
|
||||
framebuffer_to_image(fb, image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -27,3 +27,9 @@ CFLAGS += -I$(TOP_DIR)/ports/$(PORT)/modules
|
||||
|
||||
PORT_SRC_C = $(wildcard ports/$(PORT)/*.c)
|
||||
OMV_FIRM_OBJ += $(addprefix $(BUILD)/, $(PORT_SRC_C:.c=.o))
|
||||
|
||||
# Enable instrumentation.
|
||||
ifeq ($(PROFILE_ENABLE), 1)
|
||||
$(BUILD)/ports/alif/%.o: override CFLAGS += -finstrument-functions
|
||||
$(BUILD)/ports/stm32/%.o: override CFLAGS += -finstrument-functions
|
||||
endif
|
||||
|
@ -225,7 +225,7 @@ static int rp2_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
fb->pixfmt = csi->pixformat;
|
||||
|
||||
// Set the user image.
|
||||
framebuffer_init_image(fb, image);
|
||||
framebuffer_to_image(fb, image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -210,10 +210,10 @@ SECTIONS
|
||||
_fb_alloc_end = .;
|
||||
|
||||
. = ALIGN(4);
|
||||
_jpeg_memory_start = .;
|
||||
. = . + OMV_JPEG_SIZE;
|
||||
_sb_memory_start = .;
|
||||
. = . + OMV_SB_SIZE;
|
||||
. = ALIGN(4);
|
||||
_jpeg_memory_end = .;
|
||||
_sb_memory_end = .;
|
||||
} >OMV_FB_MEMORY
|
||||
|
||||
.heap (COPY):
|
||||
|
73
ports/stm32/omv_crc.c
Normal file
73
ports/stm32/omv_crc.c
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Only compile for STM32 families with configurable CRC hardware
|
||||
#include STM32_HAL_H
|
||||
#include "omv_crc.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(STM32F7) || defined(STM32H7) || defined(STM32N6)
|
||||
static CRC_HandleTypeDef hcrc = {0};
|
||||
static bool crc_initialized = false;
|
||||
|
||||
void omv_crc_init(void) {
|
||||
hcrc.Instance = CRC;
|
||||
hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE;
|
||||
hcrc.Init.GeneratingPolynomial = OMV_CRC_POLY;
|
||||
hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE;
|
||||
hcrc.Init.InitValue = OMV_CRC_INIT;
|
||||
hcrc.Init.CRCLength = CRC_POLYLENGTH_16B;
|
||||
hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE;
|
||||
hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLE;
|
||||
hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
|
||||
|
||||
HAL_CRC_Init(&hcrc);
|
||||
crc_initialized = true;
|
||||
}
|
||||
|
||||
omv_crc_t omv_crc_start(const void *buf, size_t size) {
|
||||
if (!crc_initialized) {
|
||||
omv_crc_init();
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return OMV_CRC_INIT;
|
||||
}
|
||||
|
||||
return (omv_crc_t) HAL_CRC_Calculate(&hcrc, (uint32_t *)buf, size);
|
||||
}
|
||||
|
||||
omv_crc_t omv_crc_update(omv_crc_t crc, const void *buf, size_t size) {
|
||||
if (!crc_initialized) {
|
||||
omv_crc_init();
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return crc;
|
||||
}
|
||||
|
||||
return (omv_crc_t) HAL_CRC_Accumulate(&hcrc, (uint32_t *)buf, size);
|
||||
}
|
||||
|
||||
#endif // STM32F7 || STM32H7 || STM32N6
|
@ -758,7 +758,7 @@ static int stm_csi_snapshot(omv_csi_t *csi, image_t *image, uint32_t flags) {
|
||||
#endif
|
||||
|
||||
// Set the user image.
|
||||
framebuffer_init_image(fb, image);
|
||||
framebuffer_to_image(fb, image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -95,8 +95,6 @@ int omv_gpu_draw_image(image_t *src_img,
|
||||
const uint8_t *alpha_palette,
|
||||
image_hint_t hint,
|
||||
float *transform) {
|
||||
OMV_PROFILE_START();
|
||||
|
||||
// GPU2D can only draw on RGB565/GRAYSCALE buffers.
|
||||
if ((dst_img->pixfmt != PIXFORMAT_RGB565) && (dst_img->pixfmt != PIXFORMAT_GRAYSCALE)) {
|
||||
return -1;
|
||||
@ -197,8 +195,6 @@ int omv_gpu_draw_image(image_t *src_img,
|
||||
HAL_ICACHE_Invalidate_IT();
|
||||
|
||||
SCB_InvalidateDCache_by_Addr(dst_img->data, image_size(dst_img));
|
||||
|
||||
OMV_PROFILE_PRINT();
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
@ -211,8 +207,6 @@ int omv_gpu_draw_image(image_t *src_img,
|
||||
const uint8_t *alpha_palette,
|
||||
image_hint_t hint,
|
||||
float *transform) {
|
||||
OMV_PROFILE_START();
|
||||
|
||||
// DMA2D can only draw on RGB565 buffers and the destination/source buffers must be accessible by DMA.
|
||||
if ((dst_img->pixfmt != PIXFORMAT_RGB565) || (!DMA_BUFFER(dst_img->data)) || (!DMA_BUFFER(src_img->data))) {
|
||||
return -1;
|
||||
@ -401,8 +395,6 @@ int omv_gpu_draw_image(image_t *src_img,
|
||||
#endif
|
||||
|
||||
HAL_DMA2D_DeInit(&dma2d);
|
||||
|
||||
OMV_PROFILE_PRINT();
|
||||
return 0;
|
||||
}
|
||||
#endif // OMV_GPU_NEMA
|
||||
|
@ -146,54 +146,39 @@ void HAL_MspInit(void) {
|
||||
|
||||
// Enable GPIO clocks.
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOA_CLK_SLEEP_ENABLE();
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOB_CLK_SLEEP_ENABLE();
|
||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOC_CLK_SLEEP_ENABLE();
|
||||
__HAL_RCC_GPIOD_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOD_CLK_SLEEP_ENABLE();
|
||||
__HAL_RCC_GPIOE_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOE_CLK_SLEEP_ENABLE();
|
||||
#if OMV_GPIO_PORT_F_ENABLE
|
||||
__HAL_RCC_GPIOF_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOF_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if OMV_GPIO_PORT_G_ENABLE
|
||||
__HAL_RCC_GPIOG_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOG_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if OMV_GPIO_PORT_H_ENABLE
|
||||
__HAL_RCC_GPIOH_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOH_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if OMV_GPIO_PORT_I_ENABLE
|
||||
__HAL_RCC_GPIOI_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOI_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if OMV_GPIO_PORT_J_ENABLE
|
||||
__HAL_RCC_GPIOJ_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOJ_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if OMV_GPIO_PORT_K_ENABLE
|
||||
__HAL_RCC_GPIOK_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOK_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if OMV_GPIO_PORT_N_ENABLE
|
||||
__HAL_RCC_GPION_CLK_ENABLE();
|
||||
__HAL_RCC_GPION_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if OMV_GPIO_PORT_O_ENABLE
|
||||
__HAL_RCC_GPIOO_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOO_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if OMV_GPIO_PORT_P_ENABLE
|
||||
__HAL_RCC_GPIOP_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOP_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if OMV_GPIO_PORT_Q_ENABLE
|
||||
__HAL_RCC_GPIOQ_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOQ_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
|
||||
// Enable DMA clocks.
|
||||
@ -205,11 +190,9 @@ void HAL_MspInit(void) {
|
||||
#endif
|
||||
#if defined(__HAL_RCC_GPDMA1_CLK_ENABLE)
|
||||
__HAL_RCC_GPDMA1_CLK_ENABLE();
|
||||
__HAL_RCC_GPDMA1_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if defined(__HAL_RCC_HPDMA1_CLK_ENABLE)
|
||||
__HAL_RCC_HPDMA1_CLK_ENABLE();
|
||||
__HAL_RCC_HPDMA1_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if defined(__HAL_RCC_MDMA_CLK_ENABLE)
|
||||
__HAL_RCC_MDMA_CLK_ENABLE();
|
||||
@ -220,10 +203,7 @@ void HAL_MspInit(void) {
|
||||
// Enable SDMMCx clocks.
|
||||
#if defined(STM32N6)
|
||||
__HAL_RCC_SDMMC1_CLK_ENABLE();
|
||||
__HAL_RCC_SDMMC1_CLK_SLEEP_ENABLE();
|
||||
|
||||
__HAL_RCC_SDMMC2_CLK_ENABLE();
|
||||
__HAL_RCC_SDMMC2_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
|
||||
// Setup AXI QoS
|
||||
@ -277,6 +257,23 @@ void HAL_MspInit(void) {
|
||||
HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PC3, SYSCFG_SWITCH_PC3_CLOSE);
|
||||
#endif
|
||||
|
||||
#if defined(STM32N6)
|
||||
// Enable AHB peripherals during sleep.
|
||||
LL_AHB1_GRP1_EnableClockLowPower(LL_AHB1_GRP1_PERIPH_ALL);
|
||||
LL_AHB2_GRP1_EnableClockLowPower(LL_AHB2_GRP1_PERIPH_ALL);
|
||||
LL_AHB3_GRP1_EnableClockLowPower(LL_AHB3_GRP1_PERIPH_ALL);
|
||||
LL_AHB4_GRP1_EnableClockLowPower(LL_AHB4_GRP1_PERIPH_ALL);
|
||||
LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_ALL);
|
||||
|
||||
// Enable APB peripherals during sleep.
|
||||
LL_APB1_GRP1_EnableClockLowPower(LL_APB1_GRP1_PERIPH_ALL);
|
||||
LL_APB1_GRP2_EnableClockLowPower(LL_APB1_GRP2_PERIPH_ALL);
|
||||
LL_APB2_GRP1_EnableClockLowPower(LL_APB2_GRP1_PERIPH_ALL);
|
||||
LL_APB4_GRP1_EnableClockLowPower(LL_APB4_GRP1_PERIPH_ALL);
|
||||
LL_APB4_GRP2_EnableClockLowPower(LL_APB4_GRP2_PERIPH_ALL);
|
||||
LL_APB5_GRP1_EnableClockLowPower(LL_APB5_GRP1_PERIPH_ALL);
|
||||
#endif
|
||||
|
||||
#if defined(STM32N6)
|
||||
__HAL_RCC_RIFSC_CLK_ENABLE();
|
||||
|
||||
@ -315,28 +312,24 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c) {
|
||||
scl_pin = OMV_I2C1_SCL_PIN;
|
||||
sda_pin = OMV_I2C1_SDA_PIN;
|
||||
__HAL_RCC_I2C1_CLK_ENABLE();
|
||||
__HAL_RCC_I2C1_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if defined(OMV_I2C2_ID)
|
||||
} else if (hi2c->Instance == I2C2) {
|
||||
scl_pin = OMV_I2C2_SCL_PIN;
|
||||
sda_pin = OMV_I2C2_SDA_PIN;
|
||||
__HAL_RCC_I2C2_CLK_ENABLE();
|
||||
__HAL_RCC_I2C2_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if defined(OMV_I2C3_ID)
|
||||
} else if (hi2c->Instance == I2C3) {
|
||||
scl_pin = OMV_I2C3_SCL_PIN;
|
||||
sda_pin = OMV_I2C3_SDA_PIN;
|
||||
__HAL_RCC_I2C3_CLK_ENABLE();
|
||||
__HAL_RCC_I2C3_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
#if defined(OMV_I2C4_ID)
|
||||
} else if (hi2c->Instance == I2C4) {
|
||||
scl_pin = OMV_I2C4_SCL_PIN;
|
||||
sda_pin = OMV_I2C4_SDA_PIN;
|
||||
__HAL_RCC_I2C4_CLK_ENABLE();
|
||||
__HAL_RCC_I2C4_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -353,28 +346,24 @@ void HAL_I2C_MspDeInit(I2C_HandleTypeDef *hi2c) {
|
||||
__HAL_RCC_I2C1_FORCE_RESET();
|
||||
__HAL_RCC_I2C1_RELEASE_RESET();
|
||||
__HAL_RCC_I2C1_CLK_DISABLE();
|
||||
__HAL_RCC_I2C1_CLK_SLEEP_DISABLE();
|
||||
#endif
|
||||
#if defined(OMV_I2C2_ID)
|
||||
} else if (hi2c->Instance == I2C2) {
|
||||
__HAL_RCC_I2C2_FORCE_RESET();
|
||||
__HAL_RCC_I2C2_RELEASE_RESET();
|
||||
__HAL_RCC_I2C2_CLK_DISABLE();
|
||||
__HAL_RCC_I2C2_CLK_SLEEP_DISABLE();
|
||||
#endif
|
||||
#if defined(OMV_I2C3_ID)
|
||||
} else if (hi2c->Instance == I2C3) {
|
||||
__HAL_RCC_I2C3_FORCE_RESET();
|
||||
__HAL_RCC_I2C3_RELEASE_RESET();
|
||||
__HAL_RCC_I2C3_CLK_DISABLE();
|
||||
__HAL_RCC_I2C3_CLK_SLEEP_DISABLE();
|
||||
#endif
|
||||
#if defined(OMV_I2C4_ID)
|
||||
} else if (hi2c->Instance == I2C4) {
|
||||
__HAL_RCC_I2C4_FORCE_RESET();
|
||||
__HAL_RCC_I2C4_RELEASE_RESET();
|
||||
__HAL_RCC_I2C4_CLK_DISABLE();
|
||||
__HAL_RCC_I2C4_CLK_SLEEP_DISABLE();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -384,7 +373,6 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) {
|
||||
if (htim->Instance == OMV_CSI_TIM) {
|
||||
// Enable DCMI timer clock.
|
||||
OMV_CSI_TIM_CLK_ENABLE();
|
||||
OMV_CSI_TIM_CLK_SLEEP_ENABLE();
|
||||
// Timer GPIO configuration.
|
||||
omv_gpio_config(OMV_CSI_TIM_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_UP, OMV_GPIO_SPEED_HIGH, -1);
|
||||
#if defined(OMV_CSI_TIM_EXT_PIN)
|
||||
@ -399,7 +387,6 @@ void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef *htim) {
|
||||
if (htim->Instance == OMV_CSI_TIM) {
|
||||
// Disable DCMI timer clock.
|
||||
OMV_CSI_TIM_CLK_DISABLE();
|
||||
OMV_CSI_TIM_CLK_SLEEP_DISABLE();
|
||||
// Deinit timer GPIO.
|
||||
omv_gpio_deinit(OMV_CSI_TIM_PIN);
|
||||
#if defined(OMV_CSI_TIM_EXT_PIN)
|
||||
@ -429,10 +416,8 @@ void HAL_DCMI_MspInit(DCMI_HandleTypeDef *hdcmi) {
|
||||
// Enable DCMI clock.
|
||||
#if defined(PSSI)
|
||||
__HAL_RCC_DCMI_PSSI_CLK_ENABLE();
|
||||
__HAL_RCC_DCMI_PSSI_CLK_SLEEP_ENABLE();
|
||||
#else
|
||||
__HAL_RCC_DCMI_CLK_ENABLE();
|
||||
__HAL_RCC_DCMI_CLK_SLEEP_ENABLE();
|
||||
#endif
|
||||
|
||||
#ifdef OMV_CSI_VSYNC_PIN
|
||||
@ -469,10 +454,8 @@ void HAL_DCMI_MspDeInit(DCMI_HandleTypeDef *hdcmi) {
|
||||
// Disable DCMI clock.
|
||||
#if defined(PSSI)
|
||||
__HAL_RCC_DCMI_PSSI_CLK_DISABLE();
|
||||
__HAL_RCC_DCMI_PSSI_CLK_SLEEP_DISABLE();
|
||||
#else
|
||||
__HAL_RCC_DCMI_CLK_DISABLE();
|
||||
__HAL_RCC_DCMI_CLK_SLEEP_DISABLE();
|
||||
#endif
|
||||
|
||||
// Deinit pins.
|
||||
@ -485,13 +468,11 @@ void HAL_DCMI_MspDeInit(DCMI_HandleTypeDef *hdcmi) {
|
||||
void HAL_DCMIPP_MspInit(DCMIPP_HandleTypeDef *hdcmipp) {
|
||||
// Enable DCMIPP clock.
|
||||
__HAL_RCC_DCMIPP_CLK_ENABLE();
|
||||
__HAL_RCC_DCMIPP_CLK_SLEEP_ENABLE();
|
||||
__HAL_RCC_DCMIPP_FORCE_RESET();
|
||||
__HAL_RCC_DCMIPP_RELEASE_RESET();
|
||||
|
||||
// Enable CSI clock
|
||||
__HAL_RCC_CSI_CLK_ENABLE();
|
||||
__HAL_RCC_CSI_CLK_SLEEP_ENABLE();
|
||||
__HAL_RCC_CSI_FORCE_RESET();
|
||||
__HAL_RCC_CSI_RELEASE_RESET();
|
||||
}
|
||||
@ -501,12 +482,10 @@ void HAL_DCMIPP_MspDeInit(DCMIPP_HandleTypeDef *hdcmipp) {
|
||||
__HAL_RCC_DCMIPP_FORCE_RESET();
|
||||
__HAL_RCC_DCMIPP_RELEASE_RESET();
|
||||
__HAL_RCC_DCMIPP_CLK_DISABLE();
|
||||
__HAL_RCC_DCMIPP_CLK_SLEEP_DISABLE();
|
||||
|
||||
__HAL_RCC_CSI_FORCE_RESET();
|
||||
__HAL_RCC_CSI_RELEASE_RESET();
|
||||
__HAL_RCC_CSI_CLK_DISABLE();
|
||||
__HAL_RCC_CSI_CLK_SLEEP_DISABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -524,7 +503,6 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
|
||||
#if defined(OMV_SPI1_ID)
|
||||
} else if (hspi->Instance == SPI1) {
|
||||
__HAL_RCC_SPI1_CLK_ENABLE();
|
||||
__HAL_RCC_SPI1_CLK_SLEEP_ENABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI1_SCLK_PIN, OMV_SPI1_MISO_PIN, OMV_SPI1_MOSI_PIN, OMV_SPI1_SSEL_PIN
|
||||
};
|
||||
@ -532,7 +510,6 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
|
||||
#if defined(OMV_SPI2_ID)
|
||||
} else if (hspi->Instance == SPI2) {
|
||||
__HAL_RCC_SPI2_CLK_ENABLE();
|
||||
__HAL_RCC_SPI2_CLK_SLEEP_ENABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI2_SCLK_PIN, OMV_SPI2_MISO_PIN, OMV_SPI2_MOSI_PIN, OMV_SPI2_SSEL_PIN
|
||||
};
|
||||
@ -540,7 +517,6 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
|
||||
#if defined(OMV_SPI3_ID)
|
||||
} else if (hspi->Instance == SPI3) {
|
||||
__HAL_RCC_SPI3_CLK_ENABLE();
|
||||
__HAL_RCC_SPI3_CLK_SLEEP_ENABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI3_SCLK_PIN, OMV_SPI3_MISO_PIN, OMV_SPI3_MOSI_PIN, OMV_SPI3_SSEL_PIN
|
||||
};
|
||||
@ -548,7 +524,6 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
|
||||
#if defined(OMV_SPI4_ID)
|
||||
} else if (hspi->Instance == SPI4) {
|
||||
__HAL_RCC_SPI4_CLK_ENABLE();
|
||||
__HAL_RCC_SPI4_CLK_SLEEP_ENABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI4_SCLK_PIN, OMV_SPI4_MISO_PIN, OMV_SPI4_MOSI_PIN, OMV_SPI4_SSEL_PIN
|
||||
};
|
||||
@ -556,7 +531,6 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
|
||||
#if defined(OMV_SPI5_ID)
|
||||
} else if (hspi->Instance == SPI5) {
|
||||
__HAL_RCC_SPI5_CLK_ENABLE();
|
||||
__HAL_RCC_SPI5_CLK_SLEEP_ENABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI5_SCLK_PIN, OMV_SPI5_MISO_PIN, OMV_SPI5_MOSI_PIN, OMV_SPI5_SSEL_PIN
|
||||
};
|
||||
@ -564,7 +538,6 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
|
||||
#if defined(SPI6_ID)
|
||||
} else if (hspi->Instance == SPI6) {
|
||||
__HAL_RCC_SPI6_CLK_ENABLE();
|
||||
__HAL_RCC_SPI6_CLK_SLEEP_ENABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
SPI6_SCLK_PIN, SPI6_MISO_PIN, SPI6_MOSI_PIN, SPI6_SSEL_PIN
|
||||
};
|
||||
@ -630,7 +603,6 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi) {
|
||||
__HAL_RCC_SPI1_FORCE_RESET();
|
||||
__HAL_RCC_SPI1_RELEASE_RESET();
|
||||
__HAL_RCC_SPI1_CLK_DISABLE();
|
||||
__HAL_RCC_SPI1_CLK_SLEEP_DISABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI1_SCLK_PIN, OMV_SPI1_MISO_PIN, OMV_SPI1_MOSI_PIN, OMV_SPI1_SSEL_PIN
|
||||
};
|
||||
@ -640,7 +612,6 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi) {
|
||||
__HAL_RCC_SPI2_FORCE_RESET();
|
||||
__HAL_RCC_SPI2_RELEASE_RESET();
|
||||
__HAL_RCC_SPI2_CLK_DISABLE();
|
||||
__HAL_RCC_SPI2_CLK_SLEEP_DISABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI2_SCLK_PIN, OMV_SPI2_MISO_PIN, OMV_SPI2_MOSI_PIN, OMV_SPI2_SSEL_PIN
|
||||
};
|
||||
@ -650,7 +621,6 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi) {
|
||||
__HAL_RCC_SPI3_FORCE_RESET();
|
||||
__HAL_RCC_SPI3_RELEASE_RESET();
|
||||
__HAL_RCC_SPI3_CLK_DISABLE();
|
||||
__HAL_RCC_SPI3_CLK_SLEEP_DISABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI3_SCLK_PIN, OMV_SPI3_MISO_PIN, OMV_SPI3_MOSI_PIN, OMV_SPI3_SSEL_PIN
|
||||
};
|
||||
@ -660,7 +630,6 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi) {
|
||||
__HAL_RCC_SPI4_FORCE_RESET();
|
||||
__HAL_RCC_SPI4_RELEASE_RESET();
|
||||
__HAL_RCC_SPI4_CLK_DISABLE();
|
||||
__HAL_RCC_SPI4_CLK_SLEEP_DISABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI4_SCLK_PIN, OMV_SPI4_MISO_PIN, OMV_SPI4_MOSI_PIN, OMV_SPI4_SSEL_PIN
|
||||
};
|
||||
@ -670,7 +639,6 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi) {
|
||||
__HAL_RCC_SPI5_FORCE_RESET();
|
||||
__HAL_RCC_SPI5_RELEASE_RESET();
|
||||
__HAL_RCC_SPI5_CLK_DISABLE();
|
||||
__HAL_RCC_SPI5_CLK_SLEEP_DISABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
OMV_SPI5_SCLK_PIN, OMV_SPI5_MISO_PIN, OMV_SPI5_MOSI_PIN, OMV_SPI5_SSEL_PIN
|
||||
};
|
||||
@ -680,7 +648,6 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi) {
|
||||
__HAL_RCC_SPI6_FORCE_RESET();
|
||||
__HAL_RCC_SPI6_RELEASE_RESET();
|
||||
__HAL_RCC_SPI6_CLK_DISABLE();
|
||||
__HAL_RCC_SPI6_CLK_SLEEP_DISABLE();
|
||||
spi_pins = (spi_pins_t) {
|
||||
SPI6_SCLK_PIN, SPI6_MISO_PIN, SPI6_MOSI_PIN, SPI6_SSEL_PIN
|
||||
};
|
||||
@ -742,10 +709,8 @@ void HAL_MDF_MspInit(MDF_HandleTypeDef *hmdf) {
|
||||
if (hmdf->Instance == OMV_MDF) {
|
||||
if (IS_ADF_INSTANCE(hmdf->Instance)) {
|
||||
__HAL_RCC_ADF1_CLK_ENABLE();
|
||||
__HAL_RCC_ADF1_CLK_SLEEP_ENABLE();
|
||||
} else {
|
||||
__HAL_RCC_MDF1_CLK_ENABLE();
|
||||
__HAL_RCC_MDF1_CLK_SLEEP_ENABLE();
|
||||
}
|
||||
|
||||
omv_gpio_config(OMV_MDF_CK_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
|
||||
@ -757,10 +722,8 @@ void HAL_MDF_MspDeInit(MDF_HandleTypeDef *hmdf) {
|
||||
if (hmdf->Instance == OMV_MDF) {
|
||||
if (IS_ADF_INSTANCE(hmdf->Instance)) {
|
||||
__HAL_RCC_ADF1_CLK_DISABLE();
|
||||
__HAL_RCC_ADF1_CLK_SLEEP_DISABLE();
|
||||
} else {
|
||||
__HAL_RCC_MDF1_CLK_DISABLE();
|
||||
__HAL_RCC_MDF1_CLK_SLEEP_DISABLE();
|
||||
}
|
||||
|
||||
omv_gpio_deinit(OMV_MDF_CK_PIN);
|
||||
@ -771,65 +734,53 @@ void HAL_MDF_MspDeInit(MDF_HandleTypeDef *hmdf) {
|
||||
|
||||
void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc) {
|
||||
__HAL_RCC_CRC_CLK_ENABLE();
|
||||
__HAL_RCC_CRC_CLK_SLEEP_ENABLE();
|
||||
}
|
||||
|
||||
void HAL_CRC_MspDeInit(CRC_HandleTypeDef *hcrc) {
|
||||
__HAL_RCC_CRC_CLK_DISABLE();
|
||||
__HAL_RCC_CRC_CLK_SLEEP_DISABLE();
|
||||
}
|
||||
|
||||
void HAL_DMA2D_MspInit(DMA2D_HandleTypeDef *hdma2d) {
|
||||
__HAL_RCC_DMA2D_CLK_ENABLE();
|
||||
__HAL_RCC_DMA2D_CLK_SLEEP_ENABLE();
|
||||
}
|
||||
|
||||
void HAL_DMA2D_MspDeInit(DMA2D_HandleTypeDef *hdma2d) {
|
||||
__HAL_RCC_DMA2D_FORCE_RESET();
|
||||
__HAL_RCC_DMA2D_RELEASE_RESET();
|
||||
|
||||
__HAL_RCC_DMA2D_CLK_DISABLE();
|
||||
__HAL_RCC_DMA2D_CLK_SLEEP_DISABLE();
|
||||
}
|
||||
|
||||
#if defined(GPU2D)
|
||||
void HAL_GPU2D_MspInit(GPU2D_HandleTypeDef *hgpu2d) {
|
||||
__HAL_RCC_GPU2D_FORCE_RESET();
|
||||
__HAL_RCC_GPU2D_RELEASE_RESET();
|
||||
|
||||
__HAL_RCC_GPU2D_CLK_ENABLE();
|
||||
__HAL_RCC_GPU2D_CLK_SLEEP_ENABLE();
|
||||
}
|
||||
|
||||
void HAL_GPU2D_MspDeInit(GPU2D_HandleTypeDef *hgpu2d) {
|
||||
__HAL_RCC_GPU2D_CLK_DISABLE();
|
||||
__HAL_RCC_GPU2D_CLK_SLEEP_DISABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GFXMMU)
|
||||
void HAL_GFXMMU_MspInit(GFXMMU_HandleTypeDef *hgfxmmu) {
|
||||
__HAL_RCC_GFXMMU_CLK_ENABLE();
|
||||
__HAL_RCC_GFXMMU_CLK_SLEEP_ENABLE();
|
||||
}
|
||||
|
||||
void HAL_GFXMMU_MspDeInit(GFXMMU_HandleTypeDef *hgfxmmu) {
|
||||
__HAL_RCC_GFXMMU_CLK_DISABLE();
|
||||
__HAL_RCC_GFXMMU_CLK_SLEEP_DISABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (OMV_JPEG_CODEC_ENABLE == 1)
|
||||
void HAL_JPEG_MspInit(JPEG_HandleTypeDef *hjpeg) {
|
||||
__HAL_RCC_JPEG_CLK_ENABLE();
|
||||
__HAL_RCC_JPEG_CLK_SLEEP_ENABLE();
|
||||
}
|
||||
|
||||
void HAL_JPEG_MspDeInit(JPEG_HandleTypeDef *hjpeg) {
|
||||
__HAL_RCC_JPEG_FORCE_RESET();
|
||||
__HAL_RCC_JPEG_RELEASE_RESET();
|
||||
__HAL_RCC_JPEG_CLK_DISABLE();
|
||||
__HAL_RCC_JPEG_CLK_SLEEP_DISABLE();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -974,17 +925,14 @@ void HAL_XSPI_MspInit(XSPI_HandleTypeDef *hxspi) {
|
||||
__HAL_RCC_XSPI1_FORCE_RESET();
|
||||
__HAL_RCC_XSPI1_RELEASE_RESET();
|
||||
__HAL_RCC_XSPI1_CLK_ENABLE();
|
||||
__HAL_RCC_XSPI1_CLK_SLEEP_ENABLE();
|
||||
} else if (hxspi->Instance == XSPI2) {
|
||||
__HAL_RCC_XSPI2_FORCE_RESET();
|
||||
__HAL_RCC_XSPI2_RELEASE_RESET();
|
||||
__HAL_RCC_XSPI2_CLK_ENABLE();
|
||||
__HAL_RCC_XSPI2_CLK_SLEEP_ENABLE();
|
||||
} else if (hxspi->Instance == XSPI3) {
|
||||
__HAL_RCC_XSPI3_FORCE_RESET();
|
||||
__HAL_RCC_XSPI3_RELEASE_RESET();
|
||||
__HAL_RCC_XSPI3_CLK_ENABLE();
|
||||
__HAL_RCC_XSPI3_CLK_SLEEP_ENABLE();
|
||||
}
|
||||
|
||||
for (int i = 0; i < OMV_ARRAY_SIZE(xspi_pins); i++) {
|
||||
@ -1021,17 +969,14 @@ void HAL_XSPI_MspDeInit(XSPI_HandleTypeDef *hxspi) {
|
||||
__HAL_RCC_XSPI1_FORCE_RESET();
|
||||
__HAL_RCC_XSPI1_RELEASE_RESET();
|
||||
__HAL_RCC_XSPI1_CLK_DISABLE();
|
||||
__HAL_RCC_XSPI1_CLK_SLEEP_DISABLE();
|
||||
} else if (hxspi->Instance == XSPI2) {
|
||||
__HAL_RCC_XSPI2_FORCE_RESET();
|
||||
__HAL_RCC_XSPI2_RELEASE_RESET();
|
||||
__HAL_RCC_XSPI2_CLK_DISABLE();
|
||||
__HAL_RCC_XSPI2_CLK_SLEEP_DISABLE();
|
||||
} else if (hxspi->Instance == XSPI3) {
|
||||
__HAL_RCC_XSPI3_FORCE_RESET();
|
||||
__HAL_RCC_XSPI3_RELEASE_RESET();
|
||||
__HAL_RCC_XSPI3_CLK_DISABLE();
|
||||
__HAL_RCC_XSPI3_CLK_SLEEP_DISABLE();
|
||||
}
|
||||
|
||||
for (int i = 0; i < OMV_ARRAY_SIZE(xspi_pins); i++) {
|
||||
|
@ -129,7 +129,6 @@ static void jpeg_compress_data_ready(JPEG_HandleTypeDef *hjpeg, uint8_t *pDataOu
|
||||
}
|
||||
|
||||
bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc, jpeg_subsampling_t subsampling) {
|
||||
OMV_PROFILE_START();
|
||||
HAL_JPEG_RegisterGetDataCallback(&JPEG_state.jpeg_descr, jpeg_compress_get_data);
|
||||
HAL_JPEG_RegisterDataReadyCallback(&JPEG_state.jpeg_descr, jpeg_compress_data_ready);
|
||||
|
||||
@ -394,8 +393,6 @@ exit_cleanup:
|
||||
HAL_JPEG_UnRegisterGetDataCallback(&JPEG_state.jpeg_descr);
|
||||
|
||||
fb_free(); // mcu_row_buffer (after DMA is aborted)
|
||||
|
||||
OMV_PROFILE_PRINT();
|
||||
return jpeg_overflow;
|
||||
}
|
||||
|
||||
@ -427,8 +424,6 @@ static void jpeg_decompress_data_ready(JPEG_HandleTypeDef *hjpeg, uint8_t *pData
|
||||
}
|
||||
|
||||
void jpeg_decompress(image_t *dst, image_t *src) {
|
||||
OMV_PROFILE_START();
|
||||
|
||||
// Verify the jpeg image is not a non-baseline jpeg image and check that is has
|
||||
// valid headers up to the start-of-scan header (which cannot be trivially walked).
|
||||
if (!jpeg_is_valid(src)) {
|
||||
@ -795,8 +790,6 @@ exit_cleanup:
|
||||
if (((uint32_t) src->data) % __SCB_DCACHE_LINE_SIZE) {
|
||||
fb_free(); // JPEG_state.jpeg_descr.pJpegInBuffPtr (after DMA is aborted)
|
||||
}
|
||||
|
||||
OMV_PROFILE_PRINT();
|
||||
}
|
||||
|
||||
void imlib_hardware_jpeg_init() {
|
||||
|
@ -0,0 +1,49 @@
|
||||
# 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 BlazeFace face detection model.
|
||||
|
||||
import csi
|
||||
import time
|
||||
import ml
|
||||
from ml.postprocessing import mediapipe_face_detection_postprocess
|
||||
|
||||
# Initialize the sensor.
|
||||
csi0 = csi.CSI()
|
||||
csi0.reset()
|
||||
csi0.pixformat(csi.RGB565)
|
||||
csi0.framesize(csi.VGA)
|
||||
csi0.window((400, 400))
|
||||
|
||||
# Load built-in face detection model
|
||||
model = ml.Model("/rom/blazeface_front_128.tflite")
|
||||
print(model)
|
||||
|
||||
# Create the face detection post-processor. This post-processor dynamically
|
||||
# generates anchors for the model input size which should only be done once.
|
||||
face_detection_postprocess = mediapipe_face_detection_postprocess(threshold=0.6)
|
||||
|
||||
clock = time.clock()
|
||||
while True:
|
||||
clock.tick()
|
||||
img = csi0.snapshot()
|
||||
|
||||
# faces is a list of ((x, y, w, h), score, keypoints) tuples
|
||||
faces = model.predict([img], callback=face_detection_postprocess)
|
||||
|
||||
# Draw bounding boxes around the detected faces and keypoints.
|
||||
if faces:
|
||||
for r, score, keypoints in faces[0]:
|
||||
ml.utils.draw_predictions(img, [r], ["face"], [(0, 0, 255)], format=None)
|
||||
# keypoints is a ndarray of shape (6, 2)
|
||||
# 0 - right eye (x, y)
|
||||
# 1 - left eye (x, y)
|
||||
# 2 - nose (x, y)
|
||||
# 3 - mouth (x, y)
|
||||
# 4 - right ear (x, y)
|
||||
# 5 - left ear (x, y)
|
||||
for kp in keypoints.tolist():
|
||||
img.draw_circle(int(kp[0]), int(kp[1]), 4, color=(255, 0, 0))
|
||||
|
||||
print(clock.fps(), "fps")
|
@ -9,9 +9,8 @@
|
||||
import sensor
|
||||
import time
|
||||
import ml
|
||||
from ml.utils import NMS
|
||||
from ml.postprocessing import fomo_postprocess
|
||||
import math
|
||||
import image
|
||||
|
||||
sensor.reset() # Reset and initialize the sensor.
|
||||
sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
|
||||
@ -19,9 +18,6 @@ sensor.set_framesize(sensor.QVGA) # Set frame size to QVGA (320x240)
|
||||
sensor.set_windowing((240, 240)) # Set 240x240 window.
|
||||
sensor.skip_frames(time=2000) # Let the camera adjust.
|
||||
|
||||
min_confidence = 0.4
|
||||
threshold_list = [(math.ceil(min_confidence * 255), 255)]
|
||||
|
||||
# Load built-in FOMO face detection model
|
||||
model = ml.Model("/rom/fomo_face_detection.tflite")
|
||||
print(model)
|
||||
@ -40,39 +36,13 @@ colors = [ # Add more colors if you are detecting more than 7 types of classes
|
||||
(255, 255, 255),
|
||||
]
|
||||
|
||||
|
||||
# FOMO outputs an image per class where each pixel in the image is the centroid of the trained
|
||||
# object. So, we will get those output images and then run find_blobs() on them to extract the
|
||||
# centroids. We will also run get_stats() on the detected blobs to determine their score.
|
||||
# The Non-Max-Supression (NMS) object then filters out overlapping detections and maps their
|
||||
# position in the output image back to the original input image. The function then returns a
|
||||
# list per class which each contain a list of (rect, score) tuples representing the detected
|
||||
# objects.
|
||||
def fomo_post_process(model, inputs, outputs):
|
||||
n, oh, ow, oc = model.output_shape[0]
|
||||
nms = NMS(ow, oh, inputs[0].roi)
|
||||
for i in range(oc):
|
||||
img = image.Image(outputs[0][0, :, :, i] * 255)
|
||||
blobs = img.find_blobs(
|
||||
threshold_list, x_stride=1, area_threshold=1, pixels_threshold=1
|
||||
)
|
||||
for b in blobs:
|
||||
rect = b.rect()
|
||||
x, y, w, h = rect
|
||||
score = (
|
||||
img.get_statistics(thresholds=threshold_list, roi=rect).l_mean() / 255.0
|
||||
)
|
||||
nms.add_bounding_box(x, y, x + w, y + h, score, i)
|
||||
return nms.get_bounding_boxes()
|
||||
|
||||
|
||||
clock = time.clock()
|
||||
while True:
|
||||
clock.tick()
|
||||
|
||||
img = sensor.snapshot()
|
||||
|
||||
for i, detection_list in enumerate(model.predict([img], callback=fomo_post_process)):
|
||||
for i, detection_list in enumerate(model.predict([img], callback=fomo_postprocess())):
|
||||
if i == 0:
|
||||
continue # background class
|
||||
if len(detection_list) == 0:
|
||||
|
@ -1,13 +1,37 @@
|
||||
# This work is licensed under the MIT license.
|
||||
# Copyright (c) 2013-2023 OpenMV LLC. All rights reserved.
|
||||
# Copyright (c) 2013-2024 OpenMV LLC. All rights reserved.
|
||||
# https://github.com/openmv/openmv/blob/master/LICENSE
|
||||
#
|
||||
# This example shows off using the light shield with both the PYB and Machine Module.
|
||||
|
||||
import time
|
||||
from pyb import Pin, Timer
|
||||
|
||||
# 50kHz pin6 timer2 channel1
|
||||
light = Timer(2, freq=50000).channel(1, Timer.PWM, pin=Pin("P6"))
|
||||
light.pulse_width_percent(100) # adjust light 0~100
|
||||
|
||||
while True:
|
||||
time.sleep_ms(1000)
|
||||
try:
|
||||
from pyb import Pin, Timer
|
||||
|
||||
# 50kHz pin6 timer2 channel1
|
||||
light = Timer(2, freq=50000).channel(1, Timer.PWM, pin=Pin("P6"))
|
||||
|
||||
while True:
|
||||
for i in range(101):
|
||||
light.pulse_width_percent(i)
|
||||
time.sleep_ms(10)
|
||||
for i in range(101):
|
||||
light.pulse_width_percent(100 - i)
|
||||
time.sleep_ms(10)
|
||||
|
||||
|
||||
except ImportError:
|
||||
from machine import PWM, Pin
|
||||
|
||||
# 50kHz pin6 timer2 channel1
|
||||
pwm = PWM(Pin("P6"), freq=50000, duty_u16=0)
|
||||
|
||||
while True:
|
||||
for i in range(101):
|
||||
pwm.duty_u16((i * 65535) // 100)
|
||||
time.sleep_ms(10)
|
||||
for i in range(101):
|
||||
pwm.duty_u16(((100 - i) * 65535) // 100)
|
||||
time.sleep_ms(10)
|
||||
|
@ -26,8 +26,6 @@
|
||||
# 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.
|
||||
import math
|
||||
import image
|
||||
from ml.utils import NMS
|
||||
from micropython import const
|
||||
from ulab import numpy as np
|
||||
@ -36,32 +34,94 @@ from ulab import numpy as np
|
||||
_NO_DETECTION = const(())
|
||||
|
||||
|
||||
# FOMO generates an image per class, where each pixel represents the centroid
|
||||
# of the trained object. These images are processed with `find_blobs()` to
|
||||
# extract centroids, and `get_stats()` is used to get their scores. Overlapping
|
||||
# detections are then filtered with NMS and positions are mapped back to the
|
||||
# original image, and a list of (rect, score) tuples is returned for each class,
|
||||
# representing detected objects.
|
||||
def logit(x):
|
||||
return np.log(x / (1.0 - x))
|
||||
|
||||
|
||||
def sigmoid(x):
|
||||
return 1.0 / (1.0 + np.exp(-x))
|
||||
|
||||
|
||||
def mod(a, b):
|
||||
return a - (b * (a // b))
|
||||
|
||||
|
||||
def threshold(scores, threshold, scale, find_max=False, find_max_axis=1):
|
||||
if scale > 0:
|
||||
if find_max:
|
||||
scores = np.max(scores, axis=find_max_axis)
|
||||
return np.nonzero(scores > threshold)[0]
|
||||
else:
|
||||
if find_max:
|
||||
scores = np.min(scores, axis=find_max_axis)
|
||||
return np.nonzero(scores < threshold)[0]
|
||||
|
||||
|
||||
def quantize(model, value, index=0):
|
||||
if model.output_dtype[index] == 'f':
|
||||
return value
|
||||
return (value / model.output_scale[index]) + model.output_zero_point[index]
|
||||
|
||||
|
||||
def dequantize(model, value, index=0):
|
||||
if model.output_dtype[index] == 'f':
|
||||
return value
|
||||
return (value - float(model.output_zero_point[index])) * model.output_scale[index]
|
||||
|
||||
|
||||
class fomo_postprocess:
|
||||
def __init__(self, threshold=0.4):
|
||||
self.threshold_list = [(math.ceil(threshold * 255), 255)]
|
||||
_FOMO_CLASSES = const(1)
|
||||
|
||||
def __init__(self, threshold=0.4, w_scale=1.414214, h_scale=1.414214,
|
||||
nms_threshold=0.1, nms_sigma=0.001):
|
||||
self.threshold = threshold
|
||||
self.w_scale = w_scale
|
||||
self.h_scale = h_scale
|
||||
self.nms_threshold = nms_threshold
|
||||
self.nms_sigma = nms_sigma
|
||||
|
||||
def __call__(self, model, inputs, outputs):
|
||||
n, oh, ow, oc = model.output_shape[0]
|
||||
nms = NMS(ow, oh, inputs[0].roi)
|
||||
for i in range(oc):
|
||||
img = image.Image(outputs[0][0, :, :, i] * 255)
|
||||
blobs = img.find_blobs(
|
||||
self.threshold_list, x_stride=1, area_threshold=1, pixels_threshold=1
|
||||
)
|
||||
for b in blobs:
|
||||
rect = b.rect()
|
||||
x, y, w, h = rect
|
||||
score = (
|
||||
img.get_statistics(thresholds=self.threshold_list, roi=rect).l_mean() / 255.0
|
||||
)
|
||||
nms.add_bounding_box(x, y, x + w, y + h, score, i)
|
||||
return nms.get_bounding_boxes()
|
||||
ob, oh, ow, oc = model.output_shape[0]
|
||||
scale = model.output_scale[0]
|
||||
t = quantize(model, self.threshold)
|
||||
|
||||
# Reshape the output to a 2D array
|
||||
row_outputs = outputs[0].reshape((oh * ow, oc))
|
||||
|
||||
# Threshold all the scores
|
||||
score_indices = row_outputs[:, _FOMO_CLASSES:]
|
||||
score_indices = threshold(score_indices, t, scale, find_max=True, find_max_axis=1)
|
||||
if not len(score_indices):
|
||||
return _NO_DETECTION
|
||||
|
||||
# Get the bounding boxes that have a valid score
|
||||
bb = dequantize(model, np.take(row_outputs, score_indices, axis=0))
|
||||
|
||||
# Extract rows and columns
|
||||
bb_rows = score_indices // ow
|
||||
bb_cols = mod(score_indices, ow)
|
||||
|
||||
# Get the score information
|
||||
bb_scores = np.max(bb[:, _FOMO_CLASSES:], axis=1)
|
||||
|
||||
# Get the class information
|
||||
bb_classes = np.argmax(bb[:, _FOMO_CLASSES:], axis=1) + _FOMO_CLASSES
|
||||
|
||||
# Scale the bounding boxes to have enough integer precision for NMS
|
||||
ib, ih, iw, ic = model.input_shape[0]
|
||||
x_center = ((bb_cols + 0.5) / ow) * iw
|
||||
y_center = ((bb_rows + 0.5) / oh) * ih
|
||||
w_rel = np.full(len(bb_cols), self.w_scale / ow) * iw
|
||||
h_rel = np.full(len(bb_rows), self.h_scale / oh) * ih
|
||||
|
||||
nms = NMS(iw, ih, inputs[0].roi)
|
||||
for i in range(bb.shape[0]):
|
||||
nms.add_bounding_box(x_center[i] - (w_rel[i] / 2),
|
||||
y_center[i] - (h_rel[i] / 2),
|
||||
x_center[i] + (w_rel[i] / 2),
|
||||
y_center[i] + (h_rel[i] / 2),
|
||||
bb_scores[i], bb_classes[i])
|
||||
return nms.get_bounding_boxes(threshold=self.nms_threshold, sigma=self.nms_sigma)
|
||||
|
||||
|
||||
# This is a lightweight version of the tiny yolo v2 object detection algorithm.
|
||||
@ -88,31 +148,28 @@ class yolo_v2_postprocess:
|
||||
self.nms_sigma = nms_sigma
|
||||
|
||||
def __call__(self, model, inputs, outputs):
|
||||
ob, oh, ow, oc = model.output_shape[0]
|
||||
class_count = (oc // self.anchors_len) - _YOLO_V2_CLASSES
|
||||
|
||||
def sigmoid(x):
|
||||
return 1.0 / (1.0 + np.exp(-x))
|
||||
|
||||
def mod(a, b):
|
||||
return a - (b * (a // b))
|
||||
|
||||
def softmax(x):
|
||||
e_x = np.exp(x - np.max(x, axis=1, keepdims=True))
|
||||
return e_x / np.sum(e_x, axis=1, keepdims=True)
|
||||
|
||||
ob, oh, ow, oc = model.output_shape[0]
|
||||
scale = model.output_scale[0]
|
||||
t = quantize(model, logit(self.threshold))
|
||||
class_count = (oc // self.anchors_len) - _YOLO_V2_CLASSES
|
||||
|
||||
# Reshape the output to a 2D array
|
||||
row_outputs = outputs[0].reshape((oh * ow * self.anchors_len,
|
||||
_YOLO_V2_CLASSES + class_count))
|
||||
|
||||
# Threshold all the scores
|
||||
score_indices = sigmoid(row_outputs[:, _YOLO_V2_SCORE])
|
||||
score_indices = np.nonzero(score_indices > self.threshold)[0]
|
||||
score_indices = row_outputs[:, _YOLO_V2_SCORE]
|
||||
score_indices = threshold(score_indices, t, scale)
|
||||
if not len(score_indices):
|
||||
return _NO_DETECTION
|
||||
|
||||
# Get the bounding boxes that have a valid score
|
||||
bb = np.take(row_outputs, score_indices, axis=0)
|
||||
bb = dequantize(model, np.take(row_outputs, score_indices, axis=0))
|
||||
|
||||
# Extract rows, columns, and anchor indices
|
||||
bb_rows = score_indices // (ow * self.anchors_len)
|
||||
@ -179,6 +236,8 @@ class yolo_v5_postprocess:
|
||||
|
||||
def __call__(self, model, inputs, outputs):
|
||||
oh, ow, oc = model.output_shape[0]
|
||||
scale = model.output_scale[0]
|
||||
t = quantize(model, self.threshold)
|
||||
class_count = oc - _YOLO_V5_CLASSES
|
||||
|
||||
# Reshape the output to a 2D array
|
||||
@ -186,12 +245,12 @@ class yolo_v5_postprocess:
|
||||
|
||||
# Threshold all the scores
|
||||
score_indices = row_outputs[:, _YOLO_V5_SCORE]
|
||||
score_indices = np.nonzero(score_indices > self.threshold)[0]
|
||||
score_indices = threshold(score_indices, t, scale)
|
||||
if not len(score_indices):
|
||||
return _NO_DETECTION
|
||||
|
||||
# Get the bounding boxes that have a valid score
|
||||
bb = np.take(row_outputs, score_indices, axis=0)
|
||||
bb = dequantize(model, np.take(row_outputs, score_indices, axis=0))
|
||||
|
||||
# Get the score information
|
||||
bb_scores = bb[:, _YOLO_V5_SCORE]
|
||||
@ -233,19 +292,21 @@ class yolo_v8_postprocess:
|
||||
|
||||
def __call__(self, model, inputs, outputs):
|
||||
oh, ow, oc = model.output_shape[0]
|
||||
scale = model.output_scale[0]
|
||||
t = quantize(model, self.threshold)
|
||||
class_count = ow - _YOLO_V8_CLASSES
|
||||
|
||||
# Reshape the output to a 2D array
|
||||
column_outputs = outputs[0].reshape((oh * (_YOLO_V8_CLASSES + class_count), oc))
|
||||
|
||||
# Threshold all the scores
|
||||
score_indices = np.max(column_outputs[_YOLO_V8_CLASSES:, :], axis=0)
|
||||
score_indices = np.nonzero(score_indices > self.threshold)[0]
|
||||
score_indices = column_outputs[_YOLO_V8_CLASSES:, :]
|
||||
score_indices = threshold(score_indices, t, scale, find_max=True, find_max_axis=0)
|
||||
if not len(score_indices):
|
||||
return _NO_DETECTION
|
||||
|
||||
# Get the bounding boxes that have a valid score
|
||||
bb = np.take(column_outputs, score_indices, axis=1)
|
||||
bb = dequantize(model, np.take(column_outputs, score_indices, axis=1))
|
||||
|
||||
# Get the score information
|
||||
bb_scores = np.max(bb[_YOLO_V8_CLASSES:, :], axis=0)
|
||||
@ -271,3 +332,94 @@ class yolo_v8_postprocess:
|
||||
nms.add_bounding_box(xmin[i], ymin[i], xmax[i], ymax[i],
|
||||
bb_scores[i], bb_classes[i])
|
||||
return nms.get_bounding_boxes(threshold=self.nms_threshold, sigma=self.nms_sigma)
|
||||
|
||||
|
||||
class mediapipe_face_detection_postprocess:
|
||||
_BLAZEFACE_CX = const(0)
|
||||
_BLAZEFACE_CY = const(1)
|
||||
_BLAZEFACE_CW = const(2)
|
||||
_BLAZEFACE_CH = const(3)
|
||||
_BLAZEFACE_KP = const(4)
|
||||
|
||||
def __init__(self, threshold=0.6, anchors=None, nms_threshold=0.1, nms_sigma=0.1):
|
||||
self.threshold = threshold
|
||||
self.anchors = anchors
|
||||
|
||||
if self.anchors is None:
|
||||
self.anchors = np.empty((896, 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 gy in range(grid_size):
|
||||
cy = (gy + 0.5) / grid_size
|
||||
for gx in range(grid_size):
|
||||
cx = (gx + 0.5) / grid_size
|
||||
for _ in range(scales):
|
||||
self.anchors[idx, 0] = cx
|
||||
self.anchors[idx, 1] = cy
|
||||
idx += 1
|
||||
|
||||
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)
|
||||
output_len = outputs[0].shape[1]
|
||||
|
||||
self.blazeface_post_process(ih, iw, nms, model, inputs, outputs, 1, 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.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):
|
||||
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)
|
||||
|
||||
# Threshold all the scores
|
||||
score_row_outputs = outputs[score_idx].reshape((s_oh * s_ow * s_oc))
|
||||
score_indices = threshold(score_row_outputs, t, scale)
|
||||
if not len(score_indices):
|
||||
return _NO_DETECTION
|
||||
|
||||
# Get the score information
|
||||
bb_scores = np.take(score_row_outputs, score_indices, axis=0)
|
||||
bb_scores = sigmoid(dequantize(model, bb_scores, index=score_idx))
|
||||
|
||||
# Get the bounding boxes that have a valid score
|
||||
c_oh, c_ow, c_oc = model.output_shape[cords_idx]
|
||||
cords_row_outputs = outputs[cords_idx].reshape((c_oh * c_ow, c_oc))
|
||||
bb = dequantize(model, np.take(cords_row_outputs, score_indices, axis=0), index=cords_idx)
|
||||
|
||||
# Get the anchor box information
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
# Scale the bounding boxes to have enough integer precision for NMS
|
||||
xmin = (x_center - w_rel) * iw
|
||||
ymin = (y_center - h_rel) * ih
|
||||
xmax = (x_center + w_rel) * iw
|
||||
ymax = (y_center + h_rel) * ih
|
||||
|
||||
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])
|
||||
|
@ -93,4 +93,6 @@ class Normalization:
|
||||
fadd = (fadd - np.array(self.mean)) / np.array(self.stdev)
|
||||
fscale = fscale / np.array(self.stdev)
|
||||
|
||||
array = (array * fscale) + fadd
|
||||
# Apply normalization in-place (must be done in two steps for ulab).
|
||||
array *= fscale
|
||||
array += fadd
|
||||
|
@ -43,7 +43,7 @@ class NMS:
|
||||
raise ValueError("Invalid ROI dimensions!")
|
||||
self.boxes = []
|
||||
|
||||
def add_bounding_box(self, xmin, ymin, xmax, ymax, score, label_index):
|
||||
def add_bounding_box(self, xmin, ymin, xmax, ymax, score, label_index, keypoints=None):
|
||||
if score >= 0.0 and score <= 1.0:
|
||||
xmin = max(0.0, min(xmin, self.window_w))
|
||||
ymin = max(0.0, min(ymin, self.window_h))
|
||||
@ -52,7 +52,7 @@ class NMS:
|
||||
w = int(xmax - xmin)
|
||||
h = int(ymax - ymin)
|
||||
if w > 0 and h > 0:
|
||||
self.boxes.append([int(xmin), int(ymin), w, h, score, label_index])
|
||||
self.boxes.append([int(xmin), int(ymin), w, h, score, label_index, keypoints])
|
||||
|
||||
def get_bounding_boxes(self, threshold=0.1, sigma=0.1):
|
||||
sorted_boxes = sorted(self.boxes, key=lambda x: x[4], reverse=True)
|
||||
@ -107,15 +107,22 @@ class NMS:
|
||||
output_boxes[i][1] = int((output_boxes[i][1] * scale) + y_offset)
|
||||
output_boxes[i][2] = int(output_boxes[i][2] * scale)
|
||||
output_boxes[i][3] = int(output_boxes[i][3] * scale)
|
||||
keypoints = output_boxes[i][6]
|
||||
if keypoints is not None:
|
||||
keypoints *= scale
|
||||
keypoints[:, 0] += x_offset
|
||||
keypoints[:, 1] += y_offset
|
||||
|
||||
# Create a list per class with (rect, score) tuples.
|
||||
|
||||
output_list = [[] for i in range(max_label_index + 1)]
|
||||
|
||||
for i in range(len(output_boxes)):
|
||||
output_list[output_boxes[i][5]].append(
|
||||
(output_boxes[i][0:4], output_boxes[i][4])
|
||||
)
|
||||
rect_score = [output_boxes[i][:4], output_boxes[i][4]]
|
||||
keypoints = output_boxes[i][6]
|
||||
if keypoints is not None:
|
||||
rect_score.append(keypoints)
|
||||
output_list[output_boxes[i][5]].append(tuple(rect_score))
|
||||
|
||||
return output_list
|
||||
|
||||
|
46
tools/ci.sh
46
tools/ci.sh
@ -54,8 +54,11 @@ ci_build_target() {
|
||||
make -j$(nproc) -C docker TARGET=${BOARD}
|
||||
else
|
||||
make -j$(nproc) -C lib/micropython/mpy-cross
|
||||
make -j$(nproc) TARGET=${1}
|
||||
mv build/bin ${1}
|
||||
make -j$(nproc) TARGET=${1} PROFILE_ENABLE=${2}
|
||||
# Don't copy artifacts for profiling builds
|
||||
if [ "$2" = 0 ]; then
|
||||
mv build/bin ${1}
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@ -72,45 +75,6 @@ ci_package_firmware_development() {
|
||||
(cd firmware && for i in *; do zip -r -j "firmware_${i%/}.zip" "$i"; done)
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
# Install code formatter deps
|
||||
CODEFORMAT_PATH=${HOME}/cache/deps/
|
||||
UNCRUSTIFY_PATH=${CODEFORMAT_PATH}/uncrustify
|
||||
UNCRUSTIFY_URL="https://github.com/uncrustify/uncrustify/archive/uncrustify-0.75.0.tar.gz"
|
||||
|
||||
ci_install_code_format_deps() {
|
||||
sudo apt-get install wget cmake build-essential colordiff
|
||||
|
||||
mkdir -p ${UNCRUSTIFY_PATH}
|
||||
wget --no-check-certificate -O - ${UNCRUSTIFY_URL} | tar xvz --strip-components=1 -C ${UNCRUSTIFY_PATH}
|
||||
(cd ${UNCRUSTIFY_PATH} && mkdir build && cd build && cmake .. && cmake --build .)
|
||||
|
||||
# Copy binaries to cache
|
||||
mkdir -p ${CODEFORMAT_PATH}/bin
|
||||
cp ${UNCRUSTIFY_PATH}/build/uncrustify ${CODEFORMAT_PATH}/bin/
|
||||
cp `which colordiff` ${CODEFORMAT_PATH}/bin/
|
||||
chmod +x ${CODEFORMAT_PATH}/bin/uncrustify
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
# Run code formatter
|
||||
ci_run_code_format_check() {
|
||||
export PATH=${CODEFORMAT_PATH}/bin:${PATH}
|
||||
UNCRUSTIFY_CONFIG=tools/uncrustify.cfg
|
||||
|
||||
exit_code=0
|
||||
for file in "$@"; do
|
||||
file_fmt="${file}.tmp"
|
||||
uncrustify -q -c ${UNCRUSTIFY_CONFIG} -f ${file} -o ${file_fmt} || true
|
||||
|
||||
diff -q -u ${file} ${file_fmt} >> /dev/null 2>&1 || {
|
||||
colordiff -u ${file} ${file_fmt} || true
|
||||
exit_code=1
|
||||
}
|
||||
done
|
||||
exit $exit_code
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
# Install STEdgeAI tools
|
||||
STEDGEAI_URL="https://upload.openmv.io/stedgeai/STEdgeAI-2.1.0.tar.gz"
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
# This file is part of the OpenMV project.
|
||||
#
|
||||
# Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||
@ -16,7 +16,7 @@ import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
__serial = None
|
||||
__FB_HDR_SIZE =12
|
||||
__FB_HDR_SIZE = 12
|
||||
|
||||
# USB Debug commands
|
||||
__USBDBG_CMD = 48
|
||||
@ -38,20 +38,27 @@ __USBDBG_FB_ENABLE = 0x0D
|
||||
__USBDBG_TX_BUF_LEN = 0x8E
|
||||
__USBDBG_TX_BUF = 0x8F
|
||||
__USBDBG_GET_STATE = 0x93
|
||||
__USBDBG_PROFILE_SIZE = 0x94
|
||||
__USBDBG_PROFILE_DUMP = 0x95
|
||||
__USBDBG_PROFILE_MODE = 0x16
|
||||
__USBDBG_PROFILE_EVENT = 0x17
|
||||
__USBDBG_PROFILE_RESET = 0x18
|
||||
|
||||
__USBDBG_STATE_FLAGS_SCRIPT = (1 << 0)
|
||||
__USBDBG_STATE_FLAGS_TEXT = (1 << 1)
|
||||
__USBDBG_STATE_FLAGS_FRAME = (1 << 2)
|
||||
__USBDBG_FLAG_SCRIPT_RUNNING = (1 << 0)
|
||||
__USBDBG_FLAG_TEXTBUF_NOTEMPTY = (1 << 1)
|
||||
__USBDBG_FLAG_FRAMEBUF_LOCKED = (1 << 2)
|
||||
__USBDBG_FLAG_PROFILE_ENABLED = (1 << 3)
|
||||
__USBDBG_FLAG_PROFILE_HAS_PMU = (1 << 4)
|
||||
|
||||
ATTR_CONTRAST =0
|
||||
ATTR_BRIGHTNESS =1
|
||||
ATTR_SATURATION =2
|
||||
ATTR_GAINCEILING=3
|
||||
ATTR_CONTRAST = 0
|
||||
ATTR_BRIGHTNESS = 1
|
||||
ATTR_SATURATION = 2
|
||||
ATTR_GAINCEILING = 3
|
||||
|
||||
__BOOTLDR_START = 0xABCD0001
|
||||
__BOOTLDR_RESET = 0xABCD0002
|
||||
__BOOTLDR_ERASE = 0xABCD0004
|
||||
__BOOTLDR_WRITE = 0xABCD0008
|
||||
__BOOTLDR_START = 0xABCD0001
|
||||
__BOOTLDR_RESET = 0xABCD0002
|
||||
__BOOTLDR_ERASE = 0xABCD0004
|
||||
__BOOTLDR_WRITE = 0xABCD0008
|
||||
|
||||
def init(port, baudrate=921600, timeout=0.3):
|
||||
global __serial
|
||||
@ -67,29 +74,40 @@ def disconnect():
|
||||
except:
|
||||
pass
|
||||
|
||||
def write_pack(fmt, *values):
|
||||
__serial.write(struct.pack(fmt, *values))
|
||||
|
||||
def read_unpack(fmt):
|
||||
return struct.unpack(fmt, __serial.read(struct.calcsize(fmt)))
|
||||
|
||||
def set_timeout(timeout):
|
||||
__serial.timeout = timeout
|
||||
|
||||
def fb_size():
|
||||
# read fb header
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_FRAME_SIZE, __FB_HDR_SIZE))
|
||||
return struct.unpack("III", __serial.read(12))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_FRAME_SIZE, __FB_HDR_SIZE)
|
||||
return read_unpack("III")
|
||||
|
||||
def read_state():
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_GET_STATE, 64))
|
||||
flags, w, h, size, text_buf = struct.unpack("IIII48s", __serial.read(64))
|
||||
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_GET_STATE, 64)
|
||||
flags, w, h, size, text_buf = read_unpack("IIII48s")
|
||||
|
||||
text = None
|
||||
if flags & __USBDBG_STATE_FLAGS_TEXT:
|
||||
profile = False
|
||||
|
||||
if flags & __USBDBG_FLAG_PROFILE_ENABLED:
|
||||
profile = True
|
||||
|
||||
if flags & __USBDBG_FLAG_TEXTBUF_NOTEMPTY:
|
||||
text = text_buf.split(b'\0', 1)[0].decode()
|
||||
|
||||
if flags & __USBDBG_STATE_FLAGS_FRAME == 0:
|
||||
return 0, 0, None, 0, text, ""
|
||||
if flags & __USBDBG_FLAG_FRAMEBUF_LOCKED == 0:
|
||||
return 0, 0, None, 0, text, "", profile
|
||||
|
||||
num_bytes = size if size > 2 else (w * h * size)
|
||||
|
||||
# read fb data
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_FRAME_DUMP, num_bytes))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_FRAME_DUMP, num_bytes)
|
||||
buff = __serial.read(num_bytes)
|
||||
|
||||
if size == 1: # Grayscale
|
||||
@ -113,7 +131,7 @@ def read_state():
|
||||
if (buff.size != (w*h*3)):
|
||||
raise ValueError(f"Unexpected frame size. Expected: {w*h*3} received: {buff.size}")
|
||||
|
||||
return w, h, buff.reshape((h, w, 3)), num_bytes, text, fmt
|
||||
return w, h, buff.reshape((h, w, 3)), num_bytes, text, fmt, profile
|
||||
|
||||
|
||||
def fb_dump():
|
||||
@ -129,7 +147,7 @@ def fb_dump():
|
||||
num_bytes = size[0]*size[1]*size[2]
|
||||
|
||||
# read fb data
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_FRAME_DUMP, num_bytes))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_FRAME_DUMP, num_bytes)
|
||||
buff = __serial.read(num_bytes)
|
||||
|
||||
if size[2] == 1: # Grayscale
|
||||
@ -153,72 +171,119 @@ def fb_dump():
|
||||
|
||||
return (size[0], size[1], buff.reshape((size[1], size[0], 3)))
|
||||
|
||||
def read_profile():
|
||||
records = []
|
||||
|
||||
# Read and unpack profiling data size
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_PROFILE_SIZE, 12)
|
||||
record_count, record_size, event_count = read_unpack("<III")
|
||||
|
||||
if record_count:
|
||||
offset = 0
|
||||
record_format = f"<5I2Q{event_count}QI"
|
||||
profile_size = record_count * record_size
|
||||
|
||||
# Read profiling data
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_PROFILE_DUMP, profile_size)
|
||||
profile_data = __serial.read(profile_size)
|
||||
|
||||
for i in range(record_count):
|
||||
# Unpack the record
|
||||
profile = struct.unpack(record_format, profile_data[offset:offset + record_size])
|
||||
|
||||
# Parse the profile data
|
||||
records.append({
|
||||
'address': profile[0],
|
||||
'caller': profile[1],
|
||||
'call_count': profile[2],
|
||||
'min_ticks': profile[3],
|
||||
'max_ticks': profile[4],
|
||||
'total_ticks': profile[5],
|
||||
'total_cycles': profile[6],
|
||||
'events': profile[7:7 + event_count] if event_count > 0 else []
|
||||
})
|
||||
|
||||
offset += record_size
|
||||
|
||||
return records
|
||||
|
||||
def set_profile_mode(mode):
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_PROFILE_MODE, 4)
|
||||
write_pack("<I", mode)
|
||||
|
||||
def set_event_counter(event_num, event_type):
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_PROFILE_EVENT, 8)
|
||||
write_pack("<II", event_num, event_type)
|
||||
|
||||
def reset_profiler():
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_PROFILE_RESET, 0)
|
||||
|
||||
def exec_script(buf):
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_SCRIPT_EXEC, len(buf)))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_SCRIPT_EXEC, len(buf))
|
||||
__serial.write(buf.encode())
|
||||
|
||||
def stop_script():
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_SCRIPT_STOP, 0))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_SCRIPT_STOP, 0)
|
||||
|
||||
def script_running():
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_SCRIPT_RUNNING, 4))
|
||||
return struct.unpack("I", __serial.read(4))[0]
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_SCRIPT_RUNNING, 4)
|
||||
return read_unpack("I")[0]
|
||||
|
||||
def save_template(x, y, w, h, path):
|
||||
buf = struct.pack("IIII", x, y, w, h) + path
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_TEMPLATE_SAVE, len(buf)))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_TEMPLATE_SAVE, len(buf))
|
||||
__serial.write(buf)
|
||||
|
||||
def save_descriptor(x, y, w, h, path):
|
||||
buf = struct.pack("HHHH", x, y, w, h) + path
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_DESCRIPTOR_SAVE, len(buf)))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_DESCRIPTOR_SAVE, len(buf))
|
||||
__serial.write(buf)
|
||||
|
||||
def set_attr(attr, value):
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_ATTR_WRITE, 8))
|
||||
__serial.write(struct.pack("<II", attr, value))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_ATTR_WRITE, 8)
|
||||
write_pack("<II", attr, value)
|
||||
|
||||
def get_attr(attr):
|
||||
__serial.write(struct.pack("<BBIh", __USBDBG_CMD, __USBDBG_ATTR_READ, 1, attr))
|
||||
write_pack("<BBIh", __USBDBG_CMD, __USBDBG_ATTR_READ, 1, attr)
|
||||
return __serial.read(1)
|
||||
|
||||
def reset():
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_SYS_RESET, 0))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_SYS_RESET, 0)
|
||||
|
||||
def reset_to_bl():
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_SYS_RESET_TO_BL, 0))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_SYS_RESET_TO_BL, 0)
|
||||
|
||||
def bootloader_start():
|
||||
__serial.write(struct.pack("<I", __BOOTLDR_START))
|
||||
return struct.unpack("I", __serial.read(4))[0] == __BOOTLDR_START
|
||||
write_pack("<I", __BOOTLDR_START)
|
||||
return read_unpack("I")[0] == __BOOTLDR_START
|
||||
|
||||
def bootloader_reset():
|
||||
__serial.write(struct.pack("<I", __BOOTLDR_RESET))
|
||||
write_pack("<I", __BOOTLDR_RESET)
|
||||
|
||||
def flash_erase(sector):
|
||||
__serial.write(struct.pack("<II", __BOOTLDR_ERASE, sector))
|
||||
write_pack("<II", __BOOTLDR_ERASE, sector)
|
||||
|
||||
def flash_write(buf):
|
||||
__serial.write(struct.pack("<I", __BOOTLDR_WRITE) + buf)
|
||||
write_pack("<I", __BOOTLDR_WRITE) + buf
|
||||
|
||||
def tx_buf_len():
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_TX_BUF_LEN, 4))
|
||||
return struct.unpack("I", __serial.read(4))[0]
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_TX_BUF_LEN, 4)
|
||||
return read_unpack("I")[0]
|
||||
|
||||
def tx_buf(bytes):
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_TX_BUF, bytes))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_TX_BUF, bytes)
|
||||
return __serial.read(bytes)
|
||||
|
||||
def fw_version():
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_FW_VERSION, 12))
|
||||
return struct.unpack("III", __serial.read(12))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_FW_VERSION, 12)
|
||||
return read_unpack("III")
|
||||
|
||||
def enable_fb(enable):
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_FB_ENABLE, 4))
|
||||
__serial.write(struct.pack("<I", enable))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_FB_ENABLE, 4)
|
||||
write_pack("<I", enable)
|
||||
|
||||
def arch_str():
|
||||
__serial.write(struct.pack("<BBI", __USBDBG_CMD, __USBDBG_ARCH_STR, 64))
|
||||
write_pack("<BBI", __USBDBG_CMD, __USBDBG_ARCH_STR, 64)
|
||||
return __serial.read(64).split(b'\0', 1)[0]
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python
|
||||
# This file is part of the OpenMV project.
|
||||
#
|
||||
# Copyright (c) 2013-2021 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||
# Copyright (c) 2013-2021 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
# Copyright (c) 2013-2025 Ibrahim Abdelkader <iabdalkader@openmv.io>
|
||||
# Copyright (c) 2013-2025 Kwabena W. Agyeman <kwagyeman@openmv.io>
|
||||
#
|
||||
# This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
#
|
||||
@ -39,7 +39,355 @@ while(True):
|
||||
img.flush()
|
||||
"""
|
||||
|
||||
def pygame_test(port, poll_rate, scale, benchmark):
|
||||
def addr_to_symbol(symbols, address):
|
||||
# Binary search for speed
|
||||
lo, hi = 0, len(symbols) - 1
|
||||
while lo <= hi:
|
||||
mid = (lo + hi) // 2
|
||||
start, end, name = symbols[mid]
|
||||
if start <= address < end:
|
||||
return name
|
||||
elif address < start:
|
||||
hi = mid - 1
|
||||
else:
|
||||
lo = mid + 1
|
||||
return None
|
||||
|
||||
def get_color_by_percentage(percentage, base_color=(220, 220, 220)):
|
||||
"""
|
||||
Return a color based on percentage with fine-grained intensity levels.
|
||||
"""
|
||||
def clamp(value):
|
||||
return max(0, min(255, int(value)))
|
||||
|
||||
if percentage >= 50:
|
||||
# Very high - bright red
|
||||
intensity = min(1.0, (percentage - 50) / 50)
|
||||
return (255, clamp(120 - 120 * intensity), clamp(120 - 120 * intensity))
|
||||
elif percentage >= 30:
|
||||
# High - red-orange
|
||||
intensity = (percentage - 30) / 20
|
||||
return (255, clamp(160 + 40 * intensity), clamp(160 - 40 * intensity))
|
||||
elif percentage >= 20:
|
||||
# Medium-high - orange
|
||||
intensity = (percentage - 20) / 10
|
||||
return (255, clamp(200 + 55 * intensity), clamp(180 - 20 * intensity))
|
||||
elif percentage >= 15:
|
||||
# Medium - yellow-orange
|
||||
intensity = (percentage - 15) / 5
|
||||
return (255, clamp(220 + 35 * intensity), clamp(180 + 20 * intensity))
|
||||
elif percentage >= 10:
|
||||
# Medium-low - yellow
|
||||
intensity = (percentage - 10) / 5
|
||||
return (clamp(255 - 75 * intensity), 255, clamp(180 + 75 * intensity))
|
||||
elif percentage >= 5:
|
||||
# Low - light green
|
||||
intensity = (percentage - 5) / 5
|
||||
return (clamp(180 + 75 * intensity), 255, clamp(180 + 75 * intensity))
|
||||
elif percentage >= 2:
|
||||
# Very low - green
|
||||
intensity = (percentage - 2) / 3
|
||||
return (clamp(160 + 95 * intensity), clamp(255 - 55 * intensity), clamp(160 + 95 * intensity))
|
||||
elif percentage >= 1:
|
||||
# Minimal - light blue-green
|
||||
intensity = (percentage - 1) / 1
|
||||
return (clamp(140 + 120 * intensity), clamp(200 + 55 * intensity), clamp(255 - 95 * intensity))
|
||||
else:
|
||||
# Zero or negligible - base color
|
||||
return base_color
|
||||
|
||||
def draw_rounded_rect(surface, color, rect, radius=5):
|
||||
x, y, w, h = rect
|
||||
if w <= 0 or h <= 0:
|
||||
return
|
||||
pygame.draw.rect(surface, color, (x + radius, y, w - 2*radius, h))
|
||||
pygame.draw.rect(surface, color, (x, y + radius, w, h - 2*radius))
|
||||
pygame.draw.circle(surface, color, (x + radius, y + radius), radius)
|
||||
pygame.draw.circle(surface, color, (x + w - radius, y + radius), radius)
|
||||
pygame.draw.circle(surface, color, (x + radius, y + h - radius), radius)
|
||||
pygame.draw.circle(surface, color, (x + w - radius, y + h - radius), radius)
|
||||
|
||||
|
||||
def draw_table(overlay_surface, config, title, headers, col_widths):
|
||||
"""Draw the common table background, title, and header."""
|
||||
# Draw main table background
|
||||
table_rect = (0, 0, config['width'], config['height'])
|
||||
draw_rounded_rect(overlay_surface, config['colors']['bg'], table_rect, int(8 * config['scale_factor']))
|
||||
pygame.draw.rect(overlay_surface, config['colors']['border'], table_rect, max(1, int(2 * config['scale_factor'])))
|
||||
|
||||
# Table title
|
||||
title_text = config['fonts']['title'].render(title, True, config['colors']['header_text'])
|
||||
title_rect = title_text.get_rect()
|
||||
title_x = (config['width'] - title_rect.width) // 2
|
||||
overlay_surface.blit(title_text, (title_x, int(12 * config['scale_factor'])))
|
||||
|
||||
# Header
|
||||
header_y = int(50 * config['scale_factor'])
|
||||
header_height = int(40 * config['scale_factor'])
|
||||
|
||||
# Draw header background
|
||||
header_rect = (int(5 * config['scale_factor']), header_y,
|
||||
config['width'] - int(10 * config['scale_factor']), header_height)
|
||||
draw_rounded_rect(overlay_surface, config['colors']['header_bg'], header_rect, int(4 * config['scale_factor']))
|
||||
|
||||
# Draw header text and separators
|
||||
current_x = int(10 * config['scale_factor'])
|
||||
for i, (header, width) in enumerate(zip(headers, col_widths)):
|
||||
header_surface = config['fonts']['header'].render(header, True, config['colors']['header_text'])
|
||||
overlay_surface.blit(header_surface, (current_x, header_y + int(6 * config['scale_factor'])))
|
||||
|
||||
if i < len(headers) - 1:
|
||||
sep_x = current_x + width - int(5 * config['scale_factor'])
|
||||
pygame.draw.line(overlay_surface, config['colors']['border'],
|
||||
(sep_x, header_y + 2), (sep_x, header_y + header_height - 2), 1)
|
||||
current_x += width
|
||||
|
||||
|
||||
def draw_event_table(overlay_surface, config, profile_data, profile_mode, symbols):
|
||||
"""Draw the event counter mode table."""
|
||||
|
||||
# Prepare data
|
||||
num_events = len(profile_data[0]['events']) if profile_data else 0
|
||||
if not num_events:
|
||||
sorted_data = sorted(profile_data, key=lambda x: x['address'])
|
||||
else:
|
||||
sort_func = lambda x: x['events'][0] // max(1, x['call_count'])
|
||||
sorted_data = sorted(profile_data, key=sort_func, reverse=True)
|
||||
|
||||
headers = ["Function"] + [f"E{i}" for i in range(num_events)]
|
||||
proportions = [0.30] + [0.70/num_events] * num_events
|
||||
col_widths = [config['width'] * prop for prop in proportions]
|
||||
profile_mode = "Exclusive" if profile_mode else "Inclusive"
|
||||
|
||||
# Calculate event totals for percentage calculation
|
||||
event_totals = [0] * num_events
|
||||
for record in sorted_data:
|
||||
for i, event_count in enumerate(record['events']):
|
||||
event_totals[i] += event_count // max(1, record['call_count'])
|
||||
|
||||
# Draw table structure
|
||||
draw_table(overlay_surface, config, f"Event Counters ({profile_mode})", headers, col_widths)
|
||||
|
||||
# Draw data rows
|
||||
row_height = int(30 * config['scale_factor'])
|
||||
data_start_y = int(50 * config['scale_factor'] + 40 * config['scale_factor'] + 8 * config['scale_factor'])
|
||||
available_height = config['height'] - data_start_y - int(60 * config['scale_factor'])
|
||||
visible_rows = min(len(sorted_data), available_height // row_height)
|
||||
|
||||
for i in range(visible_rows):
|
||||
record = sorted_data[i]
|
||||
row_y = data_start_y + i * row_height
|
||||
|
||||
# Draw row background
|
||||
row_color = config['colors']['row_alt'] if i % 2 == 0 else config['colors']['row_normal']
|
||||
row_rect = (int(5 * config['scale_factor']), row_y,
|
||||
config['width'] - int(10 * config['scale_factor']), row_height)
|
||||
pygame.draw.rect(overlay_surface, row_color, row_rect)
|
||||
|
||||
# Function name
|
||||
name = addr_to_symbol(symbols, record['address']) if symbols else "<no symbols>"
|
||||
max_name_chars = int(col_widths[0] // (11 * config['scale_factor']))
|
||||
display_name = name if len(name) <= max_name_chars else name[:max_name_chars - 3] + "..."
|
||||
|
||||
row_data = [display_name]
|
||||
|
||||
# Event data
|
||||
for j, event_count in enumerate(record['events']):
|
||||
event_scale = ""
|
||||
event_count //= max(1, record['call_count'])
|
||||
if event_count > 1_000_000_000:
|
||||
event_count //= 1_000_000_000
|
||||
event_scale = "B"
|
||||
elif event_count > 1_000_000:
|
||||
event_count //= 1_000_000
|
||||
event_scale = "M"
|
||||
row_data.append(f"{event_count:,}{event_scale}")
|
||||
|
||||
# Determine row color based on sorting key (event 0)
|
||||
if len(record['events']) > 0 and event_totals[0] > 0:
|
||||
sort_key_value = record['events'][0] // max(1, record['call_count'])
|
||||
percentage = (sort_key_value / event_totals[0] * 100)
|
||||
row_text_color = get_color_by_percentage(percentage, config['colors']['content_text'])
|
||||
else:
|
||||
row_text_color = config['colors']['content_text']
|
||||
|
||||
# Draw row data with uniform color
|
||||
current_x = 10
|
||||
for j, (data, width) in enumerate(zip(row_data, col_widths)):
|
||||
text_surface = config['fonts']['content'].render(str(data), True, row_text_color)
|
||||
overlay_surface.blit(text_surface, (current_x, row_y + int(8 * config['scale_factor'])))
|
||||
|
||||
if j < len(row_data) - 1:
|
||||
sep_x = current_x + width - 8
|
||||
pygame.draw.line(overlay_surface, (60, 70, 85),
|
||||
(sep_x, row_y), (sep_x, row_y + row_height), 1)
|
||||
current_x += width
|
||||
|
||||
# Draw summary
|
||||
summary_y = config['height'] - int(50 * config['scale_factor'])
|
||||
total_functions = len(profile_data)
|
||||
grand_total = sum(event_totals)
|
||||
summary_text = (
|
||||
f"Profiles: {total_functions} | "
|
||||
f"Events: {num_events} | "
|
||||
f"Total Events: {grand_total:,}"
|
||||
)
|
||||
|
||||
summary_surface = config['fonts']['summary'].render(summary_text, True, config['colors']['content_text'])
|
||||
summary_rect = summary_surface.get_rect()
|
||||
summary_x = (config['width'] - summary_rect.width) // 2
|
||||
overlay_surface.blit(summary_surface, (summary_x, summary_y))
|
||||
|
||||
# Instructions
|
||||
instruction_str = "Press 'P' to toggle event counter overlay"
|
||||
instruction_text = config['fonts']['instruction'].render(instruction_str, True, (180, 180, 180))
|
||||
overlay_surface.blit(instruction_text, (0, summary_y + int(20 * config['scale_factor'])))
|
||||
|
||||
|
||||
def draw_profile_table(overlay_surface, config, profile_data, profile_mode, symbols):
|
||||
"""Draw the profile mode table."""
|
||||
|
||||
# Prepare data
|
||||
sort_func = lambda x: x['total_ticks']
|
||||
sorted_data = sorted(profile_data, key=sort_func, reverse=True)
|
||||
total_ticks_all = sum(record['total_ticks'] for record in profile_data)
|
||||
profile_mode = "Exclusive" if profile_mode else "Inclusive"
|
||||
|
||||
headers = ["Function", "Calls", "Min", "Max", "Total", "Avg", "Cycles", "%"]
|
||||
proportions = [0.30, 0.08, 0.10, 0.10, 0.13, 0.10, 0.13, 0.05]
|
||||
col_widths = [config['width'] * prop for prop in proportions]
|
||||
|
||||
# Draw table structure
|
||||
draw_table(overlay_surface, config, f"Performance Profile ({profile_mode})", headers, col_widths)
|
||||
|
||||
# Draw data rows
|
||||
row_height = int(30 * config['scale_factor'])
|
||||
data_start_y = int(50 * config['scale_factor'] + 40 * config['scale_factor'] + 8 * config['scale_factor'])
|
||||
available_height = config['height'] - data_start_y - int(60 * config['scale_factor'])
|
||||
visible_rows = min(len(sorted_data), available_height // row_height)
|
||||
|
||||
for i in range(visible_rows):
|
||||
record = sorted_data[i]
|
||||
row_y = data_start_y + i * row_height
|
||||
|
||||
# Draw row background
|
||||
row_color = config['colors']['row_alt'] if i % 2 == 0 else config['colors']['row_normal']
|
||||
row_rect = (int(5 * config['scale_factor']), row_y,
|
||||
config['width'] - int(10 * config['scale_factor']), row_height)
|
||||
pygame.draw.rect(overlay_surface, row_color, row_rect)
|
||||
|
||||
# Function name
|
||||
name = addr_to_symbol(symbols, record['address']) if symbols else "<no symbols>"
|
||||
max_name_chars = int(col_widths[0] // (11 * config['scale_factor']))
|
||||
display_name = name if len(name) <= max_name_chars else name[:max_name_chars - 3] + "..."
|
||||
|
||||
# Calculate values
|
||||
call_count = record['call_count']
|
||||
min_ticks = record['min_ticks'] if call_count else 0
|
||||
max_ticks = record['max_ticks'] if call_count else 0
|
||||
total_ticks = record['total_ticks']
|
||||
avg_cycles = record['total_cycles'] // max(1, call_count)
|
||||
avg_ticks = total_ticks // max(1, call_count)
|
||||
percentage = (total_ticks / total_ticks_all * 100)
|
||||
|
||||
ticks_scale = ""
|
||||
if total_ticks > 1_000_000_000:
|
||||
total_ticks //= 1_000_000
|
||||
ticks_scale = "M"
|
||||
|
||||
row_data = [
|
||||
display_name,
|
||||
f"{call_count:,}",
|
||||
f"{min_ticks:,}",
|
||||
f"{max_ticks:,}",
|
||||
f"{total_ticks:,}{ticks_scale}",
|
||||
f"{avg_ticks:,}",
|
||||
f"{avg_cycles:,}",
|
||||
f"{percentage:.1f}%"
|
||||
]
|
||||
|
||||
# Determine row color based on percentage
|
||||
text_color = get_color_by_percentage(percentage, config['colors']['content_text'])
|
||||
|
||||
# Draw row data
|
||||
current_x = int(10 * config['scale_factor'])
|
||||
for j, (data, width) in enumerate(zip(row_data, col_widths)):
|
||||
text_surface = config['fonts']['content'].render(str(data), True, text_color)
|
||||
overlay_surface.blit(text_surface, (current_x, row_y + int(8 * config['scale_factor'])))
|
||||
|
||||
if j < len(row_data) - 1:
|
||||
sep_x = current_x + width - int(8 * config['scale_factor'])
|
||||
pygame.draw.line(overlay_surface, (60, 70, 85),
|
||||
(sep_x, row_y), (sep_x, row_y + row_height), 1)
|
||||
current_x += width
|
||||
|
||||
# Draw summary
|
||||
summary_y = config['height'] - int(50 * config['scale_factor'])
|
||||
total_calls = sum(record['call_count'] for record in profile_data)
|
||||
total_cycles = sum(record['total_cycles'] for record in profile_data)
|
||||
total_ticks_summary = sum(record['total_ticks'] for record in profile_data)
|
||||
|
||||
summary_text = (
|
||||
f"Profiles: {len(profile_data)} | "
|
||||
f"Total Calls: {total_calls:,} | "
|
||||
f"Total Ticks: {total_ticks_summary:,} | "
|
||||
f"Total Cycles: {total_cycles:,}"
|
||||
)
|
||||
|
||||
summary_surface = config['fonts']['summary'].render(summary_text, True, config['colors']['content_text'])
|
||||
summary_rect = summary_surface.get_rect()
|
||||
summary_x = (config['width'] - summary_rect.width) // 2
|
||||
overlay_surface.blit(summary_surface, (summary_x, summary_y))
|
||||
|
||||
# Instructions
|
||||
instruction_str = "Press 'P' to toggle event counter overlay"
|
||||
instruction_text = config['fonts']['instruction'].render(instruction_str, True, (180, 180, 180))
|
||||
overlay_surface.blit(instruction_text, (0, summary_y + int(20 * config['scale_factor'])))
|
||||
|
||||
def draw_profile_overlay(screen, screen_width, screen_height, profile_data,
|
||||
profile_mode, profile_type, scale, symbols, alpha=250):
|
||||
"""Main entry point for drawing the profile overlay."""
|
||||
# Calculate dimensions and create surface
|
||||
base_width, base_height = 800, 800
|
||||
screen_width *= scale
|
||||
screen_height *= scale
|
||||
scale_factor = min(screen_width / base_width, screen_height / base_height)
|
||||
|
||||
overlay_surface = pygame.Surface((screen_width, screen_height), pygame.SRCALPHA)
|
||||
overlay_surface.set_alpha(alpha)
|
||||
|
||||
# Setup common configuration
|
||||
config = {
|
||||
'width': screen_width,
|
||||
'height': screen_height,
|
||||
'scale_factor': scale_factor,
|
||||
'colors': {
|
||||
'bg': (40, 50, 65),
|
||||
'border': (70, 80, 100),
|
||||
'header_bg': (60, 80, 120),
|
||||
'header_text': (255, 255, 255),
|
||||
'content_text': (220, 220, 220),
|
||||
'row_alt': (35, 45, 60),
|
||||
'row_normal': (45, 55, 70)
|
||||
},
|
||||
'fonts': {
|
||||
'title': pygame.font.SysFont("arial", int(28 * scale_factor), bold=True),
|
||||
'header': pygame.font.SysFont("monospace", int(20 * scale_factor), bold=True),
|
||||
'content': pygame.font.SysFont("monospace", int(18 * scale_factor)),
|
||||
'summary': pygame.font.SysFont("arial", int(20 * scale_factor)),
|
||||
'instruction': pygame.font.SysFont("arial", int(22 * scale_factor))
|
||||
}
|
||||
}
|
||||
|
||||
# Draw based on mode
|
||||
if profile_type == 1:
|
||||
draw_profile_table(overlay_surface, config, profile_data, profile_mode, symbols)
|
||||
elif profile_type == 2:
|
||||
draw_event_table(overlay_surface, config, profile_data, profile_mode, symbols)
|
||||
|
||||
screen.blit(overlay_surface, (0, 0))
|
||||
|
||||
def pygame_test(port, script, poll_rate, scale, benchmark, symbols):
|
||||
# init pygame
|
||||
pygame.init()
|
||||
pyopenmv.disconnect()
|
||||
@ -67,27 +415,63 @@ def pygame_test(port, poll_rate, scale, benchmark):
|
||||
pyopenmv.set_timeout(1*2) # SD Cards can cause big hicups.
|
||||
pyopenmv.stop_script()
|
||||
pyopenmv.enable_fb(True)
|
||||
pyopenmv.exec_script(bench_script if benchmark else test_script)
|
||||
pyopenmv.reset_profiler()
|
||||
|
||||
# Configure some event counters.
|
||||
pyopenmv.set_event_counter(0, 0x0039)
|
||||
pyopenmv.set_event_counter(1, 0x0023)
|
||||
pyopenmv.set_event_counter(2, 0x0024)
|
||||
pyopenmv.set_event_counter(3, 0x0001)
|
||||
pyopenmv.set_event_counter(4, 0x0003)
|
||||
pyopenmv.set_event_counter(5, 0xC102)
|
||||
pyopenmv.set_event_counter(6, 0x02CC)
|
||||
pyopenmv.set_event_counter(7, 0xC303)
|
||||
|
||||
pyopenmv.exec_script(script)
|
||||
|
||||
# init screen
|
||||
running = True
|
||||
screen = None
|
||||
|
||||
# Profiling control
|
||||
profile_type = 0
|
||||
profile_mode = 0
|
||||
profile_data = []
|
||||
last_profile_read = 0
|
||||
|
||||
clock = pygame.time.Clock()
|
||||
fps_clock = pygame.time.Clock()
|
||||
font = pygame.font.SysFont("monospace", 30)
|
||||
|
||||
if benchmark:
|
||||
if not benchmark:
|
||||
pygame.display.set_caption("OpenMV Camera")
|
||||
else:
|
||||
pygame.display.set_caption("OpenMV Camera (Benchmark)")
|
||||
screen = pygame.display.set_mode((640, 120), pygame.DOUBLEBUF, 32)
|
||||
|
||||
try:
|
||||
while running:
|
||||
# Read state
|
||||
w, h, data, size, text, fmt = pyopenmv.read_state()
|
||||
w, h, data, size, text, fmt, profiling = pyopenmv.read_state()
|
||||
|
||||
if text is not None:
|
||||
print(text, end="")
|
||||
|
||||
# Read profiling data (maximum 10Hz)
|
||||
if profiling and profile_type:
|
||||
current_time = time.time()
|
||||
if current_time - last_profile_read >= 0.1: # 10Hz = 0.1s interval
|
||||
tmp_data = pyopenmv.read_profile()
|
||||
if tmp_data:
|
||||
profile_data = tmp_data
|
||||
last_profile_read = current_time
|
||||
|
||||
#if profile_data:
|
||||
# for r in profile_data:
|
||||
# print(f"Func: {addr_to_symbol(symbols, r['address'])}@0x{r['address']:x} ")
|
||||
# print(f"Call: {addr_to_symbol(symbols, r['caller'])}@0x{r['caller']:x}")
|
||||
# sys.exit(0)
|
||||
|
||||
if data is not None:
|
||||
fps = fps_clock.get_fps()
|
||||
|
||||
@ -104,7 +488,14 @@ def pygame_test(port, poll_rate, scale, benchmark):
|
||||
screen.fill((0, 0, 0))
|
||||
else:
|
||||
screen.blit(image, (0, 0))
|
||||
screen.blit(font.render(f"{fps:.2f} FPS {fps * size / 1024**2:.2f} MB/s {w}x{h} {fmt}", 5, (255, 0, 0)), (0, 0))
|
||||
|
||||
# FPS text
|
||||
fps_text = f"{fps:.2f} FPS {fps * size / 1024**2:.2f} MB/s {w}x{h} {fmt}"
|
||||
screen.blit(font.render(fps_text, 5, (255, 0, 0)), (0, 0))
|
||||
|
||||
# Draw profile overlay if enabled
|
||||
if profile_type and profile_data:
|
||||
draw_profile_overlay(screen, w, h, profile_data, profile_mode, profile_type, scale, symbols)
|
||||
|
||||
# update display
|
||||
pygame.display.flip()
|
||||
@ -116,10 +507,18 @@ def pygame_test(port, poll_rate, scale, benchmark):
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
running = False
|
||||
if event.key == pygame.K_c:
|
||||
elif event.key == pygame.K_c:
|
||||
pygame.image.save(image, "capture.png")
|
||||
|
||||
elif event.key == pygame.K_p:
|
||||
profile_type = (profile_type + 1) % 3
|
||||
elif event.key == pygame.K_m:
|
||||
profile_mode = not profile_mode
|
||||
pyopenmv.set_profile_mode(profile_mode)
|
||||
elif event.key == pygame.K_r:
|
||||
pyopenmv.reset_profiler()
|
||||
|
||||
clock.tick(1000//poll_rate)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
@ -128,9 +527,38 @@ def pygame_test(port, poll_rate, scale, benchmark):
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='pyopenmv module')
|
||||
parser.add_argument('--port', action = 'store', help='OpenMV camera port (default /dev/ttyACM0)', default='/dev/ttyACM0', )
|
||||
parser.add_argument('--port', action = 'store', help='Serial port (dev/ttyACM0)', default='/dev/ttyACM0')
|
||||
parser.add_argument("--script", action = "store", default=None, help = "Script file")
|
||||
parser.add_argument('--poll', action = 'store', help='Poll rate (default 4ms)', default=4, type=int)
|
||||
parser.add_argument('--bench', action = 'store_true', help='Run throughput benchmark.', default=False)
|
||||
parser.add_argument('--scale', action = 'store', help='Set frame scaling factor (default 4x).', default=4, type=int)
|
||||
parser.add_argument('--firmware', action = 'store', help='Firmware for address to symbol', default=None)
|
||||
|
||||
args = parser.parse_args()
|
||||
pygame_test(args.port, args.poll, args.scale, args.bench)
|
||||
if args.script is not None:
|
||||
with open(args.script) as f:
|
||||
args.script = f.read()
|
||||
else:
|
||||
args.script = bench_script if args.bench else test_script
|
||||
|
||||
symbols = []
|
||||
|
||||
if args.firmware:
|
||||
from elftools.elf.elffile import ELFFile
|
||||
|
||||
with open(args.firmware, 'rb') as f:
|
||||
elf = ELFFile(f)
|
||||
symtab = elf.get_section_by_name('.symtab')
|
||||
if not symtab:
|
||||
raise ValueError("No symbol table found in ELF.")
|
||||
|
||||
for sym in symtab.iter_symbols():
|
||||
addr = sym['st_value']
|
||||
size = sym['st_size']
|
||||
name = sym.name
|
||||
if name and size > 0: # ignore empty symbols
|
||||
symbols.append((addr, addr + size, name))
|
||||
|
||||
symbols.sort()
|
||||
|
||||
pygame_test(args.port, args.script, args.poll, args.scale, args.bench, symbols)
|
||||
|
@ -2083,7 +2083,7 @@ nl_after_vbrace_close = false # true/false
|
||||
# Add or remove newline between the close brace and identifier,
|
||||
# as in 'struct { int a; } <here> b;'. Affects enumerations, unions and
|
||||
# structures. If set to ignore, uses nl_after_brace_close.
|
||||
nl_brace_struct_var = ignore # ignore/add/remove/force/not_defined
|
||||
nl_brace_struct_var = remove # ignore/add/remove/force/not_defined
|
||||
|
||||
# Whether to alter newlines in '#define' macros.
|
||||
nl_define_macro = false # true/false
|
||||
|
Loading…
Reference in New Issue
Block a user