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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Some traits needed to implement, in order to use [`Commands`].
//!
//! The minimum would be to implement [DcxPin] and one of [WriteU8] and
//! [WriteU8s], then use an [`adapter`] to complete the missing
//! one of [WriteU8] and [WriteU8s]. With these, the write parts of [`Commands`]
//! are already usable.
//!
//! Note that the SPI protocol of ST7735's write commands
//! are actually compatible with command SPI implementations of
//! microcontrollers, eg., STM32 SPI with `CPOL=1` (clock idles at high) and
//! `CPHA=1` (data sampled at the second edge) without an SS pin. An example
//! can be found at [examples/stm32f3348_disco](https://github.com/jeru/st7735-async-low/tree/main/st7735_async_low/examples/stm32f3348_disco).
//!
//! If the user also needs to read data from the ST7735, then [Read] and
//! [ReadBits] should also be implemented. Presumably **no** high performance is
//! needed because reading is mostly for debugging purposes; the dummy bit
//! when reading 24- or 32-bit data is quite annoying (not totally impossible
//! to implement with hardware SPI but quite challenging); and when reading,
//! the clock must toggles slower than when writing (so the user needs to
//! reconfigure the SPI anyway). Therefore, it is
//! recommended that the user simply implements [ReadBits::read_bits()] with
//! bit-bangs.
//!
//! # Performance Consideration
//!
//! The reason to allow the user to implement [WriteU8] and [WriteU8s]
//! separately is for better performance. While it is natural to think
//! [WriteU8s] as a looped version of [WriteU8], there can be quite some
//! latency and throughput differences. Eg., in a STM32 microcontroller,
//! A loop-based [WriteU8s] is suboptimal for the following reasons:
//! * As soon as a byte is started to be sent, the user can already write
//!   the next byte to SPI's TX FIFO buffer. But [WriteU8::write_u8()] finishes
//!   only after the previous byte is fully sent to the device.
//! * DMA (direct memory access) is also very beneficial for [WriteU8s],
//!   especially when sending many bytes.
//!
//! So the user should only use an [`AdapterU8`] if they doesn't care
//! about the performance difference here.
//!
//! [`Commands`]: ../struct.Commands.html
//! [`adapter`]: ../adapters/index.html
//! [`AdapterU8`]: ../adapters/struct.AdapterU8.html

use core::future::Future;

/// Defines how the `DCX` pin operates.
pub trait DcxPin {
    /// Toggles the DCX pin to the `command mode` (LOW value).
    fn set_dcx_command_mode(&mut self);
    /// Toggles the DCX pin to the `data mode` (HIGH value).
    fn set_dcx_data_mode(&mut self);
}

/// Defines how a single [u8] is written with the `SCK` and `SDA` pins.
///
/// Common MCUs' SPI peripheral can be used, with
/// CPOL=1, CPHA=1 and MSB-first. Notice the timing requirement from ST7735's
/// datasheet. Most important ones:
/// * `SCK` low duration and high durations are at least 15ns long.
/// * `SCK` period is at least 66ns long.
pub trait WriteU8<'a> {
    type WriteU8Done : 'a + Future<Output=()>;

    fn write_u8(&'a mut self, data: u8) -> Self::WriteU8Done;
}

/// Defines how a sequence of `u8` or `u16` is written with the `SCK` and `SDA`
/// pins.
pub trait WriteU8s<'a> {
    type WriteU8sDone : 'a + Future<Output=()>;

    fn write_u8s(&'a mut self, data: &'a [u8]) -> Self::WriteU8sDone;
}

/// Defines how the MCU should use the `SCK` and `SDA` pins to read data.
///
/// It is assumed the reading isn't super important (mostly for debugging
/// purposes); and the implementation is actually quite hard because some
/// commands read without a dummy bit and some read with a dummy bit.
/// So the user can choose to simply **not** implement it. The write commands
/// of [Commands](crate::Commands) will still work in that case.
///
/// Calling `start_reading()` should switch the device into reading mode,
/// which should be switched back into writing mode when the returned object
/// of `start_reading()` is dropped.
pub trait Read<'a> {
    type ReadBitsType : 'a + for<'b> ReadBits<'b>;

    fn start_reading(&'a mut self) -> Self::ReadBitsType;
}

/// Defines how the helper RAII variable returned by [Read::start_reading()]
/// should behave.
///
/// Notice the timing requirement from ST7735's datasheet. Most important ones:
/// * `SCK` low duration and high durations are at least 60ns long.
/// * `SCK` period is at least 150ns long.
pub trait ReadBits<'a> {
    type ReadBitsDone : 'a + Future<Output=u32>;

    fn read_bits(&'a mut self, num_bits: usize) -> Self::ReadBitsDone;
}

#[cfg(test)]
mod test {
    use core::marker::PhantomData;
    use core::pin::Pin;
    use core::task::{Context, Poll};
    use super::*;

    struct FutureDummy1<'a, T, R = ()> {
        _t: &'a T,
        _r: PhantomData<R>,
    }
    impl<'a, T, R> FutureDummy1<'a, T, R> {
        pub fn new(t: &'a T) -> Self { Self{_t: t, _r: Default::default()} }
    }
    impl<'a, T, R: Default> Future for FutureDummy1<'a, T, R> {
        type Output = R;

        fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<R> {
            Poll::Ready(Default::default())
        }
    }

    #[derive(Default)]
    struct Dummy1 { u: usize, i: isize }

    impl<'a> WriteU8<'a> for Dummy1 {
        type WriteU8Done = FutureDummy1<'a, usize>;

        fn write_u8(&'a mut self, _data: u8) -> Self::WriteU8Done {
            FutureDummy1::new(&self.u)
        }
    }

    impl<'a> WriteU8s<'a> for Dummy1 {
        type WriteU8sDone = FutureDummy1<'a, isize>;

        fn write_u8s(&'a mut self, _data: &'a [u8]) -> Self::WriteU8sDone {
            FutureDummy1::new(&self.i)
        }
    }

    #[test]
    fn write_u8() {
        let mut dummy: Dummy1 = Default::default();
        let _ = async { dummy.write_u8(10).await; };
    }

    #[test]
    fn write_u8_slice() {
        let mut dummy: Dummy1 = Default::default();
        let items: [u8; 3] = [0, 1, 2];
        let _ = async { dummy.write_u8s(&items).await; };
    }

    #[derive(Default)]
    struct Dummy2 { i: i64 }
    struct Dummy2Reader<'a> { d: &'a mut Dummy2 }

    impl<'a> Read<'a> for Dummy2 {
        type ReadBitsType = Dummy2Reader<'a>;

        fn start_reading(&'a mut self) -> Self::ReadBitsType {
            Dummy2Reader{d: self}
        }
    }

    impl<'a, 'b> ReadBits<'b> for Dummy2Reader<'a> {
        type ReadBitsDone = FutureDummy1<'b, i64, u32>;

        fn read_bits(&'b mut self, _num_bits: usize) -> Self::ReadBitsDone {
            FutureDummy1::new(&self.d.i)
        }
    }

    #[test]
    fn read_bits() {
        let mut dummy: Dummy2 = Default::default();
        let _ = async {
            let mut r = dummy.start_reading();
            r.read_bits(12).await
        };
    }
}