Skip to content

Commit d083712

Browse files
committed
extmod: Add generic machine.I2C class, with bit-bang I2C.
Should work on any machine that provides the correct pin functions.
1 parent 53ad681 commit d083712

5 files changed

Lines changed: 416 additions & 0 deletions

File tree

extmod/machine_i2c.c

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2016 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <stdio.h>
28+
#include <stdint.h>
29+
#include <string.h>
30+
31+
#include "py/mphal.h"
32+
#include "py/runtime.h"
33+
#include "extmod/machine_i2c.h"
34+
35+
#if MICROPY_PY_MACHINE_I2C
36+
37+
typedef struct _machine_i2c_obj_t {
38+
mp_obj_base_t base;
39+
uint32_t us_delay;
40+
mp_hal_pin_obj_t *scl;
41+
mp_hal_pin_obj_t *sda;
42+
} machine_i2c_obj_t;
43+
44+
STATIC void mp_hal_i2c_delay(machine_i2c_obj_t *self) {
45+
// We need to use an accurate delay to get acceptable I2C
46+
// speeds (eg 1us should be not much more than 1us).
47+
mp_hal_delay_us_fast(self->us_delay);
48+
}
49+
50+
STATIC void mp_hal_i2c_scl_low(machine_i2c_obj_t *self) {
51+
mp_hal_pin_low(self->scl);
52+
}
53+
54+
STATIC void mp_hal_i2c_scl_release(machine_i2c_obj_t *self) {
55+
mp_hal_pin_od_high(self->scl);
56+
}
57+
58+
STATIC void mp_hal_i2c_sda_low(machine_i2c_obj_t *self) {
59+
mp_hal_pin_low(self->sda);
60+
}
61+
62+
STATIC void mp_hal_i2c_sda_release(machine_i2c_obj_t *self) {
63+
mp_hal_pin_od_high(self->sda);
64+
}
65+
66+
STATIC int mp_hal_i2c_sda_read(machine_i2c_obj_t *self) {
67+
return mp_hal_pin_read(self->sda);
68+
}
69+
70+
STATIC void mp_hal_i2c_start(machine_i2c_obj_t *self) {
71+
mp_hal_i2c_sda_release(self);
72+
mp_hal_i2c_delay(self);
73+
mp_hal_i2c_scl_release(self);
74+
mp_hal_i2c_delay(self);
75+
mp_hal_i2c_sda_low(self);
76+
mp_hal_i2c_delay(self);
77+
}
78+
79+
STATIC void mp_hal_i2c_stop(machine_i2c_obj_t *self) {
80+
mp_hal_i2c_delay(self);
81+
mp_hal_i2c_sda_low(self);
82+
mp_hal_i2c_delay(self);
83+
mp_hal_i2c_scl_release(self);
84+
mp_hal_i2c_delay(self);
85+
mp_hal_i2c_sda_release(self);
86+
mp_hal_i2c_delay(self);
87+
}
88+
89+
STATIC void mp_hal_i2c_init(machine_i2c_obj_t *self, uint32_t freq) {
90+
self->us_delay = 500000 / freq;
91+
if (self->us_delay == 0) {
92+
self->us_delay = 1;
93+
}
94+
mp_hal_pin_config_od(self->scl);
95+
mp_hal_pin_config_od(self->sda);
96+
mp_hal_i2c_stop(self);
97+
}
98+
99+
STATIC int mp_hal_i2c_write_byte(machine_i2c_obj_t *self, uint8_t val) {
100+
mp_hal_i2c_delay(self);
101+
mp_hal_i2c_scl_low(self);
102+
103+
for (int i = 7; i >= 0; i--) {
104+
if ((val >> i) & 1) {
105+
mp_hal_i2c_sda_release(self);
106+
} else {
107+
mp_hal_i2c_sda_low(self);
108+
}
109+
mp_hal_i2c_delay(self);
110+
mp_hal_i2c_scl_release(self);
111+
mp_hal_i2c_delay(self);
112+
mp_hal_i2c_scl_low(self);
113+
}
114+
115+
mp_hal_i2c_sda_release(self);
116+
mp_hal_i2c_delay(self);
117+
mp_hal_i2c_scl_release(self);
118+
mp_hal_i2c_delay(self);
119+
120+
int ret = mp_hal_i2c_sda_read(self);
121+
mp_hal_i2c_delay(self);
122+
mp_hal_i2c_scl_low(self);
123+
124+
return !ret;
125+
}
126+
127+
STATIC void mp_hal_i2c_write(machine_i2c_obj_t *self, uint8_t addr, uint8_t *data, size_t len) {
128+
mp_hal_i2c_start(self);
129+
if (!mp_hal_i2c_write_byte(self, addr << 1)) {
130+
goto er;
131+
}
132+
while (len--) {
133+
if (!mp_hal_i2c_write_byte(self, *data++)) {
134+
goto er;
135+
}
136+
}
137+
return;
138+
139+
er:
140+
mp_hal_i2c_stop(self);
141+
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
142+
}
143+
144+
STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val) {
145+
mp_hal_i2c_delay(self);
146+
mp_hal_i2c_scl_low(self);
147+
mp_hal_i2c_delay(self);
148+
149+
uint8_t data = 0;
150+
for (int i = 7; i >= 0; i--) {
151+
mp_hal_i2c_scl_release(self);
152+
mp_hal_i2c_delay(self);
153+
data = (data << 1) | mp_hal_i2c_sda_read(self);
154+
mp_hal_i2c_scl_low(self);
155+
mp_hal_i2c_delay(self);
156+
}
157+
*val = data;
158+
159+
mp_hal_i2c_scl_release(self);
160+
mp_hal_i2c_delay(self);
161+
mp_hal_i2c_scl_low(self);
162+
mp_hal_i2c_delay(self);
163+
164+
return 1; // success
165+
}
166+
167+
STATIC void mp_hal_i2c_read(machine_i2c_obj_t *self, uint8_t addr, uint8_t *data, size_t len) {
168+
mp_hal_i2c_start(self);
169+
if (!mp_hal_i2c_write_byte(self, (addr << 1) | 1)) {
170+
goto er;
171+
}
172+
while (len--) {
173+
if (!mp_hal_i2c_read_byte(self, data++)) {
174+
goto er;
175+
}
176+
}
177+
return;
178+
179+
er:
180+
mp_hal_i2c_stop(self);
181+
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
182+
}
183+
184+
/******************************************************************************/
185+
// MicroPython bindings for I2C
186+
187+
STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
188+
enum { ARG_scl, ARG_sda, ARG_freq };
189+
static const mp_arg_t allowed_args[] = {
190+
{ MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ },
191+
{ MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ },
192+
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} },
193+
};
194+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
195+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
196+
self->scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj);
197+
self->sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj);
198+
mp_hal_i2c_init(self, args[ARG_freq].u_int);
199+
}
200+
201+
STATIC mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
202+
mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true);
203+
machine_i2c_obj_t *self = m_new_obj(machine_i2c_obj_t);
204+
self->base.type = &machine_i2c_type;
205+
mp_map_t kw_args;
206+
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
207+
machine_i2c_obj_init_helper(self, n_args, args, &kw_args);
208+
return (mp_obj_t)self;
209+
}
210+
211+
STATIC mp_obj_t machine_i2c_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
212+
machine_i2c_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
213+
return mp_const_none;
214+
}
215+
MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_init_obj, 1, machine_i2c_obj_init);
216+
217+
STATIC mp_obj_t machine_i2c_scan(mp_obj_t self_in) {
218+
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
219+
mp_obj_t list = mp_obj_new_list(0, NULL);
220+
// 7-bit addresses 0b0000xxx and 0b1111xxx are reserved
221+
for (int addr = 0x08; addr < 0x78; ++addr) {
222+
mp_hal_i2c_start(self);
223+
int ack = mp_hal_i2c_write_byte(self, (addr << 1) | 1);
224+
if (ack) {
225+
mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr));
226+
}
227+
mp_hal_i2c_stop(self);
228+
}
229+
return list;
230+
}
231+
MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_scan_obj, machine_i2c_scan);
232+
233+
STATIC mp_obj_t machine_i2c_start(mp_obj_t self_in) {
234+
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
235+
mp_hal_i2c_start(self);
236+
return mp_const_none;
237+
}
238+
MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_start_obj, machine_i2c_start);
239+
240+
STATIC mp_obj_t machine_i2c_stop(mp_obj_t self_in) {
241+
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
242+
mp_hal_i2c_stop(self);
243+
return mp_const_none;
244+
}
245+
MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_stop_obj, machine_i2c_stop);
246+
247+
STATIC mp_obj_t machine_i2c_readinto(mp_obj_t self_in, mp_obj_t buf_in) {
248+
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
249+
250+
// get the buffer to read into
251+
mp_buffer_info_t bufinfo;
252+
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
253+
254+
// do the read
255+
uint8_t *dest = bufinfo.buf;
256+
while (bufinfo.len--) {
257+
if (!mp_hal_i2c_read_byte(self, dest++)) {
258+
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
259+
}
260+
}
261+
262+
return mp_const_none;
263+
}
264+
MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_readinto_obj, machine_i2c_readinto);
265+
266+
STATIC mp_obj_t machine_i2c_write(mp_obj_t self_in, mp_obj_t buf_in) {
267+
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
268+
269+
// get the buffer to write from
270+
mp_buffer_info_t bufinfo;
271+
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
272+
273+
// do the write
274+
uint8_t *src = bufinfo.buf;
275+
while (bufinfo.len--) {
276+
if (!mp_hal_i2c_write_byte(self, *src++)) {
277+
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
278+
}
279+
}
280+
281+
return mp_const_none;
282+
}
283+
MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_write_obj, machine_i2c_write);
284+
285+
STATIC mp_obj_t machine_i2c_readfrom(mp_obj_t self_in, mp_obj_t addr_in, mp_obj_t nbytes_in) {
286+
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
287+
vstr_t vstr;
288+
vstr_init_len(&vstr, mp_obj_get_int(nbytes_in));
289+
mp_hal_i2c_read(self, mp_obj_get_int(addr_in), (uint8_t*)vstr.buf, vstr.len);
290+
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
291+
}
292+
MP_DEFINE_CONST_FUN_OBJ_3(machine_i2c_readfrom_obj, machine_i2c_readfrom);
293+
294+
STATIC mp_obj_t machine_i2c_readfrom_into(mp_obj_t self_in, mp_obj_t addr_in, mp_obj_t buf_in) {
295+
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
296+
mp_buffer_info_t bufinfo;
297+
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
298+
mp_hal_i2c_read(self, mp_obj_get_int(addr_in), (uint8_t*)bufinfo.buf, bufinfo.len);
299+
return mp_const_none;
300+
}
301+
MP_DEFINE_CONST_FUN_OBJ_3(machine_i2c_readfrom_into_obj, machine_i2c_readfrom_into);
302+
303+
STATIC mp_obj_t machine_i2c_writeto(mp_obj_t self_in, mp_obj_t addr_in, mp_obj_t buf_in) {
304+
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
305+
mp_buffer_info_t bufinfo;
306+
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
307+
mp_hal_i2c_write(self, mp_obj_get_int(addr_in), bufinfo.buf, bufinfo.len);
308+
return mp_const_none;
309+
}
310+
STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_i2c_writeto_obj, machine_i2c_writeto);
311+
312+
STATIC mp_obj_t machine_i2c_readfrom_mem(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
313+
mp_not_implemented("I2C.readfrom_mem");
314+
}
315+
MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_obj, 1, machine_i2c_readfrom_mem);
316+
317+
STATIC mp_obj_t machine_i2c_readfrom_mem_into(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
318+
mp_not_implemented("I2C.readfrom_mem_into");
319+
}
320+
MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_into_obj, 1, machine_i2c_readfrom_mem_into);
321+
322+
STATIC mp_obj_t machine_i2c_writeto_mem(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
323+
mp_not_implemented("I2C.writeto_mem");
324+
}
325+
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_writeto_mem_obj, 1, machine_i2c_writeto_mem);
326+
327+
STATIC const mp_rom_map_elem_t machine_i2c_locals_dict_table[] = {
328+
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_i2c_init_obj) },
329+
{ MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&machine_i2c_scan_obj) },
330+
331+
// primitive I2C operations
332+
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&machine_i2c_start_obj) },
333+
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_i2c_stop_obj) },
334+
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&machine_i2c_readinto_obj) },
335+
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_i2c_write_obj) },
336+
337+
// standard bus operations
338+
{ MP_ROM_QSTR(MP_QSTR_readfrom), MP_ROM_PTR(&machine_i2c_readfrom_obj) },
339+
{ MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&machine_i2c_readfrom_into_obj) },
340+
{ MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&machine_i2c_writeto_obj) },
341+
342+
// memory operations
343+
{ MP_ROM_QSTR(MP_QSTR_readfrom_mem), MP_ROM_PTR(&machine_i2c_readfrom_mem_obj) },
344+
{ MP_ROM_QSTR(MP_QSTR_readfrom_mem_into), MP_ROM_PTR(&machine_i2c_readfrom_mem_into_obj) },
345+
{ MP_ROM_QSTR(MP_QSTR_writeto_mem), MP_ROM_PTR(&machine_i2c_writeto_mem_obj) },
346+
};
347+
348+
STATIC MP_DEFINE_CONST_DICT(machine_i2c_locals_dict, machine_i2c_locals_dict_table);
349+
350+
const mp_obj_type_t machine_i2c_type = {
351+
{ &mp_type_type },
352+
.name = MP_QSTR_I2C,
353+
.make_new = machine_i2c_make_new,
354+
.locals_dict = (mp_obj_dict_t*)&machine_i2c_locals_dict,
355+
};
356+
357+
#endif // MICROPY_PY_MACHINE_I2C

extmod/machine_i2c.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2016 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#ifndef __MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H__
28+
#define __MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H__
29+
30+
#include "py/obj.h"
31+
32+
extern const mp_obj_type_t machine_i2c_type;
33+
34+
#endif // __MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H__

0 commit comments

Comments
 (0)