diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index 122c45ea65..0c377f9eaf 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -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; } @@ -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()) { @@ -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*/