stmhal: Properly handle RTS/CTS flow control for buf/unbuf transfers.
Fixes issues #1912 and #1913. UART documentation is also updated.
This commit is contained in:
parent
3177ef544f
commit
22cbcd55f0
@ -40,7 +40,8 @@ using the standard stream methods::
|
|||||||
|
|
||||||
To check if there is anything to be read, use::
|
To check if there is anything to be read, use::
|
||||||
|
|
||||||
uart.any() # returns True if any characters waiting
|
uart.any() # returns the number of characters waiting
|
||||||
|
|
||||||
|
|
||||||
*Note:* The stream functions ``read``, ``write``, etc. are new in MicroPython v1.3.4.
|
*Note:* The stream functions ``read``, ``write``, etc. are new in MicroPython v1.3.4.
|
||||||
Earlier versions use ``uart.send`` and ``uart.recv``.
|
Earlier versions use ``uart.send`` and ``uart.recv``.
|
||||||
@ -66,12 +67,16 @@ Constructors
|
|||||||
- ``UART(3)`` is on ``YB``: ``(TX, RX) = (Y9, Y10) = (PB10, PB11)``
|
- ``UART(3)`` is on ``YB``: ``(TX, RX) = (Y9, Y10) = (PB10, PB11)``
|
||||||
- ``UART(2)`` is on: ``(TX, RX) = (X3, X4) = (PA2, PA3)``
|
- ``UART(2)`` is on: ``(TX, RX) = (X3, X4) = (PA2, PA3)``
|
||||||
|
|
||||||
|
The Pyboard Lite supports UART(1), UART(2) and UART(6) only. Pins are as above except:
|
||||||
|
|
||||||
|
- ``UART(2)`` is on: ``(TX, RX) = (X1, X2) = (PA2, PA3)``
|
||||||
|
|
||||||
Methods
|
Methods
|
||||||
-------
|
-------
|
||||||
|
|
||||||
.. only:: port_pyboard
|
.. only:: port_pyboard
|
||||||
|
|
||||||
.. method:: uart.init(baudrate, bits=8, parity=None, stop=1, \*, timeout=1000, flow=None, timeout_char=0, read_buf_len=64)
|
.. method:: uart.init(baudrate, bits=8, parity=None, stop=1, \*, timeout=1000, flow=0, timeout_char=0, read_buf_len=64)
|
||||||
|
|
||||||
Initialise the UART bus with the given parameters:
|
Initialise the UART bus with the given parameters:
|
||||||
|
|
||||||
@ -79,7 +84,7 @@ Methods
|
|||||||
- ``bits`` is the number of bits per character, 7, 8 or 9.
|
- ``bits`` is the number of bits per character, 7, 8 or 9.
|
||||||
- ``parity`` is the parity, ``None``, 0 (even) or 1 (odd).
|
- ``parity`` is the parity, ``None``, 0 (even) or 1 (odd).
|
||||||
- ``stop`` is the number of stop bits, 1 or 2.
|
- ``stop`` is the number of stop bits, 1 or 2.
|
||||||
- ``flow`` sets the flow control type. Can be None, ``UART.RTS``, ``UART.CTS``
|
- ``flow`` sets the flow control type. Can be 0, ``UART.RTS``, ``UART.CTS``
|
||||||
or ``UART.RTS | UART.CTS``.
|
or ``UART.RTS | UART.CTS``.
|
||||||
- ``timeout`` is the timeout in milliseconds to wait for the first character.
|
- ``timeout`` is the timeout in milliseconds to wait for the first character.
|
||||||
- ``timeout_char`` is the timeout in milliseconds to wait between characters.
|
- ``timeout_char`` is the timeout in milliseconds to wait between characters.
|
||||||
@ -103,16 +108,18 @@ Methods
|
|||||||
|
|
||||||
.. method:: uart.any()
|
.. method:: uart.any()
|
||||||
|
|
||||||
Returns the number of characters waiting (may be 0).
|
Returns the number of bytes waiting (may be 0).
|
||||||
|
|
||||||
.. method:: uart.writechar(char)
|
.. method:: uart.writechar(char)
|
||||||
|
|
||||||
Write a single character on the bus. ``char`` is an integer to write.
|
Write a single character on the bus. ``char`` is an integer to write.
|
||||||
Return value: ``None``.
|
Return value: ``None``. See note below if CTS flow control is used.
|
||||||
|
|
||||||
.. method:: uart.read([nbytes])
|
.. method:: uart.read([nbytes])
|
||||||
|
|
||||||
Read characters. If ``nbytes`` is specified then read at most that many bytes.
|
Read characters. If ``nbytes`` is specified then read at most that many bytes.
|
||||||
|
If ``nbytes`` are available in the buffer, returns immediately, otherwise returns
|
||||||
|
when sufficient characters arrive or the timeout elapses.
|
||||||
|
|
||||||
.. only:: port_pyboard
|
.. only:: port_pyboard
|
||||||
|
|
||||||
@ -124,9 +131,9 @@ Methods
|
|||||||
|
|
||||||
.. method:: uart.readall()
|
.. method:: uart.readall()
|
||||||
|
|
||||||
Read as much data as possible.
|
Read as much data as possible. Returns after the timeout has elapsed.
|
||||||
|
|
||||||
Return value: a bytes object or ``None`` on timeout.
|
Return value: a bytes object or ``None`` if timeout prevents any data being read.
|
||||||
|
|
||||||
.. method:: uart.readchar()
|
.. method:: uart.readchar()
|
||||||
|
|
||||||
@ -144,9 +151,11 @@ Methods
|
|||||||
|
|
||||||
.. method:: uart.readline()
|
.. method:: uart.readline()
|
||||||
|
|
||||||
Read a line, ending in a newline character.
|
Read a line, ending in a newline character. If such a line exists, return is
|
||||||
|
immediate. If the timeout elapses, all available data is returned regardless
|
||||||
|
of whether a newline exists.
|
||||||
|
|
||||||
Return value: the line read or ``None`` on timeout.
|
Return value: the line read or ``None`` on timeout if no data is available.
|
||||||
|
|
||||||
.. method:: uart.write(buf)
|
.. method:: uart.write(buf)
|
||||||
|
|
||||||
@ -157,7 +166,8 @@ Methods
|
|||||||
bytes are used for each character (little endian), and ``buf`` must contain
|
bytes are used for each character (little endian), and ``buf`` must contain
|
||||||
an even number of bytes.
|
an even number of bytes.
|
||||||
|
|
||||||
Return value: number of bytes written or ``None`` on timeout.
|
Return value: number of bytes written. If a timeout occurs and no bytes
|
||||||
|
were written returns ``None``.
|
||||||
|
|
||||||
.. method:: uart.sendbreak()
|
.. method:: uart.sendbreak()
|
||||||
|
|
||||||
@ -173,4 +183,63 @@ Constants
|
|||||||
.. data:: UART.RTS
|
.. data:: UART.RTS
|
||||||
.. data:: UART.CTS
|
.. data:: UART.CTS
|
||||||
|
|
||||||
to select the flow control type
|
to select the flow control type.
|
||||||
|
|
||||||
|
Flow Control
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. only:: port_pyboard
|
||||||
|
|
||||||
|
On Pyboards V1 and V1.1 ``UART(2)`` and ``UART(3)`` support RTS/CTS hardware flow control
|
||||||
|
using the following pins:
|
||||||
|
|
||||||
|
- ``UART(2)`` is on: ``(TX, RX, nRTS, nCTS) = (X3, X4, X2, X1) = (PA2, PA3, PA1, PA0)``
|
||||||
|
- ``UART(3)`` is on :``(TX, RX, nRTS, nCTS) = (Y9, Y10, Y7, Y6) = (PB10, PB11, PB14, PB13)``
|
||||||
|
|
||||||
|
On the Pyboard Lite only ``UART(2)`` supports flow control on these pins:
|
||||||
|
|
||||||
|
``(TX, RX, nRTS, nCTS) = (X1, X2, X4, X3) = (PA2, PA3, PA1, PA0)``
|
||||||
|
|
||||||
|
In the following paragraphs the term "target" refers to the device connected to
|
||||||
|
the UART.
|
||||||
|
|
||||||
|
When the UART's ``init()`` method is called with ``flow`` set to one or both of
|
||||||
|
``UART.RTS`` and ``UART.CTS`` the relevant flow control pins are configured.
|
||||||
|
``nRTS`` is an active low output, ``nCTS`` is an active low input with pullup
|
||||||
|
enabled. To achieve flow control the Pyboard's ``nCTS`` signal should be connected
|
||||||
|
to the target's ``nRTS`` and the Pyboard's ``nRTS`` to the target's ``nCTS``.
|
||||||
|
|
||||||
|
CTS: target controls Pyboard transmitter
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If CTS flow control is enabled the write behaviour is as follows:
|
||||||
|
|
||||||
|
If the Pyboard's ``uart.write(buf)`` method is called, transmission will stall for
|
||||||
|
any periods when ``nCTS`` is ``False``. This will result in a timeout if the entire
|
||||||
|
buffer was not transmitted in the timeout period. The method returns the number of
|
||||||
|
bytes written, enabling the user to write the remainder of the data if required. In
|
||||||
|
the event of a timeout, a character will remain in the UART pending ``nCTS``. The
|
||||||
|
number of bytes composing this character will be included in the return value.
|
||||||
|
|
||||||
|
If ``uart.writechar()`` is called when ``nCTS`` is ``False`` the method will time
|
||||||
|
out unless the target asserts ``nCTS`` in time. If it times out ``OSError 116``
|
||||||
|
will be raised. The character will be transmitted as soon as the target asserts ``nCTS``.
|
||||||
|
|
||||||
|
RTS: Pyboard controls target's transmitter
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If RTS flow control is enabled, behaviour is as follows:
|
||||||
|
|
||||||
|
If buffered input is used (``read_buf_len`` > 0), incoming characters are buffered.
|
||||||
|
If the buffer becomes full, the next character to arrive will cause ``nRTS`` to go
|
||||||
|
``False``: the target should cease transmission. ``nRTS`` will go ``True`` when
|
||||||
|
characters are read from the buffer.
|
||||||
|
|
||||||
|
Note that the ``any()`` method returns the number of bytes in the buffer. Assume a
|
||||||
|
buffer length of ``N`` bytes. If the buffer becomes full, and another character arrives,
|
||||||
|
``nRTS`` will be set False, and ``any()`` will return the count ``N``. When
|
||||||
|
characters are read the additional character will be placed in the buffer and will
|
||||||
|
be included in the result of a subsequent ``any()`` call.
|
||||||
|
|
||||||
|
If buffered input is not used (``read_buf_len`` == 0) the arrival of a character will
|
||||||
|
cause ``nRTS`` to go ``False`` until the character is read.
|
||||||
|
@ -320,6 +320,10 @@ int uart_rx_char(pyb_uart_obj_t *self) {
|
|||||||
data = self->read_buf[self->read_buf_tail];
|
data = self->read_buf[self->read_buf_tail];
|
||||||
}
|
}
|
||||||
self->read_buf_tail = (self->read_buf_tail + 1) % self->read_buf_len;
|
self->read_buf_tail = (self->read_buf_tail + 1) % self->read_buf_len;
|
||||||
|
if (__HAL_UART_GET_FLAG(&self->uart, UART_FLAG_RXNE) != RESET) {
|
||||||
|
// UART was stalled by flow ctrl: re-enable IRQ now we have room in buffer
|
||||||
|
__HAL_UART_ENABLE_IT(&self->uart, UART_IT_RXNE);
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
// no buffering
|
// no buffering
|
||||||
@ -347,6 +351,11 @@ STATIC bool uart_tx_wait(pyb_uart_obj_t *self, uint32_t timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
STATIC HAL_StatusTypeDef uart_tx_data(pyb_uart_obj_t *self, uint8_t *data, uint16_t len) {
|
STATIC HAL_StatusTypeDef uart_tx_data(pyb_uart_obj_t *self, uint8_t *data, uint16_t len) {
|
||||||
|
if (self->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) {
|
||||||
|
// CTS can hold off transmission for an arbitrarily long time. Apply
|
||||||
|
// the overall timeout rather than the character timeout.
|
||||||
|
return HAL_UART_Transmit(&self->uart, data, len, self->timeout);
|
||||||
|
}
|
||||||
// The timeout specified here is for waiting for the TX data register to
|
// The timeout specified here is for waiting for the TX data register to
|
||||||
// become empty (ie between chars), as well as for the final char to be
|
// become empty (ie between chars), as well as for the final char to be
|
||||||
// completely transferred. The default value for timeout_char is long
|
// completely transferred. The default value for timeout_char is long
|
||||||
@ -385,25 +394,25 @@ void uart_irq_handler(mp_uint_t uart_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (__HAL_UART_GET_FLAG(&self->uart, UART_FLAG_RXNE) != RESET) {
|
if (__HAL_UART_GET_FLAG(&self->uart, UART_FLAG_RXNE) != RESET) {
|
||||||
|
if (self->read_buf_len != 0) {
|
||||||
|
uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len;
|
||||||
|
if (next_head != self->read_buf_tail) {
|
||||||
|
// only read data if room in buf
|
||||||
#if defined(MCU_SERIES_F7)
|
#if defined(MCU_SERIES_F7)
|
||||||
int data = self->uart.Instance->RDR; // clears UART_FLAG_RXNE
|
int data = self->uart.Instance->RDR; // clears UART_FLAG_RXNE
|
||||||
#else
|
#else
|
||||||
int data = self->uart.Instance->DR; // clears UART_FLAG_RXNE
|
int data = self->uart.Instance->DR; // clears UART_FLAG_RXNE
|
||||||
#endif
|
#endif
|
||||||
data &= self->char_mask;
|
data &= self->char_mask;
|
||||||
if (self->read_buf_len != 0) {
|
|
||||||
uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len;
|
|
||||||
if (next_head != self->read_buf_tail) {
|
|
||||||
// only store data if room in buf
|
|
||||||
if (self->char_width == CHAR_WIDTH_9BIT) {
|
if (self->char_width == CHAR_WIDTH_9BIT) {
|
||||||
((uint16_t*)self->read_buf)[self->read_buf_head] = data;
|
((uint16_t*)self->read_buf)[self->read_buf_head] = data;
|
||||||
} else {
|
} else {
|
||||||
self->read_buf[self->read_buf_head] = data;
|
self->read_buf[self->read_buf_head] = data;
|
||||||
}
|
}
|
||||||
self->read_buf_head = next_head;
|
self->read_buf_head = next_head;
|
||||||
|
} else { // No room: leave char in buf, disable interrupt
|
||||||
|
__HAL_UART_DISABLE_IT(&self->uart, UART_IT_RXNE);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// TODO set flag for buffer overflow
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -427,6 +436,15 @@ STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k
|
|||||||
} else {
|
} else {
|
||||||
mp_printf(print, "%u", self->uart.Init.Parity == UART_PARITY_EVEN ? 0 : 1);
|
mp_printf(print, "%u", self->uart.Init.Parity == UART_PARITY_EVEN ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
if (self->uart.Init.HwFlowCtl) {
|
||||||
|
mp_printf(print, ", flow=");
|
||||||
|
if (self->uart.Init.HwFlowCtl & UART_HWCONTROL_RTS) {
|
||||||
|
mp_printf(print, "RTS%s", self->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS ? "|" : "");
|
||||||
|
}
|
||||||
|
if (self->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) {
|
||||||
|
mp_printf(print, "CTS");
|
||||||
|
}
|
||||||
|
}
|
||||||
mp_printf(print, ", stop=%u, timeout=%u, timeout_char=%u, read_buf_len=%u)",
|
mp_printf(print, ", stop=%u, timeout=%u, timeout_char=%u, read_buf_len=%u)",
|
||||||
self->uart.Init.StopBits == UART_STOPBITS_1 ? 1 : 2,
|
self->uart.Init.StopBits == UART_STOPBITS_1 ? 1 : 2,
|
||||||
self->timeout, self->timeout_char,
|
self->timeout, self->timeout_char,
|
||||||
@ -434,7 +452,7 @@ STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \method init(baudrate, bits=8, parity=None, stop=1, *, timeout=1000, timeout_char=0, read_buf_len=64)
|
/// \method init(baudrate, bits=8, parity=None, stop=1, *, timeout=1000, timeout_char=0, flow=0, read_buf_len=64)
|
||||||
///
|
///
|
||||||
/// Initialise the UART bus with the given parameters:
|
/// Initialise the UART bus with the given parameters:
|
||||||
///
|
///
|
||||||
@ -444,6 +462,7 @@ STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k
|
|||||||
/// - `stop` is the number of stop bits, 1 or 2.
|
/// - `stop` is the number of stop bits, 1 or 2.
|
||||||
/// - `timeout` is the timeout in milliseconds to wait for the first character.
|
/// - `timeout` is the timeout in milliseconds to wait for the first character.
|
||||||
/// - `timeout_char` is the timeout in milliseconds to wait between characters.
|
/// - `timeout_char` is the timeout in milliseconds to wait between characters.
|
||||||
|
/// - `flow` is RTS | CTS where RTS == 256, CTS == 512
|
||||||
/// - `read_buf_len` is the character length of the read buffer (0 to disable).
|
/// - `read_buf_len` is the character length of the read buffer (0 to disable).
|
||||||
STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
static const mp_arg_t allowed_args[] = {
|
static const mp_arg_t allowed_args[] = {
|
||||||
@ -847,7 +866,7 @@ STATIC mp_uint_t pyb_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t
|
|||||||
return MP_STREAM_ERROR;
|
return MP_STREAM_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait to be able to write the first character
|
// wait to be able to write the first character. EAGAIN causes write to return None
|
||||||
if (!uart_tx_wait(self, self->timeout)) {
|
if (!uart_tx_wait(self, self->timeout)) {
|
||||||
*errcode = EAGAIN;
|
*errcode = EAGAIN;
|
||||||
return MP_STREAM_ERROR;
|
return MP_STREAM_ERROR;
|
||||||
@ -859,6 +878,17 @@ STATIC mp_uint_t pyb_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t
|
|||||||
if (status == HAL_OK) {
|
if (status == HAL_OK) {
|
||||||
// return number of bytes written
|
// return number of bytes written
|
||||||
return size;
|
return size;
|
||||||
|
} else if (status == HAL_TIMEOUT) { // UART_WaitOnFlagUntilTimeout() disables RXNE interrupt on timeout
|
||||||
|
if (self->read_buf_len > 0) {
|
||||||
|
__HAL_UART_ENABLE_IT(&self->uart, UART_IT_RXNE); // re-enable RXNE
|
||||||
|
}
|
||||||
|
// return number of bytes written
|
||||||
|
if (self->char_width == CHAR_WIDTH_8BIT) {
|
||||||
|
return size - self->uart.TxXferCount - 1;
|
||||||
|
} else {
|
||||||
|
int written = self->uart.TxXferCount * 2;
|
||||||
|
return size - written - 2;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*errcode = mp_hal_status_to_errno_table[status];
|
*errcode = mp_hal_status_to_errno_table[status];
|
||||||
return MP_STREAM_ERROR;
|
return MP_STREAM_ERROR;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user