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 need to differentiate due to name collisions
(e.g.use esp_hal::gpio::etm::Event as GpioEtmEvent
). - The old task and event types have been replaced by
Task
andEvent
. - GPIO tasks and events are no longer generic.
Changes to peripheral configuration
The uart::config
module has been removed
The module's contents have been moved into uart
.
-use esp_hal::uart::config::Config;
+use esp_hal::uart::Config;
If you work with multiple configurable peripherals, you may want to import the uart
module and
refer to the Config
struct as uart::Config
.
SPI drivers can now be configured using spi::master::Config
- The old methods to change configuration have been removed.
- The
new
andnew_typed
constructor no longer takesfrequency
andmode
. - The default configuration is now:
- bus frequency: 1 MHz
- bit order: MSB first
- mode: SPI mode 0
- There are new constructors (
new_with_config
,new_typed_with_config
) and a newapply_config
method to apply custom configuration.
-use esp_hal::spi::{master::Spi, SpiMode};
+use esp_hal::spi::{master::{Config, Spi}, SpiMode};
-Spi::new(SPI2, 100.kHz(), SpiMode::Mode1);
+Spi::new_with_config(
+ SPI2,
+ Config {
+ frequency: 100.kHz(),
+ mode: SpiMode::Mode0,
+ ..Config::default()
+ },
+)
LCD_CAM changes
I8080 driver split set_byte_order()
into set_8bits_order()
and set_byte_order()
.
If you were using an 8-bit bus.
- i8080.set_byte_order(ByteOrder::default());
+ i8080.set_8bits_order(ByteOrder::default());
If you were using an 16-bit bus, you don't need to change anything, set_byte_order()
now works correctly.
If you were sharing the bus between an 8-bit and 16-bit device, you will have to call the corresponding method when
you switch between devices. Be sure to read the documentation of the new methods.
Mixed mode constructors
It is no longer possible to construct I8080
or Camera
with an async-mode DMA channel.
Convert the DMA channel into blocking before passing it to these constructors.
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
let channel = ctx
.dma
.channel0
- .configure(false, DmaPriority::Priority0)
- .into_async();
+ .configure(false, DmaPriority::Priority0);
let i8080 = I8080::new(
lcd_cam.lcd,
channel.tx,
pins,
20.MHz(),
Config::default(),
);
rmt::Channel::transmit
now returns Result
, PulseCode
is now u32
When trying to send a one-shot transmission will fail if it doesn't end with an end-marker.
- let mut data = [PulseCode {
- level1: true,
- length1: 200,
- level2: false,
- length2: 50,
- }; 20];
-
- data[data.len() - 2] = PulseCode {
- level1: true,
- length1: 3000,
- level2: false,
- length2: 500,
- };
- data[data.len() - 1] = PulseCode::default();
+ let mut data = [PulseCode::new(true, 200, false, 50); 20];
+ data[data.len() - 2] = PulseCode::new(true, 3000, false, 500);
+ data[data.len() - 1] = PulseCode::empty();
- let transaction = channel.transmit(&data);
+ let transaction = channel.transmit(&data).unwrap();
The parl_io::NoClkPin
and no_clk_pin()
have been removed
You can use gpio::NoPin
instead.
use esp_hal:: {
- parl_io::no_clk_pin,
+ gpio::NoPin,
}
-parl_io.rx.with_config(&mut rx_pins, no_clk_pin(), BitPackOrder::Msb, Some(0xfff))
+let mut rx_clk_pin = NoPin;
+parl_io.rx.with_config(&mut rx_pins, &mut rx_clk_pin, BitPackOrder::Msb, Some(0xfff))
get_
prefixes have been removed from functions
In order to better comply with the Rust API Guidelines getter names convention, we have removed the get_
prefixes from all functions which previously had it. Due to the number of changes it's not practical to list all changes here, however if a function previous began with get_
, you can simply remove this prefix.
The get_core()
function has been removed in favour of Cpu::current()
- let core = esp_hal::get_core();
+ let core = esp_hal::Cpu::current();
Changelog
Added
- A new config option
PLACE_SWITCH_TABLES_IN_RAM
to improve performance (especially for interrupts) at the cost of slightly more RAM usage (#2331) - A new config option
PLACE_ANON_IN_RAM
to improve performance (especially for interrupts) at the cost of RAM usage (#2331) - Add burst transfer support to DMA buffers (#2336)
AnyPin
now implementsFrom<GpioPin<N>>
. (#2326)- Added
AnySpi
andAnySpiDmaChannel
. (#2334) - Added
AnyI2s
andAnyI2sDmaChannel
. (#2367) - Added
AnyTwai
. (#2359) - Added
AnyUart
. (#2381) Pins::steal()
to unsafely obtain GPIO. (#2335)I2c::with_timeout
(#2361)Spi::half_duplex_read
andSpi::half_duplex_write
(#2373)- Add RGB/DPI driver (#2415)
- Add
DmaLoopBuf
(#2415) Cpu::COUNT
andCpu::current()
(#2411)UartInterrupt
and related functions (#2406)- I2S Parallel output driver for ESP32. (#2348, #2436, #2472)
- Add an option to configure
WDT
action (#2330) DmaDescriptor
is nowSend
(#2456)into_async
andinto_blocking
functions for most peripherals (#2430, #2461)- API mode type parameter (currently always
Blocking
) tomaster::Spi
andslave::Spi
(#2430) gpio::{GpioPin, AnyPin, Flex, Output, OutputOpenDrain}::split()
to obtain peripheral interconnect signals. (#2418)gpio::Input::{split(), into_peripheral_output()}
when used with output pins. (#2418)gpio::Output::peripheral_input()
(#2418){Uart, UartRx, UartTx}::apply_config()
(#2449){Uart, UartRx, UartTx}
now implementembassy_embedded_hal::SetConfig
(#2449)- GPIO ETM tasks and events now accept
InputSignal
andOutputSignal
(#2427) spi::master::Config
and{Spi, SpiDma, SpiDmaBus}::apply_config
(#2448)embassy_embedded_hal::SetConfig
is now implemented forspi::master::{Spi, SpiDma, SpiDmaBus}
,i2c::master::I2c
(#2448, #2477)slave::Spi::{with_mosi(), with_miso(), with_sclk(), with_cs()}
functions (#2485)- I8080: Added
set_8bits_order()
to set the byte order in 8-bit mode (#2487) I2c::{apply_config(), with_sda(), with_scl()}
(#2477)- ESP32-S2: Added missing GPIO alternate functions (#2512)
Changed
- Peripheral type erasure for SPI (#2334)
- Peripheral type erasure for I2S (#2367)
- Peripheral type erasure for I2C (#2361)
- Peripheral type erasure for TWAI (#2359)
- The SPI driver has been rewritten to allow using half-duplex and full-duplex functionality on the same bus. See the migration guide for details. (#2373)
- Renamed
SpiDma
functions:dma_transfer
totransfer
,dma_write
towrite
,dma_read
toread
. (#2373) - Peripheral type erasure for UART (#2381)
- Changed listening for UART events (#2406)
- Circular DMA transfers now correctly error,
available
returnsResult<usize,DmaError>
now (#2409) - Interrupt listen/unlisten/clear functions now accept any type that converts into
EnumSet
(i.e. single interrupt flags). (#2442) - SPI interrupt listening is now only available in Blocking mode. The
set_interrupt_handler
is available viaInterruptConfigurable
(#2442) - Allow users to create DMA
Preparation
s (#2455) - The
rmt::asynch::RxChannelAsync
andrmt::asynch::TxChannelAsync
traits have been moved tormt
(#2430) - Calling
AnyPin::output_signals
on an input-only pin (ESP32 GPIO 34-39) will now result in a panic. (#2418) - UART configuration types have been moved to
esp_hal::uart
(#2449) spi::master::Spi::new()
no longer takesfrequency
andmode
as a parameter. (#2448)- Peripheral interconnections via GPIO pins now use the GPIO matrix. (#2419)
- The I2S driver has been moved to
i2s::master
(#2472) slave::Spi
constructors no longer take pins (#2485)- The
I2c
master driver has been moved fromesp_hal::i2c
toesp_hal::i2c::master
. (#2476) I2c
SCL timeout is now defined in bus clock cycles. (#2477)- Trying to send a single-shot RMT transmission will result in an error now,
RMT
deals withu32
now,PulseCode
is a convenience trait now (#2463) - Removed
get_
prefixes from functions (#2528) - The
Camera
andI8080
drivers' constructors now only accepts blocking-mode DMA channels. (#2519)
Fixed
- Fix conflict between
RtcClock::get_xtal_freq
andRtc::disable_rom_message_printing
(#2360) - Fixed an issue where interrupts enabled before
esp_hal::init
were disabled. This issue caused the executor created by#[esp_hal_embassy::main]
to behave incorrectly in multi-core applications. (#2377) - Fixed
TWAI::transmit_async
: bus-off state is not reached when CANH and CANL are shorted. (#2421) - ESP32: added UART-specific workaround for https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html (#2441)
- Fixed some SysTimer race conditions and panics (#2451)
- TWAI: accept all messages by default (#2467)
- I8080:
set_byte_order()
now works correctly in 16-bit mode (#2487) - ESP32-C6/ESP32-H2: Make higher LEDC frequencies work (#2520)
Removed
- The
i2s::{I2sWrite, I2sWriteDma, I2sRead, I2sReadDma, I2sWriteDmaAsync, I2sReadDmaAsync}
traits have been removed. (#2316) - The
ledc::ChannelHW
trait is no longer generic. (#2387) - The
I2c::new_with_timeout
constructors have been removed (#2361) I2c::new()
no longer takesfrequency
and pins as parameters. (#2477)- The
spi::master::HalfDuplexReadWrite
trait has been removed. (#2373) - The
Spi::with_pins
methods have been removed. (#2373) - The
Spi::new_half_duplex
constructor have been removed. (#2373) - The
HalfDuplexMode
andFullDuplexMode
parameters have been removed fromSpi
. (#2373) - Removed the output pin type parameter from
ledc::{Channel, ChannelIFace}
(#2388) - Removed the output pin type parameter from
mcpwm::operator::{PwmPin, LinkedPins}
(#2388) - Removed the output pin type parameter from
parl_io::{ClkOutPin, ClkInPin, RxClkInPin}
(#2388) - Removed the valid pin type parameter from
parl_io::{TxPinConfigWithValidPin, RxPinConfigWithValidPin}
(#2388) - Removed the pin type parameters from
parl_io::{TxOneBit, TxTwoBits, TxFourBits, TxEightBits, TxSixteenBits}
(#2388) - Removed the pin type parameters from
parl_io::{RxOneBit, RxTwoBits, RxFourBits, RxEightBits, RxSixteenBits}
(#2388) - Removed the pin type parameters from
lcd_cam::lcd::i8080::{TxEightBits, TxSixteenBits}
(#2388) - Removed the pin type parameters from
lcd_cam::cam::{RxEightBits, RxSixteenBits}
(#2388) - Most of the async-specific constructors (
new_async
,new_async_no_transceiver
) have been removed. (#2430) - The
configure_for_async
DMA functions have been removed (#2430) - The
Uart::{change_baud, change_stop_bits}
functions have been removed (#2449) gpio::{Input, Output, OutputOpenDrain, Flex, GpioPin}::{peripheral_input, into_peripheral_output}
have been removed. (#2418)- The
GpioEtm
prefix has been removed fromgpio::etm
types (#2427) - The
TimerEtm
prefix has been removed fromtimer::timg::etm
types (#2427) - The
SysTimerEtm
prefix has been removed fromtimer::systimer::etm
types (#2427) - The
GpioEtmEventRising
,GpioEtmEventFalling
,GpioEtmEventAny
types have been replaced withEvent
(#2427) - The
TaskSet
,TaskClear
,TaskToggle
types have been replaced withTask
(#2427) {Spi, SpiDma, SpiDmaBus}
configuration methods (#2448)Io::new_with_priority
andIo::new_no_bind_interrupt
. (#2486)parl_io::{no_clk_pin(), NoClkPin}
(#2531)- Removed
get_core
function in favour ofCpu::current
(#2533)