Compare commits

...

87 Commits

Author SHA1 Message Date
Kwabena W Agyeman
d09c6c303c
Merge 447e782304 into 87b01fac51 2025-09-22 13:40:29 +02:00
Ibrahim Abdelkader
87b01fac51
Merge pull request #2855 from openmv/fix_raw_stream_size
Some checks failed
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions
🔎 Check Code Formatting / formatting-check (push) Has been cancelled
lib/imlib: Fix raw stream buffer size check.
2025-09-21 21:26:28 +03:00
iabdalkader
552f4ada8e lib/imlib: Fix raw stream buffer size check.
Check frame dimensions as well as total image size
top scale the image if needed.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-21 16:41:34 +02:00
Ibrahim Abdelkader
7a7788a03a
Merge pull request #2853 from openmv/fix_uncrust_cfg
Some checks failed
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Has been cancelled
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / code-size-report (push) Has been cancelled
🔥 Firmware Build / stable-release (push) Has been cancelled
🔥 Firmware Build / development-release (push) Has been cancelled
tools: Fix uncrustify union variable formatting
2025-09-19 20:52:51 +03:00
Ibrahim Abdelkader
16dfe5f5cb
Merge pull request #2852 from openmv/fix_jlink_target
misc/Makefile: Fix jlink target.
2025-09-19 20:50:06 +03:00
iabdalkader
4257464982 tools: Fix uncrustify union variable formatting
Set nl_brace_struct_var = remove to keep union/struct variable
declarations on the same line as the closing brace.
2025-09-19 19:49:42 +02:00
iabdalkader
daa681dfe6 misc/Makefile: Fix jlink target.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-19 19:37:50 +02:00
Ibrahim Abdelkader
9310becba0
Merge pull request #2851 from openmv/boot_v_header
Some checks failed
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions
🔎 Check Code Formatting / formatting-check (push) Has been cancelled
boot: Add version header.
2025-09-19 10:30:28 +03:00
iabdalkader
304a2d36e8 boot: Add version header.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-19 09:24:49 +02:00
Ibrahim Abdelkader
a19d832f1e
Merge pull request #2850 from openmv/fix_mutex_unlock
Some checks are pending
🔎 Check Code Formatting / formatting-check (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions
common/mutex: Return true, if unlocked.
2025-09-19 01:15:04 +03:00
iabdalkader
9beb565882 common/mutex: Return true, if unlocked.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-19 00:06:15 +02:00
Ibrahim Abdelkader
6a1c9b8ab9
Merge pull request #2849 from openmv/update_workflow
github: Update codeformat workflow.
2025-09-19 00:27:35 +03:00
iabdalkader
72e04221c7 github: Update codeformat workflow.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-18 23:26:34 +02:00
Ibrahim Abdelkader
c9e42a4e45
Merge pull request #2830 from kwagyeman/kwabena/add_blazeface
scripts/examples: Add BlazeFace detection example.
2025-09-18 23:54:35 +03:00
Kwabena W. Agyeman
3667b54319 scripts/examples: Add BlazeFace detection example. 2025-09-18 13:41:57 -07:00
Kwabena W. Agyeman
f6f39f924b lib/models: Add blazeface model. 2025-09-18 13:41:56 -07:00
Kwabena W. Agyeman
65829023f0 scripts/libraries: Add face detection post-processing. 2025-09-18 13:30:14 -07:00
Kwabena W. Agyeman
7124b5134d scripts/libraries: Add keypoint support to NMS.
NMS will now pass-through keypoints along with scaling/offsetting
them to be drawn correctly on the image.
2025-09-18 13:30:14 -07:00
Kwabena W. Agyeman
eccb90699d scripts/libraries: Fix pre-processing float input array normalization.
ULAB only does simple assignment operator operations in-place.
2025-09-18 13:30:14 -07:00
Kwabena W. Agyeman
14d277c95b lib/stai: Add multi-tensor input/output support.
The name of the buffer being NULL defines the end of the buffer list.
2025-09-18 13:30:14 -07:00
Ibrahim Abdelkader
3a79080713
Merge pull request #2847 from openmv/update_workflow
github/workflows/codeformat: Fix workflow.
2025-09-18 23:05:37 +03:00
iabdalkader
f16f566141 github/workflows/codeformat: Fix workflow.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-18 22:05:04 +02:00
Ibrahim Abdelkader
aaabe05565
Merge pull request #2845 from openmv/update_workflow
github/workflows/codeformat: Fix workflow.
2025-09-18 21:36:42 +03:00
iabdalkader
2205f177f3 github/workflows/codeformat: Fix workflow.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-18 20:36:15 +02:00
Ibrahim Abdelkader
0de60ff3d5
Merge pull request #2842 from openmv/update_code_formatter
misc: Update code formatter.
2025-09-18 21:11:14 +03:00
iabdalkader
75c7a4fb82 github/codeformat: Switch to uncrustify-action.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-18 20:01:17 +02:00
iabdalkader
04145abea8 tools/ci: Remove uncrustify functions.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-18 18:54:38 +02:00
Ibrahim Abdelkader
1dab51addf
Merge pull request #2828 from openmv/dependabot/github_actions/actions/setup-python-6
Some checks failed
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Has been cancelled
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / code-size-report (push) Has been cancelled
🔥 Firmware Build / stable-release (push) Has been cancelled
🔥 Firmware Build / development-release (push) Has been cancelled
build(deps): bump actions/setup-python from 5 to 6
2025-09-17 19:15:32 +03:00
Ibrahim Abdelkader
a559f03553
Merge pull request #2841 from openmv/update_micropython
Some checks are pending
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions
micropython: Update submodule
2025-09-16 19:54:36 +03:00
Ibrahim Abdelkader
3946a19cd2
Merge pull request #2840 from openmv/stm32n6_sleep_enable
stm32: Disable clock gating during sleep for STM32N6 peripherals
2025-09-16 19:30:20 +03:00
iabdalkader
f994fe1d06 micropython: Update submodule
- Revert "stm32/uart: Enable UART clock in low power modes."
- stm32/uart: Enable UART FIFO for STM32N6.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-16 18:26:34 +02:00
iabdalkader
01113ebee9 stm32: Disable clock gating during sleep for STM32N6 peripherals
Remove individual peripheral sleep clock enable/disable calls and instead
enable all AHB/APB peripheral clocks during sleep mode. This is needed
because MicroPython doesn't manage peripheral sleep clocks individually.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-16 18:20:38 +02:00
Ibrahim Abdelkader
28f0ad2d7d
Merge pull request #2832 from openmv/dependabot/github_actions/softprops/action-gh-release-2.3.3
build(deps): bump softprops/action-gh-release from 2.2.2 to 2.3.3
2025-09-16 18:23:40 +03:00
Ibrahim Abdelkader
e60750b5cc
Merge pull request #2838 from openmv/revert_lock_fix
Some checks failed
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Has been cancelled
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / code-size-report (push) Has been cancelled
🔥 Firmware Build / stable-release (push) Has been cancelled
🔥 Firmware Build / development-release (push) Has been cancelled
Revert "common/mutex: Clear tid when releasing mutex"
2025-09-13 12:22:27 +03:00
iabdalkader
1b59e95162 Revert "common/mutex: Clear tid when releasing mutex"
This reverts commit bb52288561.

Event though this fixes a deadlock in the new protocol, it
actually breaks fair locking as one side could lock multiple
times in a row.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-13 10:52:00 +02:00
Ibrahim Abdelkader
085f282e4b
Merge pull request #2837 from openmv/fix_jlink_output
Some checks are pending
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions
Makefile: Suppress J-Link GDB server output
2025-09-12 19:16:46 +03:00
Ibrahim Abdelkader
ce4922c1bc
Merge pull request #2836 from openmv/fix_mutex_tid
common/mutex: Clear tid when releasing mutex
2025-09-12 19:16:38 +03:00
iabdalkader
bcb20f45ac Makefile: Suppress J-Link GDB server output
Redirect J-Link GDB server stdout and stderr to /dev/null to prevent
its output from mixing with GDB output in the terminal.
2025-09-12 18:06:40 +02:00
iabdalkader
bb52288561 common/mutex: Clear tid when releasing mutex
Clear the thread ID to 0 when unlocking the mutex to prevent deadlock.
If the thread holding never reacquire it, other threads would be
permanently blocked with a stale thread ID.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-12 18:00:32 +02:00
Ibrahim Abdelkader
4f40ae487e
Merge pull request #2835 from kwagyeman/kwabena/genx320_pag7936
Some checks are pending
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions
drivers/sensors: Add aux flag to genx320 driver.
2025-09-12 11:14:47 +03:00
Kwabena W. Agyeman
050d750afd drivers/sensors: Add aux flag to genx320 driver. 2025-09-11 19:54:19 -07:00
Ibrahim Abdelkader
9937b9cc1e
Merge pull request #2834 from openmv/makefile-debug-improvements
Some checks are pending
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions
Makefile: Integrate GDB debugging into jlink target
2025-09-12 00:13:42 +03:00
Ibrahim Abdelkader
8b19717c21
Merge pull request #2821 from openmv/add_crc_hal
common: Add CRC HAL with hardware acceleration support.
2025-09-12 00:13:13 +03:00
iabdalkader
a4ce426308 Makefile: Integrate GDB debugging into jlink target
Combines JLink server startup and GDB launch into a single command.
Uses setsid to isolate JLink server from terminal signals, allowing
Ctrl+C to work normally in GDB without terminating the debug server.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-11 23:08:14 +02:00
iabdalkader
616ce71b96 ports/stm32: Implement hardware accelerated CRC.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-11 22:08:58 +02:00
iabdalkader
1dd9b33acc ports/alif: Implement hardware accelerated CRC.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-11 22:08:58 +02:00
iabdalkader
facd715c12 common: Add CRC HAL.
CRC-16 support.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-11 22:08:58 +02:00
Ibrahim Abdelkader
384c3e734d
Merge pull request #2833 from openmv/add_raw_stream_ctrl
lib/imlib: Add an embedded frame header in the streaming buffer.
2025-09-11 22:57:17 +03:00
iabdalkader
1308129fdb lib/imlib: Enable raw streaming control at runtime.
Not for use with USBDBG.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-11 21:43:16 +02:00
iabdalkader
be25f116ab common: Skip embedded framebuffer header.
Not used for USBDBG.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-11 18:10:58 +02:00
iabdalkader
22981bdb61 lib/imlib: Add an embedded frame header in the streaming buffer.
Embed framebuffer header before frame data in streaming protocol to
enable protocol channels to read header+data from contiguous memory
with zero-copy operations. Header contains width, height, pixformat
and compressed size.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-11 18:09:24 +02:00
dependabot[bot]
51e87a4e40
build(deps): bump softprops/action-gh-release from 2.2.2 to 2.3.3
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.2.2 to 2.3.3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](da05d55257...6cbd405e2c)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.3.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 15:06:18 +00:00
dependabot[bot]
960ae86545
build(deps): bump actions/setup-python from 5 to 6
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 15:22:50 +00:00
Ibrahim Abdelkader
23808f81cf
Merge pull request #2827 from openmv/fix_update_preview
Some checks failed
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Has been cancelled
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / code-size-report (push) Has been cancelled
🔥 Firmware Build / stable-release (push) Has been cancelled
🔥 Firmware Build / development-release (push) Has been cancelled
lib/imlib: Remove test loop.
2025-09-02 19:59:22 +03:00
iabdalkader
f177d221e9 lib/imlib: Remove test loop.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-02 18:54:38 +02:00
Ibrahim Abdelkader
30a62c2c05
Merge pull request #2826 from openmv/refactor_jpegbuffer
lib/imlib: Use framebuffer_t for main and streaming buffers.
2025-09-02 17:16:59 +03:00
iabdalkader
cd2a4d4f95 drivers: Update framebuffer API.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-02 16:05:55 +02:00
iabdalkader
b1f81f7cdd common: Update framebuffer API.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-02 16:05:55 +02:00
iabdalkader
fbc8d14cda modules: Update framebuffer API.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-02 16:05:55 +02:00
iabdalkader
66c8e7e8d5 ports/all: Update framebuffer API.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-02 16:05:55 +02:00
iabdalkader
4d69dd396c boards/all: Rename JPEG buffer linker script variables.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-02 16:05:52 +02:00
iabdalkader
d05d5f1d3c lib/imlib: Use framebuffer_t for main and streaming buffers.
Removes separate jpegbuffer_t and consolidates streaming/preview
functionality into the main framebuffer_t structure.

