Skip to content

Commit 4a4d84b

Browse files
authored
Merge pull request #1064 from notro/i2cslave
Add busio.I2CSlave
2 parents 5dd420f + 11cbeb8 commit 4a4d84b

File tree

18 files changed

+985
-21
lines changed

18 files changed

+985
-21
lines changed

lib/utils/interrupt_char.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,9 @@ void mp_keyboard_interrupt(void) {
4747
#endif
4848
}
4949

50+
// Check to see if we've been CTRL-C'ed by autoreload or the user.
51+
bool mp_hal_is_interrupted(void) {
52+
return MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception));
53+
}
54+
5055
#endif

lib/utils/interrupt_char.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@
2626
#ifndef MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H
2727
#define MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H
2828

29+
#include <stdbool.h>
30+
2931
extern int mp_interrupt_char;
3032
void mp_hal_set_interrupt_char(int c);
3133
void mp_keyboard_interrupt(void);
34+
bool mp_hal_is_interrupted(void);
3235

3336
#endif // MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H

ports/atmel-samd/Makefile

100755100644
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ SRC_COMMON_HAL = \
308308
busio/UART.c \
309309
digitalio/__init__.c \
310310
digitalio/DigitalInOut.c \
311+
i2cslave/__init__.c \
312+
i2cslave/I2CSlave.c \
311313
microcontroller/__init__.c \
312314
microcontroller/Pin.c \
313315
microcontroller/Processor.c \

ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,5 @@
6262
// USB is always used internally so skip the pin objects for it.
6363
#define IGNORE_PIN_PA24 1
6464
#define IGNORE_PIN_PA25 1
65+
66+
#define CIRCUITPY_I2CSLAVE

ports/atmel-samd/boards/feather_m4_express/mpconfigboard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@
4747
// USB is always used internally so skip the pin objects for it.
4848
#define IGNORE_PIN_PA24 1
4949
#define IGNORE_PIN_PA25 1
50+
51+
#define CIRCUITPY_I2CSLAVE

ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,5 @@
6363
// USB is always used internally so skip the pin objects for it.
6464
#define IGNORE_PIN_PA24 1
6565
#define IGNORE_PIN_PA25 1
66+
67+
#define CIRCUITPY_I2CSLAVE

ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@
4848
// USB is always used internally so skip the pin objects for it.
4949
#define IGNORE_PIN_PA24 1
5050
#define IGNORE_PIN_PA25 1
51+
52+
#define CIRCUITPY_I2CSLAVE

ports/atmel-samd/common-hal/busio/I2C.c

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,39 +39,41 @@
3939
// Number of times to try to send packet if failed.
4040
#define ATTEMPTS 2
4141

