Skip to content

Commit 4007010

Browse files
authored
Merge pull request #3279 from hierophect/esp32-pulseinout
ESP32-S2: Add PulseOut and PulseIn
2 parents e47447b + dd425ee commit 4007010

File tree

18 files changed

+308
-57
lines changed

18 files changed

+308
-57
lines changed

locale/circuitpython.pot

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ msgid ""
88
msgstr ""
99
"Project-Id-Version: PACKAGE VERSION\n"
1010
"Report-Msgid-Bugs-To: \n"
11-
"POT-Creation-Date: 2020-08-14 09:36-0400\n"
11+
"POT-Creation-Date: 2020-08-18 11:19-0400\n"
1212
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414
"Language-Team: LANGUAGE <[email protected]>\n"
@@ -744,7 +744,7 @@ msgstr ""
744744

745745
#: shared-bindings/aesio/aes.c shared-bindings/busio/SPI.c
746746
#: shared-bindings/microcontroller/Pin.c
747-
#: shared-bindings/neopixel_write/__init__.c shared-bindings/pulseio/PulseOut.c
747+
#: shared-bindings/neopixel_write/__init__.c
748748
#: shared-bindings/terminalio/Terminal.c
749749
msgid "Expected a %q"
750750
msgstr ""
@@ -1328,6 +1328,15 @@ msgstr ""
13281328
msgid "Polygon needs at least 3 points"
13291329
msgstr ""
13301330

1331+
#: ports/atmel-samd/common-hal/pulseio/PulseOut.c
1332+
#: ports/cxd56/common-hal/pulseio/PulseOut.c
1333+
#: ports/nrf/common-hal/pulseio/PulseOut.c
1334+
#: ports/stm/common-hal/pulseio/PulseOut.c
1335+
msgid ""
1336+
"Port does not accept pins or frequency. "
1337+
"Construct and pass a PWMOut Carrier instead"
1338+
msgstr ""
1339+
13311340
#: shared-bindings/_bleio/Adapter.c
13321341
msgid "Prefix buffer must be on the heap"
13331342
msgstr ""
@@ -1340,6 +1349,10 @@ msgstr ""
13401349
msgid "Pull not used when direction is output."
13411350
msgstr ""
13421351

1352+
#: ports/stm/ref/pulseout-pre-timeralloc.c
1353+
msgid "PulseOut not supported on this chip"
1354+
msgstr ""
1355+
13431356
#: ports/stm/common-hal/os/__init__.c
13441357
msgid "RNG DeInit Error"
13451358
msgstr ""

ports/atmel-samd/common-hal/pulseio/PulseOut.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,15 @@ void pulseout_reset() {
9696
}
9797