Main changes:
- Remove jpegbuffer_t structure and global jpegbuffer variable
- Add streaming fields (enabled, quality, lock, raw_w, raw_h) to framebuffer_t
- Replace framebuffer_update_jpeg_buffer() with framebuffer_update_preview()

API improvements:
- Rename framebuffer_init_image() -> framebuffer_to_image()
- Rename framebuffer_init_from_image() -> framebuffer_from_image()
- Add framebuffer_set_enabled() and framebuffer_set_preview() utilities
- Remove accessor macros, use direct field access (kept framebuffer_get_buffer_size)
- Update framebuffer_init() to accept enabled parameter
- Add configurable raw preview dimensions per framebuffer

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-02 15:52:17 +02:00
iabdalkader
4c2c59d0e5 lib/imlib: Remove old framebuffer macros.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-09-02 13:41:30 +02:00
Ibrahim Abdelkader
bc39969581
Merge pull request #2801 from kwagyeman/kwabena/post_processing_speed_up
Some checks failed
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Has been cancelled
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / code-size-report (push) Has been cancelled
🔥 Firmware Build / stable-release (push) Has been cancelled
🔥 Firmware Build / development-release (push) Has been cancelled
scripts/libraries: Post-processing speedup.
2025-08-31 16:05:25 +03:00
Ibrahim Abdelkader
0e124a215d
Merge pull request #2825 from openmv/update_profiler
Some checks are pending
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions
common/profiler: Add get_size function.
2025-08-30 22:34:50 +03:00
iabdalkader
49f330f2dc common/profiler: Add get_size function.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-30 21:15:38 +02:00
Ibrahim Abdelkader
67a014ebdc
Merge pull request #2822 from kwagyeman/kwabena/fix_usb_crash
Some checks failed
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Has been cancelled
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / code-size-report (push) Has been cancelled
🔥 Firmware Build / stable-release (push) Has been cancelled
🔥 Firmware Build / development-release (push) Has been cancelled
common/usbdbg: Flush data from unsupported commands.
2025-08-25 12:59:07 +03:00
Kwabena W. Agyeman
657c9a6325 common/usbdbg: Flush data from unsupported commands.
The removal of unused USB commands that are still sent by the IDE
causes tinyusb boards to crash on connect currently. Flushing the
excess received bytes for unknown commands solves this issue.
2025-08-24 21:34:02 -07:00
Kwabena W. Agyeman
e64877bbea scripts/libraries: Quantize threshold instead of dequantizing scores.
Compare a quantized threshold against the scores to save having to
dequantized all the scores which takes a significant amount of time
the more classes there are in the score array output.
2025-08-24 15:22:52 -07:00
Kwabena W. Agyeman
8e72debf11 scripts/examples: Update fomo example to use the new post-processor. 2025-08-24 15:22:52 -07:00
Kwabena W. Agyeman
de7a761d3b scipts/libraries: Update fomo post-processor to use numpy vectors. 2025-08-24 15:22:51 -07:00
Kwabena W. Agyeman
84e6ee6507 modules/py_ml: Return tensor references for post-processors.
Converting the output tensors into floats for the prost-processors
causes memory exhaustion when models become very large. Additionally,
it wastes processing time converting values which may not be used. By
moving the conversion step into the post-processors we avoid this issue.

