Skip to content

Commit 618943d

Browse files
authored
Merge pull request #926 from nickzoic/circuitpython-nickzoic-716-pulseio-esp8266
Implement pulseio.PulseIn and PulseOut for ESP8266 #716
2 parents 144c059 + f72dcc6 commit 618943d

File tree

7 files changed

+192
-13
lines changed

7 files changed

+192
-13
lines changed

ports/esp8266/common-hal/microcontroller/Pin.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* THE SOFTWARE.
2525
*/
2626

27+
#include "shared-bindings/microcontroller/__init__.h"
2728
#include "common-hal/microcontroller/__init__.h"
2829
#include "common-hal/microcontroller/Pin.h"
2930
#include "shared-bindings/microcontroller/Pin.h"
@@ -35,6 +36,28 @@
3536
bool adc_in_use;
3637
bool gpio16_in_use;
3738

39+
typedef struct {
40+
void (*func)(void *);
41+
void *data;
42+
} pin_intr_handler_t;
43+
44+
static pin_intr_handler_t _pin_intr_handlers[GPIO_PIN_COUNT];
45+
46+
void microcontroller_pin_call_intr_handlers(uint32_t status) {
47+
status &= (1 << GPIO_PIN_COUNT) - 1;
48+
for (int p = 0; status; ++p, status >>= 1) {
49+
if ((status & 1) && _pin_intr_handlers[p].func) {
50+
_pin_intr_handlers[p].func(_pin_intr_handlers[p].data);
51+
}
52+
}
53+
}
54+
55+
void microcontroller_pin_register_intr_handler(uint8_t gpio_number, void (*func)(void *), void *data) {
56+
common_hal_mcu_disable_interrupts();
57+
_pin_intr_handlers[gpio_number] = (pin_intr_handler_t){ func, data };
58+
common_hal_mcu_enable_interrupts();
59+
}
60+
3861
bool common_hal_mcu_pin_is_free(const mcu_pin_obj_t* pin) {
3962
if (pin == &pin_TOUT) {
4063
return !adc_in_use;
@@ -92,4 +115,4 @@ void reset_pins(void) {
92115

93116
adc_in_use = false;
94117
gpio16_in_use = false;
95-
}
118+
}

ports/esp8266/common-hal/microcontroller/Pin.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,7 @@ void claim_pin(const mcu_pin_obj_t* pin);
4545
void reset_pin(const mcu_pin_obj_t* pin);
4646
void reset_pins(void);
4747

48+
void microcontroller_pin_register_intr_handler(uint8_t gpio_number, void (*func)(void *), void *data);
49+
void microcontroller_pin_call_intr_handlers(uint32_t status);
50+
4851
#endif // MICROPY_INCLUDED_ESP8266_COMMON_HAL_MICROCONTROLLER_PIN_H

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

Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,51 +26,161 @@
2626

2727
#include <stdint.h>
2828

29+
#include <user_interface.h>
30+
#include <eagle_soc.h>
31+
#include "esp_mphal.h"
32+
2933
#include "mpconfigport.h"
3034
#include "py/gc.h"
3135
#include "py/runtime.h"
3236
#include "shared-bindings/microcontroller/__init__.h"
3337
#include "shared-bindings/pulseio/PulseIn.h"
38+
#include "common-hal/microcontroller/__init__.h"
39+
40+
static void pulsein_set_interrupt(pulseio_pulsein_obj_t *self, bool rising, bool falling) {
41+
ETS_GPIO_INTR_DISABLE();
42+
// Set interrupt mode
43+
GPIO_REG_WRITE(
44+
GPIO_PIN_ADDR(self->pin->gpio_number),
45+
(GPIO_REG_READ(GPIO_PIN_ADDR(self->pin->gpio_number) & ~GPIO_PIN_INT_TYPE_MASK)) |
46+
GPIO_PIN_INT_TYPE_SET(
47+
(rising ? GPIO_PIN_INTR_POSEDGE : 0) | (falling ? GPIO_PIN_INTR_NEGEDGE : 0)
48+
)
49+
);
50+
// Clear interrupt status
51+
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << self->pin->gpio_number);
52+
ETS_GPIO_INTR_ENABLE();
53+
}
54+
55+
void pulseio_pulsein_interrupt_handler(void *data) {
56+
pulseio_pulsein_obj_t *self = data;
57+
uint32_t time_us = system_get_time();
58+
if (self->first_edge) {
59+
self->first_edge = false;
60+
pulsein_set_interrupt(self, true, true);
61+
} else {
62+
uint16_t elapsed_us = (uint16_t)(time_us - self->last_us);
63+
uint16_t i = (self->start + self->len) % self->maxlen;
64+
self->buffer[i] = elapsed_us;
65+
if (self->len < self->maxlen) {
66+
self->len++;
67+
} else {
68+
self->start++;
69+
}
70+
}
71+
self->last_us = time_us;
72+
}
3473

