8000 esp32/machine_uart: Implement UART.RX_IDLE based on machine.Timer. · projectgus/micropython@a38b4f4 · GitHub
[go: up one dir, main page]

Skip to content

Commit a38b4f4

Browse files
robert-hhdpgeorge
authored andcommitted
esp32/machine_uart: Implement UART.RX_IDLE based on machine.Timer.
The UART.IRQ_IDLE callback is called about two character times after the last byte, or 1 ms, whichever is larger. For the irq, timer 0 is used. machine_timer.c had to be reworked to make it's mechanisms available for machine_uart.c. The irq.flags() value is change only at a requested event. Otherwise keep the state. Signed-off-by: robert-hh <robert@hammelrath.com>
1 parent 7045975 commit a38b4f4

File tree

3 files changed

+173
-35
lines changed

3 files changed

+173
-35
lines changed

ports/esp32/machine_timer.c

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "hal/timer_hal.h"
3939
#include "hal/timer_ll.h"
4040
#include "soc/timer_periph.h"
41+
#include "machine_timer.h"
4142

4243
#define TIMER_DIVIDER 8
4344

@@ -46,27 +47,8 @@
4647

4748
#define TIMER_FLAGS 0
4849

49-
typedef struct _machine_timer_obj_t {
50-
mp_obj_base_t base;
51-
52-
timer_hal_context_t hal_context;
53-
mp_uint_t group;
54-
mp_uint_t index;
55-
56-
mp_uint_t repeat;
57-
// ESP32 timers are 64 or 54-bit
58-
uint64_t period;
59-
60-
mp_obj_t callback;
61-
62-
intr_handle_t handle;
63-
64-
struct _machine_timer_obj_t *next;
65-
} machine_timer_obj_t;
66-
6750
const mp_obj_type_t machine_timer_type;
6851

69-
static void machine_timer_disable(machine_timer_obj_t *self);
7052
static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
7153

7254
void machine_timer_deinit_all(void) {
@@ -91,18 +73,17 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr
9173
#endif
9274
}
9375

94-
static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
95-
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
76+
machine_timer_obj_t *machine_timer_create(mp_uint_t timer) {
77+
78+
machine_timer_obj_t *self = NULL;
9679
#if CONFIG_IDF_TARGET_ESP32C3
97-
mp_uint_t group = mp_obj_get_int(args[0]) & 1;
80+
mp_uint_t group = timer & 1;
9881
mp_uint_t index = 0;
9982
#else
100-
mp_uint_t group = (mp_obj_get_int(args[0]) >> 1) & 1;
101-
mp_uint_t index = mp_obj_get_int(args[0]) & 1;
83+
mp_uint_t group = (timer >> 1) & 1;
84+
mp_uint_t index = timer & 1;
10285
#endif
10386

104-
machine_timer_obj_t *self = NULL;
105-
10687
// Check whether the timer is already initialized, if so use it
10788
for (machine_timer_obj_t *t = MP_STATE_PORT(machine_timer_obj_head); t; t = t->next) {
10889
if (t->group == group && t->index == index) {
@@ -120,6 +101,14 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args,
120101
self->next = MP_STATE_PORT(machine_timer_obj_head);
121102
MP_STATE_PORT(machine_timer_obj_head) = self;
122103
}
104+
return self;
105+
}
106+
107+
static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
108+
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
109+
110+
// Create the new timer.
111+
machine_timer_obj_t *self = machine_timer_create(mp_obj_get_int(args[0]));
123112

124113
if (n_args > 1 || n_kw > 0) {
125114
mp_map_t kw_args;
@@ -130,7 +119,7 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args,
130119
return self;
131120
}
132121

133-
static void machine_timer_disable(machine_timer_obj_t *self) {
122+
void machine_timer_disable(machine_timer_obj_t *self) {
134123
if (self->hal_context.dev != NULL) {
135124
// Disable the counter and alarm.
136125
timer_ll_enable_counter(self->hal_context.dev, self->index, false);
@@ -162,7 +151,7 @@ static void machine_timer_isr(void *self_in) {
162151
}
163152
}
164153

165-
static void machine_timer_enable(machine_timer_obj_t *self) {
154+
void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) {
166155
// Initialise the timer.
167156
timer_hal_init(&self->hal_context, self->group, self->index);
168157
timer_ll_enable_counter(self->hal_context.dev, self->index, false);
@@ -176,7 +165,7 @@ static void machine_timer_enable(machine_timer_obj_t *self) {
176165
timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index));
177166
ESP_ERROR_CHECK(
178167
esp_intr_alloc(timer_group_periph_signals.groups[self->group].timer_irq_id[self->index],
179-
TIMER_FLAGS, machine_timer_isr, self, &self->handle)
168+
TIMER_FLAGS, timer_isr, self, &self->handle)
180169
);
181170
timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true);
182171

@@ -234,7 +223,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n
234223
self->callback = args[ARG_callback].u_obj;
235224
self->handle = NULL;
236225

237-
machine_timer_enable(self);
226+
machine_timer_enable(self, machine_timer_isr);
238227

239228
return mp_const_none;
240229
}