If no callback is passed for post-processing the converted output to
a floating point ndarray is returned still.
2025-08-24 14:43:24 -07:00
Kwabena W. Agyeman
7cbf85cd1c modules/py_ml: Fix py_ml_dtype_size typo. 2025-08-24 14:42:56 -07:00
Ibrahim Abdelkader
0dbb5c8aa7
Merge pull request #2816 from openmv/profiling_on_steroids
Some checks are pending
🔥 Firmware Build / build-firmware (0, ARDUINO_GIGA) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_33_BLE_SENSE) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NANO_RP2040_CONNECT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_NICLA_VISION) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, ARDUINO_PORTENTA_H7) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, DOCKER) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV2) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV4P) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMVPT) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_AE3) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / build-firmware (0, OPENMV_RT1060) (push) Waiting to run
🔥 Firmware Build / build-firmware (1, OPENMV_N6) (push) Waiting to run
🔥 Firmware Build / code-size-report (push) Blocked by required conditions
🔥 Firmware Build / stable-release (push) Blocked by required conditions
🔥 Firmware Build / development-release (push) Blocked by required conditions
common: Add code profiler.
2025-08-24 20:13:33 +03:00
iabdalkader
d4ce288ae2 workflow: Add firmware target to test profiling.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-24 17:17:46 +02:00
iabdalkader
d057f85f0b tools/pyopenmv: Support profiling data and elf symbols.
- Support new commands to read profiling data.
- Support rendering profiling data (ticks, cycles and event counters).
- Support loading symbol table from elf to map addresses to symbols.
- Support running scripts via command line args.

