Skip to content

Commit 7adc69b

Browse files
committed
Initial pulseio.PulseIn implmentation for #716 ESP8266
1 parent 74ced17 commit 7adc69b

File tree

3 files changed

+139
-7
lines changed

3 files changed

+139
-7
lines changed

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

+123-6
Original file line numberDiff line numberDiff line change
@@ -26,47 +26,164 @@
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+
// XXX map gpio pins to pulsein objects: kinda clumsy.
41+
static pulseio_pulsein_obj_t *pulseio_pulsein_objs[GPIO_PIN_COUNT] = {0};
42+
43+
static void pulsein_set_interrupt(pulseio_pulsein_obj_t *self, bool rising, bool falling) {
44+
ETS_GPIO_INTR_DISABLE();
45+
// Set interrupt mode
46+
GPIO_REG_WRITE(
47+
GPIO_PIN_ADDR(self->pin->gpio_number),
48+
(GPIO_REG_READ(GPIO_PIN_ADDR(self->pin->gpio_number) & ~GPIO_PIN_INT_TYPE_MASK)) |
49+
GPIO_PIN_INT_TYPE_SET(
50+
(rising ? GPIO_PIN_INTR_POSEDGE : 0) | (falling ? GPIO_PIN_INTR_NEGEDGE : 0)
51+
)
52+
);
53+
// Clear interrupt status
54+
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << self->pin->gpio_number);
55+
ETS_GPIO_INTR_ENABLE();
56+
}
57+
58+
void pulseio_pulsein_interrupt_handler(pulseio_pulsein_obj_t *self, uint32_t time_us) {
59+
if (self->first_edge) {
60+
self->first_edge = false;
61+
pulsein_set_interrupt(self, true, true);
62+
} else {
63+
uint16_t elapsed_us = (uint16_t)(time_us - self->last_us);
64+
uint16_t i = (self->start + self->len) % self->maxlen;
65+
self->buffer[i] = elapsed_us;
66+
if (self->len < self->maxlen) {
67+
self->len++;
68+
} else {
69+
self->start++;
70+
}
71+
}
72+
self->last_us = time_us;
73+
}
74+
75+
// XXX needs a better name, or a better abstraction
76+
// XXX called from intr.c:pin_intr_handler_iram ... inelegantly
77+
void pulsein_interrupt_handler(uint32_t status) {
78+
uint32_t time_us = system_get_time();
79+
for (int i=0; i<GPIO_PIN_COUNT; i++) {
80+
if (status & 1<<i) {
81+
pulseio_pulsein_obj_t *self = pulseio_pulsein_objs[i];
82+
if (self) pulseio_pulsein_interrupt_handler(self, time_us);
83+
}
84+
}
85+
}
3486

3587
void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self,
3688
const mcu_pin_obj_t* pin, uint16_t maxlen, bool idle_state) {
37-
mp_raise_NotImplementedError("");
89+
if (pin->gpio_number == NO_GPIO || pin->gpio_function == SPECIAL_CASE) {
90+
mp_raise_msg_varg(&mp_type_ValueError, "No PulseIn support for %q", pin->name );
91+
}
92+
PIN_FUNC_SELECT(pin->peripheral, pin->gpio_function);
93+
PIN_PULLUP_DIS(pin->peripheral);
94+
self->pin = pin;
95+
96+
self->buffer = (uint16_t *) m_malloc(maxlen * sizeof(uint16_t), false);
97+
if (self->buffer == NULL) {
98+
mp_raise_msg_varg(&mp_type_MemoryError, "Failed to allocate RX buffer of %d bytes", maxlen * sizeof(uint16_t));
99+
}
100+
101+
self->maxlen = maxlen;
102+
self->idle_state = idle_state;
103+
self->start = 0;
104+
self->len = 0;
105+
self->first_edge = true;
106+
self->last_us = 0;
107+
pulseio_pulsein_objs[self->pin->gpio_number] = self;
108+
pulsein_set_interrupt(self, !idle_state, idle_state);
38109
}
39110

40111
bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) {
41-
return true;
112+
return pulseio_pulsein_objs[self->pin->gpio_number] == NULL;
42113
}
43114

44115
void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) {
116+
pulsein_set_interrupt(self, false, false);
117+
pulseio_pulsein_objs[self->pin->gpio_number] = NULL;
118+
PIN_FUNC_SELECT(self->pin->peripheral, 0);
119+
m_free(self->buffer);
45120
}
46121

47122
void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) {
123+
pulsein_set_interrupt(self, false, false);
48124
}
49125

50126
void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self,
51127
uint16_t trigger_duration) {
128+
// Make sure we're paused.
129+
common_hal_pulseio_pulsein_pause(self);
130+
131+
// Send the trigger pulse.
132+
if (trigger_duration > 0) {
133+
uint32_t mask = 1 << self->pin->gpio_number;
134+
// switch pin to an output with state opposite idle state
135+
gpio_output_set(self->idle_state ? 0 : mask, self->idle_state ? mask : 0, 0, 0);
136+
gpio_output_set(0, 0, mask, 0);
137+
common_hal_mcu_delay_us((uint32_t)trigger_duration);
138+
// switch pin back to an open input
139+
gpio_output_set(0, 0, 0, mask);
140+
}
141+
142+
common_hal_mcu_disable_interrupts();
143+
self->first_edge = true;
144+
pulsein_set_interrupt(self, !self->idle_state, self->idle_state);
145+
common_hal_mcu_enable_interrupts();
52146
}
53147

54148
void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t* self) {
149+
common_hal_mcu_disable_interrupts();
150+
self->start = 0;
151+
self->len = 0;
152+
common_hal_mcu_enable_interrupts();
55153
}
56154

57155
uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self) {
58-
return 0;
156+
if (self->len == 0) {
157+
mp_raise_IndexError("pop from an empty PulseIn");
158+
}
159+
common_hal_mcu_disable_interrupts();
160+
uint16_t value = self->buffer[self->start];
161+
self->start = (self->start + 1) % self->maxlen;
162+
self->len--;
163+
common_hal_mcu_enable_interrupts();
164+
165+
return value;
59166
}
60167

61168
uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self) {
62-
return 0;
169+
return self->maxlen;
63170
}
64171

65172
uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self) {
66-
return 0;
173+
return self->len;
67174
}
68175

69176
uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self,
70177
int16_t index) {
71-
return 0;
178+
common_hal_mcu_disable_interrupts();
179+
if (index < 0) {
180+
index += self->len;
181+
}
182+
if (index < 0 || index >= self->len) {
183+
common_hal_mcu_enable_interrupts();
184+
mp_raise_IndexError("index out of range");
185+
}
186+
uint16_t value = self->buffer[(self->start + index) % self->maxlen];
187+
common_hal_mcu_enable_interrupts();
188+
return value;
72189
}

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

+12-1
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,22 @@
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+
volatile uint16_t start;
40+
volatile uint16_t len;
41+
volatile bool first_edge;
42+
volatile uint32_t last_us;
3443
} pulseio_pulsein_obj_t;
3544

36-
void pwmout_reset(void);
45+
void pulsein_reset(void);
46+
47+
void pulsein_interrupt_handler(uint32_t);
3748

3849
#endif // MICROPY_INCLUDED_ESP8266_COMMON_HAL_PULSEIO_PULSEIN_H

ports/esp8266/intr.c

+4
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@
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);
3637
pin_intr_handler(status);
38+
39+
// XXX bit of a hack
40+
pulsein_interrupt_handler(status);
3741
}

0 commit comments

Comments
 (0)