Remove pin generics from I8080 (#2171)

* Remove pin generics from I8080

* Doc example

* temp

* More docs

* doc fix

---------

Co-authored-by: Dominic Fischer <git@dominicfischer.me>
This commit is contained in:
Dominic Fischer 2024-09-17 07:56:01 -06:00 committed by GitHub
parent a787a13441
commit 5ae76d727e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 82 additions and 44 deletions

View File

@ -38,6 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The (previously undocumented) `ErasedPin` enum has been replaced with the `ErasedPin` struct. (#2094)
- Renamed and merged `Rtc::get_time_us` and `Rtc::get_time_ms` into `Rtc::time_since_boot` (#1883)
- ESP32: Added support for touch sensing on GPIO32 and 33 (#2109)
- Removed gpio pin generics from I8080 driver type. (#2171)
- I8080 driver now decides bus width at transfer time rather than construction time. (#2171)
- Replaced `AnyPin` with `InputSignal` and `OutputSignal` and renamed `ErasedPin` to `AnyPin` (#2128)
- Replaced the `ErasedTimer` enum with the `AnyTimer` struct. (#?)
- Changed the parameters of `Spi::with_pins` to no longer be optional (#2133)

View File

@ -179,3 +179,22 @@ You can pass `NoPin` or `Level` as inputs, and `NoPin` as output if you don't ne
- .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.
```diff
- let _: I8080<'a, DmaChannel3, TxEightBits<AnyPin, AnyPin, AnyPin, ....>, Blocking>;
+ let _: I8080<'a, DmaChannel3, Blocking>;
```
## I8080 start transfer type inference
The I8080 driver now decides bus width at transfer time, which means you don't get inference.
```diff
let mut i8080 = I8080::new(....);
- i8080.send(0x12, 0, &[0, 1, 2, 3, 4]);
+ i8080.send(0x12u8, 0, &[0, 1, 2, 3, 4]);
```

View File

@ -53,7 +53,7 @@
//! )
//! .with_ctrl_pins(io.pins.gpio0, io.pins.gpio47);
//!
//! i8080.send(0x3A, 0, &[0x55]).unwrap(); // RGB565
//! i8080.send(0x3Au8, 0, &[0x55]).unwrap(); // RGB565
//! # }
//! ```
@ -91,21 +91,19 @@ use crate::{
};
/// Represents the I8080 LCD interface.
pub struct I8080<'d, CH: DmaChannel, P, DM: Mode> {
pub struct I8080<'d, CH: DmaChannel, DM: Mode> {
lcd_cam: PeripheralRef<'d, LCD_CAM>,
tx_channel: ChannelTx<'d, CH>,
tx_chain: DescriptorChain,
_pins: P,
_phantom: PhantomData<DM>,
}
impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> I8080<'d, CH, P, DM>
impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM>
where
CH::P: LcdCamPeripheral,
P::Word: Into<u16>,
{
/// Creates a new instance of the I8080 LCD interface.
pub fn new(
pub fn new<P: TxPins>(
lcd: Lcd<'d, DM>,
mut channel: ChannelTx<'d, CH>,
descriptors: &'static mut [DmaDescriptor],
@ -113,8 +111,6 @@ where
frequency: HertzU32,
config: Config,
) -> Self {
let is_2byte_mode = size_of::<P::Word>() == 2;
let lcd_cam = lcd.lcd_cam;
let clocks = Clocks::get();
@ -168,7 +164,7 @@ where
.lcd_byte_order()
.bit(false)
.lcd_2byte_en()
.bit(is_2byte_mode)
.bit(false)
});
lcd_cam.lcd_misc().write(|w| unsafe {
// Set the threshold for Async Tx FIFO full event. (5 bits)
@ -252,13 +248,12 @@ where
lcd_cam,
tx_channel: channel,
tx_chain: DescriptorChain::new(descriptors),
_pins: pins,
_phantom: PhantomData,
}
}
}
impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> DmaSupport for I8080<'d, CH, P, DM> {
impl<'d, CH: DmaChannel, DM: Mode> DmaSupport for I8080<'d, CH, DM> {
fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) {
let lcd_user = self.lcd_cam.lcd_user();
// Wait until LCD_START is cleared by hardware.
@ -271,7 +266,7 @@ impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> DmaSupport for I8080<'d, CH, P, DM
}
}
impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> DmaSupportTx for I8080<'d, CH, P, DM> {
impl<'d, CH: DmaChannel, DM: Mode> DmaSupportTx for I8080<'d, CH, DM> {
type TX = ChannelTx<'d, CH>;
fn tx(&mut self) -> &mut Self::TX {
@ -283,19 +278,15 @@ impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> DmaSupportTx for I8080<'d, CH, P,
}
}
impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> I8080<'d, CH, P, DM>
where
P::Word: Into<u16>,
{
impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM> {
/// Configures the byte order for data transmission.
pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self {
let is_inverted = byte_order != ByteOrder::default();
self.lcd_cam.lcd_user().modify(|_, w| {
if size_of::<P::Word>() == 2 {
w.lcd_byte_order().bit(is_inverted)
} else {
w.lcd_8bits_order().bit(is_inverted)
}
w.lcd_byte_order()
.bit(is_inverted)
.lcd_8bits_order()
.bit(is_inverted)
});
self
}
@ -336,11 +327,18 @@ where
}
/// Sends a command and data to the LCD using the I8080 interface.
pub fn send(
///
/// Passing a `Command<u8>` will make this an 8-bit transfer and a
/// `Command<u16>` will make this a 16-bit transfer.
///
/// Note: A 16-bit transfer on an 8-bit bus will silently truncate the 2nd
/// byte and an 8-bit transfer on a 16-bit bus will silently pad each
/// byte to 2 bytes.
pub fn send<W: Copy + Into<u16>>(
&mut self,
cmd: impl Into<Command<P::Word>>,
cmd: impl Into<Command<W>>,
dummy: u8,
data: &[P::Word],
data: &[W],
) -> Result<(), DmaError> {
self.setup_send(cmd.into(), dummy);
self.start_write_bytes_dma(data.as_ptr() as _, core::mem::size_of_val(data))?;
@ -356,13 +354,21 @@ where
}
/// Sends a command and data to the LCD using DMA.
pub fn send_dma<'t, TXBUF>(
///
/// Passing a `Command<u8>` will make this an 8-bit transfer and a
/// `Command<u16>` will make this a 16-bit transfer.
///
/// Note: A 16-bit transfer on an 8-bit bus will silently truncate the 2nd
/// byte and an 8-bit transfer on a 16-bit bus will silently pad each
/// byte to 2 bytes.
pub fn send_dma<'t, W, TXBUF>(
&'t mut self,
cmd: impl Into<Command<P::Word>>,
cmd: impl Into<Command<W>>,
dummy: u8,
data: &'t TXBUF,
) -> Result<DmaTransferTx<'_, Self>, DmaError>
where
W: Copy + Into<u16>,
TXBUF: ReadBuffer,
{
let (ptr, len) = unsafe { data.read_buffer() };
@ -375,18 +381,23 @@ where
}
}
impl<'d, CH: DmaChannel, P: TxPins> I8080<'d, CH, P, crate::Async>
where
P::Word: Into<u16>,
{
impl<'d, CH: DmaChannel> I8080<'d, CH, crate::Async> {
/// Asynchronously sends a command and data to the LCD using DMA.
pub async fn send_dma_async<'t, TXBUF>(
///
/// Passing a `Command<u8>` will make this an 8-bit transfer and a
/// `Command<u16>` will make this a 16-bit transfer.
///
/// Note: A 16-bit transfer on an 8-bit bus will silently truncate the 2nd
/// byte and an 8-bit transfer on a 16-bit bus will silently pad each
/// byte to 2 bytes.
pub async fn send_dma_async<'t, W, TXBUF>(
&'t mut self,
cmd: impl Into<Command<P::Word>>,
cmd: impl Into<Command<W>>,
dummy: u8,
data: &'t TXBUF,
) -> Result<(), DmaError>
where
W: Copy + Into<u16>,
TXBUF: ReadBuffer,
{
let (ptr, len) = unsafe { data.read_buffer() };
@ -403,7 +414,7 @@ where
}
}
impl<'d, CH: DmaChannel, P, DM: Mode> I8080<'d, CH, P, DM> {
impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM> {
fn setup_send<T: Copy + Into<u16>>(&mut self, cmd: Command<T>, dummy: u8) {
// Reset LCD control unit and Async Tx FIFO
self.lcd_cam
@ -439,8 +450,10 @@ impl<'d, CH: DmaChannel, P, DM: Mode> I8080<'d, CH, P, DM> {
}
}
// Set dummy length
let is_2byte_mode = size_of::<T>() == 2;
self.lcd_cam.lcd_user().modify(|_, w| unsafe {
// Set dummy length
if dummy > 0 {
// Enable DUMMY phase in LCD sequence when LCD starts.
w.lcd_dummy()
@ -451,6 +464,8 @@ impl<'d, CH: DmaChannel, P, DM: Mode> I8080<'d, CH, P, DM> {
} else {
w.lcd_dummy().clear_bit()
}
.lcd_2byte_en()
.bit(is_2byte_mode)
});
}
@ -508,7 +523,7 @@ impl<'d, CH: DmaChannel, P, DM: Mode> I8080<'d, CH, P, DM> {
}
}
impl<'d, CH: DmaChannel, P, DM: Mode> core::fmt::Debug for I8080<'d, CH, P, DM> {
impl<'d, CH: DmaChannel, DM: Mode> core::fmt::Debug for I8080<'d, CH, DM> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("I8080").finish()
}
@ -572,8 +587,14 @@ pub enum Command<T> {
Two(T, T),
}
impl<T> From<T> for Command<T> {
fn from(value: T) -> Self {
impl From<u8> for Command<u8> {
fn from(value: u8) -> Self {
Command::One(value)
}
}
impl From<u16> for Command<u16> {
fn from(value: u16) -> Self {
Command::One(value)
}
}
@ -647,8 +668,6 @@ where
P6: PeripheralOutput,
P7: PeripheralOutput,
{
type Word = u8;
fn configure(&mut self) {
self.pin_0.set_to_push_pull_output(crate::private::Internal);
self.pin_0
@ -796,7 +815,6 @@ where
P14: PeripheralOutput,
P15: PeripheralOutput,
{
type Word = u16;
fn configure(&mut self) {
self.pin_0.set_to_push_pull_output(crate::private::Internal);
self.pin_0
@ -857,7 +875,6 @@ where
mod private {
pub trait TxPins {
type Word: Copy;
fn configure(&mut self);
}
}

View File

@ -222,19 +222,19 @@ fn main() -> ! {
let mut bytes_left_to_write = total_bytes;
let transfer = i8080.send_dma(0x2C, 0, &buffer).unwrap();
let transfer = i8080.send_dma(0x2Cu8, 0, &buffer).unwrap();
transfer.wait().unwrap();
bytes_left_to_write -= buffer.len();
while bytes_left_to_write >= buffer.len() {
let transfer = i8080.send_dma(0x3C, 0, &buffer).unwrap();
let transfer = i8080.send_dma(0x3Cu8, 0, &buffer).unwrap();
transfer.wait().unwrap();
bytes_left_to_write -= buffer.len();
}
if bytes_left_to_write > 0 {
let transfer = i8080.send_dma(0x3C, 0, &buffer).unwrap();
let transfer = i8080.send_dma(0x3Cu8, 0, &buffer).unwrap();
transfer.wait().unwrap();
}