embassy/embassy-stm32/src/dsihost.rs
2024-05-14 13:53:02 +02:00

429 lines
14 KiB
Rust

//! DSI HOST
use core::marker::PhantomData;
use embassy_hal_internal::{into_ref, PeripheralRef};
//use crate::gpio::{AnyPin, SealedPin};
use crate::gpio::{AFType, AnyPin, Pull, Speed};
use crate::rcc::RccPeripheral;
use crate::{peripherals, Peripheral};
/// Performs a busy-wait delay for a specified number of microseconds.
pub fn blocking_delay_ms(ms: u32) {
#[cfg(time)]
embassy_time::block_for(embassy_time::Duration::from_millis(ms));
#[cfg(not(time))]
cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 / 1_000 * ms);
}
/// PacketTypes extracted from CubeMX
#[repr(u8)]
#[allow(dead_code)]
pub enum PacketType {
/// DCS short write, no parameters
DcsShortPktWriteP0,
/// DCS short write, one parameter
DcsShortPktWriteP1,
/// Generic short write, no parameters
GenShortPktWriteP0,
/// Generic short write, one parameter
GenShortPktWriteP1,
/// Generic short write, two parameters
GenShortPktWriteP2,
/// DCS long write
DcsLongPktWrite,
/// Generic long write
GenLongPktWrite,
/// DCS short read
DcsShortPktRead(u8),
/// Generic short read, no parameters
GenShortPktReadP0,
/// Generic short read, one parameter
GenShortPktReadP1(u8),
/// Generic short read, two parameters
GenShortPktReadP2(u8, u8),
/// Used to set the maximum return packet size for reading data
MaxReturnPktSize,
}
impl From<PacketType> for u8 {
fn from(packet_type: PacketType) -> u8 {
match packet_type {
PacketType::DcsShortPktWriteP0 => 0x05,
PacketType::DcsShortPktWriteP1 => 0x15,
PacketType::GenShortPktWriteP0 => 0x03,
PacketType::GenShortPktWriteP1 => 0x13,
PacketType::GenShortPktWriteP2 => 0x23,
PacketType::DcsLongPktWrite => 0x39,
PacketType::GenLongPktWrite => 0x29,
PacketType::DcsShortPktRead(_) => 0x06,
PacketType::GenShortPktReadP0 => 0x04,
PacketType::GenShortPktReadP1(_) => 0x14,
PacketType::GenShortPktReadP2(_, _) => 0x24,
PacketType::MaxReturnPktSize => 0x37,
}
}
}
/// DSIHOST driver.
pub struct DsiHost<'d, T: Instance> {
_peri: PhantomData<&'d mut T>,
_te: PeripheralRef<'d, AnyPin>,
}
impl<'d, T: Instance> DsiHost<'d, T> {
/// Note: Full-Duplex modes are not supported at this time
pub fn new(_peri: impl Peripheral<P = T> + 'd, te: impl Peripheral<P = impl TePin<T>> + 'd) -> Self {
into_ref!(te);
T::enable_and_reset();
// Set Tearing Enable pin according to CubeMx example
te.set_as_af_pull(te.af_num(), AFType::OutputPushPull, Pull::None);
te.set_speed(Speed::Low);
/*
T::regs().wcr().modify(|w| {
w.set_dsien(true);
});
*/
Self {
_peri: PhantomData,
_te: te.map_into(),
}
}
/// Get the DSIHOST hardware version. Found in the reference manual for comparison.
pub fn get_version(&self) -> u32 {
T::regs().vr().read().version()
}
/// Set the enable bit in the control register and assert that it has been enabled
pub fn enable(&mut self) {
T::regs().cr().modify(|w| w.set_en(true));
assert!(T::regs().cr().read().en())
}
/// Unset the enable bit in the control register and assert that it has been disabled
pub fn disable(&mut self) {
T::regs().cr().modify(|w| w.set_en(false));
assert!(!T::regs().cr().read().en())
}
/// Set the DSI enable bit in the wrapper control register and assert that it has been enabled
pub fn enable_wrapper_dsi(&mut self) {
T::regs().wcr().modify(|w| w.set_dsien(true));
assert!(T::regs().wcr().read().dsien())
}
/// Unset the DSI enable bit in the wrapper control register and assert that it has been disabled
pub fn disable_wrapper_dsi(&mut self) {
T::regs().wcr().modify(|w| w.set_dsien(false));
assert!(!T::regs().wcr().read().dsien())
}
/// DCS or Generic short/long write command
pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> {
assert!(data.len() > 0);
if data.len() == 1 {
self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0])
} else {
self.long_write(
channel_id,
PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well...
address,
data,
)
}
}
fn short_write(&mut self, channel_id: u8, packet_type: PacketType, param1: u8, param2: u8) -> Result<(), Error> {
#[cfg(feature = "defmt")]
defmt::debug!("short_write: BEGIN wait for command fifo empty");
// Wait for Command FIFO empty
self.wait_command_fifo_empty()?;
#[cfg(feature = "defmt")]
defmt::debug!("short_write: END wait for command fifo empty");
// Configure the packet to send a short DCS command with 0 or 1 parameters
// Update the DSI packet header with new information
self.config_packet_header(channel_id, packet_type, param1, param2);
self.wait_command_fifo_empty()?;
let status = T::regs().isr1().read().0;
if status != 0 {
error!("ISR1 after short_write(): {:b}", status);
}
Ok(())
}
fn config_packet_header(&mut self, channel_id: u8, packet_type: PacketType, param1: u8, param2: u8) {
T::regs().ghcr().write(|w| {
w.set_dt(packet_type.into());
w.set_vcid(channel_id);
w.set_wclsb(param1);
w.set_wcmsb(param2);
});
}
/// Write long DCS or long Generic command.
///
/// `params` is expected to contain at least 2 elements. Use [`short_write`] for a single element.
fn long_write(&mut self, channel_id: u8, packet_type: PacketType, address: u8, data: &[u8]) -> Result<(), Error> {
// Must be a long packet if we do the long write, obviously.
assert!(matches!(
packet_type,
PacketType::DcsLongPktWrite | PacketType::GenLongPktWrite
));
// params needs to have at least 2 elements, otherwise short_write should be used
assert!(data.len() >= 2);
#[cfg(feature = "defmt")]
defmt::debug!("long_write: BEGIN wait for command fifo empty");
self.wait_command_fifo_empty()?;
#[cfg(feature = "defmt")]
defmt::debug!("long_write: DONE wait for command fifo empty");
// Note: CubeMX example "NbParams" is always one LESS than params.len()
// DCS code (last element of params) must be on payload byte 1 and if we have only 2 more params,
// then they must go into data2 and data3
T::regs().gpdr().write(|w| {
// data[2] may or may not exist.
if let Some(x) = data.get(2) {
w.set_data4(*x);
}
// data[0] and [1] have to exist if long_write is called.
w.set_data3(data[1]);
w.set_data2(data[0]);
// DCS Code
w.set_data1(address);
});
self.wait_command_fifo_empty()?;
// These steps are only necessary if more than 1x 4 bytes need to go into the FIFO
if data.len() >= 4 {
// Generate an iterator that iterates over chunks of exactly 4 bytes
let iter = data[3..data.len()].chunks_exact(4);
// Obtain remainder before consuming iter
let remainder = iter.remainder();
// Keep filling the buffer with remaining data
for param in iter {
self.wait_command_fifo_not_full()?;
T::regs().gpdr().write(|w| {
w.set_data4(param[3]);
w.set_data3(param[2]);
w.set_data2(param[1]);
w.set_data1(param[0]);
});
self.wait_command_fifo_empty().unwrap();
}
// If the remaining data was not devisible by 4 we get a remainder
if remainder.len() >= 1 {
self.wait_command_fifo_not_full()?;
T::regs().gpdr().write(|w| {
if let Some(x) = remainder.get(2) {
w.set_data3(*x);
}
if let Some(x) = remainder.get(1) {
w.set_data2(*x);
}
w.set_data1(remainder[0]);
});
self.wait_command_fifo_empty().unwrap();
}
}
// Configure the packet to send a long DCS command
self.config_packet_header(
channel_id,
packet_type,
((data.len() + 1) & 0x00FF) as u8, // +1 to account for address byte
(((data.len() + 1) & 0xFF00) >> 8) as u8, // +1 to account for address byte
);
self.wait_command_fifo_empty()?;
let status = T::regs().isr1().read().0;
if status != 0 {
error!("ISR1 after long_write(): {:b}", status);
}
Ok(())
}
/// Read DSI Register
pub fn read(
&mut self,
channel_id: u8,
packet_type: PacketType,
read_size: u16,
data: &mut [u8],
) -> Result<(), Error> {
if data.len() != read_size as usize {
return Err(Error::InvalidReadSize);
}
// Set the maximum return packet size
self.short_write(
channel_id,
PacketType::MaxReturnPktSize,
(read_size & 0xFF) as u8,
((read_size & 0xFF00) >> 8) as u8,
)?;
// Set the packet header according to the packet_type
use PacketType::*;
match packet_type {
DcsShortPktRead(cmd) => self.config_packet_header(channel_id, packet_type, cmd, 0),
GenShortPktReadP0 => self.config_packet_header(channel_id, packet_type, 0, 0),
GenShortPktReadP1(param1) => self.config_packet_header(channel_id, packet_type, param1, 0),
GenShortPktReadP2(param1, param2) => self.config_packet_header(channel_id, packet_type, param1, param2),
_ => return Err(Error::InvalidPacketType),
}
self.wait_read_not_busy()?;
// Obtain chunks of 32-bit so the entire FIFO data register can be read
for bytes in data.chunks_exact_mut(4) {
self.wait_payload_read_fifo_not_empty()?;
// Only perform a single read on the entire register to avoid unintended side-effects
let gpdr = T::regs().gpdr().read();
bytes[0] = gpdr.data1();
bytes[1] = gpdr.data2();
bytes[2] = gpdr.data3();
bytes[3] = gpdr.data4();
}
// Collect the remaining chunks and read the corresponding number of bytes from the FIFO
let remainder = data.chunks_exact_mut(4).into_remainder();
if !remainder.is_empty() {
self.wait_payload_read_fifo_not_empty()?;
// Only perform a single read on the entire register to avoid unintended side-effects
let gpdr = T::regs().gpdr().read();
if let Some(x) = remainder.get_mut(0) {
*x = gpdr.data1()
}
if let Some(x) = remainder.get_mut(1) {
*x = gpdr.data2()
}
if let Some(x) = remainder.get_mut(2) {
*x = gpdr.data3()
}
}
/*
// Used this to check whether there are read errors. Does not seem like it.
if !self.read_busy() {
defmt::debug!("Read not busy!");
if self.packet_size_error() {
return Err(Error::ReadError);
}
}
*/
Ok(())
}
fn wait_command_fifo_empty(&self) -> Result<(), Error> {
for _ in 1..1000 {
// Wait for Command FIFO empty
if T::regs().gpsr().read().cmdfe() {
return Ok(());
}
blocking_delay_ms(1);
}
Err(Error::FifoTimeout)
}
fn wait_command_fifo_not_full(&self) -> Result<(), Error> {
for _ in 1..1000 {
// Wait for Command FIFO not empty
if !T::regs().gpsr().read().cmdff() {
return Ok(());
}
blocking_delay_ms(1);
}
Err(Error::FifoTimeout)
}
fn wait_read_not_busy(&self) -> Result<(), Error> {
for _ in 1..1000 {
// Wait for read not busy
if !self.read_busy() {
return Ok(());
}
blocking_delay_ms(1);
}
Err(Error::ReadTimeout)
}
fn wait_payload_read_fifo_not_empty(&self) -> Result<(), Error> {
for _ in 1..1000 {
// Wait for payload read FIFO not empty
if !T::regs().gpsr().read().prdfe() {
return Ok(());
}
blocking_delay_ms(1);
}
Err(Error::FifoTimeout)
}
fn _packet_size_error(&self) -> bool {
T::regs().isr1().read().pse()
}
fn read_busy(&self) -> bool {
T::regs().gpsr().read().rcb()
}
}
/// Possible Error Types for DSI HOST
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
/// Waiting for FIFO empty flag timed out
FifoTimeout,
/// The specified `PacketType` is invalid for the selected operation
InvalidPacketType,
/// Provided read size does not match the read buffer length
InvalidReadSize,
/// Error during read
ReadError,
/// Read operation timed out
ReadTimeout,
}
impl<'d, T: Instance> Drop for DsiHost<'d, T> {
fn drop(&mut self) {}
}
trait SealedInstance: crate::rcc::SealedRccPeripheral {
fn regs() -> &'static crate::pac::dsihost::Dsihost;
}
/// DSI instance trait.
#[allow(private_bounds)]
pub trait Instance: SealedInstance + RccPeripheral + 'static {}
pin_trait!(TePin, Instance);
foreach_peripheral!(
(dsihost, $inst:ident) => {
impl crate::dsihost::SealedInstance for peripherals::$inst {
fn regs() -> &'static crate::pac::dsihost::Dsihost {
&crate::pac::$inst
}
}
impl crate::dsihost::Instance for peripherals::$inst {}
};
);