9898
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
99-
const pulseio_pwmout_obj_t* carrier) {
99+
const pulseio_pwmout_obj_t* carrier,
100+
const mcu_pin_obj_t* pin,
101+
uint32_t frequency,
102+
uint16_t duty_cycle) {
103+
if (!carrier || pin || frequency) {
104+
mp_raise_NotImplementedError(translate("Port does not accept pins or frequency. \
105+
Construct and pass a PWMOut Carrier instead"));
106+
}
107+
100108
if (refcount == 0) {
101109
// Find a spare timer.
102110
Tc *tc = NULL;

ports/cxd56/common-hal/pulseio/PulseOut.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,16 @@ static bool pulseout_timer_handler(unsigned int *next_interval_us, void *arg)
5858
return true;
5959
}
6060

61-
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t *self,
62-
const pulseio_pwmout_obj_t *carrier) {
61+
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
62+
const pulseio_pwmout_obj_t* carrier,
63+
const mcu_pin_obj_t* pin,
64+
uint32_t frequency,
65+
uint16_t duty_cycle) {
66+
if (!carrier || pin || frequency) {
67+
mp_raise_NotImplementedError(translate("Port does not accept pins or frequency. \
68+
Construct and pass a PWMOut Carrier instead"));
69+
}
70+
6371
if (pulse_fd < 0) {
6472
pulse_fd = open("/dev/timer0", O_RDONLY);
6573
}

ports/esp32s2/background.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,17 @@
3535
#include "shared-module/displayio/__init__.h"
3636
#endif
3737

38+
#if CIRCUITPY_PULSEIO
39+
#include "common-hal/pulseio/PulseIn.h"
40+
#endif
41+
3842

3943
void port_background_task(void) {
4044
// Zero delay in case FreeRTOS wants to switch to something else.
4145
vTaskDelay(0);
46+
#if CIRCUITPY_PULSEIO
47+
pulsein_background();
48+
#endif
4249
}
4350

4451
void port_start_background_task(void) {}

ports/esp32s2/common-hal/neopixel_write/__init__.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout
9393
// Reserve channel
9494
uint8_t number = digitalinout->pin->number;
9595
rmt_channel_t channel = esp32s2_peripherals_find_and_reserve_rmt();
96+
if (channel == RMT_CHANNEL_MAX) {
97+
mp_raise_RuntimeError(translate("All timers in use"));
98+
}
9699

97100
// Configure Channel
98101
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(number, channel);

ports/esp32s2/common-hal/pulseio/PulseIn.c

Lines changed: 142 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,51 +25,184 @@
2525
*/
2626

2727
#include "common-hal/pulseio/PulseIn.h"
28+
#include "shared-bindings/microcontroller/__init__.h"
2829
#include "py/runtime.h"
2930

30-
// STATIC void pulsein_handler(uint8_t num) {
31-
// }
31+
STATIC uint8_t refcount = 0;
32+
STATIC pulseio_pulsein_obj_t * handles[RMT_CHANNEL_MAX];
33+
34+
// Requires rmt.c void esp32s2_peripherals_reset_all(void) to reset
35+
36+
STATIC void update_internal_buffer(pulseio_pulsein_obj_t* self) {
37+
uint32_t length = 0;
38+
rmt_item32_t *items = (rmt_item32_t *) xRingbufferReceive(self->buf_handle, &length, 0);
39+
if (items) {
40+
length /= 4;
41+
for (size_t i=0; i < length; i++) {
42+
uint16_t pos = (self->start + self->len) % self->maxlen;
43+
self->buffer[pos] = items[i].duration0 * 3;
44+
// Check if second item exists before incrementing
45+
if (items[i].duration1) {
46+
self->buffer[pos+1] = items[i].duration1 * 3;
47+
if (self->len < (self->maxlen - 1)) {
48+
self->len += 2;
49+
} else {
50+
self->start += 2;
51+
}
52+
} else {
53+
if (self->len < self->maxlen) {
54+
self->len++;
55+
} else {
56+
self->start++;
57+
}
58+
}
59+
}
60+
vRingbufferReturnItem(self->buf_handle, (void *) items);
61+
}
62+
}
63+
64+
// We can't access the RMT interrupt, so we need a global service to prevent
65+
// the ringbuffer from overflowing and crashing the peripheral
66+
void pulsein_background(void) {
67+
for (size_t i = 0; i < RMT_CHANNEL_MAX; i++) {
68+
if (handles[i]) {
69+
update_internal_buffer(handles[i]);
70+
UBaseType_t items_waiting;
71+
vRingbufferGetInfo(handles[i]->buf_handle, NULL, NULL, NULL, NULL, &items_waiting);
72+
}
73+
}
74+
}
3275

3376
void pulsein_reset(void) {
77+
for (size_t i = 0; i < RMT_CHANNEL_MAX; i++) {
78+
handles[i] = NULL;
79+
}
80+
supervisor_disable_tick();
81+
refcount = 0;
3482
}
3583

3684
void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self, const mcu_pin_obj_t* pin,
3785
uint16_t maxlen, bool idle_state) {
38-
mp_raise_NotImplementedError(translate("PulseIn not supported on this chip"));
86+
self->buffer = (uint16_t *) m_malloc(maxlen * sizeof(uint16_t), false);
87+
if (self->buffer == NULL) {
88+
mp_raise_msg_varg(&mp_type_MemoryError, translate("Failed to allocate RX buffer of %d bytes"), maxlen * sizeof(uint16_t));
89+
}
90+
self->pin = pin;
91+
self->maxlen = maxlen;
92+
self->idle_state = idle_state;
93+
self->start = 0;
94+
self->len = 0;
95+
self->paused = false;
96+
97+
// Set pull settings
98+
gpio_pullup_dis(pin->number);
99+
gpio_pulldown_dis(pin->number);
100+
if (idle_state) {
101+
gpio_pullup_en(pin->number);
102+
} else {
103+
gpio_pulldown_en(pin->number);
104+
}
105+
106+
// Find a free RMT Channel and configure it
107+
rmt_channel_t channel = esp32s2_peripherals_find_and_reserve_rmt();
108+
if (channel == RMT_CHANNEL_MAX) {
109+
mp_raise_RuntimeError(translate("All timers in use"));
110+
}
111+
rmt_config_t config = RMT_DEFAULT_CONFIG_RX(pin->number, channel);
112+
config.rx_config.filter_en = true;
113+
config.rx_config.idle_threshold = 30000; // 30*3=90ms idle required to register a sequence
114+
config.clk_div = 240; // All measurements are divided by 3 to accomodate 65ms pulses
115+
rmt_config(&config);
116+
rmt_driver_install(channel, 1000, 0); //TODO: pick a more specific buffer size?
117+
118+
// Store this object and the buffer handle for background updates
119+
self->channel = channel;
120+
handles[channel] = self;
121+
rmt_get_ringbuf_handle(channel, &(self->buf_handle));
122+
123+
// start RMT RX, and enable ticks so the core doesn't turn off.
124+
rmt_rx_start(channel, true);
125+
supervisor_enable_tick();
126+
refcount++;
39127
}
40128

41129
bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) {
42-
return false;
130+
return handles[self->channel] ? false : true;
43131
}
44132

45133
void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) {
134+
handles[self->channel] = NULL;
135+
esp32s2_peripherals_free_rmt(self->channel);
136+
reset_pin_number(self->pin->number);
137+
refcount--;
138+
if (refcount == 0) {
139+
supervisor_disable_tick();
140+
}
46141
}
47142

48143
void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) {
144+
self->paused = true;
145+
rmt_rx_stop(self->channel);
49146
}
50147

51148
void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, uint16_t trigger_duration) {
149+
// Make sure we're paused.
150+
if ( !self->paused ) {
151+
common_hal_pulseio_pulsein_pause(self);
152+
}
153+
154+
if (trigger_duration > 0) {
155+
gpio_set_direction(self->pin->number, GPIO_MODE_DEF_OUTPUT);
156+
gpio_set_level(self->pin->number, !self->idle_state);
157+
common_hal_mcu_delay_us((uint32_t)trigger_duration);
158+
gpio_set_level(self->pin->number, self->idle_state);
159+
gpio_set_direction(self->pin->number, GPIO_MODE_INPUT); // should revert to pull direction
160+
}
161+
162+
self->paused = false;
163+
rmt_rx_start(self->channel, false);
52164
}
53165

