use embassy_rp::dma::{self, ChannelInstance};
use embassy_rp::interrupt::typelevel::Binding;
use embassy_rp::peripherals::{
DMA_CH0, DMA_CH1, DMA_CH2, DMA_CH3, DMA_CH4, DMA_CH5, DMA_CH6, DMA_CH7, DMA_CH8, DMA_CH9,
DMA_CH10, DMA_CH11, DMA_CH12, DMA_CH13, DMA_CH14, DMA_CH15, I2C0, I2C1, PIO0, PIO1, PIO2, SPI0,
SPI1, UART0, UART1,
};
use embassy_rp::pio::PioPin;
use embassy_rp::spi::{self, ClkPin, MisoPin, MosiPin};
use embassy_rp::{Peri, i2c, uart};
use alloc::boxed::Box;
use crate::bus::i2c::I2cBusHandle;
use crate::bus::i2c_bitbang::BitBangI2cBus;
use crate::bus::i2c_hardware::HardwareI2cBus;
use crate::bus::pio::{PioManager, gpio_base_for_pins, spi_program_instructions};
use crate::bus::spi::{SpiBusHandle, SpiError};
use crate::bus::spi_bitbang::BitBangSpiBus;
use crate::bus::spi_hardware::HardwareSpiBus;
use crate::bus::uart::UartBusHandle;
use crate::bus::uart_bitbang::BitBangUartBus;
use crate::bus::uart_hardware::HardwareUartBus;
#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
pub enum AllocatorError {
Exhausted,
InvalidConfiguration,
}
impl From<SpiError> for AllocatorError {
fn from(_: SpiError) -> Self {
Self::InvalidConfiguration
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PioBlock {
Block0 = 0,
Block1 = 1,
Block2 = 2,
}
impl PioBlock {
pub const ALL: [PioBlock; 3] = [PioBlock::Block0, PioBlock::Block1, PioBlock::Block2];
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Sm {
Sm0 = 0,
Sm1 = 1,
Sm2 = 2,
Sm3 = 3,
}
impl Sm {
pub const ALL: [Sm; 4] = [Sm::Sm0, Sm::Sm1, Sm::Sm2, Sm::Sm3];
}
mod private {
pub trait Sealed {}
impl Sealed for super::SPI0 {}
impl Sealed for super::SPI1 {}
impl Sealed for super::I2C0 {}
impl Sealed for super::I2C1 {}
impl Sealed for super::UART0 {}
impl Sealed for super::UART1 {}
impl Sealed for super::DMA_CH0 {}
impl Sealed for super::DMA_CH1 {}
impl Sealed for super::DMA_CH2 {}
impl Sealed for super::DMA_CH3 {}
impl Sealed for super::DMA_CH4 {}
impl Sealed for super::DMA_CH5 {}
impl Sealed for super::DMA_CH6 {}
impl Sealed for super::DMA_CH7 {}
impl Sealed for super::DMA_CH8 {}
impl Sealed for super::DMA_CH9 {}
impl Sealed for super::DMA_CH10 {}
impl Sealed for super::DMA_CH11 {}
impl Sealed for super::DMA_CH12 {}
impl Sealed for super::DMA_CH13 {}
impl Sealed for super::DMA_CH14 {}
impl Sealed for super::DMA_CH15 {}
}
pub trait SpiHw: spi::Instance + private::Sealed + Send + 'static {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>>;
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>);
}
pub trait I2cHw: i2c::Instance + private::Sealed + Send + 'static {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>>;
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>);
}
pub trait UartHw: uart::Instance + private::Sealed + Send + 'static {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>>;
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>);
}
pub trait DmaChannel: ChannelInstance + private::Sealed + 'static {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>>;
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>);
}
impl SpiHw for SPI0 {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>> {
alloc.spi0_peri.take()
}
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>) {
alloc.spi0_peri = Some(peri);
}
}
impl SpiHw for SPI1 {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>> {
alloc.spi1_peri.take()
}
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>) {
alloc.spi1_peri = Some(peri);
}
}
impl I2cHw for I2C0 {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>> {
alloc.i2c0_peri.take()
}
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>) {
alloc.i2c0_peri = Some(peri);
}
}
impl I2cHw for I2C1 {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>> {
alloc.i2c1_peri.take()
}
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>) {
alloc.i2c1_peri = Some(peri);
}
}
impl UartHw for UART0 {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>> {
alloc.uart0_peri.take()
}
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>) {
alloc.uart0_peri = Some(peri);
}
}
impl UartHw for UART1 {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>> {
alloc.uart1_peri.take()
}
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>) {
alloc.uart1_peri = Some(peri);
}
}
macro_rules! impl_dma_channel {
($($ch:ident => $field:ident),* $(,)?) => {
$(
impl DmaChannel for $ch {
fn take_peri(alloc: &mut BusAllocator) -> Option<Peri<'static, Self>> {
alloc.$field.take()
}
fn return_peri(alloc: &mut BusAllocator, peri: Peri<'static, Self>) {
alloc.$field = Some(peri);
}
}
)*
};
}
impl_dma_channel! {
DMA_CH0 => dma_ch0, DMA_CH1 => dma_ch1, DMA_CH2 => dma_ch2, DMA_CH3 => dma_ch3,
DMA_CH4 => dma_ch4, DMA_CH5 => dma_ch5, DMA_CH6 => dma_ch6, DMA_CH7 => dma_ch7,
DMA_CH8 => dma_ch8, DMA_CH9 => dma_ch9, DMA_CH10 => dma_ch10, DMA_CH11 => dma_ch11,
DMA_CH12 => dma_ch12, DMA_CH13 => dma_ch13, DMA_CH14 => dma_ch14, DMA_CH15 => dma_ch15,
}
pub struct BusAllocator {
spi0_peri: Option<Peri<'static, SPI0>>,
spi1_peri: Option<Peri<'static, SPI1>>,
i2c0_peri: Option<Peri<'static, I2C0>>,
i2c1_peri: Option<Peri<'static, I2C1>>,
uart0_peri: Option<Peri<'static, UART0>>,
uart1_peri: Option<Peri<'static, UART1>>,
dma_ch0: Option<Peri<'static, DMA_CH0>>,
dma_ch1: Option<Peri<'static, DMA_CH1>>,
dma_ch2: Option<Peri<'static, DMA_CH2>>,
dma_ch3: Option<Peri<'static, DMA_CH3>>,
dma_ch4: Option<Peri<'static, DMA_CH4>>,
dma_ch5: Option<Peri<'static, DMA_CH5>>,
dma_ch6: Option<Peri<'static, DMA_CH6>>,
dma_ch7: Option<Peri<'static, DMA_CH7>>,
dma_ch8: Option<Peri<'static, DMA_CH8>>,
dma_ch9: Option<Peri<'static, DMA_CH9>>,
dma_ch10: Option<Peri<'static, DMA_CH10>>,
dma_ch11: Option<Peri<'static, DMA_CH11>>,
dma_ch12: Option<Peri<'static, DMA_CH12>>,
dma_ch13: Option<Peri<'static, DMA_CH13>>,
dma_ch14: Option<Peri<'static, DMA_CH14>>,
dma_ch15: Option<Peri<'static, DMA_CH15>>,
pio_manager: PioManager,
}
pub struct DmaPool {
pub ch0: Option<Peri<'static, DMA_CH0>>,
pub ch1: Option<Peri<'static, DMA_CH1>>,
pub ch2: Option<Peri<'static, DMA_CH2>>,
pub ch3: Option<Peri<'static, DMA_CH3>>,
pub ch4: Option<Peri<'static, DMA_CH4>>,
pub ch5: Option<Peri<'static, DMA_CH5>>,
pub ch6: Option<Peri<'static, DMA_CH6>>,
pub ch7: Option<Peri<'static, DMA_CH7>>,
pub ch8: Option<Peri<'static, DMA_CH8>>,
pub ch9: Option<Peri<'static, DMA_CH9>>,
pub ch10: Option<Peri<'static, DMA_CH10>>,
pub ch11: Option<Peri<'static, DMA_CH11>>,
pub ch12: Option<Peri<'static, DMA_CH12>>,
pub ch13: Option<Peri<'static, DMA_CH13>>,
pub ch14: Option<Peri<'static, DMA_CH14>>,
pub ch15: Option<Peri<'static, DMA_CH15>>,
}
impl DmaPool {
pub const fn none() -> Self {
Self {
ch0: None,
ch1: None,
ch2: None,
ch3: None,
ch4: None,
ch5: None,
ch6: None,
ch7: None,
ch8: None,
ch9: None,
ch10: None,
ch11: None,
ch12: None,
ch13: None,
ch14: None,
ch15: None,
}
}
}
impl BusAllocator {
pub fn new(
spi0: Option<Peri<'static, SPI0>>,
spi1: Option<Peri<'static, SPI1>>,
i2c0: Option<Peri<'static, I2C0>>,
i2c1: Option<Peri<'static, I2C1>>,
uart0: Option<Peri<'static, UART0>>,
uart1: Option<Peri<'static, UART1>>,
dma: DmaPool,
pio0: Peri<'static, PIO0>,
pio1: Peri<'static, PIO1>,
pio2: Peri<'static, PIO2>,
) -> Self {
Self {
spi0_peri: spi0,
spi1_peri: spi1,
i2c0_peri: i2c0,
i2c1_peri: i2c1,
uart0_peri: uart0,
uart1_peri: uart1,
dma_ch0: dma.ch0,
dma_ch1: dma.ch1,
dma_ch2: dma.ch2,
dma_ch3: dma.ch3,
dma_ch4: dma.ch4,
dma_ch5: dma.ch5,
dma_ch6: dma.ch6,
dma_ch7: dma.ch7,
dma_ch8: dma.ch8,
dma_ch9: dma.ch9,
dma_ch10: dma.ch10,
dma_ch11: dma.ch11,
dma_ch12: dma.ch12,
dma_ch13: dma.ch13,
dma_ch14: dma.ch14,
dma_ch15: dma.ch15,
pio_manager: PioManager::new(pio0, pio1, pio2),
}
}
pub fn request_spi_hardware<I: SpiHw>(&mut self) -> Result<Peri<'static, I>, AllocatorError> {
I::take_peri(self).ok_or(AllocatorError::Exhausted)
}
pub fn release_spi_hardware<I: SpiHw>(&mut self, peri: Peri<'static, I>) {
I::return_peri(self, peri);
}
pub fn request_dma<C: DmaChannel>(&mut self) -> Result<Peri<'static, C>, AllocatorError> {
C::take_peri(self).ok_or(AllocatorError::Exhausted)
}
pub fn release_dma<C: DmaChannel>(&mut self, peri: Peri<'static, C>) {
C::return_peri(self, peri);
}
pub fn create_spi_hardware<I, TxDma, RxDma, Irq>(
&mut self,
clk: Peri<'static, impl ClkPin<I>>,
mosi: Peri<'static, impl MosiPin<I>>,
miso: Peri<'static, impl MisoPin<I>>,
irq: Irq,
config: spi::Config,
) -> Result<SpiBusHandle, AllocatorError>
where
I: SpiHw,
TxDma: DmaChannel,
RxDma: DmaChannel,
Irq: Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>
+ Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>
+ 'static,
{
HardwareSpiBus::<I>::validate_config(&config)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
if core::any::TypeId::of::<TxDma>() == core::any::TypeId::of::<RxDma>() {
return Err(AllocatorError::InvalidConfiguration);
}
let peri = self.request_spi_hardware::<I>()?;
let tx_dma = match self.request_dma::<TxDma>() {
Ok(tx_dma) => tx_dma,
Err(error) => {
self.release_spi_hardware(peri);
return Err(error);
}
};
let rx_dma = match self.request_dma::<RxDma>() {
Ok(rx_dma) => rx_dma,
Err(error) => {
self.release_dma(tx_dma);
self.release_spi_hardware(peri);
return Err(error);
}
};
let bus = HardwareSpiBus::new(peri, clk, mosi, miso, tx_dma, rx_dma, irq, config)
.expect("SPI configuration was validated before allocation");
Ok(SpiBusHandle::new(
Box::new(bus),
crate::bus::spi::SpiBusVersion::Hardware,
))
}
pub fn create_spi_pio<TxDma, RxDma, Irq>(
&mut self,
clk: Peri<'static, impl PioPin>,
mosi: Peri<'static, impl PioPin>,
miso: Peri<'static, impl PioPin>,
irq: Irq,
config: spi::Config,
) -> Result<SpiBusHandle, AllocatorError>
where
TxDma: DmaChannel,
RxDma: DmaChannel,
Irq: Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>
+ Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>
+ 'static,
{
crate::bus::spi_pio::PioSpiBus::<PIO0, 0>::validate_config(&config)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
if core::any::TypeId::of::<TxDma>() == core::any::TypeId::of::<RxDma>() {
return Err(AllocatorError::InvalidConfiguration);
}
let gpio_base_high = gpio_base_for_pins(&[clk.pin(), mosi.pin(), miso.pin()])
.ok_or(AllocatorError::InvalidConfiguration)?;
let instructions = spi_program_instructions(&config);
let (block, sm) = self
.pio_manager
.find_free_sm(gpio_base_high, instructions)
.ok_or(AllocatorError::Exhausted)?;
let tx_dma = self.request_dma::<TxDma>()?;
let rx_dma = match self.request_dma::<RxDma>() {
Ok(rx_dma) => rx_dma,
Err(error) => {
self.release_dma(tx_dma);
return Err(error);
}
};
Ok(self.pio_manager.build_spi_at(
block,
sm,
gpio_base_high,
instructions,
clk,
mosi,
miso,
tx_dma,
rx_dma,
irq,
config,
))
}
pub fn create_spi_bitbang(
&mut self,
clk: Peri<'static, impl embassy_rp::gpio::Pin>,
mosi: Peri<'static, impl embassy_rp::gpio::Pin>,
miso: Peri<'static, impl embassy_rp::gpio::Pin>,
config: spi::Config,
) -> Result<SpiBusHandle, AllocatorError> {
BitBangSpiBus::validate_config(&config)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
let bus = BitBangSpiBus::new(clk, mosi, miso, config)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
Ok(SpiBusHandle::new(
Box::new(bus),
crate::bus::spi::SpiBusVersion::BitBang,
))
}
pub fn create_spi_no_hardware<TxDma, RxDma, Irq>(
&mut self,
clk: Peri<'static, impl PioPin>,
mosi: Peri<'static, impl PioPin>,
miso: Peri<'static, impl PioPin>,
irq: Irq,
config: spi::Config,
) -> Result<SpiBusHandle, AllocatorError>
where
TxDma: DmaChannel,
RxDma: DmaChannel,
Irq: Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>
+ Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>
+ 'static,
{
crate::bus::spi_pio::PioSpiBus::<PIO0, 0>::validate_config(&config)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
if core::any::TypeId::of::<TxDma>() == core::any::TypeId::of::<RxDma>() {
return Err(AllocatorError::InvalidConfiguration);
}
let gpio_base_high = gpio_base_for_pins(&[clk.pin(), mosi.pin(), miso.pin()])
.ok_or(AllocatorError::InvalidConfiguration);
let instructions = spi_program_instructions(&config);
let tx_dma = self.request_dma::<TxDma>();
let rx_dma = self.request_dma::<RxDma>();
match (gpio_base_high, tx_dma, rx_dma) {
(Ok(gpio_base_high), Ok(tx_dma), Ok(rx_dma)) => {
if let Ok((block, sm)) = self
.pio_manager
.find_free_sm(gpio_base_high, instructions)
.ok_or(AllocatorError::Exhausted)
{
return Ok(self.pio_manager.build_spi_at(
block,
sm,
gpio_base_high,
instructions,
clk,
mosi,
miso,
tx_dma,
rx_dma,
irq,
config,
));
}
}
(_, tx_dma, rx_dma) => {
if let Ok(tx_dma) = tx_dma {
self.release_dma(tx_dma);
}
if let Ok(rx_dma) = rx_dma {
self.release_dma(rx_dma);
}
}
}
self.create_spi_bitbang(clk, mosi, miso, config)
}
pub fn create_spi<I, TxDma, RxDma, Irq>(
&mut self,
clk: Peri<'static, impl ClkPin<I> + PioPin>,
mosi: Peri<'static, impl MosiPin<I> + PioPin>,
miso: Peri<'static, impl MisoPin<I> + PioPin>,
irq: Irq,
config: spi::Config,
) -> Result<SpiBusHandle, AllocatorError>
where
I: SpiHw,
TxDma: DmaChannel,
RxDma: DmaChannel,
Irq: Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>
+ Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>
+ 'static,
{
HardwareSpiBus::<I>::validate_config(&config)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
if core::any::TypeId::of::<TxDma>() == core::any::TypeId::of::<RxDma>() {
return Err(AllocatorError::InvalidConfiguration);
}
let peri = self.request_spi_hardware::<I>();
let tx_dma = self.request_dma::<TxDma>();
let rx_dma = self.request_dma::<RxDma>();
match (peri, tx_dma, rx_dma) {
(Ok(peri), Ok(tx_dma), Ok(rx_dma)) => {
let bus = HardwareSpiBus::new(peri, clk, mosi, miso, tx_dma, rx_dma, irq, config)
.expect("SPI configuration was validated before allocation");
Ok(SpiBusHandle::new(
Box::new(bus),
crate::bus::spi::SpiBusVersion::Hardware,
))
}
(peri, tx_dma, rx_dma) => {
if let Ok(peri) = peri {
self.release_spi_hardware(peri);
}
if let Ok(tx_dma) = tx_dma {
self.release_dma(tx_dma);
}
if let Ok(rx_dma) = rx_dma {
self.release_dma(rx_dma);
}
self.create_spi_no_hardware::<TxDma, RxDma, Irq>(clk, mosi, miso, irq, config)
}
}
}
pub fn request_i2c_hardware<I: I2cHw>(&mut self) -> Result<Peri<'static, I>, AllocatorError> {
I::take_peri(self).ok_or(AllocatorError::Exhausted)
}
pub fn release_i2c_hardware<I: I2cHw>(&mut self, peri: Peri<'static, I>) {
I::return_peri(self, peri);
}
pub fn create_i2c_hardware<I, Irq>(
&mut self,
scl: Peri<'static, impl i2c::SclPin<I>>,
sda: Peri<'static, impl i2c::SdaPin<I>>,
irq: Irq,
config: i2c::Config,
) -> Result<I2cBusHandle, AllocatorError>
where
I: I2cHw,
Irq: Binding<I::Interrupt, i2c::InterruptHandler<I>> + 'static,
{
HardwareI2cBus::<I>::validate_config(&config)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
let peri = self.request_i2c_hardware::<I>()?;
let bus = HardwareI2cBus::new(peri, scl, sda, irq, config)
.expect("I2C configuration was validated before allocation");
Ok(I2cBusHandle::new(
Box::new(bus),
crate::bus::i2c::I2cBusVersion::Hardware,
))
}
pub fn create_i2c_bitbang(
&mut self,
scl: Peri<'static, impl embassy_rp::gpio::Pin>,
sda: Peri<'static, impl embassy_rp::gpio::Pin>,
frequency_hz: u32,
) -> Result<I2cBusHandle, AllocatorError> {
let bus = BitBangI2cBus::new(scl, sda, frequency_hz)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
Ok(I2cBusHandle::new(
Box::new(bus),
crate::bus::i2c::I2cBusVersion::BitBang,
))
}
pub fn request_uart_hardware<I: UartHw>(&mut self) -> Result<Peri<'static, I>, AllocatorError> {
I::take_peri(self).ok_or(AllocatorError::Exhausted)
}
pub fn release_uart_hardware<I: UartHw>(&mut self, peri: Peri<'static, I>) {
I::return_peri(self, peri);
}
pub fn create_uart_hardware<I, TxDma, RxDma, Irq>(
&mut self,
tx: Peri<'static, impl uart::TxPin<I>>,
rx: Peri<'static, impl uart::RxPin<I>>,
irq: Irq,
config: uart::Config,
) -> Result<UartBusHandle, AllocatorError>
where
I: UartHw,
TxDma: DmaChannel,
RxDma: DmaChannel,
Irq: Binding<I::Interrupt, uart::InterruptHandler<I>>
+ Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>
+ Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>
+ 'static,
{
HardwareUartBus::validate_config(&config)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
if core::any::TypeId::of::<TxDma>() == core::any::TypeId::of::<RxDma>() {
return Err(AllocatorError::InvalidConfiguration);
}
let peri = self.request_uart_hardware::<I>()?;
let tx_dma = match self.request_dma::<TxDma>() {
Ok(tx_dma) => tx_dma,
Err(error) => {
self.release_uart_hardware(peri);
return Err(error);
}
};
let rx_dma = match self.request_dma::<RxDma>() {
Ok(rx_dma) => rx_dma,
Err(error) => {
self.release_dma(tx_dma);
self.release_uart_hardware(peri);
return Err(error);
}
};
let bus = HardwareUartBus::new(peri, tx, rx, irq, tx_dma, rx_dma, config)
.expect("UART configuration was validated before allocation");
Ok(UartBusHandle::new(
Box::new(bus),
crate::bus::uart::UartBusVersion::Hardware,
))
}
pub fn create_uart_pio(
&mut self,
tx: Peri<'static, impl PioPin>,
rx: Peri<'static, impl PioPin>,
baud_rate: u32,
) -> Result<UartBusHandle, AllocatorError> {
crate::bus::uart_pio::PioUartBus::<PIO0, 0, 1>::validate_baud(baud_rate)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
let gpio_base_high =
gpio_base_for_pins(&[tx.pin()]).ok_or(AllocatorError::InvalidConfiguration)?;
if gpio_base_for_pins(&[rx.pin()]) != Some(gpio_base_high) {
return Err(AllocatorError::InvalidConfiguration);
}
self.pio_manager
.build_uart_pio(tx, rx, baud_rate)
.ok_or(AllocatorError::Exhausted)
}
pub fn create_uart_bitbang(
&mut self,
tx: Peri<'static, impl embassy_rp::gpio::Pin>,
rx: Peri<'static, impl embassy_rp::gpio::Pin>,
baud_rate: u32,
) -> Result<UartBusHandle, AllocatorError> {
BitBangUartBus::validate_baud(baud_rate)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
let bus = BitBangUartBus::new(tx, rx, baud_rate)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
Ok(UartBusHandle::new(
Box::new(bus),
crate::bus::uart::UartBusVersion::BitBang,
))
}
pub fn create_uart_no_hardware(
&mut self,
tx: Peri<'static, impl PioPin>,
rx: Peri<'static, impl PioPin>,
baud_rate: u32,
) -> Result<UartBusHandle, AllocatorError> {
crate::bus::uart_pio::PioUartBus::<PIO0, 0, 1>::validate_baud(baud_rate)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
let tx_base = gpio_base_for_pins(&[tx.pin()]);
let rx_base = gpio_base_for_pins(&[rx.pin()]);
if let (Some(gpio_base_high), true) = (tx_base, tx_base == rx_base)
&& let Some((block, sm_tx, sm_rx)) = self
.pio_manager
.find_free_sm_pair(gpio_base_high, crate::bus::pio::PIO_UART_INSTRUCTIONS)
{
return Ok(self.pio_manager.build_uart_at(
block,
sm_tx,
sm_rx,
gpio_base_high,
crate::bus::pio::PIO_UART_INSTRUCTIONS,
tx,
rx,
baud_rate,
));
}
self.create_uart_bitbang(tx, rx, baud_rate)
}
pub fn create_uart<I, TxDma, RxDma, Irq>(
&mut self,
tx: Peri<'static, impl uart::TxPin<I> + PioPin>,
rx: Peri<'static, impl uart::RxPin<I> + PioPin>,
irq: Irq,
config: uart::Config,
) -> Result<UartBusHandle, AllocatorError>
where
I: UartHw,
TxDma: DmaChannel,
RxDma: DmaChannel,
Irq: Binding<I::Interrupt, uart::InterruptHandler<I>>
+ Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>
+ Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>
+ 'static,
{
HardwareUartBus::validate_config(&config)
.map_err(|_| AllocatorError::InvalidConfiguration)?;
if core::any::TypeId::of::<TxDma>() == core::any::TypeId::of::<RxDma>() {
return Err(AllocatorError::InvalidConfiguration);
}
let peri = self.request_uart_hardware::<I>();
let tx_dma = self.request_dma::<TxDma>();
let rx_dma = self.request_dma::<RxDma>();
match (peri, tx_dma, rx_dma) {
(Ok(peri), Ok(tx_dma), Ok(rx_dma)) => {
let bus = HardwareUartBus::new(peri, tx, rx, irq, tx_dma, rx_dma, config)
.expect("UART configuration was validated before allocation");
Ok(UartBusHandle::new(
Box::new(bus),
crate::bus::uart::UartBusVersion::Hardware,
))
}
(peri, tx_dma, rx_dma) => {
if let Ok(peri) = peri {
self.release_uart_hardware(peri);
}
if let Ok(tx_dma) = tx_dma {
self.release_dma(tx_dma);
}
if let Ok(rx_dma) = rx_dma {
self.release_dma(rx_dma);
}
self.create_uart_no_hardware(tx, rx, config.baudrate)
}
}
}
pub fn request_pio<P: PioPin>(
&mut self,
pin: &Peri<'static, P>,
) -> Option<crate::bus::pio::PioAccess<'_>> {
self.pio_manager.request_pio(pin)
}
}