1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! Various traits allowing to tie all the flexcomm-related pins
//! together and have compile-time safety.
//!
//! We use "chip select" instead of "slave select" terminology.
use core::ops::Deref;

use crate::{
    raw,
};

use super::{
    PinId,
    PinType,
};

pub trait I2c: Deref<Target = raw::i2c0::RegisterBlock> {}
pub trait I2s {}
pub trait Spi: Deref<Target = raw::spi0::RegisterBlock> {}
pub trait Usart: Deref<Target = raw::usart0::RegisterBlock> {}


/// I2C serial clock
pub trait I2cSclPin<PIO, I2C> where PIO: PinId, I2C: I2c {}
/// I2C serial data
pub trait I2cSdaPin<PIO, I2C> where PIO: PinId, I2C: I2c {}

/// I2S serial clock
pub trait I2sSckPin<PIO, I2S> where PIO: PinId, I2S: I2s {}
/// I2S word select
pub trait I2sWsPin<PIO, I2S> where PIO: PinId, I2S: I2s {}
/// I2S serial data
pub trait I2sSdaPin<PIO, I2S> where PIO: PinId, I2S: I2s {}
/// I2S master clock
pub trait I2sMclkPin<PIO, I2S> where PIO: PinId, I2S: I2s {}

pub enum ChipSelect {
    Chip0,
    Chip1,
    Chip2,
    Chip3,
    NoChips,
}

/// SPI serial clock
pub trait SpiSckPin<PIO, SPI> where PIO: PinId, SPI: Spi {}
/// SPI master-out/chip-in data
pub trait SpiMosiPin<PIO, SPI> where PIO: PinId, SPI: Spi {}
/// SPI master-in/chip-out data
pub trait SpiMisoPin<PIO, SPI> where PIO: PinId, SPI: Spi {}
/// SPI chip select
pub trait SpiCsPin<PIO, SPI> where PIO: PinId, SPI: Spi { const CS: ChipSelect; }

/// Filler type for when no Mosi is necessary
pub struct NoMosi;
impl<SPI: Spi> SpiMosiPin<NoPio, SPI> for NoMosi {}
/// Filler type for when no Miso is necessary
pub struct NoMiso;
impl<SPI: Spi> SpiMisoPin<NoPio, SPI> for NoMiso {}
/// Filler type for when no Cs is necessary
pub struct NoCs;
impl<SPI: Spi> SpiCsPin<NoPio, SPI> for NoCs { const CS: ChipSelect = ChipSelect::NoChips; }

// /// SPI chip select 0
// pub trait SpiCs0Pin<PIO, SPI> where PIO: PinId, SPI: Spi { const CS: u8 = 0; }
// /// SPI chip select 1
// pub trait SpiCs1Pin<PIO, SPI> where PIO: PinId, SPI: Spi { const CS: u8 = 1; }
// /// SPI chip select 2
// pub trait SpiCs2Pin<PIO, SPI> where PIO: PinId, SPI: Spi { const CS: u8 = 2; }
// /// SPI chip select 3
// pub trait SpiCs3Pin<PIO, SPI> where PIO: PinId, SPI: Spi { const CS: u8 = 3; }

/// USART transmitter output
pub trait UsartTxPin<PIO, USART> where PIO: PinId, USART: Usart {}
/// USART receiver input
pub trait UsartRxPin<PIO, USART> where PIO: PinId, USART: Usart {}
/// USART request-to-send output
pub trait UsartRtsPin<PIO, USART> where PIO: PinId, USART: Usart {}
/// USART clear-to-send input
pub trait UsartCtsPin<PIO, USART> where PIO: PinId, USART: Usart {}
/// USART serial clock
pub trait UsartSclkPin<PIO, USART> where PIO: PinId, USART: Usart {}

pub struct NoPio;
impl PinId for NoPio {
    const PORT: usize = !0;
    const NUMBER: u8 = !0;
    const MASK: u32 = !0;
    const OFFSET: usize = !0;

    const TYPE: PinType = PinType::D;
}

// TODO: revisit this. Instead of passing in fake pins,
// write proper drivers for the use cases.
// Think about using a generic enum {Read, Write, ReadWrite}
// parameter
/// Filler type for when no Tx is necessary
pub struct NoTx;
/// Filler type for when no Rx is necessary
pub struct NoRx;
impl<USART: Usart> UsartTxPin<NoPio, USART> for NoTx {}
impl<USART: Usart> UsartRxPin<NoPio, USART> for NoRx {}


pub trait I2cPins<PIO1: PinId, PIO2: PinId, I2C: I2c> {}

impl<PIO1, PIO2, I2C, SCL, SDA> I2cPins<PIO1, PIO2, I2C> for (SCL, SDA)
where
    PIO1: PinId,
    PIO2: PinId,
    I2C: I2c,
    SCL: I2cSclPin<PIO1, I2C>,
    SDA: I2cSdaPin<PIO2, I2C>,
{}


pub trait SpiPins<PIO1: PinId, PIO2: PinId, PIO3: PinId, PIO4: PinId, SPI: Spi> {
    const CS: ChipSelect;
}

impl<PIO1, PIO2, PIO3, PIO4, SPI, SCK, MISO, MOSI, CS>
    SpiPins<PIO1, PIO2, PIO3, PIO4, SPI>
for (SCK, MOSI, MISO, CS) where
    PIO1: PinId,
    PIO2: PinId,
    PIO3: PinId,
    PIO4: PinId,
    SPI: Spi,
    SCK: SpiSckPin<PIO1, SPI>,
    MOSI: SpiMosiPin<PIO2, SPI>,
    MISO: SpiMisoPin<PIO3, SPI>,
    CS: SpiCsPin<PIO4, SPI>,
{
    const CS: ChipSelect = CS::CS;
}

pub trait UsartPins<PIO1: PinId, PIO2: PinId, USART: Usart> {}

impl<PIO1, PIO2, USART, TX, RX> UsartPins<PIO1, PIO2, USART> for (TX, RX)
where
    PIO1: PinId,
    PIO2: PinId,
    USART: Usart,
    TX: UsartTxPin<PIO1, USART>,
    RX: UsartRxPin<PIO2, USART>,
{}

// Note: Pio0_12 can be both: into_i2c_3_scl_pin() and into_i2c_6_scl_pin() [alt1 vs alt7]
//
// pin.into_I2C3_SCL_pin()
//
// what about: let scl_pin: <_, Special<I2c4, Scl>> = pins.pio1_20.into();
// what about: let scl_pin = Pin<Pio1_20, Special<I2c4, Scl>>::from(pins.pio1_20);
//
// what about... `I2cMaster(i2c, (p0_12.into(), p1_1.into()))` <-- too much magic/work in `From`?