New keys:
p - Cycle through available profiling data (events, counters).
m - Toggle profiling mode (inclusive/exclusive).
r - Reset profiler.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-24 17:17:40 +02:00
iabdalkader
927a2c7b54 lib/tflm: Add manual profiling instrumentation.
Use OMV_PROFILER_ENTER/EXIT macros to manually instrument the TensorFlow
Lite inference function instead of automatic -finstrument-functions to
avoid C++ linking issues with operator delete and atomics.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-24 16:57:15 +02:00
iabdalkader
872735212f common: Support reading profiling data via USB debug.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-24 16:57:11 +02:00
iabdalkader
e5cdccc72e common: Introduce profiling support.
This adds a simple code profiler that leverages both SysTick and
the Performance Monitoring Unit (PMU) available on Armv8.1-M cores.
SysTick provides a simple high-resolution timer source, while the PMU
offers a cycle counter and multiple configurable event counters that
can track low-level performance metrics such as cache misses, pipeline
stalls, etc... See the Armv8.1-M Performance Monitoring User Guide
for additional details.

Function-level instrumentation is enabled using -finstrument-functions
on select files to automatically record statistics on entry/exit.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-24 16:57:06 +02:00
iabdalkader
cf7b84d682 ports/all: Remove old profiling macros.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-24 14:40:02 +02:00
Ibrahim Abdelkader
e6d693cabf
Merge pull request #2820 from openmv/refactor_usbdbg
common: Refactor TinyUSB debug interface implementation.
2025-08-24 12:24:32 +03:00
iabdalkader
90bd11e937 common: Refactor TinyUSB debug interface implementation.
This commit refactors the TinyUSB debug interface with the following changes:

