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
30
31
#include "extmod/modmachine.h"
31
32
#include "machine_i2c.h"
32
33
33
- #include "driver/i2c .h"
34
+ #include "driver/i2c_master .h"
34
35
#include "hal/i2c_ll.h"
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
41
typedef struct _machine_hw_i2c_obj_t {
53
42
mp_obj_base_t base ;
54
- i2c_port_t port : 8 ;
43
+ i2c_master_bus_handle_t bus_handle ;
44
+ uint8_t port : 8 ;
55
45
gpio_num_t scl : 8 ;
56
46
gpio_num_t sda : 8 ;
47
+ uint32_t freq ;
48
+ uint32_t timeout_us ;
57
49
} machine_hw_i2c_obj_t ;
58
50
59
51
static machine_hw_i2c_obj_t machine_hw_i2c_obj [I2C_NUM_MAX ];
60
52
61
- 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 );
53
+ static void machine_hw_i2c_init (machine_hw_i2c_obj_t * self , bool first_init ) {
54
+
55
+ if (!first_init && self -> bus_handle ) {
56
+ i2c_del_master_bus (self -> bus_handle );
57
+ self -> bus_handle = NULL ;
64
58
}
65
- i2c_config_t conf = {
66
- .mode = I2C_MODE_MASTER ,
67
- .sda_io_num = self -> sda ,
68
- .sda_pullup_en = GPIO_PULLUP_ENABLE ,
59
+
60
+ i2c_master_bus_config_t bus_cfg = {
61
+ .i2c_port = self -> port ,
69
62
.scl_io_num = self -> scl ,
70
- .scl_pullup_en = GPIO_PULLUP_ENABLE ,
71
- .master .clk_speed = freq ,
63
+ .sda_io_num = self -> sda ,
64
+ .clk_source = I2C_CLK_SRC_DEFAULT ,
65
+ .glitch_ignore_cnt = 7 ,
66
+ .flags .enable_internal_pullup = true,
72
67
};
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 );
68
+ ESP_ERROR_CHECK (i2c_new_master_bus (& bus_cfg , & self -> bus_handle ));
77
69
}
78
70
79
- 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 ) {
80
72
machine_hw_i2c_obj_t * self = MP_OBJ_TO_PTR (self_in );
81
73
82
- i2c_cmd_handle_t cmd = i2c_cmd_link_create ();
83
- int data_len = 0 ;
84
-
85
- 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);
89
- data_len += bufs -> len ;
90
- -- n ;
91
- ++ bufs ;
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 );
77
+ if (err != ESP_OK ) {
78
+ return - MP_ENODEV ; // No device at address, return immediately
92
79
}
93
80
94
- i2c_master_start (cmd );
95
- i2c_master_write_byte (cmd , addr << 1 | (flags & MP_MACHINE_I2C_FLAG_READ ), true);
96
-
97
- for (; n -- ; ++ bufs ) {
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 ;
94
+ }
95
+ // 3. Transfer data
98
96
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 );
97
+ err = i2c_master_receive ( dev_handle , buf , len , self -> timeout_us / 1000 );
100
98
} else {
101
- if (bufs -> len != 0 ) {
102
- i2c_master_write (cmd , bufs -> buf , bufs -> len , true);
103
- }
99
+ err = i2c_master_transmit (dev_handle , buf , len , self -> timeout_us / 1000 );
100
+ }
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 ;
106
+ }
107
+ if (err == ESP_ERR_TIMEOUT ) {
108
+ return - MP_ETIMEDOUT ;
109
+ }
110
+ if (err != ESP_OK ) {
111
+ return - abs (err );
104
112
}
105
- data_len += bufs -> len ;
106
- }
107
-
108
- if (flags & MP_MACHINE_I2C_FLAG_STOP ) {
109
- i2c_master_stop (cmd );
110
- }
111
-
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 );
115
-
116
- if (err == ESP_FAIL ) {
117
- return - MP_ENODEV ;
118
- } else if (err == ESP_ERR_TIMEOUT ) {
119
- return - MP_ETIMEDOUT ;
120
- } else if (err != ESP_OK ) {
121
- return - abs (err );
122
113
}
123
-
124
- return data_len ;
114
+ return len ;
125
115
}
126
116
127
- /******************************************************************************/
128
- // MicroPython bindings for machine API
129
-
130
117
static void machine_hw_i2c_print (const mp_print_t * print , mp_obj_t self_in , mp_print_kind_t kind ) {
131
118
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 ));
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 );
136
121
}
137
122
138
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 ) {
@@ -147,55 +132,49 @@ mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_
147
132
{ MP_QSTR_id , MP_ARG_INT , {.u_int = I2C_NUM_0 } },
148
133
{ MP_QSTR_scl , MP_ARG_KW_ONLY | MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
149
134
{ MP_QSTR_sda , MP_ARG_KW_ONLY | MP_ARG_OBJ , {.u_obj = MP_OBJ_NULL } },
150
- { MP_QSTR_freq , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 400000 } },
151
- { 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 } },
152
137
};
153
138
mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
154
139
mp_arg_parse_all_kw_array (n_args , n_kw , all_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
155
140
156
- // Get I2C bus
157
141
mp_int_t i2c_id = args [ARG_id ].u_int ;
158
-
159
- // Check if the I2C bus is valid
160
142
if (!(I2C_NUM_0 <= i2c_id && i2c_id < I2C_NUM_MAX )) {
161
143
mp_raise_msg_varg (& mp_type_ValueError , MP_ERROR_TEXT ("I2C(%d) doesn't exist" ), i2c_id );
162
144
}
163
145
164
- // Get static peripheral object
165
- machine_hw_i2c_obj_t * self = (machine_hw_i2c_obj_t * )& machine_hw_i2c_obj [i2c_id ];
146
+ machine_hw_i2c_obj_t * self = & machine_hw_i2c_obj [i2c_id ];
166
147
167
- bool first_init = false;
168
- if (self -> base .type == NULL ) {
169
- // Created for the first time, set default pins
148
+ bool first_init = (self -> base .type == NULL );
149
+ if (first_init ) {
170
150
self -> base .type = & machine_i2c_type ;
171
151
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;
152
+ self -> scl = (i2c_id == I2C_NUM_0 ) ? MICROPY_HW_I2C0_SCL : MICROPY_HW_I2C1_SCL ;
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 ;
180
156
}
181
157
182
- // Set SCL/SDA pins if given
183
158
if (args [ARG_scl ].u_obj != MP_OBJ_NULL ) {
184
159
self -> scl = machine_pin_get_id (args [ARG_scl ].u_obj );
185
160
}
186
161
if (args [ARG_sda ].u_obj != MP_OBJ_NULL ) {
187
162
self -> sda = machine_pin_get_id (args [ARG_sda ].u_obj );
188
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
+ }
189
170
190
- // Initialise the I2C peripheral
191
- machine_hw_i2c_init (self , args [ARG_freq ].u_int , args [ARG_timeout ].u_int , first_init );
192
-
171
+ machine_hw_i2c_init (self , first_init );
193
172
return MP_OBJ_FROM_PTR (self );
194
173
}
195
174
196
175
static const mp_machine_i2c_p_t machine_hw_i2c_p = {
197
- .transfer_supports_write1 = true ,
198
- .transfer = machine_hw_i2c_transfer ,
176
+ .transfer = mp_machine_i2c_transfer_adaptor ,
177
+ .transfer_single = machine_i2c_transfer_single ,
199
178
};
200
179
201
180
MP_DEFINE_CONST_OBJ_TYPE (
@@ -208,4 +187,4 @@ MP_DEFINE_CONST_OBJ_TYPE(
208
187
locals_dict , & mp_machine_i2c_locals_dict
209
188
);
210
189
211
- #endif
190
+ #endif // MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C
0 commit comments