Skip to content

Commit 158cf0e

Browse files
committed
pbio/drv/uart: Refactor async read and write.
This squashes the following intermediate commits: pbio/drv/uart: Don't broadcast uart events to all processes. Instead poll subscribers, which is just the legodev process for now. This can be expanded to poll the specific uart instead of all of them. This also drops the uart processes, which were not doing anything, saving some code size. pbio/drv/uart: Replace begin/end with single protothread. UART read and write always used begin/end sequentially, each wrapped in a wait for err != PBIO_ERROR_AGAIN. This replaces the begin/end pattern with one awaitable protothread. This makes the code easier to follow and reduces code size. pbio/drv/uart: Set callback per device. Not all devices may be used by the legodev driver.
1 parent 4d457a1 commit 158cf0e

File tree

9 files changed

+297
-404
lines changed

9 files changed

+297
-404
lines changed

lib/pbio/drv/ioport/ioport_debug_uart.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#if PBDRV_CONFIG_IOPORT_DEBUG_UART
77

8+
#error "This driver can currently not be used."
9+
810
#include <stdio.h>
911
#include <string.h>
1012
#include <stdarg.h>

lib/pbio/drv/legodev/legodev_pup.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <contiki.h>
1515

1616
#include <pbdrv/gpio.h>
17+
#include <pbdrv/uart.h>
1718

1819
#include <pbsys/status.h>
1920

@@ -107,6 +108,11 @@ static void legodev_pup_enable_uart(const pbdrv_ioport_pup_pins_t *pins) {
107108
pbdrv_gpio_out_low(&pins->uart_buf);
108109
}
109110

