If the mcu is using a traditional serial port, then only send a new message block if there is space available in the mcu receive buffer. This should make it significantly less likely that high load on the mcu will result in retransmits. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
123 lines
3.4 KiB
C
123 lines
3.4 KiB
C
// Generic interrupt based serial uart helper code
|
|
//
|
|
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
|
|
//
|
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
#include <string.h> // memmove
|
|
#include "autoconf.h" // CONFIG_SERIAL_BAUD
|
|
#include "board/io.h" // readb
|
|
#include "board/irq.h" // irq_save
|
|
#include "board/misc.h" // console_sendf
|
|
#include "board/pgm.h" // READP
|
|
#include "command.h" // DECL_CONSTANT
|
|
#include "sched.h" // sched_wake_tasks
|
|
#include "serial_irq.h" // serial_enable_tx_irq
|
|
|
|
#define RX_BUFFER_SIZE 192
|
|
|
|
static uint8_t receive_buf[RX_BUFFER_SIZE], receive_pos;
|
|
static uint8_t transmit_buf[96], transmit_pos, transmit_max;
|
|
|
|
DECL_CONSTANT(SERIAL_BAUD, CONFIG_SERIAL_BAUD);
|
|
DECL_CONSTANT(RECEIVE_WINDOW, RX_BUFFER_SIZE);
|
|
|
|
// Rx interrupt - store read data
|
|
void
|
|
serial_rx_byte(uint_fast8_t data)
|
|
{
|
|
if (data == MESSAGE_SYNC)
|
|
sched_wake_tasks();
|
|
if (receive_pos >= sizeof(receive_buf))
|
|
// Serial overflow - ignore it as crc error will force retransmit
|
|
return;
|
|
receive_buf[receive_pos++] = data;
|
|
}
|
|
|
|
// Tx interrupt - get next byte to transmit
|
|
int
|
|
serial_get_tx_byte(uint8_t *pdata)
|
|
{
|
|
if (transmit_pos >= transmit_max)
|
|
return -1;
|
|
*pdata = transmit_buf[transmit_pos++];
|
|
return 0;
|
|
}
|
|
|
|
// Remove from the receive buffer the given number of bytes
|
|
static void
|
|
console_pop_input(uint_fast8_t len)
|
|
{
|
|
uint_fast8_t copied = 0;
|
|
for (;;) {
|
|
uint_fast8_t rpos = readb(&receive_pos);
|
|
uint_fast8_t needcopy = rpos - len;
|
|
if (needcopy) {
|
|
memmove(&receive_buf[copied], &receive_buf[copied + len]
|
|
, needcopy - copied);
|
|
copied = needcopy;
|
|
sched_wake_tasks();
|
|
}
|
|
irqstatus_t flag = irq_save();
|
|
if (rpos != readb(&receive_pos)) {
|
|
// Raced with irq handler - retry
|
|
irq_restore(flag);
|
|
continue;
|
|
}
|
|
receive_pos = needcopy;
|
|
irq_restore(flag);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Process any incoming commands
|
|
void
|
|
console_task(void)
|
|
{
|
|
uint_fast8_t rpos = readb(&receive_pos), pop_count;
|
|
int_fast8_t ret = command_find_block(receive_buf, rpos, &pop_count);
|
|
if (ret > 0)
|
|
command_dispatch(receive_buf, pop_count);
|
|
if (ret) {
|
|
console_pop_input(pop_count);
|
|
if (ret > 0)
|
|
command_send_ack();
|
|
}
|
|
}
|
|
DECL_TASK(console_task);
|
|
|
|
// Encode and transmit a "response" message
|
|
void
|
|
console_sendf(const struct command_encoder *ce, va_list args)
|
|
{
|
|
// Verify space for message
|
|
uint_fast8_t tpos = readb(&transmit_pos), tmax = readb(&transmit_max);
|
|
if (tpos >= tmax) {
|
|
tpos = tmax = 0;
|
|
writeb(&transmit_max, 0);
|
|
writeb(&transmit_pos, 0);
|
|
}
|
|
uint_fast8_t max_size = READP(ce->max_size);
|
|
if (tmax + max_size > sizeof(transmit_buf)) {
|
|
if (tmax + max_size - tpos > sizeof(transmit_buf))
|
|
// Not enough space for message
|
|
return;
|
|
// Disable TX irq and move buffer
|
|
writeb(&transmit_max, 0);
|
|
tpos = readb(&transmit_pos);
|
|
tmax -= tpos;
|
|
memmove(&transmit_buf[0], &transmit_buf[tpos], tmax);
|
|
writeb(&transmit_pos, 0);
|
|
writeb(&transmit_max, tmax);
|
|
serial_enable_tx_irq();
|
|
}
|
|
|
|
// Generate message
|
|
uint8_t *buf = &transmit_buf[tmax];
|
|
uint_fast8_t msglen = command_encode_and_frame(buf, ce, args);
|
|
|
|
// Start message transmit
|
|
writeb(&transmit_max, tmax + msglen);
|
|
serial_enable_tx_irq();
|
|
}
|