42-
void common_hal_busio_i2c_construct(busio_i2c_obj_t *self,
43-
const mcu_pin_obj_t* scl, const mcu_pin_obj_t* sda, uint32_t frequency, uint32_t timeout) {
44-
#ifdef PIRKEY_M0
45-
mp_raise_NotImplementedError(translate("Not enough pins available"));
46-
return;
47-
#endif
48-
Sercom* sercom = NULL;
49-
uint8_t sercom_index;
50-
uint32_t sda_pinmux = 0;
51-
uint32_t scl_pinmux = 0;
42+
Sercom *samd_i2c_get_sercom(const mcu_pin_obj_t* scl, const mcu_pin_obj_t* sda,
43+
uint8_t *sercom_index, uint32_t *sda_pinmux, uint32_t *scl_pinmux) {
44+
*sda_pinmux = 0;
45+
*scl_pinmux = 0;
5246
for (int i = 0; i < NUM_SERCOMS_PER_PIN; i++) {
53-
sercom_index = sda->sercom[i].index;
54-
if (sercom_index >= SERCOM_INST_NUM) {
47+
*sercom_index = sda->sercom[i].index;
48+
if (*sercom_index >= SERCOM_INST_NUM) {
5549
continue;
5650
}
57-
Sercom* potential_sercom = sercom_insts[sercom_index];
51+
Sercom* potential_sercom = sercom_insts[*sercom_index];
5852
if (potential_sercom->I2CM.CTRLA.bit.ENABLE != 0 ||
5953
sda->sercom[i].pad != 0) {
6054
continue;
6155
}
62-
sda_pinmux = PINMUX(sda->number, (i == 0) ? MUX_C : MUX_D);
56+
*sda_pinmux = PINMUX(sda->number, (i == 0) ? MUX_C : MUX_D);
6357
for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) {
64-
if (sercom_index == scl->sercom[j].index &&
58+
if (*sercom_index == scl->sercom[j].index &&
6559
scl->sercom[j].pad == 1) {
66-
scl_pinmux = PINMUX(scl->number, (j == 0) ? MUX_C : MUX_D);
67-
sercom = potential_sercom;
68-
break;
60+
*scl_pinmux = PINMUX(scl->number, (j == 0) ? MUX_C : MUX_D);
61+
return potential_sercom;
6962
}
7063
}
71-
if (sercom != NULL) {
72-
break;
73-
}
7464
}
65+
return NULL;
66+
}
67+
68+
void common_hal_busio_i2c_construct(busio_i2c_obj_t *self,
69+
const mcu_pin_obj_t* scl, const mcu_pin_obj_t* sda, uint32_t frequency, uint32_t timeout) {
70+
#ifdef PIRKEY_M0
71+
mp_raise_NotImplementedError(translate("Not enough pins available"));
72+
return;
73+
#endif
74+
uint8_t sercom_index;
75+
uint32_t sda_pinmux, scl_pinmux;
76+
Sercom* sercom = samd_i2c_get_sercom(scl, sda, &sercom_index, &sda_pinmux, &scl_pinmux);
7577
if (sercom == NULL) {
7678
mp_raise_ValueError(translate("Invalid pins"));
7779
}

ports/atmel-samd/common-hal/busio/I2C.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,7 @@ typedef struct {
4141
uint8_t sda_pin;
4242
} busio_i2c_obj_t;
4343

44+
extern Sercom *samd_i2c_get_sercom(const mcu_pin_obj_t* scl, const mcu_pin_obj_t* sda,
45+
uint8_t *sercom_index, uint32_t *sda_pinmux, uint32_t *scl_pinmux);
46+
4447
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_I2C_H
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2018 Noralf Trønnes
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 "shared-bindings/i2cslave/I2CSlave.h"
28+
#include "common-hal/busio/I2C.h"
29+
30+
#include "lib/utils/interrupt_char.h"
31+
#include "py/mperrno.h"
32+
#include "py/mphal.h"
33+
#include "py/runtime.h"
34+
35+
#include "hal/include/hal_gpio.h"
36+
#include "peripherals/samd/sercom.h"
37+
38+
void common_hal_i2cslave_i2c_slave_construct(i2cslave_i2c_slave_obj_t *self,
39+
const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda,
40+
uint8_t *addresses, unsigned int num_addresses, bool smbus) {
41+
uint8_t sercom_index;
42+
uint32_t sda_pinmux, scl_pinmux;
43+
Sercom *sercom = samd_i2c_get_sercom(scl, sda, &sercom_index, &sda_pinmux, &scl_pinmux);
44+
if (sercom == NULL) {
45+
mp_raise_ValueError("Invalid pins");
46+
}
47+
self->sercom = sercom;
48+
49+
gpio_set_pin_function(sda->number, GPIO_PIN_FUNCTION_OFF);
50+
gpio_set_pin_function(scl->number, GPIO_PIN_FUNCTION_OFF);
51+
gpio_set_pin_function(sda->number, sda_pinmux);
52+
gpio_set_pin_function(scl->number, scl_pinmux);
53+
54+
self->sda_pin = sda->number;
55+
self->scl_pin = scl->number;
56+
claim_pin(sda);
57+
claim_pin(scl);
58+
59+
samd_peripherals_sercom_clock_init(sercom, sercom_index);
60+
61+
sercom->I2CS.CTRLA.bit.SWRST = 1;
62+
while (sercom->I2CS.CTRLA.bit.SWRST || sercom->I2CS.SYNCBUSY.bit.SWRST) {}
63+
64+
sercom->I2CS.CTRLB.bit.AACKEN = 0; // Automatic acknowledge is disabled.
65+
66+
if (num_addresses == 1) {
67+
sercom->I2CS.CTRLB.bit.AMODE = 0x0; // MASK
68+
sercom->I2CS.ADDR.bit.ADDR = addresses[0];
69+
sercom->I2CS.ADDR.bit.ADDRMASK = 0x00; // Match exact address
70+
} else if (num_addresses == 2) {
71+
sercom->I2CS.CTRLB.bit.AMODE = 0x1; // 2_ADDRS
72+
sercom->I2CS.ADDR.bit.ADDR = addresses[0];
73+
sercom->I2CS.ADDR.bit.ADDRMASK = addresses[1];
74+
} else {
75+
uint32_t combined = 0; // all addresses OR'ed
76+
uint32_t differ = 0; // bits that differ between addresses
77+
for (unsigned int i = 0; i < num_addresses; i++) {
78+
combined |= addresses[i];
79+
differ |= addresses[0] ^ addresses[i];
80+
}
81+
sercom->I2CS.CTRLB.bit.AMODE = 0x0; // MASK
82+
sercom->I2CS.ADDR.bit.ADDR = combined;
83+
sercom->I2CS.ADDR.bit.ADDRMASK = differ;
84+
}
85+
self->addresses = addresses;
86+
self->num_addresses = num_addresses;
87+
88+
if (smbus) {
89+
sercom->I2CS.CTRLA.bit.LOWTOUTEN = 1; // Errata 12003
90+
sercom->I2CS.CTRLA.bit.SEXTTOEN = 1; // Slave SCL Low Extend/Cumulative Time-Out 25ms
91+
}
92+
sercom->I2CS.CTRLA.bit.SCLSM = 0; // Clock stretch before ack
93+
sercom->I2CS.CTRLA.bit.MODE = 0x04; // Slave mode
94+
sercom->I2CS.CTRLA.bit.ENABLE = 1;
95+
}
96+
97+
bool common_hal_i2cslave_i2c_slave_deinited(i2cslave_i2c_slave_obj_t *self) {
98+
return self->sda_pin == NO_PIN;
99+
}
100+
101+
void common_hal_i2cslave_i2c_slave_deinit(i2cslave_i2c_slave_obj_t *self) {
102+
if (common_hal_i2cslave_i2c_slave_deinited(self)) {
103+
return;
104+
}
105+
106+
self->sercom->I2CS.CTRLA.bit.ENABLE = 0;
107+
108+
reset_pin(self->sda_pin);
109+
reset_pin(self->scl_pin);
110+
self->sda_pin = NO_PIN;
111+
self->scl_pin = NO_PIN;
112+
}
113+
114+
static int i2c_slave_check_error(i2cslave_i2c_slave_obj_t *self, bool raise) {
115+
if (!self->sercom->I2CS.INTFLAG.bit.ERROR) {
116+
return 0;
117+
}
118+
119+
int err = MP_EIO;
120+
121+
if (self->sercom->I2CS.STATUS.bit.LOWTOUT || self->sercom->I2CS.STATUS.bit.SEXTTOUT) {
122+
err = MP_ETIMEDOUT;
123+
}
124+
125+
self->sercom->I2CS.INTFLAG.reg = SERCOM_I2CS_INTFLAG_ERROR; // Clear flag
126+
127+
if (raise) {
128+
mp_raise_OSError(err);
129+
}
130+
return -err;
131+
}
132+
133+
int common_hal_i2cslave_i2c_slave_is_addressed(i2cslave_i2c_slave_obj_t *self, uint8_t *address, bool *is_read, bool *is_restart)
134+
{
135+
int err = i2c_slave_check_error(self, false);
136+
if (err) {
137+
return err;
138+
}
139+
140+
if (!self->sercom->I2CS.INTFLAG.bit.AMATCH) {
141+
return 0;
142+
}
143+
144+
self->writing = false;
145+
146+
*address = self->sercom->I2CS.DATA.reg >> 1;
147+
*is_read = self->sercom->I2CS.STATUS.bit.DIR;
148+
*is_restart = self->sercom->I2CS.STATUS.bit.SR;
149+
150+
for (unsigned int i = 0; i < self->num_addresses; i++) {
151+
if (*address == self->addresses[i]) {
152+
common_hal_i2cslave_i2c_slave_ack(self, true);
153+
return 1;
154+
}
155+
}
156+
157+
// This should clear AMATCH, but it doesn't...
158+
common_hal_i2cslave_i2c_slave_ack(self, false);
159+
return 0;
160+
}
161+
162+
int common_hal_i2cslave_i2c_slave_read_byte(i2cslave_i2c_slave_obj_t *self, uint8_t *data) {
163+
for (int t = 0; t < 100 && !self->sercom->I2CS.INTFLAG.reg; t++) {
164+
mp_hal_delay_us(10);
165+
}
166+
167+
i2c_slave_check_error(self, true);
168+
169+
if (!self->sercom->I2CS.INTFLAG.bit.DRDY ||
170+
self->sercom->I2CS.INTFLAG.bit.PREC ||
171+
self->sercom->I2CS.INTFLAG.bit.AMATCH) {
172+
return 0;
173+
}
174+
175+
*data = self->sercom->I2CS.DATA.reg;
176+
return 1;
177+
}
178+
179+
int common_hal_i2cslave_i2c_slave_write_byte(i2cslave_i2c_slave_obj_t *self, uint8_t data) {
180+
for (int t = 0; !self->sercom->I2CS.INTFLAG.reg && t < 100; t++) {
181+
mp_hal_delay_us(10);
182+
}
183+
184+
i2c_slave_check_error(self, true);
185+
186+
if (self->sercom->I2CS.INTFLAG.bit.PREC) {
187+
return 0;
188+
}
189+
190+
// RXNACK can carry over from the previous transfer
191+
if (self->writing && self->sercom->I2CS.STATUS.bit.RXNACK) {
192+
return 0;
193+
}
194+
195+
self->writing = true;
196+
197+
if (!self->sercom->I2CS.INTFLAG.bit.DRDY) {
198+
return 0;
199+
}
200+
201+
self->sercom->I2CS.DATA.bit.DATA = data; // Send data
202+
203+
return 1;
204+
}
205+
206+
void common_hal_i2cslave_i2c_slave_ack(i2cslave_i2c_slave_obj_t *self, bool ack) {
207+
self->sercom->I2CS.CTRLB.bit.ACKACT = !ack;
208+
self->sercom->I2CS.CTRLB.bit.CMD = 0x03;
209+
}
210+
211+
void common_hal_i2cslave_i2c_slave_close(i2cslave_i2c_slave_obj_t *self) {
212+
for (int t = 0; !self->sercom->I2CS.INTFLAG.reg && t < 100; t++) {
213+
mp_hal_delay_us(10);
214+
}
215+
216+
if (self->sercom->I2CS.INTFLAG.bit.AMATCH || !self->sercom->I2CS.STATUS.bit.CLKHOLD) {
217+
return;
218+
}
219+
220+
if (!self->sercom->I2CS.STATUS.bit.DIR) {
221+
common_hal_i2cslave_i2c_slave_ack(self, false);
222+
} else {
223+
int i = 0;
224+
while (self->sercom->I2CS.INTFLAG.reg == SERCOM_I2CS_INTFLAG_DRDY) {
225+
if (mp_hal_is_interrupted()) {
226+
return;
227+
}
228+
229+
self->sercom->I2CS.DATA.bit.DATA = 0xff; // Send dummy byte
230+
231+
// Wait for a result (if any).
232+
// test_byte_word.py::TestWord::test_write_seq leaves us with no INTFLAGs set in some of the tests
233+
for (int t = 0; !self->sercom->I2CS.INTFLAG.reg && t < 100; t++) {
234+
mp_hal_delay_us(10);
235+
}
236+
237+
if (++i > 1000) { // Avoid getting stuck "forever"
238+
mp_raise_OSError(MP_EIO);
239+
}
240+
}
241+
}
242+
243+
if (self->sercom->I2CS.INTFLAG.bit.AMATCH) {
244+
return;
245+
}
246+
247+
if (self->sercom->I2CS.STATUS.bit.CLKHOLD) {
248+
// Unable to release the clock.
249+
// The slave might have to be re-initialized to get unstuck.
250+
mp_raise_OSError(MP_EIO);
251+
}
252+
}

0 commit comments

Comments
 (0)