30
30
#include "extmod/modmachine.h"
31
31
#include "machine_i2c.h"
32
32
33
- #include "driver/i2c .h"
33
+ #include "driver/i2c_master .h"
34
34
#include "hal/i2c_ll.h"
35
35
36
36
#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C
49
49
50
50
#define I2C_DEFAULT_TIMEOUT_US (50000) // 50ms
51
51
52
+ // ---------------- Internal data structures ----------------
52
53
typedef struct _machine_hw_i2c_obj_t {
53
54
mp_obj_base_t base ;
54
- i2c_port_t port : 8 ;
55
+ i2c_master_bus_handle_t bus_handle ;
56
+ i2c_master_dev_handle_t dev_handle ;
57
+ uint8_t port : 8 ;
55
58
gpio_num_t scl : 8 ;
56
59
gpio_num_t sda : 8 ;
57
60
} machine_hw_i2c_obj_t ;
58
61
59
62
static machine_hw_i2c_obj_t machine_hw_i2c_obj [I2C_NUM_MAX ];
60
63
64
+ // ---------------- Initialization ----------------
61
65
static void machine_hw_i2c_init (machine_hw_i2c_obj_t * self , uint32_t freq , uint32_t timeout_us , bool first_init ) {
62
- if (!first_init ) {
63
- i2c_driver_delete (self -> port );
66
+
67
+ // 1. If already initialized, uninstall the old driver first
68
+ if (!first_init && self -> bus_handle ) {
69
+ i2c_master_bus_rm_device (self -> dev_handle );
70
+ i2c_del_master_bus (self -> bus_handle );
71
+ self -> bus_handle = NULL ;
72
+ self -> dev_handle = NULL ;
64
73
}
65
- i2c_config_t conf = {
66
- . mode = I2C_MODE_MASTER ,
67
- . sda_io_num = self -> sda ,
68
- .sda_pullup_en = GPIO_PULLUP_ENABLE ,
74
+
75
+ // 2. Configure the bus
76
+ i2c_master_bus_config_t bus_cfg = {
77
+ .i2c_port = self -> port ,
69
78
.scl_io_num = self -> scl ,
70
- .scl_pullup_en = GPIO_PULLUP_ENABLE ,
71
- .master .clk_speed = freq ,
79
+ .sda_io_num = self -> sda ,
80
+ .clk_source = I2C_CLK_SRC_DEFAULT ,
81
+ .glitch_ignore_cnt = 7 ,
82
+ .flags .enable_internal_pullup = true,
72
83
};
73
- i2c_param_config (self -> port , & conf );
74
- int timeout = I2C_SCLK_FREQ / 1000000 * timeout_us ;
75
- i2c_set_timeout (self -> port , (timeout > I2C_LL_MAX_TIMEOUT ) ? I2C_LL_MAX_TIMEOUT : timeout );
76
- i2c_driver_install (self -> port , I2C_MODE_MASTER , 0 , 0 , 0 );
84
+ 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 ));
77
93
}
78
94
79
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 ) {
80
96
machine_hw_i2c_obj_t * self = MP_OBJ_TO_PTR (self_in );
81
97
82
- i2c_cmd_handle_t cmd = i2c_cmd_link_create ();
98
+ /* 0. Probe the address to see if any device responds */
99
+ esp_err_t err = i2c_master_probe (self -> bus_handle , addr , 1000 );
100
+ if (err != ESP_OK ) {
101
+ return - MP_ENODEV ; /* No device at address, return immediately */
102
+ }
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
+
83
115
int data_len = 0 ;
84
116
117
+ /* 2. If WRITE1 segment exists, perform the write first */
85
118
if (flags & MP_MACHINE_I2C_FLAG_WRITE1 ) {
86
- i2c_master_start (cmd );
87
- i2c_master_write_byte (cmd , addr << 1 , true);
88
- i2c_master_write (cmd , bufs -> buf , bufs -> len , true);
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
+ }
124
+ }
89
125
data_len += bufs -> len ;
90
126
-- n ;
91
127
++ bufs ;
92
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 ;
141
+ }
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 ;
93
147
94
- i2c_master_start (cmd );
95
- i2c_master_write_byte (cmd , addr << 1 | (flags & MP_MACHINE_I2C_FLAG_READ ), true);
148
+ // Calculate total length
149
+ for (; n -- ; ++ bufs ) {
150
+ total_len += bufs -> len ;
151
+ }
96
152
97
- for (; n -- ; ++ bufs ) {
98
- if (flags & MP_MACHINE_I2C_FLAG_READ ) {
99
- i2c_master_read (cmd , bufs -> buf , bufs -> len , n == 0 ? I2C_MASTER_LAST_NACK : I2C_MASTER_ACK );
100
- } else {
101
- if (bufs -> len != 0 ) {
102
- i2c_master_write (cmd , bufs -> buf , bufs -> len , true);
103
- }
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 ;
161
+ }
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 );
172
+ if (err != ESP_OK ) {
173
+ goto cleanup ;
104
174
}
105
- data_len += bufs -> len ;
106
- }
107
175
108
- if ( flags & MP_MACHINE_I2C_FLAG_STOP ) {
109
- i2c_master_stop ( cmd );
176
+ // Free dynamically allocated memory
177
+ free ( write_buf );
110
178
}
111
179
112
- // TODO proper timeout
113
- esp_err_t err = i2c_master_cmd_begin ( self -> port , cmd , 100 * ( 1 + data_len ) / portTICK_PERIOD_MS );
114
- i2c_cmd_link_delete ( cmd );
180
+ cleanup :
181
+ /* 4. Immediately destroy the temporary handle */
182
+ i2c_master_bus_rm_device ( dev_handle );
115
183
184
+ /* 5. Map errors */
116
185
if (err == ESP_FAIL ) {
117
186
return - MP_ENODEV ;
118
- } else if (err == ESP_ERR_TIMEOUT ) {
187
+ }
188
+ if (err == ESP_ERR_TIMEOUT ) {
119
189
return - MP_ETIMEDOUT ;
120
- } else if (err != ESP_OK ) {
190
+ }
191
+ if (err != ESP_OK ) {
121
192
return - abs (err );
122
193
}
123
194
124
195
return data_len ;
125
196
}
126
197
127
- /******************************************************************************/
128
- // MicroPython bindings for machine API
129
-
198
+ // ---------------- Print ----------------
130
199
static void machine_hw_i2c_print (const mp_print_t * print , mp_obj_t self_in , mp_print_kind_t kind ) {
131
200
machine_hw_i2c_obj_t * self = MP_OBJ_TO_PTR (self_in );
132
- int h , l ;
133
- i2c_get_period (self -> port , & h , & l );
134
- mp_printf (print , "I2C(%u, scl=%u, sda=%u, freq=%u)" ,
135
- self -> port , self -> scl , self -> sda , I2C_SCLK_FREQ / (h + l ));
201
+ mp_printf (print , "I2C(%u, scl=%u, sda=%u)" , self -> port , self -> scl , self -> sda );
136
202
}
137
203
204
+ // ---------------- Constructor ----------------
138
205
mp_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 ) {
139
206
// Create a SoftI2C instance if no id is specified (or is -1) but other arguments are given
140
207
if (n_args != 0 ) {
@@ -153,46 +220,33 @@ mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_
153
220
mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
154
221
mp_arg_parse_all_kw_array (n_args , n_kw , all_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
155
222
156
- // Get I2C bus
157
223
mp_int_t i2c_id = args [ARG_id ].u_int ;
158
-
159
- // Check if the I2C bus is valid
160
224
if (!(I2C_NUM_0 <= i2c_id && i2c_id < I2C_NUM_MAX )) {
161
225
mp_raise_msg_varg (& mp_type_ValueError , MP_ERROR_TEXT ("I2C(%d) doesn't exist" ), i2c_id );
162
226
}
163
227
164
- // Get static peripheral object
165
- machine_hw_i2c_obj_t * self = (machine_hw_i2c_obj_t * )& machine_hw_i2c_obj [i2c_id ];
228
+ machine_hw_i2c_obj_t * self = & machine_hw_i2c_obj [i2c_id ];
166
229
167
- bool first_init = false;
168
- if (self -> base .type == NULL ) {
169
- // Created for the first time, set default pins
230
+ bool first_init = (self -> base .type == NULL );
231
+ if (first_init ) {
170
232
self -> base .type = & machine_i2c_type ;
171
233
self -> port = i2c_id ;
172
- if (self -> port == I2C_NUM_0 ) {
173
- self -> scl = MICROPY_HW_I2C0_SCL ;
174
- self -> sda = MICROPY_HW_I2C0_SDA ;
175
- } else {
176
- self -> scl = MICROPY_HW_I2C1_SCL ;
177
- self -> sda = MICROPY_HW_I2C1_SDA ;
178
- }
179
- first_init = true;
234
+ self -> scl = (i2c_id == I2C_NUM_0 ) ? MICROPY_HW_I2C0_SCL : MICROPY_HW_I2C1_SCL ;
235
+ self -> sda = (i2c_id == I2C_NUM_0 ) ? MICROPY_HW_I2C0_SDA : MICROPY_HW_I2C1_SDA ;
180
236
}
181
237
182
- // Set SCL/SDA pins if given
183
238
if (args [ARG_scl ].u_obj != MP_OBJ_NULL ) {
184
239
self -> scl = machine_pin_get_id (args [ARG_scl ].u_obj );
185
240
}
186
241
if (args [ARG_sda ].u_obj != MP_OBJ_NULL ) {
187
242
self -> sda = machine_pin_get_id (args [ARG_sda ].u_obj );
188
243
}
189
244
190
- // Initialise the I2C peripheral
191
245
machine_hw_i2c_init (self , args [ARG_freq ].u_int , args [ARG_timeout ].u_int , first_init );
192
-
193
246
return MP_OBJ_FROM_PTR (self );
194
247
}
195
248
249
+ // ---------------- Protocol table ----------------
196
250
static const mp_machine_i2c_p_t machine_hw_i2c_p = {
197
251
.transfer_supports_write1 = true,
198
252
.transfer = machine_hw_i2c_transfer ,
@@ -208,4 +262,4 @@ MP_DEFINE_CONST_OBJ_TYPE(
208
262
locals_dict , & mp_machine_i2c_locals_dict
209
263
);
210
264
211
- #endif
265
+ #endif // MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C
0 commit comments