ports/esp32/machine_timer.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* Development of the code in this file was sponsored by Microbric Pty Ltd
5+
*
6+
* The MIT License (MIT)
7+
*
8+
* Copyright (c) 2013-2015 Damien P. George
9+
* Copyright (c) 2016 Paul Sokolovsky
10+
*
11+
* Permission is hereby granted, free of charge, to any person obtaining a copy
12+
* of this software and associated documentation files (the "Software"), to deal
13+
* in the Software without restriction, including without limitation the rights
14+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15+
* copies of the Software, and to permit persons to whom the Software is
16+
* furnished to do so, subject to the following conditions:
17+
*
18+
* The above copyright notice and this permission notice shall be included in
19+
* all copies or substantial portions of the Software.
20+
*
21+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27+
* THE SOFTWARE.
28+
*/
29+
30+
#ifndef MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H
31+
#define MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H
32+
33+
#include "hal/timer_hal.h"
34+
#include "hal/timer_ll.h"
35+
#include "soc/timer_periph.h"
36+
37+
#define TIMER_DIVIDER 8
38+
39+
// TIMER_BASE_CLK is normally 80MHz. TIMER_DIVIDER ought to divide this exactly
40+
#define TIMER_SCALE (APB_CLK_FREQ / TIMER_DIVIDER)
41+
42+
#define TIMER_FLAGS 0
43+
44+
typedef struct _machine_timer_obj_t {
45+
mp_obj_base_t base;
46+
47+
timer_hal_context_t hal_context;
48+
mp_uint_t group;
49+
mp_uint_t index;
50+
51+
mp_uint_t repeat;
52+
// ESP32 timers are 64-bit
53+
uint64_t period;
54+
55+
mp_obj_t callback;
56+
57+
intr_handle_t handle;
58+
59+
struct _machine_timer_obj_t *next;
60+
} machine_timer_obj_t;
61+
62+
machine_timer_obj_t *machine_timer_create(mp_uint_t timer);
63+
void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr));
64+
void machine_timer_disable(machine_timer_obj_t *self);
65+
66+
#endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H

ports/esp32/machine_uart.c

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "py/mperrno.h"
4040
#include "py/mphal.h"
4141
#include "uart.h"
42+
#include "machine_timer.h"
4243

4344
#if SOC_UART_SUPPORT_XTAL_CLK
4445
// Works independently of APB frequency, on ESP32C3, ESP32S3.
@@ -54,8 +55,17 @@
5455

5556
#define UART_INV_MASK (UART_INV_TX | UART_INV_RX | UART_INV_RTS | UART_INV_CTS)
5657
#define UART_IRQ_RX (1 << UART_DATA)
58+
#define UART_IRQ_RXIDLE (0x1000)
5759
#define UART_IRQ_BREAK (1 << UART_BREAK)
58-
#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_BREAK)
60+
#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_RXIDLE | UART_IRQ_BREAK)
61+
#define RXIDLE_TIMER_MIN (5000) // 500 us
62+
63+
enum {
64+
RXIDLE_INACTIVE,
65+
RXIDLE_STANDBY,
66+
RXIDLE_ARMED,
67+
RXIDLE_ALERT,
68+
};
5969