111+
static void legodev_pup_disable_uart(const pbdrv_ioport_pup_pins_t *pins) {
112+
// REVISIT: Move to ioport.
113+
pbdrv_gpio_out_high(&pins->uart_buf);
114+
}
115+
110116
// This is the device connection manager (dcm). It monitors the ID1 and ID2 pins
111117
// on the port to see when devices are connected or disconnected.
112118
// It is expected for there to be a 2ms delay between calls to this function.
@@ -343,6 +349,7 @@ static PT_THREAD(pbdrv_legodev_pup_thread(ext_dev_t * dev)) {
343349
while (true) {
344350

345351
// Initially assume nothing is connected.
352+
legodev_pup_disable_uart(dev->pins);
346353
dev->dcm.connected_type_id = PBDRV_LEGODEV_TYPE_ID_NONE;
347354
dev->dcm.dev_id_match_count = 0;
348355

@@ -369,6 +376,7 @@ static PT_THREAD(pbdrv_legodev_pup_thread(ext_dev_t * dev)) {
369376
// disconnects, as observed by the UART process not getting valid data.
370377
legodev_pup_enable_uart(dev->pins);
371378
PT_SPAWN(&dev->pt, &dev->child, pbdrv_legodev_pup_uart_thread(&dev->child, dev->uart_dev));
379+
372380
}
373381
PT_END(&dev->pt);
374382
}
@@ -405,6 +413,14 @@ PROCESS_THREAD(pbio_legodev_pup_process, ev, data) {
405413
PROCESS_END();
406414
}
407415

416+
/**
417+
* We get notified when the uart driver has completed sending or receiving data.
418+
*/
419+
static void uart_poll_callback(pbdrv_uart_dev_t *uart) {
420+
// REVISIT: Only need to poll the specified uart device.
421+
process_poll(&pbio_legodev_pup_process);
422+
}
423+
408424
void pbdrv_legodev_init(void) {
409425
#if PBDRV_CONFIG_LEGODEV_PUP_NUM_INT_DEV > 0
410426
for (uint8_t i = 0; i < PBDRV_CONFIG_LEGODEV_PUP_NUM_INT_DEV; i++) {
@@ -431,7 +447,15 @@ void pbdrv_legodev_init(void) {
431447
pbio_dcmotor_get_dcmotor(legodev, &dcmotor);
432448
legodev->ext_dev->uart_dev = pbdrv_legodev_pup_uart_configure(legodev_data->ioport_index, port_data->uart_driver_index, dcmotor);
433449

450+
// legodev driver is started after all other drivers, so we
451+
// assume that we do not need to wait for this to be ready.
452+
pbdrv_uart_dev_t *uart;
453+
pbdrv_uart_get(port_data->uart_driver_index, &uart);
454+
455+
// Set callback for uart driver.
456+
pbdrv_uart_set_poll_callback(uart, uart_poll_callback);
434457
}
458+
435459
process_start(&pbio_legodev_pup_process);
436460
}
437461

lib/pbio/drv/legodev/legodev_pup_uart.c

Lines changed: 19 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ struct _pbdrv_legodev_pup_uart_dev_t {
160160
struct pt pt;
161161
/** Protothread for receiving sensor data, running in parallel to the data send thread. */
162162
struct pt recv_pt;
163+
/** Child protothread of the main protothread used for reading data */
164+
struct pt read_pt;
163165
/** Child protothread of the main protothread used for writing data */
164166
struct pt write_pt;
165167
/** Timer for sending keepalive messages and other delays. */
@@ -199,8 +201,6 @@ struct _pbdrv_legodev_pup_uart_dev_t {
199201
uint32_t err_count;
200202
/** Number of bad reads when receiving DATA ludev->msgs. */
201203
uint32_t num_data_err;
202-
/** Time of most recently started transmission. */
203-
uint32_t tx_start_time;
204204
/** Flag that indicates that good DATA ludev->msg has been received since last watchdog timeout. */
205205
bool data_rec;
206206
/** Return value for synchronization thread. */
@@ -229,8 +229,6 @@ static pbdrv_legodev_pup_uart_dev_t ludevs[PBDRV_CONFIG_LEGODEV_PUP_UART_NUM_DEV
229229
static uint8_t data_read_bufs[PBDRV_CONFIG_LEGODEV_PUP_UART_NUM_DEV][PBDRV_LEGODEV_MAX_DATA_SIZE] __attribute__((aligned(4)));
230230
static pbdrv_legodev_pup_uart_data_set_t data_set_bufs[PBDRV_CONFIG_LEGODEV_PUP_UART_NUM_DEV];
231231

232-
#define PBIO_PT_WAIT_READY(pt, expr) PT_WAIT_UNTIL((pt), (expr) != PBIO_ERROR_AGAIN)
233-
234232
pbdrv_legodev_pup_uart_dev_t *pbdrv_legodev_pup_uart_configure(uint8_t device_index, uint8_t uart_driver_index, pbio_dcmotor_t *dcmotor) {
235233
pbdrv_legodev_pup_uart_dev_t *ludev = &ludevs[device_index];
236234
ludev->dcmotor = dcmotor;
@@ -694,16 +692,6 @@ static void pbdrv_legodev_pup_uart_reset(pbdrv_legodev_pup_uart_dev_t *ludev) {
694692
}
695693
}
696694

697-
static PT_THREAD(pbdrv_legodev_pup_uart_send_prepared_msg(pbdrv_legodev_pup_uart_dev_t * ludev, pbio_error_t * err)) {
698-
PT_BEGIN(&ludev->write_pt);
699-
ludev->tx_start_time = pbdrv_clock_get_ms();
700-
PBIO_PT_WAIT_READY(&ludev->write_pt, *err = pbdrv_uart_write_begin(ludev->uart, ludev->tx_msg, ludev->tx_msg_size, EV3_UART_IO_TIMEOUT));
701-
if (*err == PBIO_SUCCESS) {
702-
PBIO_PT_WAIT_READY(&ludev->write_pt, *err = pbdrv_uart_write_end(ludev->uart));
703-
}
704-
PT_END(&ludev->write_pt);
705-
}
706-
707695
/**
708696
* The synchronization thread for the LEGO UART device.
709697
*
@@ -734,7 +722,7 @@ static PT_THREAD(pbdrv_legodev_pup_uart_synchronize_thread(pbdrv_legodev_pup_uar
734722

735723
pbdrv_uart_flush(ludev->uart);
736724

737-
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_legodev_pup_uart_send_prepared_msg(ludev, &ludev->err));
725+
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_uart_write(&ludev->write_pt, ludev->uart, ludev->tx_msg, ludev->tx_msg_size, EV3_UART_IO_TIMEOUT, &ludev->err));
738726
if (ludev->err != PBIO_SUCCESS) {
739727
DBG_ERR(ludev->last_err = "Speed tx failed");
740728
PT_EXIT(&ludev->pt);
@@ -743,13 +731,8 @@ static PT_THREAD(pbdrv_legodev_pup_uart_synchronize_thread(pbdrv_legodev_pup_uar
743731
pbdrv_uart_flush(ludev->uart);
744732

745733
// read one byte to check for ACK
746-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_begin(ludev->uart, ludev->rx_msg, 1, 10));
747-
if (ludev->err != PBIO_SUCCESS) {
748-
DBG_ERR(ludev->last_err = "UART Rx error during baud");
749-
PT_EXIT(&ludev->pt);
750-
}
734+
PT_SPAWN(&ludev->pt, &ludev->read_pt, pbdrv_uart_read(&ludev->read_pt, ludev->uart, ludev->rx_msg, 1, 10, &ludev->err));
751735

752-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_end(ludev->uart));
753736
if ((ludev->err == PBIO_SUCCESS && ludev->rx_msg[0] != LUMP_SYS_ACK) || ludev->err == PBIO_ERROR_TIMEDOUT) {
754737
// if we did not get ACK within 100ms, then switch to slow baud rate for sync
755738
pbdrv_uart_set_baud_rate(ludev->uart, EV3_UART_SPEED_MIN);
@@ -758,18 +741,14 @@ static PT_THREAD(pbdrv_legodev_pup_uart_synchronize_thread(pbdrv_legodev_pup_uar
758741
DBG_ERR(ludev->last_err = "UART Rx error during baud");
759742
PT_EXIT(&ludev->pt);
760743
}
761-
762744
sync:
763745

764746
// To get in sync with the data stream from the sensor, we look for a valid TYPE command.
765747
for (;;) {
766-
767-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_begin(ludev->uart, ludev->rx_msg, 1, EV3_UART_IO_TIMEOUT));
768-
if (ludev->err != PBIO_SUCCESS) {
769-
DBG_ERR(ludev->last_err = "UART Rx error during sync");
770-
PT_EXIT(&ludev->pt);
771-
}
772-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_end(ludev->uart));
748+
// If there are multiple bytes waiting to be read, this drains them one
749+
// by one without requiring additional polls. This means we won't need
750+
// exact timing to get in sync.
751+
PT_SPAWN(&ludev->pt, &ludev->read_pt, pbdrv_uart_read(&ludev->read_pt, ludev->uart, ludev->rx_msg, 1, EV3_UART_IO_TIMEOUT, &ludev->err));
773752
if (ludev->err == PBIO_ERROR_TIMEDOUT) {
774753
continue;
775754
}
@@ -784,13 +763,7 @@ static PT_THREAD(pbdrv_legodev_pup_uart_synchronize_thread(pbdrv_legodev_pup_uar
784763
}
785764

786765
// then read the rest of the message
787-
788-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_begin(ludev->uart, ludev->rx_msg + 1, 2, EV3_UART_IO_TIMEOUT));
789-
if (ludev->err != PBIO_SUCCESS) {
790-
DBG_ERR(ludev->last_err = "UART Rx error while reading type");
791-
PT_EXIT(&ludev->pt);
792-
}
793-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_end(ludev->uart));
766+
PT_SPAWN(&ludev->pt, &ludev->read_pt, pbdrv_uart_read(&ludev->read_pt, ludev->uart, ludev->rx_msg + 1, 2, EV3_UART_IO_TIMEOUT, &ludev->err));
794767
if (ludev->err != PBIO_SUCCESS) {
795768
DBG_ERR(ludev->last_err = "UART Rx error while reading type");
796769
PT_EXIT(&ludev->pt);
@@ -825,12 +798,7 @@ static PT_THREAD(pbdrv_legodev_pup_uart_synchronize_thread(pbdrv_legodev_pup_uar
825798

826799
while (ludev->status == PBDRV_LEGODEV_PUP_UART_STATUS_INFO) {
827800
// read the message header
828-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_begin(ludev->uart, ludev->rx_msg, 1, EV3_UART_IO_TIMEOUT));
829-
if (ludev->err != PBIO_SUCCESS) {
830-
DBG_ERR(ludev->last_err = "UART Rx begin error during info header");
831-
PT_EXIT(&ludev->pt);
832-
}
833-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_end(ludev->uart));
801+
PT_SPAWN(&ludev->pt, &ludev->read_pt, pbdrv_uart_read(&ludev->read_pt, ludev->uart, ludev->rx_msg, 1, EV3_UART_IO_TIMEOUT, &ludev->err));
834802
if (ludev->err != PBIO_SUCCESS) {
835803
DBG_ERR(ludev->last_err = "UART Rx end error during info header");
836804
PT_EXIT(&ludev->pt);
@@ -845,12 +813,7 @@ static PT_THREAD(pbdrv_legodev_pup_uart_synchronize_thread(pbdrv_legodev_pup_uar
845813

846814
// read the rest of the message
847815
if (ludev->rx_msg_size > 1) {
848-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_begin(ludev->uart, ludev->rx_msg + 1, ludev->rx_msg_size - 1, EV3_UART_IO_TIMEOUT));
849-
if (ludev->err != PBIO_SUCCESS) {
850-
DBG_ERR(ludev->last_err = "UART Rx begin error during info");
851-
PT_EXIT(&ludev->pt);
852-
}
853-
PBIO_PT_WAIT_READY(&ludev->pt, ludev->err = pbdrv_uart_read_end(ludev->uart));
816+
PT_SPAWN(&ludev->pt, &ludev->read_pt, pbdrv_uart_read(&ludev->read_pt, ludev->uart, ludev->rx_msg + 1, ludev->rx_msg_size - 1, EV3_UART_IO_TIMEOUT, &ludev->err));
854817
if (ludev->err != PBIO_SUCCESS) {
855818
DBG_ERR(ludev->last_err = "UART Rx end error during info");
856819
PT_EXIT(&ludev->pt);
@@ -871,7 +834,7 @@ static PT_THREAD(pbdrv_legodev_pup_uart_synchronize_thread(pbdrv_legodev_pup_uar
871834
// reply with ACK
872835
ludev->tx_msg[0] = LUMP_SYS_ACK;
873836
ludev->tx_msg_size = 1;
874-
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_legodev_pup_uart_send_prepared_msg(ludev, &ludev->err));
837+
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_uart_write(&ludev->write_pt, ludev->uart, ludev->tx_msg, ludev->tx_msg_size, EV3_UART_IO_TIMEOUT, &ludev->err));
875838
if (ludev->err != PBIO_SUCCESS) {
876839
DBG_ERR(ludev->last_err = "UART Tx error during ack.");
877840
PT_EXIT(&ludev->pt);
@@ -942,7 +905,7 @@ static PT_THREAD(pbdrv_legodev_pup_uart_send_thread(pbdrv_legodev_pup_uart_dev_t
942905
ludev->data_rec = false;
943906
ludev->tx_msg[0] = LUMP_SYS_NACK;
944907
ludev->tx_msg_size = 1;
945-
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_legodev_pup_uart_send_prepared_msg(ludev, &ludev->err));
908+
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_uart_write(&ludev->write_pt, ludev->uart, ludev->tx_msg, ludev->tx_msg_size, EV3_UART_IO_TIMEOUT, &ludev->err));
946909
if (ludev->err != PBIO_SUCCESS) {
947910
DBG_ERR(ludev->last_err = "Error during keepalive.");
948911
PT_EXIT(&ludev->pt);
@@ -959,7 +922,7 @@ static PT_THREAD(pbdrv_legodev_pup_uart_send_thread(pbdrv_legodev_pup_uart_dev_t
959922
if (ludev->mode_switch.requested) {
960923
ludev->mode_switch.requested = false;
961924
ev3_uart_prepare_tx_msg(ludev, LUMP_MSG_TYPE_CMD, LUMP_CMD_SELECT, &ludev->mode_switch.desired_mode, 1);
962-
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_legodev_pup_uart_send_prepared_msg(ludev, &ludev->err));
925+
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_uart_write(&ludev->write_pt, ludev->uart, ludev->tx_msg, ludev->tx_msg_size, EV3_UART_IO_TIMEOUT, &ludev->err));
963926
if (ludev->err != PBIO_SUCCESS) {
964927
DBG_ERR(ludev->last_err = "Setting requested mode failed.");
965928
PT_EXIT(&ludev->pt);
@@ -973,7 +936,7 @@ static PT_THREAD(pbdrv_legodev_pup_uart_send_thread(pbdrv_legodev_pup_uart_dev_t
973936
ev3_uart_prepare_tx_msg(ludev, LUMP_MSG_TYPE_DATA, ludev->data_set->desired_mode, ludev->data_set->bin_data, ludev->data_set->size);
974937
ludev->data_set->size = 0;
975938
ludev->data_set->time = pbdrv_clock_get_ms();
976-
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_legodev_pup_uart_send_prepared_msg(ludev, &ludev->err));
939+
PT_SPAWN(&ludev->pt, &ludev->write_pt, pbdrv_uart_write(&ludev->write_pt, ludev->uart, ludev->tx_msg, ludev->tx_msg_size, EV3_UART_IO_TIMEOUT, &ludev->err));
977940
if (ludev->err != PBIO_SUCCESS) {
978941
DBG_ERR(ludev->last_err = "Setting requested data failed.");
979942
PT_EXIT(&ludev->pt);
@@ -1004,19 +967,11 @@ static PT_THREAD(pbdrv_legodev_pup_uart_receive_data_thread(pbdrv_legodev_pup_ua
1004967
// loose data. For now, the retry after bad message size helps get back into
1005968
// sync with the data stream.
1006969

1007-
pbio_error_t err;
1008-
1009970
PT_BEGIN(&ludev->recv_pt);
1010971

1011972
while (true) {
1012-
PBIO_PT_WAIT_READY(&ludev->recv_pt,
1013-
err = pbdrv_uart_read_begin(ludev->uart, ludev->rx_msg, 1, EV3_UART_IO_TIMEOUT));
1014-
if (err != PBIO_SUCCESS) {
1015-
DBG_ERR(ludev->last_err = "UART Rx data header begin error");
1016-
break;
1017-
}
1018-
PBIO_PT_WAIT_READY(&ludev->recv_pt, err = pbdrv_uart_read_end(ludev->uart));
1019-
if (err != PBIO_SUCCESS) {
973+
PT_SPAWN(&ludev->recv_pt, &ludev->read_pt, pbdrv_uart_read(&ludev->read_pt, ludev->uart, ludev->rx_msg, 1, EV3_UART_IO_TIMEOUT, &ludev->err));
974+
if (ludev->err != PBIO_SUCCESS) {
1020975
DBG_ERR(ludev->last_err = "UART Rx data header end error");
1021976
break;
1022977
}
@@ -1035,14 +990,8 @@ static PT_THREAD(pbdrv_legodev_pup_uart_receive_data_thread(pbdrv_legodev_pup_ua
1035990
continue;
1036991
}
1037992

1038-
PBIO_PT_WAIT_READY(&ludev->recv_pt,
1039-
err = pbdrv_uart_read_begin(ludev->uart, ludev->rx_msg + 1, ludev->rx_msg_size - 1, EV3_UART_IO_TIMEOUT));
1040-
if (err != PBIO_SUCCESS) {
1041-
DBG_ERR(ludev->last_err = "UART Rx data begin error");
1042-
break;
1043-
}
1044-
PBIO_PT_WAIT_READY(&ludev->recv_pt, err = pbdrv_uart_read_end(ludev->uart));
1045-
if (err != PBIO_SUCCESS) {
993+
PT_SPAWN(&ludev->recv_pt, &ludev->read_pt, pbdrv_uart_read(&ludev->read_pt, ludev->uart, ludev->rx_msg + 1, ludev->rx_msg_size - 1, EV3_UART_IO_TIMEOUT, &ludev->err));
994+
if (ludev->err != PBIO_SUCCESS) {
1046995
DBG_ERR(ludev->last_err = "UART Rx data end error");
1047996
break;
1048997
}

0 commit comments

Comments
 (0)