4
4
* The MIT License (MIT)
5
5
*
6
6
* Copyright (c) 2019 Damien P. George
7
+ * Copyright (c) 2025 Vincent1-python
7
8
*
8
9
* Permission is hereby granted, free of charge, to any person obtaining a copy
9
10
* of this software and associated documentation files (the "Software"), to deal
35
36
36
37
#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C
37
38
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
-
50
39
#define I2C_DEFAULT_TIMEOUT_US (50000) // 50ms
51
40
52
- // ---------------- Internal data structures ----------------
53
41
typedef struct _machine_hw_i2c_obj_t {
54
42
mp_obj_base_t base ;
55
43
i2c_master_bus_handle_t bus_handle ;
56
- i2c_master_dev_handle_t dev_handle ;
57
44
uint8_t port : 8 ;
58
45
gpio_num_t scl : 8 ;
59
46
gpio_num_t sda : 8 ;
47
+ uint32_t freq ;
48
+ uint32_t timeout_us ;
60
49
} machine_hw_i2c_obj_t ;
61
50
62
51
static machine_hw_i2c_obj_t machine_hw_i2c_obj [I2C_NUM_MAX ];
63
52
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 ) {
66
54
67
- // 1. If already initialized, uninstall the old driver first
68
55
if (!first_init && self -> bus_handle ) {
69
- i2c_master_bus_rm_device (self -> dev_handle );
70
56
i2c_del_master_bus (self -> bus_handle );
71
57
self -> bus_handle = NULL ;
72
- self -> dev_handle = NULL ;
73
58
}
74
59
75
- // 2. Configure the bus
76
60
i2c_master_bus_config_t bus_cfg = {
77
61
.i2c_port = self -> port ,
78
62
.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
82
66
.flags .enable_internal_pullup = true,
83
67
};
84
68
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 ));
93
69
}
94
70
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 ) {
96
72
machine_hw_i2c_obj_t * self = MP_OBJ_TO_PTR (self_in );
97
73
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 );
100
77
if (err != ESP_OK ) {
101
- return - MP_ENODEV ; /* No device at address, return immediately */
78
+ return - MP_ENODEV ; // No device at address, return immediately
102
79
}
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 ;
116
80
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 ;
124
94
}
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 );
141
100
}
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 ;
151
106
}
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 ;
161
109
}
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
110
if (err != ESP_OK ) {
173
- goto cleanup ;
111
+ return - abs ( err ) ;
174
112
}
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 ;
187
113
}
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 ;
196
115
}
197
116
198
- // ---------------- Print ----------------
199
117
static void machine_hw_i2c_print (const mp_print_t * print , mp_obj_t self_in , mp_print_kind_t kind ) {
200
118
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 );
202
121
}
203
122
204
- // ---------------- Constructor ----------------
205
123
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 ) {
206
124
// Create a SoftI2C instance if no id is specified (or is -1) but other arguments are given
207
125
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_
214
132
{ MP_QSTR_id , MP_ARG_INT , {.u_int = I2C_NUM_0 } },
215
133
{ MP_QSTR_scl , MP_ARG_KW_ONLY | MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
216
134
{ 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 } },
219
137
};
220
138
mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
221
139
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_
233
151
self -> port = i2c_id ;
234
152
self -> scl = (i2c_id == I2C_NUM_0 ) ? MICROPY_HW_I2C0_SCL : MICROPY_HW_I2C1_SCL ;
235
153
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 ;
236
156
}
237
157
238
158
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_
241
161
if (args [ARG_sda ].u_obj != MP_OBJ_NULL ) {
242
162
self -> sda = machine_pin_get_id (args [ARG_sda ].u_obj );
243
163
}
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
+ }
244
170
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 );
246
172
return MP_OBJ_FROM_PTR (self );
247
173
}
248
174
249
- // ---------------- Protocol table ----------------
250
175
static 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 ,
253
178
};
254
179
255
180
MP_DEFINE_CONST_OBJ_TYPE (
0 commit comments