- Handle disabled IRQs.
- Replace global variables with centralized state.
- Add timeout, and validation for more robust transfers (as much as possible
  given the protocol limits).
- Remove obsolete/unsupported commands (attr read/write, script save etc..)

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-23 20:16:09 +02:00
Ibrahim Abdelkader
29f07312f8
Merge pull request #2819 from openmv/fix_n6_ic_source
Some checks failed
🔥 Firmware Build / build-firmware (ARDUINO_GIGA) (push) Has been cancelled
🔥 Firmware Build / build-firmware (ARDUINO_NANO_33_BLE_SENSE) (push) Has been cancelled
🔥 Firmware Build / build-firmware (ARDUINO_NANO_RP2040_CONNECT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (ARDUINO_NICLA_VISION) (push) Has been cancelled
🔥 Firmware Build / build-firmware (ARDUINO_PORTENTA_H7) (push) Has been cancelled
🔥 Firmware Build / build-firmware (DOCKER) (push) Has been cancelled
🔥 Firmware Build / build-firmware (OPENMV2) (push) Has been cancelled
🔥 Firmware Build / build-firmware (OPENMV3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (OPENMV4) (push) Has been cancelled
🔥 Firmware Build / build-firmware (OPENMV4P) (push) Has been cancelled
🔥 Firmware Build / build-firmware (OPENMVPT) (push) Has been cancelled
🔥 Firmware Build / build-firmware (OPENMV_AE3) (push) Has been cancelled
🔥 Firmware Build / build-firmware (OPENMV_N6) (push) Has been cancelled
🔥 Firmware Build / build-firmware (OPENMV_RT1060) (push) Has been cancelled
🔥 Firmware Build / code-size-report (push) Has been cancelled
🔥 Firmware Build / stable-release (push) Has been cancelled
🔥 Firmware Build / development-release (push) Has been cancelled
ports/stm32: Revert N6 slow-peripheral IC clock source.
2025-08-19 14:54:43 +03:00
iabdalkader
0d5c5bc6eb ports/stm32: Revert N6 slow-peripheral IC clock source.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
2025-08-19 13:39:33 +02:00
Ibrahim Abdelkader
f45c0181fc
Merge pull request #2817 from kwagyeman/kwabena/fix_cmsis_extension
lib/cmsis: Fix cmsis compile time constant folding.
2025-08-19 10:41:23 +03:00
Kwabena W. Agyeman
89fc302812 lib/cmsis: Fix cmsis compile time constant folding. 2025-08-18 13:33:25 -07:00
Kwabena W. Agyeman
447e782304 scripts/examples: Fix light shield example. 2024-07-08 20:05:58 -07:00
96 changed files with 2411 additions and 926 deletions

View File

@ -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

View File

@ -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

View File

@ -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 }}

View File

@ -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

View File

@ -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.

View File

@ -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",

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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",

View File

@ -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.

View File

@ -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",

View File

@ -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).

View File

@ -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).

View File

@ -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)

View File

@ -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.

View File

@ -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",

View File

@ -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.

View File

@ -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",

View File

@ -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)

View File

@ -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",

View File

@ -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"

View File

@ -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"
}
]
}

View File

@ -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

View File

@ -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",

View File

@ -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
View 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__

View File

@ -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 */

View File

@ -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))

View File

@ -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)) {

View File

@ -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;
}

View File

@ -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__

View File

@ -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
View 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
View 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__

View File

@ -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
View 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
View 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__

View File

@ -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

View File

@ -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__

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 \

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}

View File

@ -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__ */

View File

@ -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.

View File

@ -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))

View File

@ -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

View File

@ -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;
}

View File

@ -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

Binary file not shown.

View File

@ -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

View File

@ -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));

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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));

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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
View 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;
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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
View 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

View File

@ -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;
}

View File

@ -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

View File

@ -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++) {

View File

@ -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() {

View File

@ -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")

View File

@ -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:

View File

@ -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)

View File

@ -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])

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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__':

View File

@ -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)

View File

@ -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