3574
void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self,
3675
const mcu_pin_obj_t* pin, uint16_t maxlen, bool idle_state) {
37-
mp_raise_NotImplementedError("");
76+
if (pin->gpio_number == NO_GPIO || pin->gpio_function == SPECIAL_CASE) {
77+
mp_raise_msg_varg(&mp_type_ValueError, "No PulseIn support for %q", pin->name );
78+
}
79+
PIN_FUNC_SELECT(pin->peripheral, pin->gpio_function);
80+
PIN_PULLUP_DIS(pin->peripheral);
81+
self->pin = pin;
82+
83+
self->buffer = (uint16_t *) m_malloc(maxlen * sizeof(uint16_t), false);
84+
if (self->buffer == NULL) {
85+
mp_raise_msg_varg(&mp_type_MemoryError, "Failed to allocate RX buffer of %d bytes", maxlen * sizeof(uint16_t));
86+
}
87+
88+
self->maxlen = maxlen;
89+
self->idle_state = idle_state;
90+
self->start = 0;
91+
self->len = 0;
92+
self->first_edge = true;
93+
self->last_us = 0;
94+
self->paused = false;
95+
96+
microcontroller_pin_register_intr_handler(self->pin->gpio_number,
97+
pulseio_pulsein_interrupt_handler, (void *)self);
98+
pulsein_set_interrupt(self, !idle_state, idle_state);
3899
}
39100

40101
bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) {
41-
return true;
102+
return self->buffer == NULL;
42103
}
43104

44105
void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) {
106+
pulsein_set_interrupt(self, false, false);
107+
microcontroller_pin_register_intr_handler(self->pin->gpio_number, NULL, NULL);
108+
PIN_FUNC_SELECT(self->pin->peripheral, 0);
109+
m_free(self->buffer);
110+
self->buffer = NULL;
45111
}
46112

47113
void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) {
114+
pulsein_set_interrupt(self, false, false);
115+
self->paused = true;
48116
}
49117

50118
void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self,
51119
uint16_t trigger_duration) {
120+
// Make sure we're paused.
121+
common_hal_pulseio_pulsein_pause(self);
122+
123+
// Send the trigger pulse.
124+
if (trigger_duration > 0) {
125+
uint32_t mask = 1 << self->pin->gpio_number;
126+
// switch pin to an output with state opposite idle state
127+
gpio_output_set(self->idle_state ? 0 : mask, self->idle_state ? mask : 0, 0, 0);
128+
gpio_output_set(0, 0, mask, 0);
129+
common_hal_mcu_delay_us((uint32_t)trigger_duration);
130+
// switch pin back to an open input
131+
gpio_output_set(0, 0, 0, mask);
132+
}
133+
134+
common_hal_mcu_disable_interrupts();
135+
self->first_edge = true;
136+
pulsein_set_interrupt(self, !self->idle_state, self->idle_state);
137+
common_hal_mcu_enable_interrupts();
138+
self->paused = false;
52139
}
53140

54141
void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t* self) {
142+
common_hal_mcu_disable_interrupts();
143+
self->start = 0;
144+
self->len = 0;
145+
common_hal_mcu_enable_interrupts();
55146
}
56147

57148
uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self) {
58-
return 0;
149+
if (self->len == 0) {
150+
mp_raise_IndexError("pop from an empty PulseIn");
151+
}
152+
common_hal_mcu_disable_interrupts();
153+
uint16_t value = self->buffer[self->start];
154+
self->start = (self->start + 1) % self->maxlen;
155+
self->len--;
156+
common_hal_mcu_enable_interrupts();
157+
158+
return value;
59159
}
60160

61161
uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self) {
62-
return 0;
162+
return self->maxlen;
63163
}
64164

65165
bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self) {
66-
return false;
166+
return self->paused;
67167
}
68168

69169
uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self) {
70-
return 0;
170+
return self->len;
71171
}
72172

