44 * The MIT License (MIT)
55 *
66 * Copyright (c) 2019 Damien P. George
7+ * Copyright (c) 2025 Vincent1-python
78 *
89 * Permission is hereby granted, free of charge, to any person obtaining a copy
910 * of this software and associated documentation files (the "Software"), to deal
3536
3637#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C
3738
38- #if SOC_I2C_SUPPORT_XTAL
39- #if CONFIG_XTAL_FREQ > 0
40- #define I2C_SCLK_FREQ (CONFIG_XTAL_FREQ * 1000000)
41- #else
42- #error "I2C uses XTAL but no configured freq"
43- #endif // CONFIG_XTAL_FREQ
44- #elif SOC_I2C_SUPPORT_APB
45- #define I2C_SCLK_FREQ APB_CLK_FREQ
46- #else
47- #error "unsupported I2C for ESP32 SoC variant"
48- #endif
49-
5039#define I2C_DEFAULT_TIMEOUT_US (50000) // 50ms
5140
52- // ---------------- Internal data structures ----------------
5341typedef struct _machine_hw_i2c_obj_t {
5442 mp_obj_base_t base ;
5543 i2c_master_bus_handle_t bus_handle ;
56- i2c_master_dev_handle_t dev_handle ;
5744 uint8_t port : 8 ;
5845 gpio_num_t scl : 8 ;
5946 gpio_num_t sda : 8 ;
47+ uint32_t freq ;
48+ uint32_t timeout_us ;
6049} machine_hw_i2c_obj_t ;
6150
6251static machine_hw_i2c_obj_t machine_hw_i2c_obj [I2C_NUM_MAX ];
6352
64- // ---------------- Initialization ----------------
65- static void machine_hw_i2c_init (machine_hw_i2c_obj_t * self , uint32_t freq , uint32_t timeout_us , bool first_init ) {
53+ static void machine_hw_i2c_init (machine_hw_i2c_obj_t * self , bool first_init ) {
6654
67- // 1. If already initialized, uninstall the old driver first
6855 if (!first_init && self -> bus_handle ) {
69- i2c_master_bus_rm_device (self -> dev_handle );
7056 i2c_del_master_bus (self -> bus_handle );
7157 self -> bus_handle = NULL ;
72- self -> dev_handle = NULL ;
7358 }
7459
75- // 2. Configure the bus
7660 i2c_master_bus_config_t bus_cfg = {
7761 .i2c_port = self -> port ,
7862 .scl_io_num = self -> scl ,
@@ -82,126 +66,60 @@ static void machine_hw_i2c_init(machine_hw_i2c_obj_t *self, uint32_t freq, uint3
8266 .flags .enable_internal_pullup = true,
8367 };
8468 ESP_ERROR_CHECK (i2c_new_master_bus (& bus_cfg , & self -> bus_handle ));
85-
86- // 3. Add a device (placeholder address; will be changed dynamically later)
87- i2c_device_config_t dev_cfg = {
88- .dev_addr_length = I2C_ADDR_BIT_LEN_7 ,
89- .device_address = 0x00 , // Placeholder
90- .scl_speed_hz = freq ,
91- };
92- ESP_ERROR_CHECK (i2c_master_bus_add_device (self -> bus_handle , & dev_cfg , & self -> dev_handle ));
9369}
9470
95- int machine_hw_i2c_transfer (mp_obj_base_t * self_in , uint16_t addr , size_t n , mp_machine_i2c_buf_t * bufs , unsigned int flags ) {
71+ static int machine_i2c_transfer_single (mp_obj_base_t * self_in , uint16_t addr , size_t len , uint8_t * buf , unsigned int flags ) {
9672 machine_hw_i2c_obj_t * self = MP_OBJ_TO_PTR (self_in );
9773
98- /* 0. Probe the address to see if any device responds */
99- esp_err_t err = i2c_master_probe (self -> bus_handle , addr , 1000 );
74+ // 1. Probe the address to see if any device responds.
75+ // This test uses a fixed scl freq of 100_000.
76+ esp_err_t err = i2c_master_probe (self -> bus_handle , addr , self -> timeout_us / 1000 );
10077 if (err != ESP_OK ) {
101- return - MP_ENODEV ; /* No device at address, return immediately */
78+ return - MP_ENODEV ; // No device at address, return immediately
10279 }
103- /* 1. Create a temporary device handle for this transaction */
104- i2c_device_config_t dev_cfg = {
105- .dev_addr_length = I2C_ADDR_BIT_LEN_7 ,
106- .device_address = addr ,
107- .scl_speed_hz = 100000 , /* Use bus frequency */
108- };
109- i2c_master_dev_handle_t dev_handle ;
110- err = i2c_master_bus_add_device (self -> bus_handle , & dev_cfg , & dev_handle );
111- if (err != ESP_OK ) {
112- return - MP_ENODEV ;
113- }
114-
115- int data_len = 0 ;
11680
117- /* 2. If WRITE1 segment exists, perform the write first */
118- if (flags & MP_MACHINE_I2C_FLAG_WRITE1 ) {
119- if (bufs -> len ) {
120- err = i2c_master_transmit (dev_handle , bufs -> buf , bufs -> len , 1000 ); /* Block with 1 s timeout */
121- if (err != ESP_OK ) {
122- goto cleanup ;
123- }
81+ if (len > 0 ) {
82+ // 2. Create a temporary device handle for this transaction
83+ // This step for every transaction can be omitted in
84+ // esp idf v5.5+, which supports handle address changing.
85+ i2c_device_config_t dev_cfg = {
86+ .dev_addr_length = I2C_ADDR_BIT_LEN_7 ,
87+ .device_address = addr ,
88+ .scl_speed_hz = self -> freq ,
89+ };
90+ i2c_master_dev_handle_t dev_handle ;
91+ err = i2c_master_bus_add_device (self -> bus_handle , & dev_cfg , & dev_handle );
92+ if (err != ESP_OK ) {
93+ return - MP_ENODEV ;
12494 }
125- data_len += bufs -> len ;
126- -- n ;
127- ++ bufs ;
128- }
129- if (flags & MP_MACHINE_I2C_FLAG_READ ) {
130- /* 3. Main loop: remaining segments */
131- for (; n -- ; ++ bufs ) {
132- if (bufs -> len == 0 ) {
133- continue ;
134- }
135- err = i2c_master_receive (dev_handle , bufs -> buf , bufs -> len , 1000 );
136- if (err != ESP_OK ) {
137- break ;
138- }
139-
140- data_len += bufs -> len ;
95+ // 3. Transfer data
96+ if (flags & MP_MACHINE_I2C_FLAG_READ ) {
97+ err = i2c_master_receive (dev_handle , buf , len , self -> timeout_us / 1000 );
98+ } else if (len > 0 ) {
99+ err = i2c_master_transmit (dev_handle , buf , len , self -> timeout_us / 1000 );
141100 }
142- } else {
143- // Write operation logic
144- size_t total_len = 0 ;
145- mp_machine_i2c_buf_t * original_bufs = bufs ; // Save original pointer
146- size_t yuann = n ;
147-
148- // Calculate total length
149- for (; n -- ; ++ bufs ) {
150- total_len += bufs -> len ;
101+ // 4. Destroy the temporary handle
102+ i2c_master_bus_rm_device (dev_handle );
103+ // 5. Map errors
104+ if (err == ESP_FAIL ) {
105+ return - MP_ENODEV ;
151106 }
152-
153- // Reset pointer
154- bufs = original_bufs ;
155- // Reset n
156- n = yuann ;
157- // Dynamically allocate write_buf
158- uint8_t * write_buf = (uint8_t * )malloc (total_len );
159- if (write_buf == NULL ) {
160- return - MP_ENOMEM ;
107+ if (err == ESP_ERR_TIMEOUT ) {
108+ return - MP_ETIMEDOUT ;
161109 }
162-
163- // Copy data into write_buf
164- size_t index = 0 ;
165- for (; n -- ; ++ bufs ) {
166- memcpy (write_buf + index , bufs -> buf , bufs -> len );
167- index += bufs -> len ;
168- }
169-
170- // Transmit data
171- err = i2c_master_transmit (dev_handle , write_buf , total_len , 1000 );
172110 if (err != ESP_OK ) {
173- goto cleanup ;
111+ return - abs ( err ) ;
174112 }
175-
176- // Free dynamically allocated memory
177- free (write_buf );
178- }
179-
180- cleanup :
181- /* 4. Immediately destroy the temporary handle */
182- i2c_master_bus_rm_device (dev_handle );
183-
184- /* 5. Map errors */
185- if (err == ESP_FAIL ) {
186- return - MP_ENODEV ;
187113 }
188- if (err == ESP_ERR_TIMEOUT ) {
189- return - MP_ETIMEDOUT ;
190- }
191- if (err != ESP_OK ) {
192- return - abs (err );
193- }
194-
195- return data_len ;
114+ return len ;
196115}
197116
198- // ---------------- Print ----------------
199117static void machine_hw_i2c_print (const mp_print_t * print , mp_obj_t self_in , mp_print_kind_t kind ) {
200118 machine_hw_i2c_obj_t * self = MP_OBJ_TO_PTR (self_in );
201- mp_printf (print , "I2C(%u, scl=%u, sda=%u)" , self -> port , self -> scl , self -> sda );
119+ mp_printf (print , "I2C(%u, scl=%u, sda=%u, freq=%u, timeout=%u)" ,
120+ self -> port , self -> scl , self -> sda , self -> freq , self -> timeout_us );
202121}
203122
204- // ---------------- Constructor ----------------
205123mp_obj_t machine_hw_i2c_make_new (const mp_obj_type_t * type , size_t n_args , size_t n_kw , const mp_obj_t * all_args ) {
206124 // Create a SoftI2C instance if no id is specified (or is -1) but other arguments are given
207125 if (n_args != 0 ) {
@@ -214,8 +132,8 @@ mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_
214132 { MP_QSTR_id , MP_ARG_INT , {.u_int = I2C_NUM_0 } },
215133 { MP_QSTR_scl , MP_ARG_KW_ONLY | MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
216134 { MP_QSTR_sda , MP_ARG_KW_ONLY | MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
217- { MP_QSTR_freq , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 400000 } },
218- { MP_QSTR_timeout , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = I2C_DEFAULT_TIMEOUT_US } },
135+ { MP_QSTR_freq , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = -1 } },
136+ { MP_QSTR_timeout , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = -1 } },
219137 };
220138 mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
221139 mp_arg_parse_all_kw_array (n_args , n_kw , all_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
@@ -233,6 +151,8 @@ mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_
233151 self -> port = i2c_id ;
234152 self -> scl = (i2c_id == I2C_NUM_0 ) ? MICROPY_HW_I2C0_SCL : MICROPY_HW_I2C1_SCL ;
235153 self -> sda = (i2c_id == I2C_NUM_0 ) ? MICROPY_HW_I2C0_SDA : MICROPY_HW_I2C1_SDA ;
154+ self -> freq = 400000 ;
155+ self -> timeout_us = I2C_DEFAULT_TIMEOUT_US ;
236156 }
237157
238158 if (args [ARG_scl ].u_obj != MP_OBJ_NULL ) {
@@ -241,15 +161,20 @@ mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_
241161 if (args [ARG_sda ].u_obj != MP_OBJ_NULL ) {
242162 self -> sda = machine_pin_get_id (args [ARG_sda ].u_obj );
243163 }
164+ if (args [ARG_freq ].u_int != -1 ) {
165+ self -> freq = args [ARG_freq ].u_int ;
166+ }
167+ if (args [ARG_timeout ].u_int != -1 ) {
168+ self -> timeout_us = args [ARG_timeout ].u_int ;
169+ }
244170
245- machine_hw_i2c_init (self , args [ ARG_freq ]. u_int , args [ ARG_timeout ]. u_int , first_init );
171+ machine_hw_i2c_init (self , first_init );
246172 return MP_OBJ_FROM_PTR (self );
247173}
248174
249- // ---------------- Protocol table ----------------
250175static const mp_machine_i2c_p_t machine_hw_i2c_p = {
251- .transfer_supports_write1 = true ,
252- .transfer = machine_hw_i2c_transfer ,
176+ .transfer = mp_machine_i2c_transfer_adaptor ,
177+ .transfer_single = machine_i2c_transfer_single ,
253178};
254179
255180MP_DEFINE_CONST_OBJ_TYPE (
0 commit comments