Migrate I8080 driver to a move based API (#2191)
* Migrate I8080 driver to a move based API * fmt * comments --------- Co-authored-by: Dominic Fischer <git@dominicfischer.me>
This commit is contained in:
parent
a3304c6cff
commit
8789ca3c3d
@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- You can now use `Input`, `Output`, `OutputOpenDrain` and `Flex` pins as EXTI and RTCIO wakeup sources (#2095)
|
- You can now use `Input`, `Output`, `OutputOpenDrain` and `Flex` pins as EXTI and RTCIO wakeup sources (#2095)
|
||||||
- Added `Rtc::set_current_time` to allow setting RTC time, and `Rtc::current_time` to getting RTC time while taking into account boot time (#1883)
|
- Added `Rtc::set_current_time` to allow setting RTC time, and `Rtc::current_time` to getting RTC time while taking into account boot time (#1883)
|
||||||
- Added APIs to allow connecting signals through the GPIO matrix. (#2128)
|
- Added APIs to allow connecting signals through the GPIO matrix. (#2128)
|
||||||
|
- Allow I8080 transfers to be cancelled on the spot (#2191)
|
||||||
- Implement `TryFrom<u32>` for `ledc::timer::config::Duty` (#1984)
|
- Implement `TryFrom<u32>` for `ledc::timer::config::Duty` (#1984)
|
||||||
- Expose `RtcClock::get_xtal_freq` and `RtcClock::get_slow_freq` publically for all chips (#2183)
|
- Expose `RtcClock::get_xtal_freq` and `RtcClock::get_slow_freq` publically for all chips (#2183)
|
||||||
- TWAI support for ESP32-H2 (#2199)
|
- TWAI support for ESP32-H2 (#2199)
|
||||||
@ -46,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- ESP32: Added support for touch sensing on GPIO32 and 33 (#2109)
|
- ESP32: Added support for touch sensing on GPIO32 and 33 (#2109)
|
||||||
- Removed gpio pin generics from I8080 driver type. (#2171)
|
- Removed gpio pin generics from I8080 driver type. (#2171)
|
||||||
- I8080 driver now decides bus width at transfer time rather than construction time. (#2171)
|
- I8080 driver now decides bus width at transfer time rather than construction time. (#2171)
|
||||||
|
- Migrate the I8080 driver to a move based API (#2191)
|
||||||
- Replaced `AnyPin` with `InputSignal` and `OutputSignal` and renamed `ErasedPin` to `AnyPin` (#2128)
|
- Replaced `AnyPin` with `InputSignal` and `OutputSignal` and renamed `ErasedPin` to `AnyPin` (#2128)
|
||||||
- Replaced the `ErasedTimer` enum with the `AnyTimer` struct. (#2144)
|
- Replaced the `ErasedTimer` enum with the `AnyTimer` struct. (#2144)
|
||||||
- `Camera` and `AesDma` now support erasing the DMA channel type (#2258)
|
- `Camera` and `AesDma` now support erasing the DMA channel type (#2258)
|
||||||
@ -93,6 +95,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Removed the following functions from `GpioPin`: `is_high`, `is_low`, `set_high`, `set_low`, `set_state`, `is_set_high`, `is_set_low`, `toggle`. (#2094)
|
- Removed the following functions from `GpioPin`: `is_high`, `is_low`, `set_high`, `set_low`, `set_state`, `is_set_high`, `is_set_low`, `toggle`. (#2094)
|
||||||
- Removed `Rtc::get_time_raw` (#1883)
|
- Removed `Rtc::get_time_raw` (#1883)
|
||||||
- Removed `_with_default_pins` UART constructors (#2132)
|
- Removed `_with_default_pins` UART constructors (#2132)
|
||||||
|
- Removed transfer methods `send`, `send_dma` and `send_dma_async` from `I8080` (#2191)
|
||||||
- Removed `uart::{DefaultRxPin, DefaultTxPin}` (#2132)
|
- Removed `uart::{DefaultRxPin, DefaultTxPin}` (#2132)
|
||||||
- Removed `PcntSource` and `PcntInputConfig`. (#2134)
|
- Removed `PcntSource` and `PcntInputConfig`. (#2134)
|
||||||
- Removed the `place-spi-driver-in-ram` feature, this is now enabled via [esp-config](https://docs.rs/esp-config) (#2156)
|
- Removed the `place-spi-driver-in-ram` feature, this is now enabled via [esp-config](https://docs.rs/esp-config) (#2156)
|
||||||
|
|||||||
@ -189,14 +189,31 @@ The I8080 driver no longer holds on to pins in its type definition.
|
|||||||
+ let _: I8080<'a, DmaChannel3, Blocking>;
|
+ let _: I8080<'a, DmaChannel3, Blocking>;
|
||||||
```
|
```
|
||||||
|
|
||||||
## I8080 start transfer type inference
|
## I8080 transfer API changes
|
||||||
|
|
||||||
The I8080 driver now decides bus width at transfer time, which means you don't get inference.
|
- 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 `static` or struct.
|
||||||
|
- The transfer API no longer works with plain slices, it now works with `DmaTxBuffer`s 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 one `send` method.
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
let mut i8080 = I8080::new(....);
|
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]);
|
- i8080.send(0x12, 0, &[0, 1, 2, 3, 4]);
|
||||||
+ i8080.send(0x12u8, 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
|
### Placing drivers in RAM is now done via esp-config
|
||||||
|
|||||||
@ -17,14 +17,14 @@
|
|||||||
#![doc = crate::before_snippet!()]
|
#![doc = crate::before_snippet!()]
|
||||||
//! # use esp_hal::gpio::Io;
|
//! # use esp_hal::gpio::Io;
|
||||||
//! # use esp_hal::lcd_cam::{LcdCam, lcd::i8080::{Config, I8080, TxEightBits}};
|
//! # use esp_hal::lcd_cam::{LcdCam, lcd::i8080::{Config, I8080, TxEightBits}};
|
||||||
//! # use esp_hal::dma_buffers;
|
//! # use esp_hal::dma_tx_buffer;
|
||||||
//! # use esp_hal::dma::{Dma, DmaPriority};
|
//! # use esp_hal::dma::{Dma, DmaPriority, DmaTxBuf};
|
||||||
//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
//!
|
//!
|
||||||
//! # let dma = Dma::new(peripherals.DMA);
|
//! # let dma = Dma::new(peripherals.DMA);
|
||||||
//! # let channel = dma.channel0;
|
//! # let channel = dma.channel0;
|
||||||
//!
|
//!
|
||||||
//! # let ( _, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32678, 0);
|
//! # let mut dma_buf = dma_tx_buffer!(32678).unwrap();
|
||||||
//!
|
//!
|
||||||
//! # let channel = channel.configure(
|
//! # let channel = channel.configure(
|
||||||
//! # false,
|
//! # false,
|
||||||
@ -46,41 +46,34 @@
|
|||||||
//! let mut i8080 = I8080::new(
|
//! let mut i8080 = I8080::new(
|
||||||
//! lcd_cam.lcd,
|
//! lcd_cam.lcd,
|
||||||
//! channel.tx,
|
//! channel.tx,
|
||||||
//! tx_descriptors,
|
|
||||||
//! tx_pins,
|
//! tx_pins,
|
||||||
//! 20.MHz(),
|
//! 20.MHz(),
|
||||||
//! Config::default(),
|
//! Config::default(),
|
||||||
//! )
|
//! )
|
||||||
//! .with_ctrl_pins(io.pins.gpio0, io.pins.gpio47);
|
//! .with_ctrl_pins(io.pins.gpio0, io.pins.gpio47);
|
||||||
//!
|
//!
|
||||||
//! i8080.send(0x3Au8, 0, &[0x55]).unwrap(); // RGB565
|
//! dma_buf.fill(&[0x55]);
|
||||||
|
//! let transfer = i8080.send(0x3Au8, 0, dma_buf).unwrap(); // RGB565
|
||||||
|
//! transfer.wait();
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use core::{fmt::Formatter, marker::PhantomData, mem::size_of};
|
use core::{
|
||||||
|
fmt::Formatter,
|
||||||
|
marker::PhantomData,
|
||||||
|
mem::{size_of, ManuallyDrop},
|
||||||
|
};
|
||||||
|
|
||||||
use fugit::HertzU32;
|
use fugit::HertzU32;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::Clocks,
|
clock::Clocks,
|
||||||
dma::{
|
dma::{ChannelTx, DmaChannel, DmaError, DmaPeripheral, DmaTxBuffer, LcdCamPeripheral, Tx},
|
||||||
dma_private::{DmaSupport, DmaSupportTx},
|
|
||||||
ChannelTx,
|
|
||||||
DescriptorChain,
|
|
||||||
DmaChannel,
|
|
||||||
DmaDescriptor,
|
|
||||||
DmaError,
|
|
||||||
DmaPeripheral,
|
|
||||||
DmaTransferTx,
|
|
||||||
LcdCamPeripheral,
|
|
||||||
ReadBuffer,
|
|
||||||
Tx,
|
|
||||||
},
|
|
||||||
gpio::{OutputSignal, PeripheralOutput},
|
gpio::{OutputSignal, PeripheralOutput},
|
||||||
lcd_cam::{
|
lcd_cam::{
|
||||||
asynch::LcdDoneFuture,
|
asynch::LCD_DONE_WAKER,
|
||||||
lcd::{i8080::private::TxPins, ClockMode, DelayMode, Phase, Polarity},
|
lcd::{i8080::private::TxPins, ClockMode, DelayMode, Phase, Polarity},
|
||||||
private::calculate_clkm,
|
private::{calculate_clkm, Instance},
|
||||||
BitOrder,
|
BitOrder,
|
||||||
ByteOrder,
|
ByteOrder,
|
||||||
Lcd,
|
Lcd,
|
||||||
@ -94,7 +87,6 @@ use crate::{
|
|||||||
pub struct I8080<'d, CH: DmaChannel, DM: Mode> {
|
pub struct I8080<'d, CH: DmaChannel, DM: Mode> {
|
||||||
lcd_cam: PeripheralRef<'d, LCD_CAM>,
|
lcd_cam: PeripheralRef<'d, LCD_CAM>,
|
||||||
tx_channel: ChannelTx<'d, CH>,
|
tx_channel: ChannelTx<'d, CH>,
|
||||||
tx_chain: DescriptorChain,
|
|
||||||
_phantom: PhantomData<DM>,
|
_phantom: PhantomData<DM>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +98,6 @@ where
|
|||||||
pub fn new<P: TxPins>(
|
pub fn new<P: TxPins>(
|
||||||
lcd: Lcd<'d, DM>,
|
lcd: Lcd<'d, DM>,
|
||||||
channel: ChannelTx<'d, CH>,
|
channel: ChannelTx<'d, CH>,
|
||||||
descriptors: &'static mut [DmaDescriptor],
|
|
||||||
mut pins: P,
|
mut pins: P,
|
||||||
frequency: HertzU32,
|
frequency: HertzU32,
|
||||||
config: Config,
|
config: Config,
|
||||||
@ -214,37 +205,11 @@ where
|
|||||||
Self {
|
Self {
|
||||||
lcd_cam,
|
lcd_cam,
|
||||||
tx_channel: channel,
|
tx_channel: channel,
|
||||||
tx_chain: DescriptorChain::new(descriptors),
|
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
|
||||||
while lcd_user.read().lcd_start().bit_is_set() {}
|
|
||||||
self.tear_down_send();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn peripheral_dma_stop(&mut self) {
|
|
||||||
unreachable!("unsupported")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, CH: DmaChannel, DM: Mode> DmaSupportTx for I8080<'d, CH, DM> {
|
|
||||||
type TX = ChannelTx<'d, CH>;
|
|
||||||
|
|
||||||
fn tx(&mut self) -> &mut Self::TX {
|
|
||||||
&mut self.tx_channel
|
|
||||||
}
|
|
||||||
|
|
||||||
fn chain(&mut self) -> &mut DescriptorChain {
|
|
||||||
&mut self.tx_chain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM> {
|
impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM> {
|
||||||
/// Configures the byte order for data transmission.
|
/// Configures the byte order for data transmission.
|
||||||
pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self {
|
pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self {
|
||||||
@ -291,33 +256,6 @@ impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a command and data to the LCD using the I8080 interface.
|
|
||||||
///
|
|
||||||
/// 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<W>>,
|
|
||||||
dummy: u8,
|
|
||||||
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))?;
|
|
||||||
self.start_send();
|
|
||||||
|
|
||||||
let lcd_user = self.lcd_cam.lcd_user();
|
|
||||||
// Wait until LCD_START is cleared by hardware.
|
|
||||||
while lcd_user.read().lcd_start().bit_is_set() {}
|
|
||||||
|
|
||||||
self.tear_down_send();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends a command and data to the LCD using DMA.
|
/// Sends a command and data to the LCD using DMA.
|
||||||
///
|
///
|
||||||
/// Passing a `Command<u8>` will make this an 8-bit transfer and a
|
/// Passing a `Command<u8>` will make this an 8-bit transfer and a
|
||||||
@ -326,61 +264,14 @@ impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM> {
|
|||||||
/// Note: A 16-bit transfer on an 8-bit bus will silently truncate the 2nd
|
/// 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 and an 8-bit transfer on a 16-bit bus will silently pad each
|
||||||
/// byte to 2 bytes.
|
/// byte to 2 bytes.
|
||||||
pub fn send_dma<'t, W, TXBUF>(
|
pub fn send<W: Into<u16> + Copy, BUF: DmaTxBuffer>(
|
||||||
&'t mut self,
|
mut self,
|
||||||
cmd: impl Into<Command<W>>,
|
cmd: impl Into<Command<W>>,
|
||||||
dummy: u8,
|
dummy: u8,
|
||||||
data: &'t TXBUF,
|
mut data: BUF,
|
||||||
) -> Result<DmaTransferTx<'_, Self>, DmaError>
|
) -> Result<I8080Transfer<'d, BUF, CH, DM>, (DmaError, Self, BUF)> {
|
||||||
where
|
let cmd = cmd.into();
|
||||||
W: Copy + Into<u16>,
|
|
||||||
TXBUF: ReadBuffer,
|
|
||||||
{
|
|
||||||
let (ptr, len) = unsafe { data.read_buffer() };
|
|
||||||
|
|
||||||
self.setup_send(cmd.into(), dummy);
|
|
||||||
self.start_write_bytes_dma(ptr as _, len)?;
|
|
||||||
self.start_send();
|
|
||||||
|
|
||||||
Ok(DmaTransferTx::new(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, CH: DmaChannel> I8080<'d, CH, crate::Async> {
|
|
||||||
/// Asynchronously sends a command and data to the LCD using DMA.
|
|
||||||
///
|
|
||||||
/// 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<W>>,
|
|
||||||
dummy: u8,
|
|
||||||
data: &'t TXBUF,
|
|
||||||
) -> Result<(), DmaError>
|
|
||||||
where
|
|
||||||
W: Copy + Into<u16>,
|
|
||||||
TXBUF: ReadBuffer,
|
|
||||||
{
|
|
||||||
let (ptr, len) = unsafe { data.read_buffer() };
|
|
||||||
|
|
||||||
self.setup_send(cmd.into(), dummy);
|
|
||||||
self.start_write_bytes_dma(ptr as _, len)?;
|
|
||||||
self.start_send();
|
|
||||||
|
|
||||||
LcdDoneFuture::new().await;
|
|
||||||
if self.tx_channel.has_error() {
|
|
||||||
return Err(DmaError::DescriptorError);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// Reset LCD control unit and Async Tx FIFO
|
||||||
self.lcd_cam
|
self.lcd_cam
|
||||||
.lcd_user()
|
.lcd_user()
|
||||||
@ -417,7 +308,7 @@ impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_2byte_mode = size_of::<T>() == 2;
|
let is_2byte_mode = size_of::<W>() == 2;
|
||||||
|
|
||||||
self.lcd_cam.lcd_user().modify(|_, w| unsafe {
|
self.lcd_cam.lcd_user().modify(|_, w| unsafe {
|
||||||
// Set dummy length
|
// Set dummy length
|
||||||
@ -434,9 +325,25 @@ impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM> {
|
|||||||
.lcd_2byte_en()
|
.lcd_2byte_en()
|
||||||
.bit(is_2byte_mode)
|
.bit(is_2byte_mode)
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
fn start_send(&mut self) {
|
// Use continous mode for DMA. FROM the S3 TRM:
|
||||||
|
// > In a continuous output, LCD module keeps sending data till:
|
||||||
|
// > i. LCD_CAM_LCD_START is cleared;
|
||||||
|
// > ii. or LCD_CAM_LCD_RESET is set;
|
||||||
|
// > iii. or all the data in GDMA is sent out.
|
||||||
|
self.lcd_cam
|
||||||
|
.lcd_user()
|
||||||
|
.modify(|_, w| w.lcd_always_out_en().set_bit().lcd_dout().set_bit());
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
self.tx_channel
|
||||||
|
.prepare_transfer(DmaPeripheral::LcdCam, &mut data)
|
||||||
|
}
|
||||||
|
.and_then(|_| self.tx_channel.start_transfer());
|
||||||
|
if let Err(err) = result {
|
||||||
|
return Err((err, self, data));
|
||||||
|
}
|
||||||
|
|
||||||
// Setup interrupts.
|
// Setup interrupts.
|
||||||
self.lcd_cam
|
self.lcd_cam
|
||||||
.lc_dma_int_clr()
|
.lc_dma_int_clr()
|
||||||
@ -450,45 +357,11 @@ impl<'d, CH: DmaChannel, DM: Mode> I8080<'d, CH, DM> {
|
|||||||
w.lcd_update().set_bit();
|
w.lcd_update().set_bit();
|
||||||
w.lcd_start().set_bit()
|
w.lcd_start().set_bit()
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
fn tear_down_send(&mut self) {
|
Ok(I8080Transfer {
|
||||||
// This will already be cleared unless the user is trying to cancel,
|
i8080: ManuallyDrop::new(self),
|
||||||
// which is why this is still here.
|
tx_buf: ManuallyDrop::new(data),
|
||||||
self.lcd_cam
|
})
|
||||||
.lcd_user()
|
|
||||||
.modify(|_, w| w.lcd_start().clear_bit());
|
|
||||||
|
|
||||||
self.lcd_cam
|
|
||||||
.lc_dma_int_clr()
|
|
||||||
.write(|w| w.lcd_trans_done_int_clr().set_bit());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_write_bytes_dma(&mut self, ptr: *const u8, len: usize) -> Result<(), DmaError> {
|
|
||||||
if len == 0 {
|
|
||||||
// Set transfer length.
|
|
||||||
self.lcd_cam
|
|
||||||
.lcd_user()
|
|
||||||
.modify(|_, w| w.lcd_dout().clear_bit());
|
|
||||||
} else {
|
|
||||||
// Use continous mode for DMA. FROM the S3 TRM:
|
|
||||||
// > In a continuous output, LCD module keeps sending data till:
|
|
||||||
// > i. LCD_CAM_LCD_START is cleared;
|
|
||||||
// > ii. or LCD_CAM_LCD_RESET is set;
|
|
||||||
// > iii. or all the data in GDMA is sent out.
|
|
||||||
self.lcd_cam.lcd_user().modify(|_, w| {
|
|
||||||
w.lcd_always_out_en().set_bit();
|
|
||||||
w.lcd_dout().set_bit()
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.tx_chain.fill_for_tx(false, ptr, len)?;
|
|
||||||
self.tx_channel
|
|
||||||
.prepare_transfer_without_start(DmaPeripheral::LcdCam, &self.tx_chain)?;
|
|
||||||
}
|
|
||||||
self.tx_channel.start_transfer()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,6 +371,126 @@ impl<'d, CH: DmaChannel, DM: Mode> core::fmt::Debug for I8080<'d, CH, DM> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents an ongoing (or potentially finished) transfer using the I8080 LCD
|
||||||
|
/// interface
|
||||||
|
pub struct I8080Transfer<'d, BUF, CH: DmaChannel, DM: Mode> {
|
||||||
|
i8080: ManuallyDrop<I8080<'d, CH, DM>>,
|
||||||
|
tx_buf: ManuallyDrop<BUF>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, BUF, CH: DmaChannel, DM: Mode> I8080Transfer<'d, BUF, CH, DM> {
|
||||||
|
/// Returns true when [Self::wait] will not block.
|
||||||
|
pub fn is_done(&self) -> bool {
|
||||||
|
self.i8080
|
||||||
|
.lcd_cam
|
||||||
|
.lcd_user()
|
||||||
|
.read()
|
||||||
|
.lcd_start()
|
||||||
|
.bit_is_clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops this transfer on the spot and returns the peripheral and buffer.
|
||||||
|
pub fn cancel(mut self) -> (I8080<'d, CH, DM>, BUF) {
|
||||||
|
self.stop_peripherals();
|
||||||
|
let (_, i8080, buf) = self.wait();
|
||||||
|
(i8080, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Waits for the transfer to finish and returns the peripheral and buffer.
|
||||||
|
///
|
||||||
|
/// Note: This also clears the transfer interrupt so it can be used in
|
||||||
|
/// interrupt handlers to "handle" the interrupt.
|
||||||
|
pub fn wait(mut self) -> (Result<(), DmaError>, I8080<'d, CH, DM>, BUF) {
|
||||||
|
while !self.is_done() {}
|
||||||
|
|
||||||
|
// Clear "done" interrupt.
|
||||||
|
self.i8080
|
||||||
|
.lcd_cam
|
||||||
|
.lc_dma_int_clr()
|
||||||
|
.write(|w| w.lcd_trans_done_int_clr().set_bit());
|
||||||
|
|
||||||
|
// SAFETY: Since forget is called on self, we know that self.i8080 and
|
||||||
|
// self.tx_buf won't be touched again.
|
||||||
|
let (i8080, tx_buf) = unsafe {
|
||||||
|
let i8080 = ManuallyDrop::take(&mut self.i8080);
|
||||||
|
let tx_buf = ManuallyDrop::take(&mut self.tx_buf);
|
||||||
|
core::mem::forget(self);
|
||||||
|
(i8080, tx_buf)
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = if i8080.tx_channel.has_error() {
|
||||||
|
Err(DmaError::DescriptorError)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
(result, i8080, tx_buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop_peripherals(&mut self) {
|
||||||
|
// Stop the LCD_CAM peripheral.
|
||||||
|
self.i8080
|
||||||
|
.lcd_cam
|
||||||
|
.lcd_user()
|
||||||
|
.modify(|_, w| w.lcd_start().clear_bit());
|
||||||
|
|
||||||
|
// Stop the DMA
|
||||||
|
self.i8080.tx_channel.stop_transfer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, BUF, CH: DmaChannel> I8080Transfer<'d, BUF, CH, crate::Async> {
|
||||||
|
/// Waits for [Self::is_done] to return true.
|
||||||
|
pub async fn wait_for_done(&mut self) {
|
||||||
|
use core::{
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LcdDoneFuture {}
|
||||||
|
|
||||||
|
impl Future for LcdDoneFuture {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
LCD_DONE_WAKER.register(cx.waker());
|
||||||
|
if Instance::is_lcd_done_set() {
|
||||||
|
// Interrupt bit will be cleared in Self::wait.
|
||||||
|
// This allows `wait_for_done` to be called more than once.
|
||||||
|
//
|
||||||
|
// Instance::clear_lcd_done();
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Instance::listen_lcd_done();
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for LcdDoneFuture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
Instance::unlisten_lcd_done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LcdDoneFuture {}.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, BUF, CH: DmaChannel, DM: Mode> Drop for I8080Transfer<'d, BUF, CH, DM> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.stop_peripherals();
|
||||||
|
|
||||||
|
// SAFETY: This is Drop, we know that self.i8080 and self.tx_buf
|
||||||
|
// won't be touched again.
|
||||||
|
unsafe {
|
||||||
|
ManuallyDrop::drop(&mut self.i8080);
|
||||||
|
ManuallyDrop::drop(&mut self.tx_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
/// Configuration settings for the I8080 interface.
|
/// Configuration settings for the I8080 interface.
|
||||||
|
|||||||
@ -118,54 +118,19 @@ pub enum ByteOrder {
|
|||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod asynch {
|
pub mod asynch {
|
||||||
use core::task::Poll;
|
|
||||||
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use procmacros::handler;
|
use procmacros::handler;
|
||||||
|
|
||||||
use super::private::Instance;
|
use super::private::Instance;
|
||||||
|
|
||||||
static TX_WAKER: AtomicWaker = AtomicWaker::new();
|
pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new();
|
||||||
|
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
|
||||||
pub(crate) struct LcdDoneFuture {}
|
|
||||||
|
|
||||||
impl LcdDoneFuture {
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::future::Future for LcdDoneFuture {
|
|
||||||
type Output = ();
|
|
||||||
|
|
||||||
fn poll(
|
|
||||||
self: core::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut core::task::Context<'_>,
|
|
||||||
) -> core::task::Poll<Self::Output> {
|
|
||||||
TX_WAKER.register(cx.waker());
|
|
||||||
if Instance::is_lcd_done_set() {
|
|
||||||
Instance::clear_lcd_done();
|
|
||||||
Poll::Ready(())
|
|
||||||
} else {
|
|
||||||
Instance::listen_lcd_done();
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for LcdDoneFuture {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
Instance::unlisten_lcd_done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[handler]
|
#[handler]
|
||||||
pub(crate) fn interrupt_handler() {
|
pub(crate) fn interrupt_handler() {
|
||||||
// TODO: this is a shared interrupt with Camera and here we ignore that!
|
// TODO: this is a shared interrupt with Camera and here we ignore that!
|
||||||
if Instance::is_lcd_done_set() {
|
if Instance::is_lcd_done_set() {
|
||||||
Instance::unlisten_lcd_done();
|
Instance::unlisten_lcd_done();
|
||||||
TX_WAKER.wake()
|
LCD_DONE_WAKER.wake()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,13 +164,6 @@ mod private {
|
|||||||
.lcd_trans_done_int_raw()
|
.lcd_trans_done_int_raw()
|
||||||
.bit()
|
.bit()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear_lcd_done() {
|
|
||||||
let lcd_cam = unsafe { crate::peripherals::LCD_CAM::steal() };
|
|
||||||
lcd_cam
|
|
||||||
.lc_dma_int_clr()
|
|
||||||
.write(|w| w.lcd_trans_done_int_clr().set_bit());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub struct ClockDivider {
|
pub struct ClockDivider {
|
||||||
// Integral LCD clock divider value. (8 bits)
|
// Integral LCD clock divider value. (8 bits)
|
||||||
|
|||||||
@ -25,14 +25,15 @@
|
|||||||
use esp_backtrace as _;
|
use esp_backtrace as _;
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
delay::Delay,
|
delay::Delay,
|
||||||
dma::{Dma, DmaPriority},
|
dma::{Dma, DmaChannel0, DmaPriority, DmaTxBuf},
|
||||||
dma_buffers,
|
dma_tx_buffer,
|
||||||
gpio::{Input, Io, Level, Output, Pull},
|
gpio::{Input, Io, Level, Output, Pull},
|
||||||
lcd_cam::{
|
lcd_cam::{
|
||||||
lcd::i8080::{Config, TxEightBits, I8080},
|
lcd::i8080::{Config, TxEightBits, I8080},
|
||||||
LcdCam,
|
LcdCam,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
Blocking,
|
||||||
};
|
};
|
||||||
use esp_println::println;
|
use esp_println::println;
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ fn main() -> ! {
|
|||||||
let dma = Dma::new(peripherals.DMA);
|
let dma = Dma::new(peripherals.DMA);
|
||||||
let channel = dma.channel0;
|
let channel = dma.channel0;
|
||||||
|
|
||||||
let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32678);
|
let dma_tx_buf = dma_tx_buffer!(4000).unwrap();
|
||||||
|
|
||||||
let channel = channel.configure(false, DmaPriority::Priority0);
|
let channel = channel.configure(false, DmaPriority::Priority0);
|
||||||
|
|
||||||
@ -73,21 +74,50 @@ fn main() -> ! {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
|
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
|
||||||
let mut i8080 = I8080::new(
|
let i8080 = I8080::new(
|
||||||
lcd_cam.lcd,
|
lcd_cam.lcd,
|
||||||
channel.tx,
|
channel.tx,
|
||||||
tx_descriptors,
|
|
||||||
tx_pins,
|
tx_pins,
|
||||||
20.MHz(),
|
20.MHz(),
|
||||||
Config::default(),
|
Config::default(),
|
||||||
)
|
)
|
||||||
.with_ctrl_pins(lcd_rs, lcd_wr);
|
.with_ctrl_pins(lcd_rs, lcd_wr);
|
||||||
|
|
||||||
// This is here mostly to workaround https://github.com/esp-rs/esp-hal/issues/1532
|
// Note: This isn't provided in the HAL since different drivers may require different
|
||||||
let mut send_cmd = |cmd: u8, data: &[u8]| {
|
// considerations, like how to manage the CS pin, the CD pin, cancellation semantics,
|
||||||
let buf = &mut tx_buffer[0..data.len()];
|
// 8 vs 16 bit, non-native primitives like Rgb565, Rgb888, etc. This Bus is just provided as
|
||||||
buf.copy_from_slice(data);
|
// an example of how to implement your own.
|
||||||
i8080.send(cmd, 0, buf).unwrap();
|
struct Bus<'d> {
|
||||||
|
resources: Option<(I8080<'d, DmaChannel0, Blocking>, DmaTxBuf)>,
|
||||||
|
}
|
||||||
|
impl<'d> Bus<'d> {
|
||||||
|
fn use_resources<T>(
|
||||||
|
&mut self,
|
||||||
|
func: impl FnOnce(
|
||||||
|
I8080<'d, DmaChannel0, Blocking>,
|
||||||
|
DmaTxBuf,
|
||||||
|
) -> (T, I8080<'d, DmaChannel0, Blocking>, DmaTxBuf),
|
||||||
|
) -> T {
|
||||||
|
let (i8080, buf) = self.resources.take().unwrap();
|
||||||
|
let (result, i8080, buf) = func(i8080, buf);
|
||||||
|
self.resources = Some((i8080, buf));
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(&mut self, cmd: u8, data: &[u8]) {
|
||||||
|
self.use_resources(|i8080, mut buf| {
|
||||||
|
buf.fill(data);
|
||||||
|
match i8080.send(cmd, 0, buf) {
|
||||||
|
Ok(transfer) => transfer.wait(),
|
||||||
|
Err((result, i8080, buf)) => (Err(result), i8080, buf),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut bus = Bus {
|
||||||
|
resources: Some((i8080, dma_tx_buf)),
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -116,10 +146,10 @@ fn main() -> ! {
|
|||||||
const CMD_DOCA: u8 = 0xE8; // Display Output Ctrl Adjust
|
const CMD_DOCA: u8 = 0xE8; // Display Output Ctrl Adjust
|
||||||
const CMD_CSCON: u8 = 0xF0; // Command Set Control
|
const CMD_CSCON: u8 = 0xF0; // Command Set Control
|
||||||
|
|
||||||
send_cmd(CMD_CSCON, &[0xC3]); // Enable extension command 2 part I
|
bus.send(CMD_CSCON, &[0xC3]); // Enable extension command 2 part I
|
||||||
send_cmd(CMD_CSCON, &[0x96]); // Enable extension command 2 part II
|
bus.send(CMD_CSCON, &[0x96]); // Enable extension command 2 part II
|
||||||
send_cmd(CMD_INVCTR, &[0x01]); // 1-dot inversion
|
bus.send(CMD_INVCTR, &[0x01]); // 1-dot inversion
|
||||||
send_cmd(
|
bus.send(
|
||||||
CMD_DFUNCTR,
|
CMD_DFUNCTR,
|
||||||
&[
|
&[
|
||||||
0x80, // Display Function Control //Bypass
|
0x80, // Display Function Control //Bypass
|
||||||
@ -128,7 +158,7 @@ fn main() -> ! {
|
|||||||
0x3B,
|
0x3B,
|
||||||
],
|
],
|
||||||
); // LCD Drive Line=8*(59+1)
|
); // LCD Drive Line=8*(59+1)
|
||||||
send_cmd(
|
bus.send(
|
||||||
CMD_DOCA,
|
CMD_DOCA,
|
||||||
&[
|
&[
|
||||||
0x40, 0x8A, 0x00, 0x00, 0x29, // Source eqaulizing period time= 22.5 us
|
0x40, 0x8A, 0x00, 0x00, 0x29, // Source eqaulizing period time= 22.5 us
|
||||||
@ -137,43 +167,44 @@ fn main() -> ! {
|
|||||||
0x33,
|
0x33,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
send_cmd(CMD_PWCTR2, &[0x06]); // Power control2 //VAP(GVDD)=3.85+( vcom+vcom offset), VAN(GVCL)=-3.85+(
|
bus.send(CMD_PWCTR2, &[0x06]); // Power control2 //VAP(GVDD)=3.85+( vcom+vcom offset), VAN(GVCL)=-3.85+(
|
||||||
// vcom+vcom offset)
|
// vcom+vcom offset)
|
||||||
send_cmd(CMD_PWCTR3, &[0xA7]); // Power control 3 //Source driving current level=low, Gamma driving current
|
bus.send(CMD_PWCTR3, &[0xA7]); // Power control 3 //Source driving current level=low, Gamma driving current
|
||||||
// level=High
|
// level=High
|
||||||
send_cmd(CMD_VMCTR, &[0x18]); // VCOM Control //VCOM=0.9
|
bus.send(CMD_VMCTR, &[0x18]); // VCOM Control //VCOM=0.9
|
||||||
delay.delay_micros(120_000);
|
delay.delay_micros(120_000);
|
||||||
send_cmd(
|
bus.send(
|
||||||
CMD_GMCTRP1,
|
CMD_GMCTRP1,
|
||||||
&[
|
&[
|
||||||
0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F, 0x54, 0x42, 0x3C, 0x17, 0x14, 0x18, 0x1B,
|
0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F, 0x54, 0x42, 0x3C, 0x17, 0x14, 0x18, 0x1B,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
send_cmd(
|
bus.send(
|
||||||
CMD_GMCTRN1,
|
CMD_GMCTRN1,
|
||||||
&[
|
&[
|
||||||
0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B, 0x43, 0x42, 0x3B, 0x16, 0x14, 0x17, 0x1B,
|
0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B, 0x43, 0x42, 0x3B, 0x16, 0x14, 0x17, 0x1B,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
delay.delay_micros(120_000);
|
delay.delay_micros(120_000);
|
||||||
send_cmd(CMD_CSCON, &[0x3C]); // Command Set control // Disable extension command 2 partI
|
bus.send(CMD_CSCON, &[0x3C]); // Command Set control // Disable extension command 2 partI
|
||||||
send_cmd(CMD_CSCON, &[0x69]); // Command Set control // Disable
|
bus.send(CMD_CSCON, &[0x69]); // Command Set control // Disable
|
||||||
// extension command 2 partII
|
// extension command 2 partII
|
||||||
|
|
||||||
send_cmd(0x11, &[]); // ExitSleepMode
|
bus.send(0x11, &[]); // ExitSleepMode
|
||||||
delay.delay_micros(130_000);
|
delay.delay_micros(130_000);
|
||||||
send_cmd(0x38, &[]); // ExitIdleMode
|
bus.send(0x38, &[]); // ExitIdleMode
|
||||||
send_cmd(0x29, &[]); // SetDisplayOn
|
bus.send(0x29, &[]); // SetDisplayOn
|
||||||
|
|
||||||
send_cmd(0x21, &[]); // SetInvertMode(ColorInversion::Inverted)
|
bus.send(0x21, &[]); // SetInvertMode(ColorInversion::Inverted)
|
||||||
|
|
||||||
// let madctl = SetAddressMode::from(options);
|
// let madctl = SetAddressMode::from(options);
|
||||||
// send_cmd(madctl)?;
|
// bus.send(madctl)?;
|
||||||
|
|
||||||
send_cmd(0x3A, &[0x55]); // RGB565
|
bus.send(0x3A, &[0x55]); // RGB565
|
||||||
}
|
}
|
||||||
|
|
||||||
send_cmd(0x35, &[0]); // Tear Effect Line On
|
// Tearing Effect Line On
|
||||||
|
bus.send(0x35, &[0]);
|
||||||
|
|
||||||
let width = 320u16;
|
let width = 320u16;
|
||||||
let height = 480u16;
|
let height = 480u16;
|
||||||
@ -182,12 +213,8 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let width_b = width.to_be_bytes();
|
let width_b = width.to_be_bytes();
|
||||||
let height_b = height.to_be_bytes();
|
let height_b = height.to_be_bytes();
|
||||||
i8080
|
bus.send(0x2A, &[0, 0, width_b[0], width_b[1]]); // CASET
|
||||||
.send(0x2A, 0, &[0, 0, width_b[0], width_b[1]])
|
bus.send(0x2B, &[0, 0, height_b[0], height_b[1]]) // PASET
|
||||||
.unwrap(); // CASET
|
|
||||||
i8080
|
|
||||||
.send(0x2B, 0, &[0, 0, height_b[0], height_b[1]])
|
|
||||||
.unwrap(); // PASET
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Drawing");
|
println!("Drawing");
|
||||||
@ -200,11 +227,13 @@ fn main() -> ! {
|
|||||||
let total_pixels = width as usize * height as usize;
|
let total_pixels = width as usize * height as usize;
|
||||||
let total_bytes = total_pixels * 2;
|
let total_bytes = total_pixels * 2;
|
||||||
|
|
||||||
let buffer = tx_buffer;
|
let (mut i8080, mut dma_tx_buf) = bus.resources.take().unwrap();
|
||||||
|
|
||||||
|
dma_tx_buf.set_length(dma_tx_buf.capacity());
|
||||||
|
|
||||||
for color in [RED, BLUE].iter().cycle() {
|
for color in [RED, BLUE].iter().cycle() {
|
||||||
let color = color.to_be_bytes();
|
let color = color.to_be_bytes();
|
||||||
for chunk in buffer.chunks_mut(2) {
|
for chunk in dma_tx_buf.as_mut_slice().chunks_mut(2) {
|
||||||
chunk.copy_from_slice(&color);
|
chunk.copy_from_slice(&color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,20 +251,18 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let mut bytes_left_to_write = total_bytes;
|
let mut bytes_left_to_write = total_bytes;
|
||||||
|
|
||||||
let transfer = i8080.send_dma(0x2Cu8, 0, &buffer).unwrap();
|
(_, i8080, dma_tx_buf) = i8080.send(0x2Cu8, 0, dma_tx_buf).unwrap().wait();
|
||||||
transfer.wait().unwrap();
|
|
||||||
|
|
||||||
bytes_left_to_write -= buffer.len();
|
bytes_left_to_write -= dma_tx_buf.len();
|
||||||
|
|
||||||
while bytes_left_to_write >= buffer.len() {
|
while bytes_left_to_write >= dma_tx_buf.len() {
|
||||||
let transfer = i8080.send_dma(0x3Cu8, 0, &buffer).unwrap();
|
(_, i8080, dma_tx_buf) = i8080.send(0x3Cu8, 0, dma_tx_buf).unwrap().wait();
|
||||||
transfer.wait().unwrap();
|
bytes_left_to_write -= dma_tx_buf.len();
|
||||||
|
|
||||||
bytes_left_to_write -= buffer.len();
|
|
||||||
}
|
}
|
||||||
if bytes_left_to_write > 0 {
|
if bytes_left_to_write > 0 {
|
||||||
let transfer = i8080.send_dma(0x3Cu8, 0, &buffer).unwrap();
|
dma_tx_buf.set_length(bytes_left_to_write);
|
||||||
transfer.wait().unwrap();
|
(_, i8080, dma_tx_buf) = i8080.send(0x3Cu8, 0, dma_tx_buf).unwrap().wait();
|
||||||
|
dma_tx_buf.set_length(dma_tx_buf.capacity());
|
||||||
}
|
}
|
||||||
|
|
||||||
delay.delay_millis(1_000);
|
delay.delay_millis(1_000);
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
dma::{Dma, DmaDescriptor, DmaPriority},
|
dma::{Dma, DmaPriority, DmaTxBuf},
|
||||||
dma_buffers,
|
dma_buffers,
|
||||||
gpio::{Io, NoPin},
|
gpio::{Io, NoPin},
|
||||||
lcd_cam::{
|
lcd_cam::{
|
||||||
@ -21,7 +21,6 @@ use esp_hal::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use hil_test as _;
|
use hil_test as _;
|
||||||
use static_cell::ConstStaticCell;
|
|
||||||
|
|
||||||
const DATA_SIZE: usize = 1024 * 10;
|
const DATA_SIZE: usize = 1024 * 10;
|
||||||
|
|
||||||
@ -30,8 +29,7 @@ struct Context<'d> {
|
|||||||
pcnt: Pcnt<'d>,
|
pcnt: Pcnt<'d>,
|
||||||
io: Io,
|
io: Io,
|
||||||
dma: Dma<'d>,
|
dma: Dma<'d>,
|
||||||
tx_buffer: &'static mut [u8],
|
dma_buf: DmaTxBuf,
|
||||||
tx_descriptors: &'static mut [DmaDescriptor],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -47,14 +45,14 @@ mod tests {
|
|||||||
let pcnt = Pcnt::new(peripherals.PCNT);
|
let pcnt = Pcnt::new(peripherals.PCNT);
|
||||||
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||||
let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, DATA_SIZE);
|
let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, DATA_SIZE);
|
||||||
|
let dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
|
||||||
|
|
||||||
Context {
|
Context {
|
||||||
lcd_cam,
|
lcd_cam,
|
||||||
dma,
|
dma,
|
||||||
pcnt,
|
pcnt,
|
||||||
io,
|
io,
|
||||||
tx_buffer,
|
dma_buf,
|
||||||
tx_descriptors,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,19 +62,16 @@ mod tests {
|
|||||||
|
|
||||||
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
|
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
|
||||||
|
|
||||||
let mut i8080 = I8080::new(
|
let i8080 = I8080::new(
|
||||||
ctx.lcd_cam.lcd,
|
ctx.lcd_cam.lcd,
|
||||||
channel.tx,
|
channel.tx,
|
||||||
ctx.tx_descriptors,
|
|
||||||
pins,
|
pins,
|
||||||
20.MHz(),
|
20.MHz(),
|
||||||
Config::default(),
|
Config::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let xfer = i8080
|
let xfer = i8080.send(Command::<u8>::None, 0, ctx.dma_buf).unwrap();
|
||||||
.send_dma(Command::<u8>::None, 0, &ctx.tx_buffer)
|
xfer.wait().0.unwrap();
|
||||||
.unwrap();
|
|
||||||
xfer.wait().unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -87,19 +82,16 @@ mod tests {
|
|||||||
.configure_for_async(false, DmaPriority::Priority0);
|
.configure_for_async(false, DmaPriority::Priority0);
|
||||||
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
|
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
|
||||||
|
|
||||||
let mut i8080 = I8080::new(
|
let i8080 = I8080::new(
|
||||||
ctx.lcd_cam.lcd,
|
ctx.lcd_cam.lcd,
|
||||||
channel.tx,
|
channel.tx,
|
||||||
ctx.tx_descriptors,
|
|
||||||
pins,
|
pins,
|
||||||
20.MHz(),
|
20.MHz(),
|
||||||
Config::default(),
|
Config::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let xfer = i8080
|
let xfer = i8080.send(Command::<u8>::None, 0, ctx.dma_buf).unwrap();
|
||||||
.send_dma(Command::<u8>::None, 0, &ctx.tx_buffer)
|
xfer.wait().0.unwrap();
|
||||||
.unwrap();
|
|
||||||
xfer.wait().unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -163,7 +155,6 @@ mod tests {
|
|||||||
let mut i8080 = I8080::new(
|
let mut i8080 = I8080::new(
|
||||||
ctx.lcd_cam.lcd,
|
ctx.lcd_cam.lcd,
|
||||||
channel.tx,
|
channel.tx,
|
||||||
ctx.tx_descriptors,
|
|
||||||
pins,
|
pins,
|
||||||
20.MHz(),
|
20.MHz(),
|
||||||
Config::default(),
|
Config::default(),
|
||||||
@ -202,12 +193,12 @@ mod tests {
|
|||||||
0b1000_0000,
|
0b1000_0000,
|
||||||
];
|
];
|
||||||
|
|
||||||
let tx_buffer = ctx.tx_buffer;
|
let mut dma_buf = ctx.dma_buf;
|
||||||
tx_buffer.fill(0);
|
dma_buf.as_mut_slice().fill(0);
|
||||||
tx_buffer[..data_to_send.len()].copy_from_slice(&data_to_send);
|
dma_buf.as_mut_slice()[..data_to_send.len()].copy_from_slice(&data_to_send);
|
||||||
|
|
||||||
let xfer = i8080.send_dma(Command::<u8>::None, 0, &tx_buffer).unwrap();
|
let xfer = i8080.send(Command::<u8>::None, 0, dma_buf).unwrap();
|
||||||
xfer.wait().unwrap();
|
xfer.wait().0.unwrap();
|
||||||
|
|
||||||
let actual = [
|
let actual = [
|
||||||
pcnt.unit0.get_value(),
|
pcnt.unit0.get_value(),
|
||||||
@ -289,7 +280,6 @@ mod tests {
|
|||||||
let mut i8080 = I8080::new(
|
let mut i8080 = I8080::new(
|
||||||
ctx.lcd_cam.lcd,
|
ctx.lcd_cam.lcd,
|
||||||
channel.tx,
|
channel.tx,
|
||||||
ctx.tx_descriptors,
|
|
||||||
pins,
|
pins,
|
||||||
20.MHz(),
|
20.MHz(),
|
||||||
Config::default(),
|
Config::default(),
|
||||||
@ -316,7 +306,7 @@ mod tests {
|
|||||||
pcnt.unit3.resume();
|
pcnt.unit3.resume();
|
||||||
|
|
||||||
let data_to_send = [
|
let data_to_send = [
|
||||||
0b0000_0000_0000_0000,
|
0b0000_0000_0000_0000u16,
|
||||||
0b0001_0000_0001_0000,
|
0b0001_0000_0001_0000,
|
||||||
0b0000_0001_0001_0000,
|
0b0000_0001_0001_0000,
|
||||||
0b0001_0001_0001_0000,
|
0b0001_0001_0001_0000,
|
||||||
@ -328,12 +318,20 @@ mod tests {
|
|||||||
0b0001_0000_0000_0000,
|
0b0001_0000_0000_0000,
|
||||||
];
|
];
|
||||||
|
|
||||||
static TX_BUF: ConstStaticCell<[u16; 10]> = ConstStaticCell::new([0; 10]);
|
let mut dma_buf = ctx.dma_buf;
|
||||||
let tx_buffer = TX_BUF.take();
|
|
||||||
tx_buffer.copy_from_slice(&data_to_send);
|
|
||||||
|
|
||||||
let xfer = i8080.send_dma(Command::<u16>::None, 0, &tx_buffer).unwrap();
|
// FIXME: Replace this 16 -> 8 bit copy once DmaTxBuf takes a generic parameter.
|
||||||
xfer.wait().unwrap();
|
// i.e. DmaTxBuf<u16>
|
||||||
|
|
||||||
|
// Copy 16 bit array into 8 bit buffer.
|
||||||
|
dma_buf
|
||||||
|
.as_mut_slice()
|
||||||
|
.iter_mut()
|
||||||
|
.zip(data_to_send.iter().flat_map(|&d| d.to_ne_bytes()))
|
||||||
|
.for_each(|(d, s)| *d = s);
|
||||||
|
|
||||||
|
let xfer = i8080.send(Command::<u16>::None, 0, dma_buf).unwrap();
|
||||||
|
xfer.wait().0.unwrap();
|
||||||
|
|
||||||
let actual = [
|
let actual = [
|
||||||
pcnt.unit0.get_value(),
|
pcnt.unit0.get_value(),
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
dma::{Dma, DmaDescriptor, DmaPriority},
|
dma::{Dma, DmaPriority, DmaTxBuf},
|
||||||
dma_buffers,
|
dma_buffers,
|
||||||
gpio::NoPin,
|
gpio::NoPin,
|
||||||
lcd_cam::{
|
lcd_cam::{
|
||||||
@ -23,8 +23,7 @@ const DATA_SIZE: usize = 1024 * 10;
|
|||||||
struct Context<'d> {
|
struct Context<'d> {
|
||||||
lcd_cam: LcdCam<'d, esp_hal::Async>,
|
lcd_cam: LcdCam<'d, esp_hal::Async>,
|
||||||
dma: Dma<'d>,
|
dma: Dma<'d>,
|
||||||
tx_buffer: &'static [u8],
|
dma_buf: DmaTxBuf,
|
||||||
tx_descriptors: &'static mut [DmaDescriptor],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -38,13 +37,13 @@ mod tests {
|
|||||||
|
|
||||||
let dma = Dma::new(peripherals.DMA);
|
let dma = Dma::new(peripherals.DMA);
|
||||||
let lcd_cam = LcdCam::new_async(peripherals.LCD_CAM);
|
let lcd_cam = LcdCam::new_async(peripherals.LCD_CAM);
|
||||||
let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(DATA_SIZE, 0);
|
let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, DATA_SIZE);
|
||||||
|
let dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
|
||||||
|
|
||||||
Context {
|
Context {
|
||||||
lcd_cam,
|
lcd_cam,
|
||||||
dma,
|
dma,
|
||||||
tx_buffer,
|
dma_buf,
|
||||||
tx_descriptors,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,19 +52,22 @@ mod tests {
|
|||||||
let channel = ctx.dma.channel0.configure(false, DmaPriority::Priority0);
|
let channel = ctx.dma.channel0.configure(false, DmaPriority::Priority0);
|
||||||
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
|
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
|
||||||
|
|
||||||
let mut i8080 = I8080::new(
|
let i8080 = I8080::new(
|
||||||
ctx.lcd_cam.lcd,
|
ctx.lcd_cam.lcd,
|
||||||
channel.tx,
|
channel.tx,
|
||||||
ctx.tx_descriptors,
|
|
||||||
pins,
|
pins,
|
||||||
20.MHz(),
|
20.MHz(),
|
||||||
Config::default(),
|
Config::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
i8080
|
let mut transfer = i8080.send(Command::<u8>::None, 0, ctx.dma_buf).unwrap();
|
||||||
.send_dma_async(Command::<u8>::None, 0, &ctx.tx_buffer)
|
|
||||||
.await
|
transfer.wait_for_done().await;
|
||||||
.unwrap();
|
|
||||||
|
// This should not block forever and should immediately return.
|
||||||
|
transfer.wait_for_done().await;
|
||||||
|
|
||||||
|
transfer.wait().0.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -76,18 +78,21 @@ mod tests {
|
|||||||
.configure_for_async(false, DmaPriority::Priority0);
|
.configure_for_async(false, DmaPriority::Priority0);
|
||||||
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
|
let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin);
|
||||||
|
|
||||||
let mut i8080 = I8080::new(
|
let i8080 = I8080::new(
|
||||||
ctx.lcd_cam.lcd,
|
ctx.lcd_cam.lcd,
|
||||||
channel.tx,
|
channel.tx,
|
||||||
ctx.tx_descriptors,
|
|
||||||
pins,
|
pins,
|
||||||
20.MHz(),
|
20.MHz(),
|
||||||
Config::default(),
|
Config::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
i8080
|
let mut transfer = i8080.send(Command::<u8>::None, 0, ctx.dma_buf).unwrap();
|
||||||
.send_dma_async(Command::<u8>::None, 0, &ctx.tx_buffer)
|
|
||||||
.await
|
transfer.wait_for_done().await;
|
||||||
.unwrap();
|
|
||||||
|
// This should not block forever and should immediately return.
|
||||||
|
transfer.wait_for_done().await;
|
||||||
|
|
||||||
|
transfer.wait().0.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user