6070
typedef struct _machine_uart_obj_t {
6171
mp_obj_base_t base;
@@ -78,6 +88,9 @@ typedef struct _machine_uart_obj_t {
7888
uint16_t mp_irq_trigger; // user IRQ trigger mask
7989
uint16_t mp_irq_flags; // user IRQ active IRQ flags
8090
mp_irq_obj_t *mp_irq_obj; // user IRQ object
91+
machine_timer_obj_t *rxidle_timer;
92+
uint8_t rxidle_state;
93+
uint16_t rxidle_period;
8194
} machine_uart_obj_t;
8295

8396
static const char *_parity_name[] = {"None", "1", "0"};
@@ -93,28 +106,67 @@ static const char *_parity_name[] = {"None", "1", "0"};
93106
{ MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HW_FLOWCTRL_RTS) }, \
94107
{ MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HW_FLOWCTRL_CTS) }, \
95108
{ MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(UART_IRQ_RX) }, \
109+
{ MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \
96110
{ MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \
97111

112+
static void uart_timer_callback(void *self_in) {
113+
machine_timer_obj_t *self = self_in;
114+
115+
uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev);
116+
117+
if (intr_status & TIMER_LL_EVENT_ALARM(self->index)) {
118+
timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index));
119+
if (self->repeat) {
120+
timer_ll_enable_alarm(self->hal_context.dev, self->index, true);
121+
}
122+
}
123+
124+
// The UART object is referred here by the callback field.
125+
machine_uart_obj_t *uart = (machine_uart_obj_t *)self->callback;
126+
if (uart->rxidle_state == RXIDLE_ALERT) {
127+
// At the first call, just switch the state
128+
uart->rxidle_state = RXIDLE_ARMED;
129+
} else if (uart->rxidle_state == RXIDLE_ARMED) {
130+
// At the second call, run the irq callback and stop the timer
131+
uart->rxidle_state = RXIDLE_STANDBY;
132+
uart->mp_irq_flags = UART_IRQ_RXIDLE;
133+
mp_irq_handler(uart->mp_irq_obj);
134+
mp_hal_wake_main_task_from_isr();
135+
machine_timer_disable(uart->rxidle_timer);
136+
}
137+
}
138+
98139
static void uart_event_task(void *self_in) {
99140
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
100141
uart_event_t event;
101142
for (;;) {
102143
// Waiting for an UART event.
103144
if (xQueueReceive(self->uart_queue, (void *)&event, (TickType_t)portMAX_DELAY)) {
104-
self->mp_irq_flags = 0;
145+
uint16_t mp_irq_flags = 0;
105146
switch (event.type) {
106147
// Event of UART receiving data
107148
case UART_DATA:
108-
self->mp_irq_flags |= UART_IRQ_RX;
149+
if (self->mp_irq_trigger & UART_IRQ_RXIDLE) {
150+
if (self->rxidle_state != RXIDLE_INACTIVE) {
151+
if (self->rxidle_state == RXIDLE_STANDBY) {
152+
self->rxidle_timer->repeat = true;
153+
self->rxidle_timer->handle = NULL;
154+
machine_timer_enable(self->rxidle_timer, uart_timer_callback);
155+
}
156+
}
157+
self->rxidle_state = RXIDLE_ALERT;
158+
}
159+
mp_irq_flags |= UART_IRQ_RX;
109160
break;
110161
case UART_BREAK:
111-
self->mp_irq_flags |= UART_IRQ_BREAK;
162+
mp_irq_flags |= UART_IRQ_BREAK;
112163
break;
113164
default:
114165
break;
115166
}
116167
// Check the flags to see if the user handler should be called
117-
if (self->mp_irq_trigger & self->mp_irq_flags) {
168+
if (self->mp_irq_trigger & mp_irq_flags) {
169+
self->mp_irq_flags = mp_irq_flags;
118170
mp_irq_handler(self->mp_irq_obj);
119171
mp_hal_wake_main_task_from_isr();
120172
}
@@ -390,6 +442,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
390442
self->invert = 0;
391443
self->flowcontrol = 0;
392444
self->uart_event_task = 0;
445+
self->rxidle_state = RXIDLE_INACTIVE;
393446

394447
switch (uart_num) {
395448
case UART_NUM_0:
@@ -464,8 +517,35 @@ static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) {
464517
check_esp_err(uart_set_baudrate(self->uart_num, baudrate));
465518
}
466519

520+
// Configure the timer used for IRQ_RXIDLE
521+
static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger) {
522+
523+
self->rxidle_state = RXIDLE_INACTIVE;
524+
525+
if (trigger & UART_IRQ_RXIDLE) {
526+
// The RXIDLE event is always a soft IRQ.
527+
self->mp_irq_obj->ishard = false;
528+
uint32_t baudrate;
529+
uart_get_baudrate(self->uart_num, &baudrate);
530+
mp_int_t period = TIMER_SCALE * 20 / baudrate + 1;
531+
if (period < RXIDLE_TIMER_MIN) {
532+
period = RXIDLE_TIMER_MIN;
533+
}
534+
self->rxidle_period = period;
535+
self->rxidle_timer->period = period;
536+
// The Python callback is not used. So use this
537+
// data field to hold a reference to the UART object.
538+
self->rxidle_timer->callback = self;
539+
self->rxidle_timer->repeat = true;
540+
self->rxidle_timer->handle = NULL;
541+
self->rxidle_state = RXIDLE_STANDBY;
542+
}
543+
}
544+
467545
static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
468546
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
547+
548+
uart_irq_configure_timer(self, new_trigger);
469549
self->mp_irq_trigger = new_trigger;
470550
return 0;
471551
}
@@ -511,6 +591,9 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args
511591
}
512592
self->mp_irq_obj->ishard = false;
513593
self->mp_irq_trigger = trigger;
594+
self->rxidle_timer = machine_timer_create(0);
595+
uart_irq_configure_timer(self, trigger);
596+
514597
// Start a task for handling events
515598
if (handler != mp_const_none && self->uart_event_task == NULL) {
516599
xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self,

0 commit comments

Comments
 (0)
0