Module atsamd_hal::sercom::uart
source · [−]Expand description
Use the SERCOM peripheral for UART communications
Configuring an UART peripheral occurs in three steps. First, you must create
a set of Pads
for use by the peripheral. Next, you assemble pieces into
a Config
struct. After configuring the peripheral, you then enable
it, yielding a functional Uart
struct.
Transactions are performed using the serial
traits
from embedded HAL.
Pads
A Sercom
can use up to four Pin
s as peripheral Pad
s, but only
certain Pin
combinations are acceptable. In particular, all Pin
s
must be mapped to the same Sercom
(see the datasheet). This HAL makes it
impossible to use invalid Pin
/Pad
combinations, and the Pads
struct is responsible for enforcing these constraints.
A Pads
type takes five or six type parameters, depending on the chip.The
first type always specifies the Sercom
. On SAMx5x chips, the second type
specifies the IoSet
. The remaining four, DI
, DO
, CK
and SS
,
represent the Data In, Data Out, Sclk and SS pads respectively. Each of the
remaining type parameters is an OptionalPad
and defaults to NoneT
. A
Pad
is just a Pin
configured in the correct PinMode
that
implements IsPad
. The bsp_pins!
macro can be
used to define convenient type aliases for Pad
types.
use atsamd_hal::gpio::{PA08, PA09, AlternateC};
use atsamd_hal::sercom::{Sercom0, uart};
use atsamd_hal::typelevel::NoneT;
type Rx = Pin<PA08, AlternateC>;
type Tx = Pin<PA09, AlternateC>;
type Pads = uart::Pads<Sercom0, Rx, Tx>;
Instances of Pads
are created using the builder pattern. Start by
creating an empty set of Pads
using Default
. Then pass each
respective Pin
using the corresponding methods.
On SAMD21 and SAMx5x chips, the builder methods automatically convert each
pin to the correct PinMode
. But for SAMD11 chips, users must manually
convert each pin before calling the builder methods. This is a consequence
of inherent ambiguities in the SAMD11 SERCOM pad definitions. Specifically,
the same PinId
can correspond to two different PadNum
s for the
same Sercom
.
use atsamd_hal::pac::Peripherals;
use atsamd_hal::gpio::Pins;
use atsamd_hal::sercom::{Sercom0, uart};
let mut peripherals = Peripherals::take().unwrap();
let pins = Pins::new(peripherals.PORT);
let pads = uart::Pads::<Sercom0>::default()
.rx(pins.pa09)
.tx(pins.pa08);
To be accepted as ValidPads
, a set of Pads
must do two things:
- Specify a type for at least one of
RX
orTX
- Satisfy the
RxpoTxpo
trait (SAMD11/SAMD21), or theRxpo
andTxpo
traits (SAMx5x)
Config
Next, create a Config
struct, which represents the UART peripheral in
its disabled state. A Config
is specified with two type parameters: the
Pads
type; and a CharSize
, which defaults to EightBit
.
use atsamd_hal::gpio::{PA08, PA09};
use atsamd_hal::sercom::{Sercom0, uart};
use atsamd_hal::sercom::uart::{NineBit};
use atsamd_hal::typelevel::NoneT;
type Pads = uart::PadsFromIds<Sercom0, PA08, PA09>;
type Config = uart::Config<Pads, NineBit>;
Upon creation, the Config
takes ownership of both the Pads
struct
and the PAC Sercom
struct. It takes a reference to the PM, so that it
can enable the APB clock, and it takes a frequency to indicate the GCLK
configuration. Users are responsible for correctly configuring the GCLK.
use atsamd_hal::time::U32Ext;
let pm = peripherals.PM;
let sercom = peripherals.SERCOM0;
// Configure GCLK for 10 MHz
let freq = 10.mhz();
let config = uart::Config::new(&pm, sercom, pads, freq);
The Config
struct can configure the peripheral in one of two ways:
- A set of methods is provided to use in a builder pattern: for example
baud
,stop_bits
, etc. These methods takeself
and returnSelf
. - A set of methods is provided to use as setters: for example
set_baud
,set_stop_bits
, etc. These methods take&mut self
and return nothing.
In any case, the peripheral setup ends with a call to enable
, which
consumes the Config
and returns an enabled Uart
peripheral.
use atsamd_hal::sercom::uart::{StopBits, NineBit, BitOrder, BaudMode, Oversampling};
let uart = uart::Config::new(&mclk, sercom, pads, freq)
.baud(1.mhz(), BaudMode::Arithmetic(Oversampling::Bits16))
.char_size::<NineBit>()
.bit_order(BitOrder::LsbFirst)
.stop_bits(StopBits::TwoBits)
.enable();
Alternatively,
use atsamd_hal::sercom::uart::{StopBits, NineBit, BitOrder, BaudMode, Oversampling};
let uart = uart::Config::new(&mclk, sercom, pads, freq);
uart.set_baud(1.mhz(), BaudMode::Arithmetic(Oversampling::Bits16));
uart.set_char_size::<NineBit>();
uart.set_bit_order(BitOrder::LsbFirst);
uart.set_stop_bits(StopBits::TwoBits);
let uart = uart.enable();
To be accepted as a ValidConfig
, the Config
must have at least one
of Rx
or Tx
pads.
CharSize
The UART peripheral can be configured to use different character sizes. By
default, a Config
is configured with an EightBit
character size.
This can be changed through the char_size
method.
Changing the character normally also changes the Config
’s type.
Alternatively, you can also use a DynCharSize
through the
dyn_char_size
method. This enables you to
dynamically change the character size on the fly through the
set_dyn_char_size
method when calling
reconfigure
.
Reading the current configuration
It is possible to read the current configuration by using the getter methods
provided: for example get_baud
,
get_stop_bits
, etc.
Uart
and capabilities
Uart
structs can only be created from a Config
. They have two type
parameters: the first one represents the underlying Config
, while the
second represents the Uart
’s capabilities. The second type parameter can
be one of:
Rx
orRxDuplex
: Can perform receive transactionsTx
orTxDuplex
: Can perform transmit transactionsDuplex
: UART configured as duplex that can perform receive and transmit transactions. Additionally, thesplit
method can be called to return aUart<C, RxDuplex>, Uart<C, TxDuplex>)
tuple. See the Splitting section for more information.
The nature of the underlying Pads
contained inside Config
determines
the type returned by a call to enable
. If the pads only have a TX
pin
specified, then enable
will return a Uart<C, Tx>
. Similarly, If the
pads only have a RX
pin specified, then enable
will return a Uart<C, Rx>
. Finally, if both RX
and TX
pins are specified, then enable
will return a Uart<C, Duplex>
, which can be further split into a Uart<C, RxDuplex>
and a Uart<C, TxDuplex>
.
use atsamd_hal::gpio::{PA08, PA09};
use atsamd_hal::sercom::{Sercom0, uart};
use atsamd_hal::sercom::uart::NineBit;
use atsamd_hal::typelevel::NoneT;
// Assuming SAMD21 or SAMx5x
type Pads = uart::PadsFromIds<Sercom0, PA08, NoneT, PA09>;
type Config = uart::Config<Pads, NineBit>;
type UartRx = uart::Uart<Config, RxDuplex>;
type UartTx = uart::UartTx<Config, RxDuples>;
Only the Uart
struct can actually perform
transactions. To do so, use the embedded HAL traits, like
serial::Read
and serial::Write
.
use nb::block;
use embedded_hal::serial::Write;
block!(uart_tx.write(0x0fe));
UART flow control (CTS/RTS)
This module supports CTS and RTS pins.
The RTS
pin is a fully hardware-controlled output pin that gets deasserted
when:
- The USART receiver is disabled;
- The USART’s RX buffer is full.
The CTS
pin is an input pin that provides an interrupt when a change
(rising or falling edge) is detected on the corresponding Pad. This
interrupt, CTSIC
, can be enabled with the
enable_ctsic
method only when the corresponding
Config
has a CTS
pad specified. The
disable_ctsic
and
clear_ctsic
methods are also available under the same
conditions. This application note
provides more information about UART hardware flow control.
Splitting
A Uart<C, Duplex>
can be split into its RxDuplex
and TxDuplex
constituents:
use atsamd_hal::sercom::uart::Uart;
// Assume uart is a Uart<C, Duplex>
let (rx, tx) = uart.split();
Joining
When a Uart<C, Duplex>
has been split into its RxDuplex
and
TxDuplex
parts, these parts can be joined back into a Uart<C, Duplex>
by calling the join
function for Uart<C, Duplex>
. It takes a Uart<C, RxDuplex>
and a Uart<C, TxDuplex>
and moves them into a full Duplex
Uart
.
use atsamd_hal::sercom::uart::Uart;
// Assume rx is a Uart<C, RxDuplex> and tx is a Uart<C, TxDuplex>
let uart = Uart::join(rx, tx);
// uart is now a Uart<C, Duplex>
The AsMut<Uart<C, Duplex>>
trait is also implemented for (&mut Uart<C, RxDuplex>, &mut Uart<C, TxDuplex>)
. This is useful if you need an &mut Uart<C, Duplex>
but you only have a pair of &mut Uart<C, RxDuplex>
and
&mut Uart<C, TxDuplex>
. This can be leveraged to use the reconfigure
method when all you have is a pair of mutable references to the RxDuplex
and TxDuplex
halves.
use atsamd_hal::sercom::uart::Uart;
use atsamd_hal::time::*;
// Assume rx is a Uart<C, RxDuplex> and tx is a Uart<C, TxDuplex>
// Reconfigure peripheral from mutable references to RxDuplex
// and TxDuplex halves
(&mut rx, &mut tx).as_mut().reconfigure(|c| c.set_run_in_standby(false));
Reading the current configuration
The AsRef<Config<P, C>>
trait is implemented for Uart<Config<P, C>, D>
.
This means you can use the get_
methods implemented for Config
, since
they take an &self
argument.
// Assume uart is a Uart<C, D>
let (baud, baud_mode) = uart.as_ref().get_baud();
Disabling and reconfiguring
Some methods, such as disable
and reconfigure
, need to operate on
all parts of a UART at once. In practice, this means that these methods
operate on the type that was returned by enable
. This can be Uart<C, Rx>
, Uart<C, Tx>
, or Uart<C, Duplex>
, depending on how the
peripheral was configured.
The reconfigure
method gives out an &mut Config
reference, which can
then use the set_*
methods.
use atsamd_hal::sercom::uart::Uart;
use atsamd_hal::time::*;
// Assume config is a valid Duplex UART Config struct
let (rx, tx)= config.enable().split();
// Send/receive data with tx/rx halves...
// If the UART peripheral is configured in Duplex mode,
// the two constituting halves need to be joined back into
// a Uart<C, Duplex> before calling disable()
let uart = Uart::join(rx, tx);
// Reconfigure UART peripheral
uart.reconfigure(|c| c.set_run_in_standby(false));
// Disable UART peripheral
let config = uart.disable();
Non-supported advanced features
- Synchronous mode (USART) is not supported
- LIN mode is not supported (SAMx5x)
- 32-bit extension mode is not supported (SAMx5x). If you need to transfer
slices, consider using the DMA methods instead. The
dma
Cargo feature must be enabled.
Modules
Structs
D
, denotes what the struct’s Capability
is.Enums
enum
version of CharSize
CharSize
that can be changed on the flyConstants
Traits
enum
representing the capabilities of a UART peripheralenum
representing the UART character sizeenum
indicating a CharSize
that is not dynamicOptionalPad
types from a generic set
of Pads
Config
urationsType Definitions
DATA
register