Skip to content

Commit 26229ef

Browse files
committed
Add try_lock and unlock to I2C and SPI classes to make sure things
are shared well between threads and underlying MicroPython (SPI Flash for example.) It is recommended to use the bus device classes to manage the locks and other transaction state. https://github.com/adafruit/Adafruit_MicroPython_BusDevice Fixed adafruit#58 Fixed adafruit#59 Fixed adafruit#60
1 parent 03f49f8 commit 26229ef

23 files changed

Lines changed: 656 additions & 153 deletions

File tree

atmel-samd/common-hal/microcontroller/__init__.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,21 @@ void common_hal_mcu_delay_us(uint32_t delay) {
3232
mp_hal_delay_us(delay);
3333
}
3434

35+
// Interrupt flags that will be saved and restored during disable/Enable
36+
// interrupt functions below.
37+
static irqflags_t irq_flags;
38+
void common_hal_mcu_disable_interrupts(void) {
39+
// Disable all interrupt sources for timing critical sections.
40+
// Disable ASF-based interrupts.
41+
irq_flags = cpu_irq_save();
42+
}
43+
44+
void common_hal_mcu_enable_interrupts(void) {
45+
// Enable all interrupt sources after timing critical sections.
46+
// Restore ASF-based interrupts.
47+
cpu_irq_restore(irq_flags);
48+
}
49+
3550
// This maps MCU pin names to pin objects.
3651
STATIC const mp_map_elem_t mcu_pin_global_dict_table[] = {
3752
// Pins in datasheet order.

atmel-samd/common-hal/nativeio/I2C.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@
4040
#define TIMEOUT 1
4141

4242
void common_hal_nativeio_i2c_construct(nativeio_i2c_obj_t *self,
43-
const mcu_pin_obj_t* scl, const mcu_pin_obj_t* sda, uint32_t freq) {
43+
const mcu_pin_obj_t* scl, const mcu_pin_obj_t* sda, uint32_t frequency) {
4444
struct i2c_master_config config_i2c_master;
4545
i2c_master_get_config_defaults(&config_i2c_master);
4646
// Struct takes the argument in Khz not Hz.
47-
config_i2c_master.baud_rate = freq / 1000;
47+
config_i2c_master.baud_rate = frequency / 1000;
4848
Sercom* sercom = NULL;
4949
uint32_t sda_pinmux = 0;
5050
uint32_t scl_pinmux = 0;
@@ -106,6 +106,25 @@ bool common_hal_nativeio_i2c_probe(nativeio_i2c_obj_t *self, uint8_t addr) {
106106
return status == STATUS_OK;
107107
}
108108

109+
void common_hal_nativeio_i2c_configure(nativeio_i2c_obj_t *self,
110+
uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
111+
return;
112+
}
113+
114+
bool common_hal_nativeio_i2c_try_lock(nativeio_i2c_obj_t *self) {
115+
self->has_lock = i2c_master_lock(&self->i2c_master_instance) == STATUS_OK;
116+
return self->has_lock;
117+
}
118+
119+
bool common_hal_nativeio_i2c_has_lock(nativeio_i2c_obj_t *self) {
120+
return self->has_lock;
121+
}
122+
123+
void common_hal_nativeio_i2c_unlock(nativeio_i2c_obj_t *self) {
124+
self->has_lock = false;
125+
i2c_master_unlock(&self->i2c_master_instance);
126+
}
127+
109128
bool common_hal_nativeio_i2c_write(nativeio_i2c_obj_t *self, uint16_t addr,
110129
const uint8_t *data, size_t len, bool transmit_stop_bit) {
111130
struct i2c_master_packet packet = {

atmel-samd/common-hal/nativeio/SPI.c

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
void common_hal_nativeio_spi_construct(nativeio_spi_obj_t *self,
4141
const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi,
42-
const mcu_pin_obj_t * miso, uint32_t baudrate) {
42+
const mcu_pin_obj_t * miso) {
4343
struct spi_config config_spi_master;
4444
spi_get_config_defaults(&config_spi_master);
4545

@@ -67,9 +67,10 @@ void common_hal_nativeio_spi_construct(nativeio_spi_obj_t *self,
6767
mosi_pad = mosi->sercom[j].pad;
6868
if (miso_none) {
6969
sercom = potential_sercom;
70+
break;
7071
}
7172
} else {
72-
break;
73+
continue;
7374
}
7475
}
7576
if (!miso_none) {
@@ -130,8 +131,6 @@ void common_hal_nativeio_spi_construct(nativeio_spi_obj_t *self,
130131
*pinmuxes[miso_pad] = miso_pinmux;
131132
}
132133

133-
config_spi_master.mode_specific.master.baudrate = baudrate;
134-
135134
spi_init(&self->spi_master_instance, sercom, &config_spi_master);
136135

137136
spi_enable(&self->spi_master_instance);
@@ -141,8 +140,64 @@ void common_hal_nativeio_spi_deinit(nativeio_spi_obj_t *self) {
141140
spi_disable(&self->spi_master_instance);
142141
}
143142

143+
bool common_hal_nativeio_spi_configure(nativeio_spi_obj_t *self,
144+
uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
145+
// TODO(tannewt): Check baudrate first before changing it.
146+
enum status_code status = spi_set_baudrate(&self->spi_master_instance, baudrate);
147+
if (status != STATUS_OK) {
148+
return false;
149+
}
150+
151+
SercomSpi *const spi_module = &(self->spi_master_instance.hw->SPI);
152+
// If the settings are already what we want then don't reset them.
153+
if (spi_module->CTRLA.bit.CPHA == phase &&
154+
spi_module->CTRLA.bit.CPOL == polarity &&
155+
spi_module->CTRLB.bit.CHSIZE == (bits - 8)) {
156+
return true;
157+
}
158+
159+
spi_disable(&self->spi_master_instance);
160+
while (spi_is_syncing(&self->spi_master_instance)) {
161+
/* Wait until the synchronization is complete */
162+
}
163+
164+
spi_module->CTRLA.bit.CPHA = phase;
165+
spi_module->CTRLA.bit.CPOL = polarity;
166+
spi_module->CTRLB.bit.CHSIZE = bits - 8;
167+
168+
while (spi_is_syncing(&self->spi_master_instance)) {
169+
/* Wait until the synchronization is complete */
170+
}
171+
172+
/* Enable the module */
173+
spi_enable(&self->spi_master_instance);
174+
175+
while (spi_is_syncing(&self->spi_master_instance)) {
176+
/* Wait until the synchronization is complete */
177+
}
178+
179+
return true;
180+
}
181+
182+
bool common_hal_nativeio_spi_try_lock(nativeio_spi_obj_t *self) {
183+
self->has_lock = spi_lock(&self->spi_master_instance) == STATUS_OK;
184+
return self->has_lock;
185+
}
186+
187+
bool common_hal_nativeio_spi_has_lock(nativeio_spi_obj_t *self) {
188+
return self->has_lock;
189+
}
190+
191+
void common_hal_nativeio_spi_unlock(nativeio_spi_obj_t *self) {
192+
self->has_lock = false;
193+
spi_unlock(&self->spi_master_instance);
194+
}
195+
144196
bool common_hal_nativeio_spi_write(nativeio_spi_obj_t *self,
145197
const uint8_t *data, size_t len) {
198+
if (len == 0) {
199+
return true;
200+
}
146201
enum status_code status = spi_write_buffer_wait(
147202
&self->spi_master_instance,
148203
data,
@@ -152,6 +207,9 @@ bool common_hal_nativeio_spi_write(nativeio_spi_obj_t *self,
152207

153208
bool common_hal_nativeio_spi_read(nativeio_spi_obj_t *self,
154209
uint8_t *data, size_t len) {
210+
if (len == 0) {
211+
return true;
212+
}
155213
enum status_code status = spi_read_buffer_wait(
156214
&self->spi_master_instance,
157215
data,

atmel-samd/common-hal/nativeio/types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,13 @@ typedef struct {
6868
typedef struct {
6969
mp_obj_base_t base;
7070
struct i2c_master_module i2c_master_instance;
71+
bool has_lock;
7172
} nativeio_i2c_obj_t;
7273

7374
typedef struct _machine_spi_obj_t {
7475
mp_obj_base_t base;
7576
struct spi_module spi_master_instance;
77+
bool has_lock;
7678
} nativeio_spi_obj_t;
7779

7880
typedef struct {

esp8266/Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
include ../py/mkenv.mk
2-
31
# Select the board to build for: if not given on the command line,
42
# then default to PYBV10.
53
BOARD ?= feather_huzzah
@@ -8,7 +6,9 @@ $(error Invalid BOARD specified)
86
endif
97

108
# If the build directory is not given, make it reflect the board name.
11-
BUILD ?= build-$(BOARD)
9+
BUILD ?= build
10+
11+
include ../py/mkenv.mk
1212

1313
# qstr definitions (must come before including py.mk)
1414
QSTR_DEFS = qstrdefsport.h #$(BUILD)/pins_qstr.h

esp8266/common-hal/microcontroller/__init__.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,29 @@
2828
#include "shared-bindings/microcontroller/Pin.h"
2929

3030
#include "eagle_soc.h"
31-
#include "osapi.h"
31+
#include "ets_alt_task.h"
3232
#include "etshal.h"
33+
#include "osapi.h"
34+
#include "xtirq.h"
35+
36+
#define ETS_LOOP_ITER_BIT (12)
3337

3438
void common_hal_mcu_delay_us(uint32_t delay) {
3539
os_delay_us(delay);
3640
}
3741

42+
static uint16_t saved_interrupt_state;
43+
void common_hal_mcu_disable_interrupts() {
44+
saved_interrupt_state = disable_irq();
45+
saved_interrupt_state = (saved_interrupt_state & ~(1 << ETS_LOOP_ITER_BIT)) | (ets_loop_iter_disable << ETS_LOOP_ITER_BIT);
46+
ets_loop_iter_disable = 1;
47+
}
48+
49+
void common_hal_mcu_enable_interrupts() {
50+
ets_loop_iter_disable = (saved_interrupt_state >> ETS_LOOP_ITER_BIT) & 1;
51+
enable_irq(saved_interrupt_state & ~(1 << ETS_LOOP_ITER_BIT));
52+
}
53+
3854
// This macro is used to simplify pin definition in boards/<board>/pins.c
3955
#define PIN(p_name, p_gpio_number, p_gpio_function, p_peripheral) \
4056
const mcu_pin_obj_t pin_## p_name = { \

esp8266/common-hal/nativeio/DigitalInOut.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ void common_hal_nativeio_digitalinout_switch_to_output(
7171
WRITE_PERI_REG(RTC_GPIO_CONF, READ_PERI_REG(RTC_GPIO_CONF) & ~1);
7272
WRITE_PERI_REG(RTC_GPIO_ENABLE, (READ_PERI_REG(RTC_GPIO_ENABLE) & ~1) | 1); // output
7373
} else if (!self->open_drain) {
74-
gpio_output_set(0, 0, 1 << self->pin->gpio_function, 0);
74+
gpio_output_set(0, 0, 1 << self->pin->gpio_number, 0);
7575
PIN_PULLUP_DIS(self->pin->peripheral);
7676
}
7777
common_hal_nativeio_digitalinout_set_value(self, value);
@@ -88,15 +88,13 @@ void common_hal_nativeio_digitalinout_set_value(
8888
if (self->open_drain) {
8989
// Disable output.
9090
gpio_output_set(0, 0, 0, 1 << self->pin->gpio_number);
91-
PIN_PULLUP_EN(self->pin->peripheral);
9291
} else {
9392
// Set high
9493
gpio_output_set(1 << self->pin->gpio_number, 0, 0, 0);
9594
}
9695
} else {
9796
if (self->open_drain) {
9897
// Enable the output
99-
PIN_PULLUP_DIS(self->pin->peripheral);
10098
gpio_output_set(0, 0, 1 << self->pin->gpio_number, 0);
10199
}
102100
// Set low

esp8266/common-hal/nativeio/I2C.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ bool common_hal_nativeio_i2c_probe(nativeio_i2c_obj_t *self, uint8_t addr) {
4040
return false;
4141
}
4242

43+
bool common_hal_nativeio_i2c_try_lock(nativeio_i2c_obj_t *self) {
44+
return false;
45+
}
46+
47+
bool common_hal_nativeio_i2c_has_lock(nativeio_i2c_obj_t *self) {
48+
return false;
49+
}
50+
51+
void common_hal_nativeio_i2c_unlock(nativeio_i2c_obj_t *self) {
52+
}
53+
4354
bool common_hal_nativeio_i2c_write(nativeio_i2c_obj_t *self, uint16_t addr,
4455
const uint8_t * data, size_t len, bool transmit_stop_bit) {
4556
return false;

esp8266/common-hal/nativeio/SPI.c

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include "esp8266/ets_alt_task.h"
2828
#include "esp8266/hspi.h"
29+
#include "shared-bindings/microcontroller/__init__.h"
2930
#include "shared-bindings/nativeio/SPI.h"
3031
#include "py/nlr.h"
3132

@@ -37,11 +38,12 @@ extern const mcu_pin_obj_t pin_MTDI;
3738

3839
void common_hal_nativeio_spi_construct(nativeio_spi_obj_t *self,
3940
const mcu_pin_obj_t * clock, const mcu_pin_obj_t * mosi,
40-
const mcu_pin_obj_t * miso, uint32_t baudrate) {
41+
const mcu_pin_obj_t * miso) {
4142
if (clock != &pin_MTMS || mosi != &pin_MTCK || miso != &pin_MTDI) {
4243
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
4344
"Pins not valid for SPI"));
4445
}
46+
spi_init(HSPI);
4547
}
4648

4749
void common_hal_nativeio_spi_deinit(nativeio_spi_obj_t *self) {
@@ -50,6 +52,50 @@ void common_hal_nativeio_spi_deinit(nativeio_spi_obj_t *self) {
5052
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 0);
5153
}
5254

55+
bool common_hal_nativeio_spi_configure(nativeio_spi_obj_t *self,
56+
uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
57+
if (bits != 8) {
58+
return false;
59+
}
60+
if (baudrate == 80000000L) {
61+
// Special case for full speed.
62+
spi_init_gpio(HSPI, SPI_CLK_80MHZ_NODIV);
63+
spi_clock(HSPI, 0, 0);
64+
} else if (baudrate > 40000000L) {
65+
return false;
66+
} else {
67+
uint32_t divider = 40000000L / baudrate;
68+
uint16_t prediv = MIN(divider, SPI_CLKDIV_PRE + 1);
69+
uint16_t cntdiv = (divider / prediv) * 2; // cntdiv has to be even
70+
if (cntdiv > SPI_CLKCNT_N + 1 || cntdiv == 0 || prediv == 0) {
71+
return false;
72+
}
73+
spi_init_gpio(HSPI, SPI_CLK_USE_DIV);
74+
spi_clock(HSPI, prediv, cntdiv);
75+
}
76+
spi_mode(HSPI, phase, polarity);
77+
return true;
78+
}
79+
80+
bool common_hal_nativeio_spi_try_lock(nativeio_spi_obj_t *self) {
81+
bool success = false;
82+
common_hal_mcu_disable_interrupts();
83+
if (!self->locked) {
84+
self->locked = true;
85+
success = true;
86+
}
87+
common_hal_mcu_enable_interrupts();
88+
return success;
89+
}
90+
91+
bool common_hal_nativeio_spi_has_lock(nativeio_spi_obj_t *self) {
92+
return self->locked;
93+
}
94+
95+
void common_hal_nativeio_spi_unlock(nativeio_spi_obj_t *self) {
96+
self->locked = false;
97+
}
98+
5399
bool common_hal_nativeio_spi_write(nativeio_spi_obj_t *self,
54100
const uint8_t * data, size_t len) {
55101
size_t chunk_size = 1024;

esp8266/common-hal/nativeio/types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ typedef struct {
5858

5959
typedef struct {
6060
mp_obj_base_t base;
61+
bool locked;
6162
} nativeio_spi_obj_t;
6263

6364
typedef struct {

0 commit comments

Comments
 (0)