54166
void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t* self) {
167+
// Buffer only updates in BG tasks or fetches, so no extra protection is needed
168+
self->start = 0;
169+
self->len = 0;
55170
}
56171

57172
uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self, int16_t index) {
58-
return false;
173+
update_internal_buffer(self);
174+
if (index < 0) {
175+
index += self->len;
176+
}
177+
if (index < 0 || index >= self->len) {
178+
mp_raise_IndexError(translate("index out of range"));
179+
}
180+
uint16_t value = self->buffer[(self->start + index) % self->maxlen];
181+
return value;
59182
}
60183

61184
uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self) {
62-
return false;
185+
update_internal_buffer(self);
186+
187+
if (self->len == 0) {
188+
mp_raise_IndexError(translate("pop from an empty PulseIn"));
189+
}
190+
191+
uint16_t value = self->buffer[self->start];
192+
self->start = (self->start + 1) % self->maxlen;
193+
self->len--;
194+
195+
return value;
63196
}
64197

65198
uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self) {
66-
return false;
199+
return self->maxlen;
67200
}
68201

69202
bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self) {
70-
return false;
203+
return self->paused;
71204
}
72205

73206
uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self) {
74-
return false;
207+
return self->len;
75208
}

ports/esp32s2/common-hal/pulseio/PulseIn.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,27 @@
3030
#include "common-hal/microcontroller/Pin.h"
3131

3232
#include "py/obj.h"
33+
#include "driver/rmt.h"
34+
#include "rmt.h"
3335

3436
typedef struct {
3537
mp_obj_base_t base;
3638

3739
const mcu_pin_obj_t* pin;
40+
rmt_channel_t channel;
3841
bool idle_state;
3942
bool paused;
40-
volatile bool first_edge;
43+
44+
RingbufHandle_t buf_handle;
4145

4246
uint16_t* buffer;
4347
uint16_t maxlen;
4448

4549
volatile uint16_t start;
4650
volatile uint16_t len;
47-
volatile uint32_t last_overflow;
48-
volatile uint16_t last_count;
4951
} pulseio_pulsein_obj_t;
5052

5153
void pulsein_reset(void);
54+
void pulsein_background(void);
5255

5356
#endif // MICROPY_INCLUDED_ESP32S2_COMMON_HAL_PULSEIO_PULSEIN_H

0 commit comments

Comments
 (0)