uart_periodic_tx Module API Manual#
Overview#
uart_periodic_tx is an optional native module that periodically sends prepared complete UART frames using a hardware timer, and can also request an immediate send when a new frame is published. The timer callback only performs native UART write operations, does not execute Python code, and does not allocate GC memory in the callback.
It is suitable for scenarios requiring stable periodic transmission of data frames of varying lengths, such as sending a status frame every 50 ms. The application calls update() in a normal Python context to publish the next frame; each publication can have a different data length, but must not exceed the max_len set at construction time.
Note
This module is a UART periodic transmitter, not a general-purpose hardware timer task framework. It cannot schedule arbitrary Python functions in the timer, nor can it be used directly for network, SPI, or I2C transmission. Three internal buffers are used to prevent updates and sends from overwriting each other; they do not form a FIFO queue. The module only guarantees sending the most recently published data; rapidly calling update() in succession may cause unsent old data to be replaced by new data.
Enabling the Module#
This feature is disabled by default. Before compiling the firmware, execute the configuration command corresponding to your build environment in the source root directory:
# When using k230-builder
k230 make menuconfig
# When compiling directly on the host
make menuconfig
Enable the following option and recompile the firmware:
CanMV Micropython Components Configurations
Enable UART periodic TX module
Importing the module in firmware where this option is not enabled will raise an ImportError.
Importing the Module#
from machine import FPIOA, UART
from uart_periodic_tx import UARTPeriodicTx
Quick Start#
The following example maps UART3’s TX to IO50 and sends one frame every 50 ms. Choose UART and pins not occupied by REPL or other system functions based on the development board and board-level configuration.
from machine import FPIOA, UART
from uart_periodic_tx import UARTPeriodicTx
import time
fpioa = FPIOA()
fpioa.set_function(50, FPIOA.UART3_TXD)
tx = UARTPeriodicTx(
UART.UART3,
0,
50,
max_len=64,
baudrate=115200,
bits=UART.EIGHTBITS,
parity=UART.PARITY_NONE,
stop=UART.STOPBITS_ONE,
repeat_last=True,
)
try:
tx.update(b"\x01\x02\x03")
tx.start()
time.sleep_ms(1000)
sent_now = tx.update(b"\x10\x20\x30\x40\x50", send_now=True)
print("immediate submitted:", sent_now)
print("latest packet submitted:", tx.is_sent())
time.sleep_ms(1000)
finally:
tx.deinit()
The FPIOA UART TX pin mapping must be completed before calling start(). In the example above, the two published frames have lengths of 3 bytes and 5 bytes respectively. update() copies the passed-in buffer, so modifying the original bytearray after the call returns will not modify the published frame.
UARTPeriodicTx Class#
Constructor#
UARTPeriodicTx(
uart_id,
timer_id,
period=50,
*,
max_len=64,
baudrate=115200,
bits=UART.EIGHTBITS,
parity=UART.PARITY_NONE,
stop=UART.STOPBITS_ONE,
repeat_last=True,
)
Creates a periodic transmitter. The constructor allocates three sets of send buffers, but does not occupy a UART or hardware timer until start() is called.
Parameter |
Description |
|---|---|
|
UART hardware number, e.g., |
|
Hardware timer number. Software timer number |
|
Send period in ms, default |
|
Maximum length of a single frame, default |
|
UART baud rate, default |
|
Data bits, using the same constants as |
|
Parity mode, e.g., |
|
Stop bits, e.g., |
|
Whether to repeat sending the last frame when there is no new |
period can be passed as the third positional argument; the remaining UART configuration parameters must be passed as keyword arguments.
update Method#
tx.update(data, send_now=False)
Copies and publishes a complete frame of data. By default, it is sent by the hardware timer on subsequent triggers; when send_now=True is set, an immediate send of the frame will also be attempted after publication.
Parameters
Parameter |
Type |
Description |
|---|---|---|
|
Buffer object |
e.g., |
|
|
Keyword-only parameter, indicates whether to attempt an immediate send after publication, default |
Return Value: bool
When
send_now=False, returnsFalse, indicating no immediate send was performed; this return value does not indicate that subsequent timers will not send this frame.When
send_now=True, returnsTrueif the frame published in this call has been completely written to UART. ReturnsFalseif the UART is busy, a short write occurred, or a write error occurred; the data remains as the most recently published frame, and subsequent timers can retry sending it.
Exceptions
ValueError: The object has been released, or the data length exceedsmax_len.RuntimeError:send_now=Truewas set, but the transmitter has not been started or has already been stopped.OSError(EBUSY): All three buffers are temporarily unwritable. You can retry later in a normal Python context; do not call this method in amachine.Timercallback.
update() does not wait for the UART to complete sending on the line, nor does it guarantee that the new frame will appear at the next hardware trigger. If the send path has already taken the current buffer, the new frame will take effect on subsequent triggers. When repeat_last=True, the timer continuously sends the most recently successfully published complete frame; when repeat_last=False, the frame is sent once after being completely written, then waits for the next update().
When repeat_last=True and send_now=True are used simultaneously, the new frame is sent immediately, and the timer will still repeat the most recent frame on each periodic trigger. To send each update only once and have the timer retry if the immediate send fails, use repeat_last=False.
start Method#
tx.start()
Requests the hardware timer and UART, and begins periodic sending. At least one successful update() call should be made before calling this, otherwise timer triggers will be counted as skipped sends.
The same timer_id cannot be used simultaneously by machine.Timer or another UARTPeriodicTx. Resource conflicts will raise OSError(EBUSY).
stop Method#
tx.stop()
Stops periodic sending and releases the runtime resources of the hardware timer and UART, but retains the object and the allocated send buffers. start() can be called again afterward.
deinit Method#
tx.deinit()
Stops sending and releases all native resources and buffers. The object cannot be used again after this call. It is recommended to call this in a try / finally block to ensure hardware timers are released even on exception paths.
active Method#
tx.active()
Returns a boolean value indicating whether the hardware timer is running.
is_sent Method#
tx.is_sent()
Returns whether the most recently published non-empty frame has been successfully and completely written to UART at least once:
Returns
Falseafter object creation and before the first successful send.Returns
Falseafter each new frame is published but not yet sent.Returns
Trueafter an immediate send or a timer send completes a full write.Returns
Falseafter publishing empty data.
This status corresponds to the UART driver’s write() having accepted the complete frame; it does not indicate that the last bit has left the transmit pin, nor that the receiver has received or acknowledged it. Calling this method again after deinit() raises ValueError.
last_error Method#
tx.last_error()
Return Value: int
Returns the errno saved when the most recent UART write() returned a negative value. Returns 0 if no write error has occurred yet; subsequent successful writes do not clear the already recorded error code. Short writes are not negative errors and will not update this error code; check the short_write count returned by stats().
Printing logs or raising Python exceptions in a hardware timer callback is not safe, so the module records the error code atomically and allows normal Python code to query it via this method.
stats Method#
sent, short_write, errors, skipped = tx.stats()
Returns a cumulative statistics tuple:
Return Value |
Description |
|---|---|
|
Number of times the UART write returned the complete frame length, including timed sends, immediate sends, and repeated sends when |
|
Number of times the UART wrote a partial frame. |
|
Number of times the UART write returned a negative value. Call |
|
Number of send attempts that did not perform a UART write, e.g., UART was busy, no sendable frame has been published yet, or when |
Statistics are cumulative over the object’s lifetime; stop() and subsequent start() do not reset them.
Resources and Timing Constraints#
UARTPeriodicTxholds the native UART driver used for transmission. During operation, do not send data through the same UART usingmachine.UART.write(), nor reconfigure or release the same UART.Multiple
UARTPeriodicTxinstances may share the same UART, but the UART configuration must be exactly identical. When two transmission moments overlap, one of the triggers will be skipped and counted intoskipped. When predictable timing is required, only one periodic transmitter should be created for a given UART.Timer triggers are driven by a hardware timer, avoiding the send-trigger latency caused by the Python VM, GC, and Python callbacks; the actual time to complete a frame on the wire is still affected by baud rate, frame length, and UART driver state.
The serial transmission time of a frame should be significantly shorter than
period. If the frame is too long, the baud rate is too low, or the UART is busy,short_write,errors, orskippedmay occur.When
repeat_last=True, the logic analyzer will see the most recently and successfullyupdate()-ed frame being transmitted continuously. Whenrepeat_last=False, each successfulupdate()produces at most one complete frame transmission; triggers without new data are counted intoskipped.send_now=Trueonly initiates a single immediate write attempt and does not pause or re-align the hardware timer. When the immediate send time is close to a timer trigger, two frames may appear consecutively on the wire.
A complete loopback verification example is available at UARTPeriodicTx Hardware Periodic Transmission.
