Skip to content

HartdwareSerial: recover original Arduino asynchronous write API #1503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 27, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 44 additions & 38 deletions cores/arduino/HardwareSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,23 @@ void HardwareSerial::_rx_complete_irq(serial_t *obj)
}
}

// Actual interrupt handlers //////////////////////////////////////////////////////////////
// Actual interrupt handlers //////////////////////////////////////////////////

int HardwareSerial::_tx_complete_irq(serial_t *obj)
{
// If interrupts are enabled, there must be more data in the output
// buffer. Send the next byte
size_t remaining_data;
// previous HAL transfer is finished, move tail pointer accordingly
obj->tx_tail = (obj->tx_tail + obj->tx_size) % SERIAL_TX_BUFFER_SIZE;

if (obj->tx_head == obj->tx_tail) {
// If buffer is not empty (head != tail), send remaining data
if (obj->tx_head != obj->tx_tail) {
remaining_data = (SERIAL_TX_BUFFER_SIZE + obj->tx_head - obj->tx_tail)
% SERIAL_TX_BUFFER_SIZE;
// Limit the next transmission to the buffer end
// because HAL is not able to manage rollover
obj->tx_size = min(remaining_data,
(size_t)(SERIAL_TX_BUFFER_SIZE - obj->tx_tail));
uart_attach_tx_callback(obj, _tx_complete_irq, obj->tx_size);
return -1;
}

Expand Down Expand Up @@ -482,9 +490,10 @@ void HardwareSerial::flush()

size_t HardwareSerial::write(const uint8_t *buffer, size_t size)
{
tx_buffer_index_t i;
size_t size_tmp;
size_t size_intermediate;
size_t ret = size;
size_t available = availableForWrite();
size_t available_till_buffer_end = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;

_written = true;
if (isHalfDuplex()) {
Expand All @@ -494,49 +503,46 @@ size_t HardwareSerial::write(const uint8_t *buffer, size_t size)
}
}

// If necessary split transfert till end of TX buffer
while (_serial.tx_head + size > SERIAL_TX_BUFFER_SIZE) {
size_t size_intermediate = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;

write(buffer, size_intermediate);
size -= size_intermediate;
buffer += size_intermediate;
// If the output buffer is full, there's nothing for it other than to
// wait for the interrupt handler to free space
while (!availableForWrite()) {
// nop, the interrupt handler will free up space for us
}

// Here size if less or equal to SERIAL_TX_BUFFER_SIZE, but SERIAL_TX_BUFFER_SIZE is not possible as tx_head = tx_tail is ambiguous empty or full
if (size == SERIAL_TX_BUFFER_SIZE) {
size_t size_intermediate = SERIAL_TX_BUFFER_SIZE - 1;

// HAL doesn't manage rollover, so split transfer till end of TX buffer
// Also, split transfer according to available space in buffer
while ((size > available_till_buffer_end) || (size > available)) {
size_intermediate = min(available, available_till_buffer_end);
write(buffer, size_intermediate);
size -= size_intermediate;
buffer += size_intermediate;
available = availableForWrite();
available_till_buffer_end = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;
}

size_tmp = size;

while (size_tmp) {
i = (_serial.tx_head + 1) % SERIAL_TX_BUFFER_SIZE;


// If the output buffer is full, there's nothing for it other than to
// wait for the interrupt handler to empty it a bit
while (i == _serial.tx_tail) {
// nop, the interrupt handler will free up space for us
}
_serial.tx_buff[_serial.tx_head] = *buffer;
_serial.tx_head = i;
size_tmp --;
buffer ++;
}

while ((_serial.tx_head != (_serial.tx_tail + size) % SERIAL_TX_BUFFER_SIZE)) {
// nop, previous transfert no yet completed
// Copy data to buffer. Take into account rollover if necessary.
if (_serial.tx_head + size <= SERIAL_TX_BUFFER_SIZE) {
memcpy(&_serial.tx_buff[_serial.tx_head], buffer, size);
size_intermediate = size;
} else {
// memcpy till end of buffer then continue memcpy from beginning of buffer
size_intermediate = SERIAL_TX_BUFFER_SIZE - _serial.tx_head;
memcpy(&_serial.tx_buff[_serial.tx_head], buffer, size_intermediate);
memcpy(&_serial.tx_buff[0], buffer + size_intermediate,
size - size_intermediate);
}

_serial.tx_size = size;
// Data are copied to buffer, move head pointer accordingly
_serial.tx_head = (_serial.tx_head + size) % SERIAL_TX_BUFFER_SIZE;

// Transfer data with HAL only is there is no TX data transfer ongoing
// otherwise, data transfer will be done asynchronously from callback
if (!serial_tx_active(&_serial)) {
uart_attach_tx_callback(&_serial, _tx_complete_irq, size);
// note: tx_size correspond to size of HAL data transfer,
// not the total amount of data in the buffer.
// To compute size of data in buffer compare head and tail
_serial.tx_size = size_intermediate;
uart_attach_tx_callback(&_serial, _tx_complete_irq, size_intermediate);
}

/* There is no real error management so just return transfer size requested*/
Expand Down