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 Pins as peripheral Pads, but only
certain Pin combinations are acceptable. In particular, all Pins
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 PadNums 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
RXorTX - Satisfy the
RxpoTxpotrait (SAMD11/SAMD21), or theRxpoandTxpotraits (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 takeselfand returnSelf. - A set of methods is provided to use as setters: for example
set_baud,set_stop_bits, etc. These methods take&mut selfand 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:
RxorRxDuplex: Can perform receive transactionsTxorTxDuplex: Can perform transmit transactionsDuplex: UART configured as duplex that can perform receive and transmit transactions. Additionally, thesplitmethod 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
dmaCargo feature must be enabled.
Modules
Structs
D, denotes what the struct’s Capability is.Enums
enum version of CharSizeCharSize 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 PadsConfigurationsType Definitions
DATA register