* cleanup prelude docs * fixups and i2c rename * changelog and migration * fixup docs and examples * fix lint
10 KiB
Migration Guide from 0.20.x to vNext
Cargo Features
A number of trait implementations which were previously feature-gated are now implemented by default. The following Cargo features have been removed:
asyncembedded-hal-02embedded-halembedded-ioembedded-io-asyncufmt
If your project enables any of these features, simply remove them from your Cargo manifest and things should continue to work as expected.
HAL Initialisation
Instead of manually grabbing peripherals and setting up clocks, you should now call esp_hal::init.
use esp_hal::{
- clock::ClockControl,
- peripherals::Peripherals,
prelude::*,
- system::SystemControl,
};
#[entry]
fn main() -> ! {
- let peripherals = Peripherals::take();
- let system = SystemControl::new(peripherals.SYSTEM);
- let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
+ let peripherals = esp_hal::init(esp_hal::Config::default());
// ...
}
GPIO changes
- The
GpioNtype aliasses are no longer available. You can useGpioPin<N>instead. - The
AnyInputOnlyPinhas been removed. Replace any use withAnyPin. - The
NoPinTypehas been removed. You can useDummyPinin its place.
Type-erased GPIO drivers
The AnyInput, AnyOutput, AnyOutputOpenDrain and AnyFlex structures have been removed.
Instead, you can use the non-Any counterparts. By default, the structures will use AnyPin in
their type names, which you don't have to specify.
-let pin = AnyInput::new(io.gpio0, Pull::Up);
+let pin = Input::new(io.gpio0, Pull::Up); // pin will have the type `Input<'some>` (or `Input<'some, AnyPin>` if you want to be explicit about it)
You can use new_typed if you want to keep using the typed form:
let pin = Input::new_typed(io.gpio0, Pull::Up); // pin will have the type `Input<'some, GpioPin<0>>`
Creating an AnyPin
Instead of AnyPin::new, you can now use the Pin trait's degrade method to obtain an AnyPin.
You can now create an AnyPin out of input only pins (like ESP32's IO34), but trying to use such
pins as output (or, generally, trying to use pins in ways they don't support) will panic.
+ use esp_hal::gpio::Pin;
-let pin = AnyPin::new(io.gpio0);
+let pin = io.gpio0.degrade();
Wakeup using pin drivers
You can now use pin drivers as wakeup sources.
use esp_hal::peripheral::Peripheral; // needed for `into_ref`
let pin2 = Input::new(io.pins.gpio2, Pull::None);
let mut pin3 = Input::new(io.pins.gpio3, Pull::None);
// ... use pin2 as an input ..
// Ext0
let ext0 = Ext0WakeupSource::new(pin2, WakeupLevel::High);
// Ext1
let mut wakeup_pins: [&mut dyn RtcPin; 2] = [
&mut *pin_0.into_ref(),
&mut io.pins.gpio4, // unconfigured pins continue to work, too!
];
let ext1 = Ext1WakeupSource::new(&mut wakeup_pins, WakeupLevel::High);
RX/TX Order
Previously, our API was pretty inconsistent with the RX/TX ordering, and different peripherals had different order. Now, all the peripherals use rx-tx. Make sure your methods are expecting the right RX/TX order, for example an SPI DMA app should be updated to:
- let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(4);
+ let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(4);
let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
...
let transfer = spi
- .dma_transfer(dma_tx_buf, dma_rx_buf)
+ .dma_transfer(dma_rx_buf, dma_tx_buf)
.map_err(|e| e.0)
.unwrap();
When using the asymmetric variant of the macro to create DMA buffers and descriptors make sure to swap the order of parameters
- let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(32000, 0);
+ let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000);
Removed constructors
UART
The Uart::new_with_default_pins and Uart::new_async_with_default_pins constructors
have been removed. Use new or new_async instead.
I2S1
The I2s::new_i2s1 constructor has been removed. Use I2s::new instead.
Timer changes
ErasedTimer rename
The ErasedTimer has been renamed to AnyTimer.
esp_hal::time::current_time rename
To avoid confusion with the Rtc::current_time wall clock time APIs, we've renamed esp_hal::time::current_time to esp_hal::time::now().
RTC Wall Clock APIs
Instead of the get_time_ms, get_time_us, and get_time_raw functions, the Rtc struct now provides the current_time function, using chrono's NaiveDateTime struct.
let rtc = Rtc::new(peripherals.LPWR);
- let current_time_ms = rtc.get_time_ms();
+ let current_time_ms = rtc.current_time().and_utc().timestamp_millis(); // assuming UTC
PCNT input config
The PcntSource and PcntInputConfig have been removed. You can use Input or Flex instead to
configure an input pin, and pass it to set_edge_signal or set_ctrl_signal.
- let mut pin_a = io.pins.gpio4;
- ch0.set_ctrl_signal(PcntSource::from_pin(
- &mut pin_a,
- PcntInputConfig { pull: Pull::Up },
- ));
+ ch0.set_ctrl_signal(Input::new(io.pins.gpio4, Pull::Up));
- let mut pin_b = io.pins.gpio5;
- ch0.set_edge_signal(PcntSource::from_pin(
- &mut pin_b,
- PcntInputConfig { pull: Pull::Down },
- ));
+ ch0.set_edge_signal(Input::new(io.pins.gpio5, Pull::Down));
SPI pins and NO_PIN
Use NoPin in place of the now-removed NO_PIN constant.
SPI pins, when using the with_pin function, are no longer optional.
You can pass NoPin or Level if you don't need a particular pin.
let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0)
- .with_pins(Some(sclk), Some(mosi), NO_PIN, NO_PIN);
+ .with_pins(sclk, mosi, Level::Low, NoPin);
I8080 type definition
The I8080 driver no longer holds on to pins in its type definition.
- let _: I8080<'a, DmaChannel3, TxEightBits<AnyPin, AnyPin, AnyPin, ....>, Blocking>;
+ let _: I8080<'a, DmaChannel3, Blocking>;
I8080 transfer API changes
- The I8080 driver now decides bus width at transfer time, which means you don't get type inference anymore.
- Starting a transfer moves the driver into the transfer object, allowing you to store it in a
staticor struct. - The transfer API no longer works with plain slices, it now works with
DmaTxBuffers which allow to bring your own DMA data structure and implement efficient queueing of transfers. - The three transfer methods (
send,send_dma,send_dma_async) have been merged into onesendmethod.
let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32678);
+ let mut dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
let mut i8080 = I8080::new(
lcd_cam.lcd,
channel.tx,
- tx_descriptors,
tx_pins,
20.MHz(),
Config::default(),
)
- i8080.send(0x12, 0, &[0, 1, 2, 3, 4]);
+ dma_buf.fill(&[0, 1, 2, 3, 4]);
+ let transfer = i8080.send(0x12u8, 0, dma_buf).unwrap();
+ // transfer.wait_for_done().await;
+ (_, i8080, dma_buf) = transfer.wait();
Placing drivers in RAM is now done via esp-config
We've replaced some usage of features with esp-config. Please remove any reference to place-spi-driver-in-ram in your Cargo.toml and migrate to the [env] section of .cargo/config.toml.
# feature in Cargo.toml
- esp-hal = { version = "0.20", features = ["place-spi-driver-in-ram"] }
# key in .cargo/config.toml [env] section
+ ESP_HAL_PLACE_SPI_DRIVER_IN_RAM=true
Camera driver now uses DmaRxBuffer and moves the driver into the transfer object.
For one shot transfers.
let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32678, 0);
+ let dma_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
let mut camera = Camera::new(
lcd_cam.cam,
channel.rx,
- rx_descriptors,
data_pins,
20u32.MHz(),
);
- let transfer = camera.read_dma(rx_buffer).unwrap();
- transfer.wait();
+ let transfer = camera.receive(dma_buf).unwrap();
+ let (_, camera, buf) = transfer.wait();
For circular transfers.
- let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32678, 0);
+ let dma_buf = dma_rx_stream_buffer!(32678);
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
let mut camera = Camera::new(
lcd_cam.cam,
channel.rx,
- rx_descriptors,
data_pins,
20u32.MHz(),
);
- let mut transfer = camera.read_dma_circular(rx_buffer).unwrap();
+ let mut transfer = camera.receive(dma_buf).unwrap();
transfer.pop(&mut [.....]);
transfer.pop(&mut [.....]);
transfer.pop(&mut [.....]);
PS-RAM
Initializing PS-RAM now takes a chip specific config and returns start of the mapped memory and the size.
Example
let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default());
If you don't specify the size of PS-RAM via PsramConfig::size the size of PS-RAM is derived from the RAM-chip id (or via probing in case of ESP32).
psram::psram_vaddr_start() and psram::PSRAM_BYTES are removed.
The features psram-Xm and opsram-Xm are removed and replaced by quad-psram/octal-psram.
The feature psram-80mhz is removed and replaced by PsramConfig
Diff of the psram_quad.rs example
-//% FEATURES: psram-2m
+//% FEATURES: esp-hal/quad-psram
...
-fn init_psram_heap() {
+fn init_psram_heap(start: *mut u8, size: usize) {
unsafe {
esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new(
- psram::psram_vaddr_start() as *mut u8,
- psram::PSRAM_BYTES,
+ start,
+ size,
esp_alloc::MemoryCapability::External.into(),
));
}
...
- psram::init_psram(peripherals.PSRAM);
- init_psram_heap();
+ let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default());
+ init_psram_heap(start, size);
...
eFuse
Calling Efuse::read_field_le::<bool>() no longer compiles. Use Efuse::read_bit() instead.
DMA
The DMA channel types have been removed from peripherals.
A non-exhausitve list demonstrating this change:
-I2sTx<'static, I2S0, DmaChannel0, Async>
+I2sTx<'static, I2S0, Async>
-SpiDma<'static, esp_hal::peripherals::SPI2, DmaChannel0, HalfDuplexMode, Blocking>
+SpiDma<'static, esp_hal::peripherals::SPI2, HalfDuplexMode, Blocking>
I2C driver renamed to I2c
To firstly match the naming in embedded-hal, but also because types should be UpperCamelCase.