modem: backends: uart_isr: improve the reception of bytes
Add a configurable delay between when a byte is received and MODEM_PIPE_EVENT_RECEIVE_READY is sent. This fixes data reception at baud rates above 460800, and most likely also reduces the workload at any baud rate when receiving bytes by not going through the work item and callbacks for every single byte. Signed-off-by: Tomi Fontanilles <tomi.fontanilles@nordicsemi.no>
This commit is contained in:
parent
9cea822cc4
commit
419a398c01
|
@ -42,7 +42,7 @@ struct modem_backend_uart_async {
|
|||
struct modem_backend_uart {
|
||||
const struct device *uart;
|
||||
struct modem_pipe pipe;
|
||||
struct k_work receive_ready_work;
|
||||
struct k_work_delayable receive_ready_work;
|
||||
struct k_work transmit_idle_work;
|
||||
|
||||
union {
|
||||
|
|
|
@ -22,14 +22,30 @@ config MODEM_BACKEND_UART_ASYNC
|
|||
bool "Modem UART backend module async implementation"
|
||||
default y if UART_ASYNC_API
|
||||
|
||||
if MODEM_BACKEND_UART_ISR
|
||||
|
||||
config MODEM_BACKEND_UART_ISR_RECEIVE_IDLE_TIMEOUT_MS
|
||||
int "Modem ISR UART delay between first byte received and RECEIVE_READY pipe event"
|
||||
default 20
|
||||
help
|
||||
This defines the delay, in milliseconds, that the backend waits
|
||||
when receiving a byte before sending the RECEIVE_READY pipe event.
|
||||
The backend will anyway send the event before this delay if buffer space runs out.
|
||||
A good value is ~90% the time it takes to fill half the receive buffer.
|
||||
It can be calculated as follows:
|
||||
(<UART receive_buf_size> / 2) / (<UART baud rate> / <UART bits per byte>) * <ms per sec>
|
||||
By default (for the modem_cellular driver): (512 / 2) / (115200 / 10) * 1000 = 22,222 => 20
|
||||
|
||||
endif
|
||||
|
||||
if MODEM_BACKEND_UART_ASYNC
|
||||
|
||||
config MODEM_BACKEND_UART_ASYNC_TRANSMIT_TIMEOUT_MS
|
||||
int "Modem UART async transmit timeout in milliseconds"
|
||||
int "Modem async UART transmit timeout in milliseconds"
|
||||
default 1000
|
||||
|
||||
config MODEM_BACKEND_UART_ASYNC_RECEIVE_IDLE_TIMEOUT_MS
|
||||
int "Modem UART async receive idle timeout in milliseconds"
|
||||
int "Modem async UART receive idle timeout in milliseconds"
|
||||
default 30
|
||||
|
||||
endif
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
static void modem_backend_uart_receive_ready_handler(struct k_work *item)
|
||||
{
|
||||
struct modem_backend_uart *backend =
|
||||
CONTAINER_OF(item, struct modem_backend_uart, receive_ready_work);
|
||||
struct modem_backend_uart *backend = CONTAINER_OF(
|
||||
k_work_delayable_from_work(item), struct modem_backend_uart, receive_ready_work);
|
||||
|
||||
modem_pipe_notify_receive_ready(&backend->pipe);
|
||||
}
|
||||
|
@ -39,7 +39,8 @@ struct modem_pipe *modem_backend_uart_init(struct modem_backend_uart *backend,
|
|||
|
||||
memset(backend, 0x00, sizeof(*backend));
|
||||
backend->uart = config->uart;
|
||||
k_work_init(&backend->receive_ready_work, modem_backend_uart_receive_ready_handler);
|
||||
k_work_init_delayable(&backend->receive_ready_work,
|
||||
modem_backend_uart_receive_ready_handler);
|
||||
k_work_init(&backend->transmit_idle_work, modem_backend_uart_transmit_idle_handler);
|
||||
|
||||
#ifdef CONFIG_MODEM_BACKEND_UART_ASYNC
|
||||
|
|
|
@ -123,7 +123,7 @@ static void modem_backend_uart_async_event_handler(const struct device *dev,
|
|||
}
|
||||
|
||||
k_spin_unlock(&backend->async.receive_rb_lock, key);
|
||||
k_work_submit(&backend->receive_ready_work);
|
||||
k_work_schedule(&backend->receive_ready_work, K_NO_WAIT);
|
||||
break;
|
||||
|
||||
case UART_RX_DISABLED:
|
||||
|
@ -215,7 +215,7 @@ static int modem_backend_uart_async_receive(void *data, uint8_t *buf, size_t siz
|
|||
k_spin_unlock(&backend->async.receive_rb_lock, key);
|
||||
|
||||
if (!empty) {
|
||||
k_work_submit(&backend->receive_ready_work);
|
||||
k_work_schedule(&backend->receive_ready_work, K_NO_WAIT);
|
||||
}
|
||||
|
||||
return (int)received;
|
||||
|
|
|
@ -30,6 +30,11 @@ static void modem_backend_uart_isr_irq_handler_receive_ready(struct modem_backen
|
|||
receive_rb = &backend->isr.receive_rdb[backend->isr.receive_rdb_used];
|
||||
size = ring_buf_put_claim(receive_rb, &buffer, UINT32_MAX);
|
||||
if (size == 0) {
|
||||
/* This can be caused by
|
||||
* - a too long CONFIG_MODEM_BACKEND_UART_ISR_RECEIVE_IDLE_TIMEOUT_MS
|
||||
* - or a too small receive_buf_size
|
||||
* relatively to the (too high) baud rate and amount of incoming data.
|
||||
*/
|
||||
LOG_WRN("Receive buffer overrun");
|
||||
ring_buf_put_finish(receive_rb, 0);
|
||||
ring_buf_reset(receive_rb);
|
||||
|
@ -37,14 +42,23 @@ static void modem_backend_uart_isr_irq_handler_receive_ready(struct modem_backen
|
|||
}
|
||||
|
||||
ret = uart_fifo_read(backend->uart, buffer, size);
|
||||
if (ret < 0) {
|
||||
if (ret <= 0) {
|
||||
ring_buf_put_finish(receive_rb, 0);
|
||||
} else {
|
||||
ring_buf_put_finish(receive_rb, (uint32_t)ret);
|
||||
return;
|
||||
}
|
||||
ring_buf_put_finish(receive_rb, (uint32_t)ret);
|
||||
|
||||
if (ret > 0) {
|
||||
k_work_submit(&backend->receive_ready_work);
|
||||
if (ring_buf_space_get(receive_rb) > ring_buf_capacity_get(receive_rb) / 20) {
|
||||
/*
|
||||
* Avoid having the receiver call modem_pipe_receive() too often (e.g. every byte).
|
||||
* It temporarily disables the UART RX IRQ when swapping buffers
|
||||
* which can cause byte loss at higher baud rates.
|
||||
*/
|
||||
k_work_schedule(&backend->receive_ready_work,
|
||||
K_MSEC(CONFIG_MODEM_BACKEND_UART_ISR_RECEIVE_IDLE_TIMEOUT_MS));
|
||||
} else {
|
||||
/* The buffer is getting full. Run the work item immediately to free up space. */
|
||||
k_work_reschedule(&backend->receive_ready_work, K_NO_WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue