Releases: esp-rs/esp-hal
v1.0.0-rc.0
To view all the documentation for all crates, along with their chip specific APIs, visit https://docs.espressif.com/projects/rust/.
Please note that only changes to the esp-hal package are tracked in these release notes, please check the esp-hal repo for other packages release notes.
Special migration note
As part of the espflash v4 release, we've updated the prebuilt esp-idf bootloaders. These new bootloaders require a small amount of metadata to boot correctly. We've made this easy to do, by depending on the esp-bootloader-esp-idf
support crate, and including the esp_app_desc!()
macro somewhere in your project. Please note if you were already depending on esp-bootloader-esp-idf
, as of v0.2.0 you must enable a chip feature.
Migration Guide from 1.0.0-beta.1 to 1.0.0-rc.0
AnyI2c and AnySpi have been moved to mode-specific submodules
-use esp_hal::i2c::AnyI2c;
+use esp_hal::i2c::master::AnyI2c;
AnySpi
has been separated into master and slave counterparts.
-use esp_hal::spi::AnySpi;
+use esp_hal::spi::master::AnySpi;
+// or:
+use esp_hal:spi::slave::AnySpi;
SPI DataMode has been moved into esp_hal::spi::master
-use esp_hal::spi::DataMode;
+use esp_hal::spi::master::DataMode;
ConfigError
variants have been changed
SPI master
UnsupportedFrequency
->FrequencyOutOfRange
UART
UnachievableBaudrate
->BaudrateNotAchievable
UnsupportedBaudrate
->BaudrateNotSupported
UnsupportedTimeout
->TimeoutTooLong
UnsupportedRxFifoThreshold
->RxFifoThresholdNotSupported
UnsupportedTxFifoThreshold
->TxFifoThresholdNotSupported
I2C master
FrequencyInvalid
->FrequencyOutOfRange
TimeoutInvalid
->TimeoutTooLong
Unstable driver migration guides
RMT Channel generic parameters have changed
Instead of being parameterized by a const CHANNEL: u8
, channels now take a generic
parameter Raw: RawChannelAccess<Dir>
where Dir=Tx
or Dir=Rx
that identifies the
channel.
+use esp_hal::rmt::{ConstChannelAccess, Rx, Tx};
+
-let channel: Channel<Blocking, 0> = {
+let channel: Channel<Blocking, ConstChannelAccess<Tx, 0>> = {
use esp_hal::rmt::TxChannelCreator;
rmt.channel0().configure(pin, TxChannelConfig::default())
};
-let channel: Channel<Blocking, 2> = {
+let channel: Channel<Blocking, ConstChannelAccess<Rx, 0>> = {
use esp_hal::rmt::RxChannelCreator;
rmt.channel2().configure(pin, RxChannelConfig::default())
};
RMT ChannelCreator traits have been re-arranged
TxChannelCreatorAsync
and RxChannelCreatorAsync
have been removed.
Instead, TxChannelCreator
and RxChannelCreator
now carry a Dm: DriverMode
generic parameter. ChannelCreator<Dm, CHANNEL>
thus implements TxChannelCreator<Dm>
and RxChannelCreator<Dm>
, respectively.
Additionally, the configure
methods have been renamed to configure_tx
and
configure_rx
to avoid trait disambiguation issues.
+use esp_hal::rmt::{RxChannelCreator, TxChannelCreator};
+
let rmt = rmt.into_async();
-let tx_channel = {
- use esp_hal::rmt::TxChannelCreatorAsync;
- rmt.channel0.configure(pin, tx_config)
-};
+ let tx_channel = rmt.channel0.configure_tx(pin, tx_config);
-let rx_channel = {
- use esp_hal::rmt::RxChannelCreatorAsync;
- rmt.channel2.configure(pin, rx_config)
-};
+ let rx_channel = rmt.channel2.configure_rx(pin, rx_config);
v1.0.0-beta.1
To view all the documentation for all crates, along with their chip specific APIs, visit https://docs.espressif.com/projects/rust/.
Please note that only changes to the esp-hal package are tracked in these release notes, please check the esp-hal repo for other packages release notes.
Migration Guide from v1.0.0-beta.0 to 1.0.0-beta.1
Peripheral singleton changes
As this is a conceptual change, we'll not list all affected types in this section.
AnyPeripheral
refers to allAny*
types at the same time -AnySpi
,AnyUart
, etc. Similarly,Driver
refers
to any matching peripheral driver.
Peripheral singletons (like SPI2
or GpioPin
) no longer implement Peripheral
. The Peripheral
trait and PeripheralRef
struct have been removed. The peripheral singletons instead have a
lifetime and implement the following methods:
steal
andclone_unchecked
to unsafely create them.reborrow
to safely create a copy with a shorter lifetime.degrade
has been removed in favour ofAnyPeripheral::from
.
Application-facing changes
Peripheral drivers no longer accept &mut singleton
.
Use reborrow
instead:
-let driver = Driver::new(&mut peripheral);
+let driver = Driver::new(peripheral.reborrow());
After dropping the driver, peripheral
should be accessible again as it used to be previously.
Peripheral driver changes
The Peripheral
and PeripheralRef
types no longer exist. The driver structs and constructors need
to be updated accordingly:
If the driver works with a single peripheral instance, for example AES
:
struct Driver<'d> {
- aes: PeripheralRef<'d, AES>,
+ aes: AES<'d>,
}
// ...
-fn new(aes: impl Peripheral<P = AES> + 'd)
+fn new(aes: AES<'d>)
If a driver works with multiple peripheral instances, e.g. SPI
:
struct Driver<'d> {
- spi: PeripheralRef<'d, AnySpi>,
+ spi: AnySpi<'d>,
}
// ...
-fn new(spi: impl Peripheral<P = impl Instance> + 'd)
+fn new(spi: impl Instance + Into<AnySpi<'d>>)
DMA changes
DMA channel types are now consistent with other peripheral singletons
For compatibility with third party libraries, as well as for consistency with other peripherals,
the DMA channel types (e.g. DmaChannel0
/Spi2DmaChannel
) have been replaced by
esp_hal::peripherals::DMA_channel<'d>
types.
-use esp_hal::gpio::DmaChannel0;
+use esp_hal::peripherals::DMA_CH0;
-use esp_hal::gpio::Spi2DmaChannel;
+use esp_hal::peripherals::DMA_SPI2;
GPIO changes
GPIO pins are now consistent with other peripheral singletons
For compatibility with third party libraries, as well as for consistency with other peripherals,
the GPIO pin types (GpioPin<N>
) have been replaced by separate esp_hal::peripherals::GPION<'d>
types.
-use esp_hal::gpio::GpioPin;
+use esp_hal::peripherals::{GPIO2, GPIO3};
-fn my_function(gpio2: GpioPin<2>, gpio3: GpioPin<3>) {...}
+fn my_function(gpio2: GPIO2<'_>, gpio3: GPIO3<'_>) {...}
GPIO matrix (interconnect) has been reworked
The GPIO matrix implementation has been reworked to solve known issues. The following changes have
been made:
InputConnection
andOutputConnection
have been removed and their functionality merged intoInputSignal
andOutputSignal
.- The
InputSignal
andOutputSignal
types now have a lifetime. This lifetime prevents them to be live for longer than the GPIO that was used to create them. - Because
InputSignal
andOutputSignal
can now represent constant levels, thenumber
method now returnsOption<u8>
. - The way to invert inputs and outputs has been changed:
InputSignal::inverted
has been renamed toInputSignal::with_input_inverter
.OutputSignal::inverted
has been renamed toOutputSignal::with_output_inverter
.InputSignal::invert
andOutputSignal::invert
have been removed.OutputSignal
now has aninverted_input
property, which can be changed by usingwith_output_inverter
.- The signals have
is_{input, output}_inverted
methods to read the state that will be used when configuring the hardware.
- Users can now force a signal through the GPIO matrix.
- The
enable_input
andenable_output
methods have been renamed toset_input_enable
andset_output_enable
. - A new
PeripheralSignal
trait has been added, which allows us to no longer implyPeripheralInput
forPeripheralOutput
types. - Functions that accept
PeripheralInput
no longer acceptPeripheralOutput
implementations. - Removed
Input::into_peripheral_output
andOutput::peripheral_input
functions. The drivers can be converted intoFlex
which offers both ways to acquire signal types, and more. - Various "unreasonable" signal conversions have been removed.
OutputSignal
can no longer be converted intoInputSignal
. InputSignal
andOutputSignal
now have a "frozen" state. If they are created by a pin driver, they start frozen. If they are created by splitting a GPIO pin, they start unfrozen. Frozen signals will not be configured by peripheral drivers.- Splitting GPIO pins into signals is now unsafe.
Flex
can now be (unsafely) split intoInput
andOutput
drivers withsplit_into_drivers
.- Converting
AnyPin
into signals will now reset the pin's configuration. Creating an InputSignal will now enable the pin's input buffer.
Flex API surface has been simplified
The enable_input
method has been renamed to set_input_enable
.
The Flex
driver no longer provides the following functions:
- set_as_input
- set_as_output
- set_drive_strength
- set_as_open_drain
- pull_direction
The individual configurations can be set via apply_input_config
and apply_output_config
.
The input buffer and output driver can be separately enabled via set_input_enable
and
set_output_enable
.
Normally you only need to configure your pin once, after which changing modes can be done by calling
set_input_enable
and/or set_output_enable
.
- flex.set_as_input(pull_direction);
+ flex.apply_input_config(&InputConfig::default().with_pull(pull_direction)); // only if needed
+ flex.set_output_enable(false);
+ flex.set_input_enable(true);
- flex.set_as_output(); // or set_as_open_drain(pull_direction)
+ flex.apply_output_config(&OutputConfig::default().with_drive_mode(open_drain_or_push_pull)); // only if needed
+ flex.set_input_enable(false); // optional
+ flex.set_level(initial_level); // optional
+ flex.set_output_enable(true);
Interrupt handling changes
The interrupt status bits are no longer cleared automatically. Depending on your use case, you will
need to either do this yourself, or disable the pin's interrupt.
If you want your interrupt to keep firing, clear the interrupt status. Keep in mind that
this affects is_interrupt_set
.
#[handler]
pub fn interrupt_handler() {
critical_section::with(|cs| {
let pin = INPUT_PIN.borrow_ref_mut(cs).as_mut().unwrap();
+ pin.clear_interrupt();
});
}
If you want your interrupt to fire once per listen
call, disable the interrupt.
#[handler]
pub fn interrupt_handler() {
critical_section::with(|cs| {
let pin = INPUT_PIN.borrow_ref_mut(cs).as_mut().unwrap();
+ pin.unlisten();
});
}
I2S driver now takes DmaDescriptor
s later in construction
let i2s = I2s::new(
peripherals.I2S0,
Standard::Philips,
DataFormat::Data16Channel16,
Rate::from_hz(44100),
dma_channel,
- rx_descriptors,
- tx_descriptors,
);
let i2s_tx = i2s
.i2s_tx
.with_bclk(peripherals.GPIO2)
.with_ws(peripherals.GPIO4)
.with_dout(peripherals.GPIO5)
- .build();
+ .build(tx_descriptors);
let i2s_rx = i2s
.i2s_rx
.with_bclk(peripherals.GPIO2)
.with_ws(peripherals.GPIO4)
.with_din(peripherals.GPIO5)
- .build();
+ .build(rx_descriptors);
PARL IO driver changes
ParlIoFullDuplex
, ParlIoTxOnly
and ParlIoRxOnly
have been merged into ParlIo
- let parl_io = ParlIoFullDuplex::new(peripherals.PARL_IO, dma_channel)?;
- let parl_io = ParlIoTxOnly::new(peripherals.PARL_IO, dma_channel)?;
- let parl_io = ParlIoRxOnly::new(peripherals.PARL_IO, dma_channel)?;
+ let parl_io = ParlIo::new(peripherals.PARL_IO, dma_channel)?;
Construction no longer asks for references
let mut parl_io_tx = parl_io
.tx
.with_config(
- &mut pin_conf,
- &mut clock_pin,
+ pin_conf,
+ clock_pin,
0,
SampleEdge::Normal,
BitPackOrder::Msb,
)?;
Construction options are passed via config
+let config = RxConfig::default()
+ .with_frequency(Rate::from_mhz(20))
+ .with_bit_order(BitPackOrder::Msb);
let mut parl_io_rx = parl_io
.rx
.with_config(
pin_conf,
clock_pin,
- BitPackOrder::Msb,
- None,
+ config,
)?;
+let config = TxConfig::default()
+ .with_frequency(Rate::from_mhz(20))
+ .with_sample_edge(SampleEdge::Normal)
+ .with_bit_order(BitPackOrder::Msb);
let mut parl_io_tx = parl_io
.tx
.with_config(
pin_conf,
clock_pin,
- 0,
- SampleEdge::Normal,
- BitPackOrder::Msb,
+ config,
)?;
SPI Slave driver now uses the newer DMA APIs
let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000);
+ let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
+ let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
- let transfer = spi.transfer(...
v1.0.0-beta.0
To view all the documentation for all crates, along with their chip specific APIs, visit https://docs.espressif.com/projects/rust/.
Please note that only changes to the esp-hal
package are tracked in these release notes.
Migration Guide from v0.23.x to v1.0.0-beta.0
Driver stability
Unstable parts of esp-hal are gated behind the unstable
feature. Previously, this feature
was enabled by default, but starting with this release, it is no longer the case.
The unstable
feature itself is unstable, we might change the way we hide APIs without notice.
Unstable APIs are not covered by semver guarantees, they may be changed or removed at any time.
Please refer to the documentation to see which APIs are marked as unstable.
esp-hal = { version = "1.0.0-beta.0" , features = [
"esp32c6",
+ "unstable"
]}
Async
drivers can no longer be sent between cores and executors
To work around this limitation, send the blocking driver, and configure it into Async
mode
in the target context.
#[embassy_executor::task]
-async fn interrupt_driven_task(mut spi: Spi<'static, Async>) {
+async fn interrupt_driven_task(spi: Spi<'static, Blocking>) {
+ let mut spi = spi.into_async();
...
}
let spi = Spi::new(...)
.unwrap()
// ...
- .into_async();
send_spawner.spawn(interrupt_driven_task(spi)).unwrap();
RMT changes
Configurations structs now support the builder-lite pattern
The TxChannelConfig
and RxChannelConfig
structs now support the builder-lite pattern.
Thus, explicit initialization of all fields can be replaced by only the necessary setter methods:
let mut channel = rmt
.channel0
.configure(
peripherals.GPIO1,
- TxChannelConfig {
- clk_divider: 1,
- idle_output_level: false,
- idle_output: false,
- carrier_modulation: false,
- carrier_high: 1,
- carrier_low: 1,
- carrier_level: false,
- },
+ TxChannelConfig::default().with_clk_divider(1)
)
.unwrap();
Some configuration fields now take gpio::Level
instead of bool
Fields related to the carrier level in the channel configuration structs now
take the more descriptive gpio::Level
type instead of a plain bool
.
let mut tx_channel = rmt
.channel0
.configure(
peripherals.GPIO1,
TxChannelConfig::default()
.with_clk_divider(1)
- .with_idle_output_level(false)
+ .with_idle_output_level(Level::Low)
- .with_carrier_level(true)
+ .with_carrier_level(Level::High)
)
.unwrap();
let mut rx_channel = rmt
.channel1
.configure(
peripherals.GPIO2,
RxChannelConfig::default()
.with_clk_divider(1)
.with_carrier_modulation(true)
.with_carrier_high(1)
.with_carrier_low(1)
- .with_carrier_level(false),
+ .with_carrier_level(Level::Low),
)
.unwrap();
PulseCode
now uses gpio::Level
instead of bool
to specify output levels
The more descriptive gpio::Level
enum is now used to specify output levels of PulseCode
:
+ use esp_hal::gpio::Level;
+
- let code = PulseCode::new(true, 200, false, 50);
+ let code = PulseCode::new(Level::High, 200, Level::Low, 50);
Global driver changes
The _bytes
postfix of driver methods that take a byte slice have been removed.
- uart0.write_bytes(b"Hello world!")?;
+ uart0.write(b"Hello world!")?;
The peripherals::Interrupts
enum is no longer available. Users (mostly third party driver
developers) will need to use the PAC crates directly.
UART changes
Uart write
is now blocking and return the number of bytes written. read
will block until it fills at least one byte into the buffer with received bytes, use read_buffered_bytes
to read the available bytes without blocking.
e.g.
- uart.write(0x42).ok();
- let read = block!(ctx.uart.read());
+ let data: [u8; 1] = [0x42];
+ uart.write(&data).unwrap();
+ let mut byte = [0u8; 1];
+ uart.read(&mut byte).unwrap();
UART errors have been split into TxError
and RxError
.
read_*
and write_*
functions now return different types. In practice this means you no longer
need to check for RX errors that can't be returned by write_*
.
The error type used by embedded-io
has been updated to reflect this. A new IoError
enum has been
added for embedded-io
errors associated to the unsplit Uart
driver. On Uart
(but not UartRx
or UartTx
) TX-related trait methods return IoError::Tx(TxError)
, while RX-related methods return
IoError::Rx(RxError)
.
UART halves have their configuration split, too
Uart::Config
structure now contains separate RxConfig
and TxConfig
:
- let config = Config::default().with_rx_fifo_full_threshold(30);
+ let config = Config::default()
+ .with_rx(RxConfig::default()
+ .with_fifo_full_threshold(30)
+ );
timer::wait
is now blocking
periodic.start(100.millis()).unwrap();
- nb::block!(periodic.wait()).unwrap();
+ periodic.wait();
SPI Changes
spi::DataMode
changed the meaning of DataMode::Single
- it now means 3-wire SPI (using one data line).
Use DataMode::SingleTwoDataLines
to get the previous behavior.
- DataMode::Single,
+ DataMode::SingleTwoDataLines,
Spi
now offers both, with_mosi
and with_sio0
. Consider using with_sio
for half-duplex SPI except for [DataMode::SingleTwoDataLines] or for a mixed-bus.
Removed flip-link
Feature
The flip-link
feature is removed and replaced by the ESP_HAL_CONFIG_FLIP_LINK
option.
Cargo.toml
- esp-hal = { version = "0.23.0", features = ["flip-link"]}
+ esp-hal = "0.23.0"
config/config.toml
[env]
+ ESP_HAL_CONFIG_FLIP_LINK = "true"
Removed psram-quad
/prsram-octal
Feature
The features psram-quad
/prsram-octal
are replaced by a single psram
feature and an additional config option (ESP_HAL_CONFIG_PSRAM_MODE
).
ESP_HAL_CONFIG_PSRAM_MODE
defaults to quad
and (for ESP32-S3) also allows octal
.
Cargo.toml
- esp-hal = { version = "0.23.0", features = ["psram-octal"]}
+ esp-hal = { version = "0.23.0", features = ["psram"]}
config/config.toml
[env]
+ ESP_HAL_CONFIG_PSRAM_MODE = "octal"
PARL_IO changes
Parallel IO now uses the newer DMA Move API.
Changes on the TX side
let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000);
+ let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
- let transfer = parl_io_tx.write_dma(&tx_buffer).unwrap();
- transfer.wait().unwrap();
+ let transfer = parl_io_tx.write(dma_tx_buf.len(), dma_tx_buf).unwrap();
+ (result, parl_io_tx, dma_tx_buf) = transfer.wait();
+ result.unwrap();
Changes on the RX side
let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32000, 0);
+ let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
- let transfer = parl_io_rx.read_dma(&mut rx_buffer).unwrap();
- transfer.wait().unwrap();
+ let transfer = parl_io_rx.read(Some(dma_rx_buf.len()), dma_rx_buf).unwrap();
+ (_, parl_io_rx, dma_rx_buf) = transfer.wait();
On the RX side, the EofMode
is now decided at transfer time, rather than config time.
EofMode::ByteLen
->Some(<number of bytes to receive>)
EofMode::EnableSignal
->None
GPIO changes
GPIO drivers now take configuration structs.
- Input::new(peripherals.GPIO0, Pull::Up);
+ Input::new(peripherals.GPIO0, InputConfig::default().with_pull(Pull::Up));
- Output::new(peripherals.GPIO0, Level::Low);
+ Output::new(peripherals.GPIO0, Level::Low, OutputConfig::default());
The OutputOpenDrain driver has been removed. You can use Output
instead with
DriveMode::OpenDrain
. The input-related methods of OutputOpenDrain
(level
,
is_high
, is_low
) are available through the (unstable) Flex
driver.
- OutputOpenDrain::new(peripherals.GPIO0, Level::Low);
+ Output::new(
peripherals.GPIO0,
Level::Low,
OutputConfig::default()
.with_drive_mode(DriveMode::OpenDrain),
);
AES DMA driver changes
AES now uses the newer DMA move API.
let (output, rx_descriptors, input, tx_descriptors) = dma_buffers!(32000);
+ let mut output = DmaRxBuf::new(rx_descriptors, output).unwrap();
+ let mut input = DmaTxBuf::new(tx_descriptors, input).unwrap();
let mut aes = Aes::new(peripherals.AES).with_dma(
dma_channel,
- rx_descriptors,
- tx_descriptors,
);
let transfer = aes
.process(
- &input,
- &mut output,
+ output.len().div_ceil(16), // Number of blocks
+ output,
+ input,
Mode::Encryption128,
CipherMode::Ecb,
keybuf,
)
+ .map_err(|e| e.0)
.unwrap();
transfer.wait();
I2C Changes
All async functions now include the _async
postfix. Additionally the non-async functions are now available in async-mode.
- let result = i2c.write_read(0x77, &[0xaa], &mut data).await;
+ let result = i2c.write_read_async(0x77, &[0xaa], &mut data).await;
ADC Changes
The ADC driver has gained a new Async
/Blocking
mode parameter.
NOTE: Async support is only supported in ESP32C3 and ESP32C6 for now
- Adc<'d, ADC>
+ Adc<'d, ADC, Blocking>
time API changes
ESP-HAL no longer publicly exposes fugit
and no longer expos...
v0.23.1
0.23.0
Please note that only changes to the esp-hal
package are tracked in these release notes.
Migration Guide
Starting with this release, unstable parts of esp-hal will be gated behind the unstable
feature.
The unstable
feature itself is unstable, we might change the way we hide APIs without notice.
Unstable APIs are not covered by semver guarantees, they may break even between patch releases.
Please refer to the documentation to see which APIs are marked as unstable.
DMA changes
Accessing channel objects
DMA channels are now available through the Peripherals
struct, which is returned
by esp_hal::init()
. The channels themselves have been renamed to match other peripheral singletons.
- ESP32-C2, C3, C6, H2 and S3:
channelX -> DMA_CHX
- ESP32 and S2:
spiXchannel -> DMA_SPIX
,i2sXchannel -> DMA_I2SX
-let dma = Dma::new(peripherals.DMA);
-let channel = dma.channel2;
+let channel = peripherals.DMA_CH2;
Channel configuration changes
configure_for_async
andconfigure
have been removed- PDMA devices (ESP32, ESP32-S2) provide no channel configurability
- GDMA devices provide
set_priority
to change DMA in/out channel priority
let mut spi = Spi::new_with_config(
peripherals.SPI2,
Config::default(),
)
// other setup
-.with_dma(dma_channel.configure(false, DmaPriority::Priority0));
+.with_dma(dma_channel);
+dma_channel.set_priority(DmaPriority::Priority1);
let mut spi = Spi::new_with_config(
peripherals.SPI2,
Config::default(),
)
// other setup
-.with_dma(dma_channel.configure(false, DmaPriority::Priority1));
+.with_dma(dma_channel);
Burst mode configuration
Burst mode is now a property of buffers, instead of DMA channels. Configuration can be done by
calling set_burst_config
on buffers that support it. The configuration options and the
corresponding BurstConfig
type are device specfic.
Usability changes affecting applications
Individual channels are no longer wrapped in Channel
, but they implement the DmaChannel
trait.
This means that if you want to split them into an rx
and a tx
half (which is only supported on
the H2, C6 and S3 currently), you can't move out of the channel but instead you need to call
the split
method.
-let tx = channel.tx;
+use esp_hal::dma::DmaChannel;
+let (rx, tx) = channel.split();
The Channel
types remain available for use in peripheral drivers.
It is now simpler to work with DMA channels in generic contexts. esp-hal now provides convenience
traits and type aliasses to specify peripheral compatibility. The ChannelCreator
types have been
removed, further simplifying use.
For example, previously you may have needed to write something like this to accept a DMA channel
in a generic function:
fn new_foo<'d, T>(
dma_channel: ChannelCreator<2>, // It wasn't possible to accept a generic ChannelCreator.
peripheral: impl Peripheral<P = T> + 'd,
)
where
T: SomePeripheralInstance,
ChannelCreator<2>: DmaChannelConvert<<T as DmaEligible>::Dma>,
{
let dma_channel = dma_channel.configure_for_async(false, DmaPriority::Priority0);
let driver = PeripheralDriver::new(peripheral, config).with_dma(dma_channel);
// ...
}
From now on a similar, but more flexible implementation may look like:
fn new_foo<'d, T, CH>(
dma_channel: impl Peripheral<P = CH> + 'd,
peripheral: impl Peripheral<P = T> + 'd,
)
where
T: SomePeripheralInstance,
CH: DmaChannelFor<T>,
{
// Optionally: dma_channel.set_priority(DmaPriority::Priority2);
let driver = PeripheralDriver::new(peripheral, config).with_dma(dma_channel);
// ...
}
Usability changes affecting third party peripheral drivers
If you are writing a driver and need to store a channel in a structure, you can use one of the
ChannelFor
type aliasses.
struct Aes<'d> {
- channel: ChannelTx<'d, Blocking, <AES as DmaEligible>::Dma>,
+ channel: ChannelTx<'d, Blocking, PeripheralTxChannel<AES>>,
}
Timer changes
The low level timers, SystemTimer
and TimerGroup
are now "dumb". They contain no logic for operating modes or trait implementations (except the low level Timer
trait).
Timer drivers - OneShotTimer
& PeriodicTimer
Both drivers now have a Mode
parameter. Both also type erase the underlying driver by default, call new_typed
to retain the type.
- OneShotTimer<'static, systimer::Alarm>;
+ OneShotTimer<'static, Blocking>;
- PeriodicTimer<'static, systimer::Alarm>;
+ PeriodicTimer<'static, Blocking>;
SystemTimer
let systimer = SystemTimer::new(peripherals.SYSTIMER);
- static UNIT0: StaticCell<SpecificUnit<'static, 0>> = StaticCell::new();
- let unit0 = UNIT0.init(systimer.unit0);
- let frozen_unit = FrozenUnit::new(unit0);
- let alarm0 = Alarm::new(systimer.comparator0, &frozen_unit);
- alarm0.set_period(1u32.secs());
+ let alarm0 = systimer.alarm0;
+ let mut timer = PeriodicTimer::new(alarm0);
+ timer.start(1u64.secs());
TIMG
Timer group timers have been type erased.
- timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking>
+ timg::Timer
ETM usage has changed
Timer dependant ETM events should be created prior to initializing the timer with the chosen driver.
let task = ...; // ETM task
let syst = SystemTimer::new(peripherals.SYSTIMER);
let alarm0 = syst.alarm0;
- alarm0.load_value(1u64.millis()).unwrap();
- alarm0.start();
- let event = Event::new(&mut alarm0);
+ let event = Event::new(&alarm0);
+ let timer = OneShotTimer::new(alarm0);
+ timer.schedule(1u64.millis()).unwrap();
let _configured_channel = channel0.setup(&event, &task);
PSRAM is now initialized automatically
Calling esp_hal::initialize
will now configure PSRAM if either the quad-psram
or octal-psram
is enabled. To retrieve the address and size of the initialized external memory, use
esp_hal::psram::psram_raw_parts
, which returns a pointer and a length.
-let peripherals = esp_hal::init(esp_hal::Config::default());
-let (start, size) = esp_hal::psram::init_psram(peripherals.PSRAM, psram_config);
+let peripherals = esp_hal::init({
+ let mut config = esp_hal::Config::default();
+ config.psram = psram_config;
+ config
+});
+let (start, size) = esp_hal::psram::psram_raw_parts(&peripherals.PSRAM, psram);
The usage of esp_alloc::psram_allocator!
remains unchanged.
embedded-hal 0.2.* is not supported anymore.
As per rust-embedded/embedded-hal#640, our driver no longer implements traits from embedded-hal 0.2.x
.
Analogs of all traits from the above mentioned version are available in embedded-hal 1.x.x
- use embedded_hal_02::can::Frame;
+ use embedded_can::Frame;
- use embedded_hal_02::digital::v2::OutputPin;
- use embedded_hal_02::digital::v2::ToggleableOutputPin;
+ use embedded_hal::digital::OutputPin;
+ use embedded_hal::digital::StatefulOutputPin;
- use embedded_hal_02::serial::{Read, Write};
+ use embedded_hal_nb::serial::{Read, Write};
You might also want to check the full official embedded-hal
migration guide:
https://github.com/rust-embedded/embedded-hal/blob/master/docs/migrating-from-0.2-to-1.0.md
Interrupt related reshuffle
- use esp_hal::InterruptConfigurable;
- use esp_hal::DEFAULT_INTERRUPT_HANDLER;
+ use esp_hal::interrupt::InterruptConfigurable;
+ use esp_hal::interrupt::DEFAULT_INTERRUPT_HANDLER;
Driver constructors now take a configuration and are fallible
The old new_with_config
constructor have been removed, and new
constructors now always take
a configuration structure. They have also been updated to return a ConfigError
if the configuration
is not compatible with the hardware.
-let mut spi = Spi::new_with_config(
+let mut spi = Spi::new(
peripherals.SPI2,
Config {
frequency: 100.kHz(),
mode: SpiMode::_0,
..Config::default()
},
-);
+)
+.unwrap();
let mut spi = Spi::new(
peripherals.SPI2,
+ Config::default(),
-);
+)
+.unwrap();
Peripheral instance type parameters and new_typed
constructors have been removed
Call new
instead and remove the type parameters if you've used them.
-let mut spi: Spi<'lt, SPI2> = Spi::new_typed(..).unwrap();
+let mut spi: Spi<'lt> = Spi::new(..).unwrap();
LCD_CAM configuration changes
cam
now has aConfig
strurct that contains frequency, bit/byte order, VSync filter options.- DPI, I8080:
frequency
has been moved intoConfig
.
+let mut cam_config = cam::Config::default();
+cam_config.frequency = 1u32.MHz();
cam::Camera::new(
lcd_cam.cam,
dma_rx_channel,
pins,
- 1u32.MHz(),
+ cam_config,
)
SpiDma now requires you specify the transfer length explicitly
dma_tx_buf.set_length(5 /* or greater */);
- spi_dma.write(dma_tx_buf);
+ spi_dma.write(5, dma_tx_buf);
dma_rx_buf.set_length(5 /* or greater */);
- spi_dma.read(dma_rx_buf);
+ spi_dma.read(5, dma_rx_buf);
dma_rx_buf.set_length(5 /* or greater */);
dma_tx_buf.set_length(5 /* or greater */);
- spi_dma.transfer(dma_rx_buf, dma_tx_buf);
+ spi_dma.transfer(5, dma_rx_buf, 5, dma_tx_buf);
I2C Error changes
To avoid abbreviations and contractions (as per the esp-hal guidelines), some error variants have changed
- Error::ExecIncomplete
+ Error::ExecutionIncomplete
- Error::CommandNrExceeded
+ Error::CommandNumberExceeded
- Error::ExceedingFifo
+ Error::FifoExceeded
- Error::TimeOut
+ Error::Timeout
- Error::InvalidZeroLengt...
0.22.0
Please note that only changes to the esp-hal
package are tracked in these release notes.
Migration Guide
IO changes
GPIO pins are now accessible via Peripherals
let peripherals = esp_hal::init(Default::default());
-let io = Io::new(peripherals.GPIO, peripherals.IOMUX);
-let pin = io.pins.gpio5;
+let pin = peripherals.GPIO5;
Io
constructor changes
new_with_priority
andnew_no_bind_interrupts
have been removed.
Useset_priority
to configure the GPIO interrupt priority.
We no longer overwrite interrupt handlers set by user code during initialization.new
no longer takesperipherals.GPIO
Removed async
-specific constructors
The following async-specific constuctors have been removed:
configure_for_async
DMA channel constructorsTwaiConfiguration::new_async
andTwaiConfiguration::new_async_no_transceiver
I2c::new_async
LcdCam::new_async
UsbSerialJtag::new_async
Rsa::new_async
Rmt::new_async
Uart::new_async
,Uart::new_async_with_config
UartRx::new_async
,UartRx::new_async_with_config
UartTx::new_async
,UartTx::new_async_with_config
You can use the blocking counterparts, then call into_async
on the returned peripheral instead.
-let mut config = twai::TwaiConfiguration::new_async(
+let mut config = twai::TwaiConfiguration::new(
peripherals.TWAI0,
loopback_pin.peripheral_input(),
loopback_pin,
twai::BaudRate::B1000K,
TwaiMode::SelfTest,
-);
+).into_async();
Some drivers were implicitly configured to the asyncness of the DMA channel used to construct them.
This is no longer the case, and the following drivers will always be created in blocking mode:
i2s::master::I2s
spi::master::SpiDma
andspi::master::SpiDmaBus
Peripheral types are now optional
You no longer have to specify the peripheral instance in the driver's type for the following
peripherals:
- SPI (both master and slave)
- I2S
- I2C
- TWAI
- UART
-Spi<'static, SPI2, FullDuplexMode>
+Spi<'static, FullDuplexMode>
-SpiDma<'static, SPI2, HalfDuplexMode, Blocking>
+SpiDma<'static, HalfDuplexMode, Blocking>
-I2sTx<'static, I2S0, Async>
+I2sTx<'static, Async>
Note that you may still specify the instance if you need to. To do this, we provide _typed
versions of the constructors (for example: new_typed
, new_half_duplex_typed
). Please note that
the peripheral instance has been moved to the last generic parameter position.
let spi: Spi<'static, FullDuplexMode, SPI2> = Spi::new_typed(peripherals.SPI2, 1.MHz(), SpiMode::Mode0);
I2C changes
The I2C master driver and related types have been moved to esp_hal::i2c::master
.
The with_timeout
constructors have been removed. new
and new_typed
now take a Config
struct
with the available configuration options.
- The default configuration is now:
- bus frequency: 100 kHz
- timeout: about 10 bus clock cycles
The constructors no longer take pins. Use with_sda
and with_scl
instead.
-use esp_hal::i2c::I2c;
+use esp_hal::i2c::{Config, I2c};
-let i2c = I2c::new_with_timeout(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 100.kHz(), timeout);
+I2c::new_with_config(
+ peripherals.I2C0,
+ {
+ let mut config = Config::default();
+ config.frequency = 100.kHz();
+ config.timeout = timeout;
+ config
+ },
+)
+.with_sda(io.pins.gpio4)
+.with_scl(io.pins.gpio5);
The calculation of I2C timeout has changed
Previously, I2C timeouts were counted in increments of I2C peripheral clock cycles. This meant that
the configure value meant different lengths of time depending on the device. With this update, the
I2C configuration now expects the timeout value in number of bus clock cycles, which is consistent
between devices.
ESP32 and ESP32-S2 use an exact number of clock cycles for its timeout. Other MCUs, however, use
the 2^timeout
value internally, and the HAL rounds up the timeout to the next appropriate value.
Changes to half-duplex SPI
The HalfDuplexMode
and FullDuplexMode
type parameters have been removed from SPI master and slave
drivers. It is now possible to execute half-duplex and full-duplex operations on the same SPI bus.
Driver construction
- The
Spi::new_half_duplex
constructor has been removed. Usenew
(ornew_typed
) instead. - The
with_pins
methods have been removed. Use the individualwith_*
functions instead. - The
with_mosi
andwith_miso
functions now take input-output peripheral signals to support half-duplex mode.
- let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0)
- .with_pins(sck, mosi, miso, sio2, sio3, cs);
+ let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0)
+ .with_sck(sck)
+ .with_cs(cs)
+ .with_mosi(mosi)
+ .with_miso(miso)
+ .with_sio2(sio2)
+ .with_sio3(sio3);
Transfer functions
The Spi<'_, SPI, HalfDuplexMode>::read
and Spi<'_, SPI, HalfDuplexMode>::write
functions have been replaced by
half_duplex_read
and half_duplex_write
.
let mut data = [0u8; 2];
let transfer = spi
- .read(
+ .half_duplex_read(
SpiDataMode::Single,
Command::Command8(0x90, SpiDataMode::Single),
Address::Address24(0x000000, SpiDataMode::Single),
0,
&mut data,
)
.unwrap();
let transfer = spi
- .write(
+ .half_duplex_write(
SpiDataMode::Quad,
Command::Command8(write as u16, command_data_mode),
Address::Address24(
write as u32 | (write as u32) << 8 | (write as u32) << 16,
SpiDataMode::Quad,
),
0,
dma_tx_buf,
)
.unwrap();
Slave-mode SPI
Driver construction
The constructors no longer accept pins. Use the with_pin_name
setters instead.
let mut spi = Spi::new(
peripherals.SPI2,
- sclk,
- mosi,
- miso,
- cs,
SpiMode::Mode0,
-);
+)
+.with_sclk(sclk)
+.with_mosi(mosi)
+.with_miso(miso)
+.with_cs(cs);
UART event listening
The following functions have been removed:
listen_at_cmd
unlisten_at_cmd
listen_tx_done
unlisten_tx_done
listen_rx_fifo_full
unlisten_rx_fifo_full
at_cmd_interrupt_set
tx_done_interrupt_set
rx_fifo_full_interrupt_set
reset_at_cmd_interrupt
reset_tx_done_interrupt
reset_rx_fifo_full_interrupt
You can now use the UartInterrupt
enum and the corresponding listen
, unlisten
, interrupts
and clear_interrupts
functions.
Use interrupts
in place of <INTERRUPT>_interrupt_set
and clear_interrupts
in place of the old reset_
functions.
UartInterrupt
:
AtCmd
TxDone
RxFifoFull
Checking interrupt bits is now done using APIs provided by enumset. For example, to see if
a particular interrupt bit is set, use contains
:
-serial.at_cmd_interrupt_set()
+serial.interupts().contains(UartInterrupt::AtCmd)
You can now listen/unlisten multiple interrupt bits at once:
-uart0.listen_at_cmd();
-uart0.listen_rx_fifo_full();
+uart0.listen(UartInterrupt::AtCmd | UartConterrupt::RxFifoFull);
I2S changes
The I2S driver has been moved to i2s::master
-use esp_hal::i2s::{DataFormat, I2s, Standard};
+use esp_hal::i2s::master::{DataFormat, I2s, Standard};
Removed i2s
traits
The following traits have been removed:
I2sWrite
I2sWriteDma
I2sRead
I2sReadDma
I2sWriteDmaAsync
I2sReadDmaAsync
You no longer have to import these to access their respective APIs. If you used these traits
in your functions as generic parameters, you can use the I2s
type directly instead.
For example:
-fn foo(i2s: &mut impl I2sWrite) {
+fn foo(i2s: &mut I2s<'_, I2S0, Blocking>) {
// ...
}
DMA related changes
Circular DMA transfer's available
returns Result<usize, DmaError>
now
In case of any error you should drop the transfer and restart it.
loop {
- let avail = transfer.available();
+ let avail = match transfer.available() {
+ Ok(avail) => avail,
+ Err(_) => {
+ core::mem::drop(transfer);
+ transfer = i2s_tx.write_dma_circular(&tx_buffer).unwrap();
+ continue;
+ },
+ };
Channel, ChannelRx and ChannelTx types have changed
Channel
'sAsync
/Blocking
mode has been moved before the channel instance parameter.ChannelRx
andChannelTx
have gained a newAsync
/Blocking
mode parameter.
-Channel<'d, DmaChannel0, Async>
+Channel<'d, Async, DmaChannel0>
-ChannelRx<'d, DmaChannel0>
+ChannelRx<'d, Async, DmaChannel0>
-ChannelTx<'d, DmaChannel0>
+ChannelTx<'d, Async, DmaChannel0>
Removed peripheral_input
and into_peripheral_output
from GPIO pin types
Creating peripheral interconnect signals now consume the GPIO pin used for the connection.
The previous signal function have been replaced by split
. This change affects the following APIs:
GpioPin
AnyPin
-let input_signal = gpioN.peripheral_input();
-let output_signal = gpioN.into_peripheral_output();
+let (input_signal, output_signal) = gpioN.split();
into_peripheral_output
, split
(for output pins only) and peripheral_input
have been added to
the GPIO drivers (Input
, Output
, OutputOpenDrain
and Flex
) instead.
ETM changes
- The types are no longer prefixed with
GpioEtm
,TimerEtm
orSysTimerEtm
. You can still use
import aliasses in case you ...
0.21.1
0.21.0
Please note that only changes to the esp-hal
package are tracked in these release notes.
Migration Guides
esp-hal
: https://github.com/esp-rs/esp-hal/blob/v0.21.0/esp-hal/MIGRATING-0.20.mdesp-hal-embassy
: https://github.com/esp-rs/esp-hal/blob/v0.21.0/esp-hal-embassy/MIGRATING-0.3.mdesp-wifi
: https://github.com/esp-rs/esp-hal/blob/v0.21.0/esp-wifi/MIGRATING-0.9.md
Changelog
Added
- Introduce traits for the DMA buffer objects (#1976, #2213)
- Implement
embedded-hal
output pin traits forNoPin
(#2019, #2133) - Added
esp_hal::init
to simplify HAL initialisation (#1970, #1999) - Added GpioPin::degrade to create ErasePins easily. Same for AnyPin by accident. (#2075)
- Added missing functions to
Flex
:unlisten
,is_interrupt_set
,wakeup_enable
,wait_for_high
,wait_for_low
,wait_for_rising_edge
,wait_for_falling_edge
,wait_for_any_edge
. (#2075) Flex
now implementsWait
. (#2075)- Added sleep and wakeup support for esp32c2 (#1922)
Input
,Output
,OutputOpenDrain
andFlex
now implementPeripheral
. (#2094)- Previously unavailable memory is available via
.dram2_uninit
section (#2079) - You can now use
Input
,Output
,OutputOpenDrain
andFlex
pins as EXTI and RTCIO wakeup sources (#2095) - Added
Rtc::set_current_time
to allow setting RTC time, andRtc::current_time
to getting RTC time while taking into account boot time (#1883) - Added APIs to allow connecting signals through the GPIO matrix. (#2128)
- Allow I8080 transfers to be cancelled on the spot (#2191)
- Implement
TryFrom<u32>
forledc::timer::config::Duty
(#1984) - Expose
RtcClock::get_xtal_freq
andRtcClock::get_slow_freq
publically for all chips (#2183) - TWAI support for ESP32-H2 (#2199)
- Make
DmaDescriptor
methods public (#2237) - Added a way to configure watchdogs in
esp_hal::init
(#2180) - Introduce
DmaRxStreamBuf
(#2242) - Implement
embedded_hal_async::delay::DelayNs
forTIMGx
timers (#2084) - Added
Efuse::read_bit
(#2259) - Limited SPI slave support for ESP32 (Modes 1 and 3 only) (#2278)
- Added
Rtc::disable_rom_message_printing
(S3 and H2 only) (#2280) - Added
esp_hal::time::{Duration, Instant}
(#2304)
Changed
- Bump MSRV to 1.79.0 (#1971)
- Make saving and restoring SHA digest state an explicit operation (#2049)
- Reordered RX-TX pairs in all APIs to be consistent (#2074)
- Make saving and restoring SHA digest state an explicit operation (#2049)
Delay::new()
is now aconst
function (#1999)Input
,Output
,OutputOpenDrain
andFlex
are now type-erased by default. Use the newnew_typed
constructor to keep using the ZST pin types. (#2075)- To avoid confusion with the
Rtc::current_time
wall clock time APIs, we've renamedesp_hal::time::current_time
toesp_hal::time::now
. (#2091) - Renamed
touch::Continous
totouch::Continuous
. (#2094) - Faster SHA (#2112)
- The (previously undocumented)
ErasedPin
enum has been replaced with theErasedPin
struct. (#2094) - Renamed and merged
Rtc::get_time_us
andRtc::get_time_ms
intoRtc::time_since_boot
(#1883) - ESP32: Added support for touch sensing on GPIO32 and 33 (#2109)
- Removed gpio pin generics from I8080 driver type. (#2171)
- I8080 driver now decides bus width at transfer time rather than construction time. (#2171)
- Migrate the I8080 driver to a move based API (#2191)
- Replaced
AnyPin
withInputSignal
andOutputSignal
and renamedErasedPin
toAnyPin
(#2128) - Replaced the
ErasedTimer
enum with theAnyTimer
struct. (#2144) Camera
andAesDma
now support erasing the DMA channel type (#2258)- Changed the parameters of
Spi::with_pins
to no longer be optional (#2133) - Renamed
DummyPin
toNoPin
and removed all internal logic from it. (#2133) - The
NO_PIN
constant has been removed. (#2133) - Allow handling interrupts while trying to lock critical section on multi-core chips. (#2197)
- Migrate
Camera
to a move based API (#2242). - Removed the PS-RAM related features, replaced by
quad-psram
/octal-psram
,init_psram
takes a configuration parameter, it's now possible to auto-detect PS-RAM size (#2178) EspTwaiFrame
constructors now accept any type that converts intoesp_hal::twai::Id
(#2207)- Change
DmaTxBuf
to support PSRAM onesp32s3
(#2161) - I2c
transaction
is now also available as a inherent function, lift size limit onwrite
,read
andwrite_read
(#2262) - SPI transactions are now cancelled if the transfer object (or async Future) is dropped. (#2216)
- The DMA channel types have been removed from peripherals (#2261)
I2C
driver renamed toI2c
(#2320)
Fixed
- SHA driver can now be safely used in multiple contexts concurrently (#2049)
- Fixed an issue with DMA transfers potentially not waking up the correct async task (#2065)
- Fixed an issue with LCD_CAM i8080 where it would send double the clocks in 16bit mode (#2085)
- Fix i2c embedded-hal transaction (#2028)
- Fix some inconsistencies in DMA interrupt bits (#2169)
- Fix SPI DMA alternating
write
andread
for ESP32 and ESP32-S2 (#2131) - Fix I2C ending up in a state when only re-creating the peripheral makes it useable again (#2141)
- Fix
SpiBus::transfer
transferring data twice in some cases (#2159) - Fixed UART freezing when using
RcFast
clock source on ESP32-C2/C3 (#2170) - I2S: on ESP32 and ESP32-S2 data is now output to the right (WS=1) channel first. (#2194)
- SPI: Fixed an issue where unexpected data was written outside of the read buffer (#2179)
- SPI: Fixed an issue where
wait
has returned before the DMA has finished writing the memory (#2179) - SPI: Fixed an issue where repeated calls to
dma_transfer
may end up looping indefinitely (#2179) - SPI: Fixed an issue that prevented correctly reading the first byte in a transaction (#2179)
- SPI: ESP32: Send address with correct data mode even when no data is sent. (#2231)
- SPI: ESP32: Allow using QSPI mode on SPI3. (#2245)
- PARL_IO: Fixed an issue that caused garbage to be output at the start of some requests (#2211)
- TWAI on ESP32 (#2207)
- TWAI should no longer panic when receiving a non-compliant frame (#2255)
- OneShotTimer: fixed
delay_nanos
behaviour (#2256) - Fixed unsoundness around
Efuse
(#2259)
Removed
- Removed
digest::Digest
implementation from SHA (#2049) - Removed
NoPinType
in favour ofDummyPin
. (#2068) - Removed the
async
,embedded-hal-02
,embedded-hal
,embedded-io
,embedded-io-async
, andufmt
features (#2070) - Removed the
GpioN
type aliasses. UseGpioPin<N>
instead. (#2073) - Removed
Peripherals::take
. Useesp_hal::init
to obtainPeripherals
(#1999) - Removed
AnyInputOnlyPin
in favour ofAnyPin
. (#2071) - Removed the following functions from
GpioPin
:is_high
,is_low
,set_high
,set_low
,set_state
,is_set_high
,is_set_low
,toggle
. (#2094) - Removed
Rtc::get_time_raw
(#1883) - Removed
_with_default_pins
UART constructors (#2132) - Removed transfer methods
send
,send_dma
andsend_dma_async
fromI8080
(#2191) - Removed
uart::{DefaultRxPin, DefaultTxPin}
(#2132) - Removed
PcntSource
andPcntInputConfig
. (#2134) - Removed the
place-spi-driver-in-ram
feature, this is now enabled via esp-config (#2156) - Removed
esp_hal::spi::slave::prelude
(#2260) - Removed
esp_hal::spi::slave::WithDmaSpiN
traits (#2260) - The
WithDmaAes
trait has been removed (#2261) - The
I2s::new_i2s1
constructor has been removed (#2261)
v0.20.1
0.20.0
Please note that only changes to the esp-hal
package are tracked in these release notes.
Migration Guide
Peripheral driver constructors don't take InterruptHandlers anymore
Some peripherals used to take the interrupt handler via constructor. We have now unified this behaviour so that all peripherals use the same set_interrupt_handler
method. We also have a new trait to abstract over this, InterruptConfigurable
.
- Peripheral::new(/* params */, handler)
+ peripheral.set_interrupt_handler(handler);
OneShotTimer
and PeriodicTimer
structs now use PeripheralRef
This was something we missed on the initial impl. The migration is quite simple, there is a new lifetime parameter to contend with.
- OneShotTimer<ErasedTimer>;
+ OneShotTimer<'static, ErasedTimer>;
DMA buffer changes and SpiDma
changes
To allow efficient queuing of DMA requests, we've changed how the DMA is interacted with. #1856 introduces new wrapper types DmaTxBuf
, DmaRxBuf
and DmaTxRxBuf
. These are currently light wrappers around slices and descriptors, but allows us to do more complex checks for future devices and peripherals. Thanks to these changes, we have been able to remove the FlashSafeDma
wrapper too.
let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(32000);
+ let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
+ let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
We've also decoupled the DMA buffers and descriptors from the SpiDma
struct itself, this means the end user can manage the queuing of DMA requests for more efficient operation. If you don't wish to manage this yourself and want to use the embedded_hal::spi::*
traits, it's simple enough to opt back into the HAL buffer management like so.
let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0, &clocks)
.with_pins(Some(sclk), Some(mosi), Some(miso), Some(cs))
.with_dma(dma_channel.configure_for_async(false, DmaPriority::Priority0))
+ .with_buffers(dma_tx_buf, dma_rx_buf);
We also added inherent methods onto SpiDmaBus
, which due to the way type inference works may introduce breaking changes if you were using the async API. You may need to fully qualify the trait method or call the new inherent _async
methods.
- spi.transfer_in_place(&mut buf).await?;
+ spi.transfer_in_place_async(&mut buf).await?;
SystemTimer
changes
- The type signature of both
SysTimer
andAlarm
has been changed to allow for creation of alarms in eitherBlocking
orAsync
separately. We have provided a convenience method to create all alarms in the same mode, just as it behaved before this release.
- let syst = SystemTimer::new_async(peripherals.SYSTIMER);
+ let syst = SystemTimer::new(peripherals.SYSTIMER);
+ let alarms = syst.split_async::<esp_hal::timer::systimer::Target>()
SystemTimer::TICKS_PER_SECOND
has been replaced bySystemTimer::ticks_per_second()
.
Sha
state is now stored outside the Sha
peripheral
The state is now stored separately to allow multiple Sha
operations concurrently.
- sha.update(remaining).unwrap();
+ let mut sha1 = Sha1::new();
+ Sha::update(&mut sha1, remaining).unwrap();
Remove fn free(self)
from HMAC
The fn free(self)
method is no longer available, in favour of the PeripheralRef
API.
RSA modular multiplication
RsaModularMultiplication
has been consolidated for all targets.
ESP32 changes
let r = compute_r(&BIGNUM_3);
let mut mod_multi =
RsaModularMultiplication::<Op512, Blocking>::new(
&mut ctx.rsa,
+ BIGNUM_1.as_words(), // Operand A
BIGNUM_3.as_words(), // Modulus
+ r.as_words(),
compute_mprime(&BIGNUM_3),
);
-mod_multi.start_step1(BIGNUM_1.as_words(), r.as_words()); // Operand A
-mod_multi.start_step2(BIGNUM_2.as_words()); // Operand B
+mod_multi.start_modular_multiplication(BIGNUM_2.as_words()); // Operand B
mod_multi.read_results(&mut outbuf);
Non-ESP32 changes
let r = compute_r(&BIGNUM_3);
let mut mod_multi =
RsaModularMultiplication::<operand_sizes::Op512, esp_hal::Blocking>::new(
&mut ctx.rsa,
BIGNUM_1.as_words(), // Operand A
- BIGNUM_2.as_words(), // Operand B
BIGNUM_3.as_words(), // Modulus
+ r.as_words(),
compute_mprime(&BIGNUM_3),
);
-mod_multi.start_modular_multiplication(r.as_words());
+mod_multi.start_modular_multiplication(BIGNUM_2.as_words()); // Operand B
mod_multi.read_results(&mut outbuf);
Simpler way to initialise embassy and esp-wifi
You no longer have to spell out a bunch of type conversions, to initialize esp-wifi or embassy with a single timer. We provide conversions for TimerGroup
times as well as SysTimer
alarms.
Note that if you need to pass multiple timers to embassy, you will still need to do the conversions yourself.
AnyPin
, AnyInputOnyPin
and DummyPin
are now accessible from gpio
directly
- use esp_hal::gpio::any_pin::AnyPin;
- - use esp_hal::gpio::dummy_pin::DummyPin;
+ use esp_hal::gpio::AnyPin;
+ use esp_hal::gpio::DummyPin;
Initialising embassy
let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks);
-let timer0: ErasedTimer = timg0.timer0.into();
-let timers = [OneShotTimer::new(timer0)];
-let timers = mk_static!([OneShotTimer<ErasedTimer>; 1], timers);
-esp_hal_embassy::init(&clocks, timers);
+esp_hal_embassy::init(&clocks, timg0.timer0);
let systimer = SystemTimer::new(peripherals.SYSTIMER).split::<Target>();
-let alarm0: ErasedTimer = systimer.alarm0.into();
-let timers = [OneShotTimer::new(alarm0)];
-let timers = mk_static!([OneShotTimer<ErasedTimer>; 1], timers);
-esp_hal_embassy::init(&clocks, timers);
+esp_hal_embassy::init(&clocks, systimer.alarm0);
Initializing esp-wifi
let timg0 = TimerGroup::new(peripherals.TIMG0, &clocks);
-let timer0: ErasedTimer = timg0.timer0.into();
-let timer = PeriodicTimer::new(timer0);
let init = initialize(
EspWifiInitFor::Wifi,
- timer,
+ timg0.timer0,
Rng::new(peripherals.RNG),
peripherals.RADIO_CLK,
clocks,
)
.unwrap();
let systimer = esp_hal::timer::systimer::SystemTimer::new(peripherals.SYSTIMER).split::<esp_hal::timer::systimer::Target>();
-let alarm0: ErasedTimer = systimer.alarm0.into();
-let alarm = PeriodicTimer::new(alarm0);
let init = initialize(
EspWifiInitFor::Wifi,
- alarm,
+ systimer.alarm0,
Rng::new(peripherals.RNG),
peripherals.RADIO_CLK,
clocks,
)
.unwrap();
Floating point support
Floating point operations in interrupt contexts are no longer allowed by default. If you experience Cp0Disabled
panics, you should try enabling the float-save-restore
feature on xtensa-lx-rt
.
Changelog
Added
- Introduce DMA buffer objects (#1856, #1985)
- Added new
Io::new_no_bind_interrupt
constructor (#1861) - Added touch pad support for esp32 (#1873, #1956)
- Allow configuration of period updating method for MCPWM timers (#1898)
- Add self-testing mode for TWAI peripheral. (#1929)
- Added a
PeripheralClockControl::reset
to the driver constructors where missing (#1893) - Added
digest::Digest
implementation to SHA (#1908) - Added
debugger::debugger_connected
. (#1961) - DMA: don't require
Sealed
to implementReadBuffer
andWriteBuffer
(#1921) - Allow DMA to/from psram for esp32s3 (#1827)
- Added missing methods to
SpiDmaBus
(#2016). - PARL_IO use ReadBuffer and WriteBuffer for Async DMA (#1996)
Changed
- Peripheral driver constructors don't take
InterruptHandler
s anymore. Useset_interrupt_handler
to explicitly set the interrupt handler now. (#1819) - Migrate SPI driver to use DMA buffer objects (#1856, #1985)
- Use the peripheral ref pattern for
OneShotTimer
andPeriodicTimer
(#1855) - Improve SYSTIMER API (#1871)
- SHA driver now use specific structs for the hashing algorithm instead of a parameter. (#1908)
- Remove
fn free(self)
in HMAC which goes against esp-hal API guidelines (#1972) AnyPin
,AnyInputOnyPin
andDummyPin
are now accessible fromgpio
module (#1918)- Changed the RSA modular multiplication API to be consistent across devices (#2002)
Fixed
- Improve error detection in the I2C driver (#1847)
- Fix I2S async-tx (#1833)
- Fix PARL_IO async-rx (#1851)
- SPI: Clear DMA interrupts before (not after) DMA starts (#1859)
- SPI: disable and re-enable MISO and MOSI in
start_transfer_dma
,start_read_bytes_dma
andstart_write_bytes_dma
accordingly (#1894) - TWAI: GPIO pins are not configured as input and output (#1906)
- ESP32C6: Make ADC usable after TRNG deinicialization (#1945)
- We should no longer generate 1GB .elf files for ESP32C2 and ESP32C3 (#1962)
- Reset peripherals in driver constructors where missing (#1893, #1961)
- Fixed ESP32-S2 systimer interrupts (#1979)
- Software interrupt 3 is no longer available when it is required by
esp-hal-embassy
. (#2011) - ESP32: Fixed async RSA (#2002)