Skip to content

Commit 1052cad

Browse files
committed
Add suport of i2c reusage (experimental)
1 parent 2cfd27b commit 1052cad

File tree

4 files changed

+106
-16
lines changed

4 files changed

+106
-16
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ If you want to play arround with AI, take a look at the [micropython binding for
2323
- [Freeing the buffer](#freeing-the-buffer)
2424
- [Is a frame available](#is-frame-available)
2525
- [Additional methods](#additional-methods)
26+
- [I2C Integration](#i2c-integration)
2627
- [Additional information](#additional-information)
2728
- [Build your custom firmware](#build-your-custom-firmware)
2829
- [Setting up the build environment (DIY method)](#setting-up-the-build-environment-diy-method)
@@ -209,6 +210,42 @@ import camera
209210
vers = camera.Version()
210211
```
211212

213+
### I2C Integration
214+
215+
The camera uses I2C (SCCB protocol) to communicate with the camera sensor. You can share this I2C bus with other devices by passing an external I2C object to the camera:
216+
217+
#### Sharing I2C with Camera
218+
219+
```python
220+
import machine
221+
222+
# Create your own I2C object first
223+
i2c = machine.I2C(0, scl=22, sda=21, freq=400000)
224+
225+
# Pass it to the camera (no need for sda_pin/scl_pin)
226+
cam = camera.Camera(i2c=i2c, data_pins=..., pclk_pin=..., ...)
227+
228+
# The same I2C object can be used for other devices on the same bus!
229+
devices = i2c.scan()
230+
print(f"I2C devices found: {devices}")
231+
232+
# You can communicate with other I2C devices while camera is running
233+
i2c.writeto(0x42, b'\x00\x01') # Write to another device
234+
235+
# Camera sensor communication works too
236+
cam.set_saturation(1) # Uses the shared I2C bus
237+
```
238+
239+
#### Alternative: Camera Creates Its Own I2C (Default)
240+
241+
```python
242+
# Camera creates and manages its own I2C internally
243+
cam = camera.Camera(sda_pin=21, scl_pin=22, ...)
244+
245+
# In this mode, you cannot share I2C with other devices
246+
# Use the first method if you need to share I2C
247+
```
248+
212249
### Additional information
213250

214251
The firmware images support the following cameras out of the box, but is therefore big: OV7670, OV7725, OV2640, OV3660, OV5640, NT99141, GC2145, GC032A, GC0308, BF3005, BF20A6, SC030IOT

src/modcamera.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ void mp_camera_hal_construct(
113113
int8_t reset_pin,
114114
int8_t sccb_sda_pin,
115115
int8_t sccb_scl_pin,
116+
int8_t sccb_i2c_port,
116117
int32_t xclk_freq_hz,
117118
mp_camera_pixformat_t pixel_format,
118119
mp_camera_framesize_t frame_size,
@@ -134,9 +135,15 @@ void mp_camera_hal_construct(
134135
self->camera_config.pin_pwdn = powerdown_pin;
135136
self->camera_config.pin_reset = reset_pin;
136137
self->camera_config.pin_xclk = external_clock_pin;
137-
self->camera_config.pin_sscb_sda = sccb_sda_pin;
138-
self->camera_config.pin_sscb_scl = sccb_scl_pin;
139-
138+
if (sccb_i2c_port != -1) {
139+
self->camera_config.sccb_i2c_port = sccb_i2c_port;
140+
self->camera_config.pin_sscb_sda = -1; // Set sda_pin = -1 to signal esp_camera_init to use SCCB_Use_Port()
141+
self->camera_config.pin_sscb_scl = -1;
142+
} else {
143+
self->camera_config.pin_sscb_sda = sccb_sda_pin;
144+
self->camera_config.pin_sscb_scl = sccb_scl_pin;
145+
self->camera_config.sccb_i2c_port = sccb_i2c_port;
146+
}
140147
self->camera_config.frame_size = frame_size;
141148
self->camera_config.jpeg_quality = jpeg_quality; //save value in here, but will be corrected (with map) before passing it to the esp32-driver
142149

src/modcamera.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,9 @@ typedef hal_camera_gainceiling_t mp_camera_gainceiling_t;
119119
* @param href_pin HREF pin.
120120
* @param powerdown_pin Power down pin.
121121
* @param reset_pin Reset pin.
122-
* @param sccb_sda_pin SCCB SDA pin.
122+
* @param sccb_sda_pin SCCB SDA pin (set to -1 to use sccb_i2c_port instead).
123123
* @param sccb_scl_pin SCCB SCL pin.
124+
* @param sccb_i2c_port I2C port number to use (only if sccb_sda_pin is -1).
124125
* @param xclk_freq_hz External clock frequency in Hz.
125126
* @param pixel_format Pixel format.
126127
* @param frame_size Frame size.
@@ -139,6 +140,7 @@ extern void mp_camera_hal_construct(
139140
int8_t reset_pin,
140141
int8_t sccb_sda_pin,
141142
int8_t sccb_scl_pin,
143+
int8_t sccb_i2c_port,
142144
int32_t xclk_freq_hz,
143145
mp_camera_pixformat_t pixel_format,
144146
mp_camera_framesize_t frame_size,

src/modcamera_api.c

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@
3535

3636
#include "modcamera.h"
3737

38+
#if MICROPY_HW_ESP_NEW_I2C_DRIVER
39+
#include "driver/i2c_master.h"
40+
#else
41+
#include "driver/i2c.h"
42+
#include "hal/i2c_ll.h"
43+
#endif
44+
3845
typedef struct mp_camera_obj_t mp_camera_obj;
3946
const mp_obj_type_t camera_type;
4047

@@ -43,7 +50,7 @@ static mp_camera_obj_t mp_camera_singleton = { .base = { NULL } };
4350

4451
//Constructor
4552
static mp_obj_t mp_camera_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
46-
enum { ARG_data_pins, ARG_pixel_clock_pin, ARG_vsync_pin, ARG_href_pin, ARG_sda_pin, ARG_scl_pin, ARG_xclock_pin, ARG_xclock_frequency, ARG_powerdown_pin, ARG_reset_pin, ARG_pixel_format, ARG_frame_size, ARG_jpeg_quality, ARG_fb_count, ARG_grab_mode, ARG_init, NUM_ARGS };
53+
enum { ARG_data_pins, ARG_pixel_clock_pin, ARG_vsync_pin, ARG_href_pin, ARG_sda_pin, ARG_scl_pin, ARG_xclock_pin, ARG_i2c, ARG_xclock_frequency, ARG_powerdown_pin, ARG_reset_pin, ARG_pixel_format, ARG_frame_size, ARG_jpeg_quality, ARG_fb_count, ARG_grab_mode, ARG_init, NUM_ARGS };
4754
static const mp_arg_t allowed_args[] = {
4855
#ifdef MICROPY_CAMERA_ALL_REQ_PINS_DEFINED
4956
{ MP_QSTR_data_pins, MP_ARG_OBJ | MP_ARG_KW_ONLY , { .u_obj = MP_ROM_NONE } },
@@ -58,10 +65,11 @@ static mp_obj_t mp_camera_make_new(const mp_obj_type_t *type, size_t n_args, siz
5865
{ MP_QSTR_pclk_pin, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED },
5966
{ MP_QSTR_vsync_pin, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED },
6067
{ MP_QSTR_href_pin, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED },
61-
{ MP_QSTR_sda_pin, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED },
62-
{ MP_QSTR_scl_pin, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED },
68+
{ MP_QSTR_sda_pin, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = -1 } },
69+
{ MP_QSTR_scl_pin, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = -1 } },
6370
{ MP_QSTR_xclk_pin, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED },
6471
#endif
72+
{ MP_QSTR_i2c, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = MP_ROM_NONE } },
6573
{ MP_QSTR_xclk_freq, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = MICROPY_CAMERA_XCLK_FREQ } },
6674
{ MP_QSTR_powerdown_pin, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = MICROPY_CAMERA_PIN_PWDN } },
6775
{ MP_QSTR_reset_pin, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = MICROPY_CAMERA_PIN_RESET } },
@@ -111,8 +119,48 @@ static mp_obj_t mp_camera_make_new(const mp_obj_type_t *type, size_t n_args, siz
111119
int8_t pixel_clock_pin = args[ARG_pixel_clock_pin].u_int;
112120
int8_t vsync_pin = args[ARG_vsync_pin].u_int;
113121
int8_t href_pin = args[ARG_href_pin].u_int;
114-
int8_t sda_pin = args[ARG_sda_pin].u_int;
115-
int8_t scl_pin = args[ARG_scl_pin].u_int;
122+
123+
// Handle I2C configuration: either use I2C object or individual pins
124+
int8_t sda_pin = -1;
125+
int8_t scl_pin = -1;
126+
int8_t i2c_port = -1;
127+
mp_obj_t i2c_obj = args[ARG_i2c].u_obj;
128+
129+
if (i2c_obj != MP_ROM_NONE) {
130+
// External I2C object provided - extract port number from it
131+
extern const mp_obj_type_t machine_i2c_type;
132+
if (!mp_obj_is_type(i2c_obj, &machine_i2c_type)) {
133+
mp_raise_TypeError(MP_ERROR_TEXT("i2c must be a machine.I2C object"));
134+
}
135+
136+
// Get the I2C port from the I2C object
137+
// Structure matches machine_hw_i2c_obj_t from machine_i2c.c
138+
typedef struct _machine_hw_i2c_obj_t {
139+
mp_obj_base_t base;
140+
i2c_port_t port : 8;
141+
gpio_num_t scl : 8;
142+
gpio_num_t sda : 8;
143+
uint32_t freq;
144+
uint32_t timeout_us;
145+
} machine_hw_i2c_obj_t;
146+
147+
machine_hw_i2c_obj_t *i2c = (machine_hw_i2c_obj_t *)MP_OBJ_TO_PTR(i2c_obj);
148+
i2c_port = (int8_t)i2c->port;
149+
sda_pin = i2c->sda;
150+
scl_pin = i2c->scl;
151+
} else {
152+
// Use individual pins
153+
sda_pin = args[ARG_sda_pin].u_int;
154+
scl_pin = args[ARG_scl_pin].u_int;
155+
156+
// Validate that pins are provided (if no default pins defined)
157+
#ifndef MICROPY_CAMERA_ALL_REQ_PINS_DEFINED
158+
if (sda_pin == -1 || scl_pin == -1) {
159+
mp_raise_ValueError(MP_ERROR_TEXT("Either i2c object or sda_pin/scl_pin must be specified"));
160+
}
161+
#endif
162+
}
163+
116164
int8_t xclock_pin = args[ARG_xclock_pin].u_int;
117165
int32_t xclock_frequency = args[ARG_xclock_frequency].u_int;
118166
if (xclock_frequency < 1000) {
@@ -132,21 +180,17 @@ static mp_obj_t mp_camera_make_new(const mp_obj_type_t *type, size_t n_args, siz
132180
// Get static singleton object
133181
mp_camera_obj_t *self = &mp_camera_singleton;
134182

135-
// If camera was already initialized, deinit it first
136-
bool first_init = false;
137183
if (self->base.type == NULL) {
138-
// First time initialization
139184
self->base.type = &camera_type;
140-
first_init = true;
141185
} else {
142-
// Camera already exists, deinit it before reinitializing
143-
mp_camera_hal_deinit(self);
186+
mp_camera_hal_deinit(self); // Camera already exists, deinit it before reinitializing
144187
}
145188

146189
mp_camera_hal_construct(self, data_pins, xclock_pin, pixel_clock_pin, vsync_pin, href_pin, powerdown_pin, reset_pin,
147-
sda_pin, scl_pin, xclock_frequency, pixel_format, frame_size, jpeg_quality, fb_count, grab_mode);
190+
sda_pin, scl_pin, i2c_port, xclock_frequency, pixel_format, frame_size, jpeg_quality, fb_count, grab_mode);
148191

149192
mp_camera_hal_init(self);
193+
mp_hal_delay_ms(10); // Small delay to ensure I2C/SCCB is fully initialized
150194
if (mp_camera_hal_capture(self) == mp_const_none){
151195
mp_camera_hal_deinit(self);
152196
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to capture initial frame. Construct a new object with appropriate configuration."));

0 commit comments

Comments
 (0)