73173
uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self,
74174
int16_t index) {
75-
return 0;
175+
common_hal_mcu_disable_interrupts();
176+
if (index < 0) {
177+
index += self->len;
178+
}
179+
if (index < 0 || index >= self->len) {
180+
common_hal_mcu_enable_interrupts();
181+
mp_raise_IndexError("index out of range");
182+
}
183+
uint16_t value = self->buffer[(self->start + index) % self->maxlen];
184+
common_hal_mcu_enable_interrupts();
185+
return value;
76186
}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,23 @@
2828
#define MICROPY_INCLUDED_ESP8266_COMMON_HAL_PULSEIO_PULSEIN_H
2929

3030
#include "py/obj.h"
31+
#include "common-hal/microcontroller/Pin.h"
3132

3233
typedef struct {
3334
mp_obj_base_t base;
35+
const mcu_pin_obj_t *pin;
36+
uint16_t *buffer;
37+
uint16_t maxlen;
38+
bool idle_state;
39+
bool paused;
40+
volatile uint16_t start;
41+
volatile uint16_t len;
42+
volatile bool first_edge;
43+
volatile uint32_t last_us;
3444
} pulseio_pulsein_obj_t;
3545

36-
void pwmout_reset(void);
46+
void pulsein_reset(void);
47+
48+
void pulsein_interrupt_handler(uint32_t);
3749

3850
#endif // MICROPY_INCLUDED_ESP8266_COMMON_HAL_PULSEIO_PULSEIN_H

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

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,41 @@
2424
* THE SOFTWARE.
2525
*/
2626

27+
#include "common-hal/pulseio/PulseOut.h"
2728

2829
#include <stdint.h>
2930

31+
#include <pwm.h>
32+
33+
#include "ets_alt_task.h"
34+
#include "py/obj.h"
3035
#include "py/runtime.h"
36+
#include "mpconfigport.h"
3137
#include "shared-bindings/pulseio/PulseOut.h"
3238

39+
void pulseout_set(pulseio_pulseout_obj_t *self, bool state) {
40+
PIN_FUNC_SELECT(self->pin->peripheral, state ? self->pin->gpio_function : 0);
41+
}
42+
3343
void common_hal_pulseio_pulseout_construct(pulseio_pulseout_obj_t* self,
3444
const pulseio_pwmout_obj_t* carrier) {
35-
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "No hardware support for PulseOut."));
45+
self->pin = carrier->pin;
3646
}
3747

3848
bool common_hal_pulseio_pulseout_deinited(pulseio_pulseout_obj_t* self) {
39-
return true;
49+
return self->pin == NULL;
4050
}
4151

4252
void common_hal_pulseio_pulseout_deinit(pulseio_pulseout_obj_t* self) {
53+
self->pin = NULL;
54+
pulseout_set(self, true);
4355
}
4456

45-
void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self, uint16_t* pulses, uint16_t length) {
57+
void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t* self,
58+
uint16_t* pulses, uint16_t length) {
59+
for (uint16_t i = 0; i<length; i++) {
60+
pulseout_set(self, i % 2 == 0);
61+
ets_delay_us(pulses[i]);
62+
}
63+
pulseout_set(self, false);
4664
}

ports/esp8266/common-hal/pulseio/PulseOut.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,19 @@
2727
#ifndef MICROPY_INCLUDED_ESP8266_COMMON_HAL_PULSEIO_PULSEOUT_H
2828
#define MICROPY_INCLUDED_ESP8266_COMMON_HAL_PULSEIO_PULSEOUT_H
2929

30+
#include "common-hal/microcontroller/Pin.h"
31+
32+
#include "esp_mphal.h"
33+
3034
#include "py/obj.h"
3135

3236
typedef struct {
3337
mp_obj_base_t base;
38+
os_timer_t timer;
39+
const mcu_pin_obj_t *pin;
3440
} pulseio_pulseout_obj_t;
3541

36-
void pwmout_reset(void);
42+
void pulseout_reset(void);
43+
void pulseout_interrupt_handler(void *);
3744

3845
#endif // MICROPY_INCLUDED_ESP8266_COMMON_HAL_PULSEIO_PULSEOUT_H

ports/esp8266/intr.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,16 @@
2828
#include "ets_alt_task.h"
2929

3030
#include "modmachine.h"
31+
#include "common-hal/pulseio/PulseIn.h"
3132

3233
// this is in a separate file so it can go in iRAM
3334
void pin_intr_handler_iram(void *arg) {
3435
uint32_t status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
3536
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, status);
37+
38+
// machine.Pin handlers
3639
pin_intr_handler(status);
40+
41+
// microcontroller.Pin handlers
42+
microcontroller_pin_call_intr_handlers(status);
3743
}

0 commit comments

Comments
 (0)