diff options
| author | Joël Schulz-Ansres <[email protected]> | 2024-05-02 13:36:44 +0200 |
|---|---|---|
| committer | Joël Schulz-Ansres <[email protected]> | 2024-05-02 13:43:42 +0200 |
| commit | 9fe50a7639a046bafe9d2003847476cbfd61db1d (patch) | |
| tree | 46f05c008a2de6e4b23c080868cb0ccc891dcf19 | |
| parent | 45a2abc392df91ce6963ac0956f48f22bfa1489b (diff) | |
Add stm32 dsihost driver
| -rw-r--r-- | embassy-stm32/Cargo.toml | 1 | ||||
| -rw-r--r-- | embassy-stm32/build.rs | 29 | ||||
| -rw-r--r-- | embassy-stm32/src/dsihost.rs | 363 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 4 | ||||
| -rw-r--r-- | embassy-stm32/src/ltdc.rs | 134 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/f247.rs | 5 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/h.rs | 3 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/l.rs | 3 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/u5.rs | 3 |
9 files changed, 543 insertions, 2 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a50da1a9e..a7137d132 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -89,7 +89,6 @@ volatile-register = { version = "0.2.1" } | |||
| 89 | bitflags = "2.4.2" | 89 | bitflags = "2.4.2" |
| 90 | 90 | ||
| 91 | 91 | ||
| 92 | |||
| 93 | [dev-dependencies] | 92 | [dev-dependencies] |
| 94 | critical-section = { version = "1.1", features = ["std"] } | 93 | critical-section = { version = "1.1", features = ["std"] } |
| 95 | 94 | ||
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ba118f338..dbc8f9153 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -836,6 +836,35 @@ fn main() { | |||
| 836 | (("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)), | 836 | (("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)), |
| 837 | (("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)), | 837 | (("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)), |
| 838 | (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)), | 838 | (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)), |
| 839 | (("dsihost", "TE"), quote!(crate::dsihost::TePin)), | ||
| 840 | (("ltdc", "CLK"), quote!(crate::ltdc::ClkPin)), | ||
| 841 | (("ltdc", "HSYNC"), quote!(crate::ltdc::HsyncPin)), | ||
| 842 | (("ltdc", "VSYNC"), quote!(crate::ltdc::VsyncPin)), | ||
| 843 | (("ltdc", "DE"), quote!(crate::ltdc::DePin)), | ||
| 844 | (("ltdc", "R0"), quote!(crate::ltdc::R0Pin)), | ||
| 845 | (("ltdc", "R1"), quote!(crate::ltdc::R1Pin)), | ||
| 846 | (("ltdc", "R2"), quote!(crate::ltdc::R2Pin)), | ||
| 847 | (("ltdc", "R3"), quote!(crate::ltdc::R3Pin)), | ||
| 848 | (("ltdc", "R4"), quote!(crate::ltdc::R4Pin)), | ||
| 849 | (("ltdc", "R5"), quote!(crate::ltdc::R5Pin)), | ||
| 850 | (("ltdc", "R6"), quote!(crate::ltdc::R6Pin)), | ||
| 851 | (("ltdc", "R7"), quote!(crate::ltdc::R7Pin)), | ||
| 852 | (("ltdc", "G0"), quote!(crate::ltdc::G0Pin)), | ||
| 853 | (("ltdc", "G1"), quote!(crate::ltdc::G1Pin)), | ||
| 854 | (("ltdc", "G2"), quote!(crate::ltdc::G2Pin)), | ||
| 855 | (("ltdc", "G3"), quote!(crate::ltdc::G3Pin)), | ||
| 856 | (("ltdc", "G4"), quote!(crate::ltdc::G4Pin)), | ||
| 857 | (("ltdc", "G5"), quote!(crate::ltdc::G5Pin)), | ||
| 858 | (("ltdc", "G6"), quote!(crate::ltdc::G6Pin)), | ||
| 859 | (("ltdc", "G7"), quote!(crate::ltdc::G7Pin)), | ||
| 860 | (("ltdc", "B0"), quote!(crate::ltdc::B0Pin)), | ||
| 861 | (("ltdc", "B1"), quote!(crate::ltdc::B1Pin)), | ||
| 862 | (("ltdc", "B2"), quote!(crate::ltdc::B2Pin)), | ||
| 863 | (("ltdc", "B3"), quote!(crate::ltdc::B3Pin)), | ||
| 864 | (("ltdc", "B4"), quote!(crate::ltdc::B4Pin)), | ||
| 865 | (("ltdc", "B5"), quote!(crate::ltdc::B5Pin)), | ||
| 866 | (("ltdc", "B6"), quote!(crate::ltdc::B6Pin)), | ||
| 867 | (("ltdc", "B7"), quote!(crate::ltdc::B7Pin)), | ||
| 839 | (("usb", "DP"), quote!(crate::usb::DpPin)), | 868 | (("usb", "DP"), quote!(crate::usb::DpPin)), |
| 840 | (("usb", "DM"), quote!(crate::usb::DmPin)), | 869 | (("usb", "DM"), quote!(crate::usb::DmPin)), |
| 841 | (("otg", "DP"), quote!(crate::usb::DpPin)), | 870 | (("otg", "DP"), quote!(crate::usb::DpPin)), |
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs new file mode 100644 index 000000000..c55fa8411 --- /dev/null +++ b/embassy-stm32/src/dsihost.rs | |||
| @@ -0,0 +1,363 @@ | |||
| 1 | //! DSI HOST | ||
| 2 | |||
| 3 | use crate::rcc::RccPeripheral; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 6 | |||
| 7 | //use crate::gpio::{AnyPin, SealedPin}; | ||
| 8 | use crate::gpio::{AFType, AnyPin, Pull, Speed}; | ||
| 9 | use crate::{peripherals, Peripheral}; | ||
| 10 | |||
| 11 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 12 | pub fn blocking_delay_ms(ms: u32) { | ||
| 13 | #[cfg(time)] | ||
| 14 | embassy_time::block_for(embassy_time::Duration::from_millis(ms)); | ||
| 15 | #[cfg(not(time))] | ||
| 16 | cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 / 1_000 * ms); | ||
| 17 | } | ||
| 18 | |||
| 19 | /// PacketTypes extracted from CubeMX | ||
| 20 | #[repr(u8)] | ||
| 21 | #[allow(dead_code)] | ||
| 22 | pub enum PacketType { | ||
| 23 | /// DCS short write, no parameters | ||
| 24 | DcsShortPktWriteP0, | ||
| 25 | /// DCS short write, one parameter | ||
| 26 | DcsShortPktWriteP1, | ||
| 27 | /// Generic short write, no parameters | ||
| 28 | GenShortPktWriteP0, | ||
| 29 | /// Generic short write, one parameter | ||
| 30 | GenShortPktWriteP1, | ||
| 31 | /// Generic short write, two parameters | ||
| 32 | GenShortPktWriteP2, | ||
| 33 | /// DCS long write | ||
| 34 | DcsLongPktWrite, | ||
| 35 | /// Generic long write | ||
| 36 | GenLongPktWrite, | ||
| 37 | /// DCS short read | ||
| 38 | DcsShortPktRead(u8), | ||
| 39 | /// Generic short read, no parameters | ||
| 40 | GenShortPktReadP0, | ||
| 41 | /// Generic short read, one parameter | ||
| 42 | GenShortPktReadP1(u8), | ||
| 43 | /// Generic short read, two parameters | ||
| 44 | GenShortPktReadP2(u8, u8), | ||
| 45 | /// Used to set the maximum return packet size for reading data | ||
| 46 | MaxReturnPktSize, | ||
| 47 | } | ||
| 48 | |||
| 49 | impl From<PacketType> for u8 { | ||
| 50 | fn from(packet_type: PacketType) -> u8 { | ||
| 51 | match packet_type { | ||
| 52 | PacketType::DcsShortPktWriteP0 => 0x05, | ||
| 53 | PacketType::DcsShortPktWriteP1 => 0x15, | ||
| 54 | PacketType::GenShortPktWriteP0 => 0x03, | ||
| 55 | PacketType::GenShortPktWriteP1 => 0x13, | ||
| 56 | PacketType::GenShortPktWriteP2 => 0x23, | ||
| 57 | PacketType::DcsLongPktWrite => 0x39, | ||
| 58 | PacketType::GenLongPktWrite => 0x29, | ||
| 59 | PacketType::DcsShortPktRead(_) => 0x06, | ||
| 60 | PacketType::GenShortPktReadP0 => 0x04, | ||
| 61 | PacketType::GenShortPktReadP1(_) => 0x14, | ||
| 62 | PacketType::GenShortPktReadP2(_, _) => 0x24, | ||
| 63 | PacketType::MaxReturnPktSize => 0x37, | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | /// DSIHOST driver. | ||
| 69 | pub struct DsiHost<'d, T: Instance> { | ||
| 70 | _peri: PhantomData<&'d mut T>, | ||
| 71 | _te: PeripheralRef<'d, AnyPin>, | ||
| 72 | } | ||
| 73 | |||
| 74 | impl<'d, T: Instance> DsiHost<'d, T> { | ||
| 75 | /// Note: Full-Duplex modes are not supported at this time | ||
| 76 | pub fn new(_peri: impl Peripheral<P = T> + 'd, te: impl Peripheral<P = impl TePin<T>> + 'd) -> Self { | ||
| 77 | into_ref!(te); | ||
| 78 | |||
| 79 | T::enable_and_reset(); | ||
| 80 | |||
| 81 | // Set Tearing Enable pin according to CubeMx example | ||
| 82 | te.set_as_af_pull(te.af_num(), AFType::OutputPushPull, Pull::None); | ||
| 83 | te.set_speed(Speed::Low); | ||
| 84 | /* | ||
| 85 | T::regs().wcr().modify(|w| { | ||
| 86 | w.set_dsien(true); | ||
| 87 | }); | ||
| 88 | */ | ||
| 89 | Self { | ||
| 90 | _peri: PhantomData, | ||
| 91 | _te: te.map_into(), | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Get the DSIHOST hardware version. Found in the reference manual for comparison. | ||
| 96 | pub fn get_version(&self) -> u32 { | ||
| 97 | T::regs().vr().read().version() | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Set the enable bit in the control register and assert that it has been enabled | ||
| 101 | pub fn enable(&mut self) { | ||
| 102 | T::regs().cr().modify(|w| w.set_en(true)); | ||
| 103 | assert!(T::regs().cr().read().en()) | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Unset the enable bit in the control register and assert that it has been disabled | ||
| 107 | pub fn disable(&mut self) { | ||
| 108 | T::regs().cr().modify(|w| w.set_en(false)); | ||
| 109 | assert!(!T::regs().cr().read().en()) | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Set the DSI enable bit in the wrapper control register and assert that it has been enabled | ||
| 113 | pub fn enable_wrapper_dsi(&mut self) { | ||
| 114 | T::regs().wcr().modify(|w| w.set_dsien(true)); | ||
| 115 | assert!(T::regs().wcr().read().dsien()) | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Unset the DSI enable bit in the wrapper control register and assert that it has been disabled | ||
| 119 | pub fn disable_wrapper_dsi(&mut self) { | ||
| 120 | T::regs().wcr().modify(|w| w.set_dsien(false)); | ||
| 121 | assert!(!T::regs().wcr().read().dsien()) | ||
| 122 | } | ||
| 123 | |||
| 124 | /// DCS or Generic short/long write command | ||
| 125 | pub fn write_cmd(&mut self, channel_id: u8, params: &[u8]) { | ||
| 126 | if params.len() <= 2 { | ||
| 127 | self.short_write(channel_id, PacketType::DcsShortPktWriteP1, params[0], params[1]) | ||
| 128 | } else { | ||
| 129 | self.long_write( | ||
| 130 | channel_id, | ||
| 131 | PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well... | ||
| 132 | params, | ||
| 133 | ) | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | fn short_write(&mut self, channel_id: u8, packet_type: PacketType, param1: u8, param2: u8) { | ||
| 138 | #[cfg(feature = "defmt")] | ||
| 139 | defmt::debug!("short_write: BEGIN wait for command fifo empty"); | ||
| 140 | |||
| 141 | // Wait for Command FIFO empty | ||
| 142 | self.wait_command_fifo_empty().unwrap(); | ||
| 143 | #[cfg(feature = "defmt")] | ||
| 144 | defmt::debug!("short_write: END wait for command fifo empty"); | ||
| 145 | |||
| 146 | // Configure the packet to send a short DCS command with 0 or 1 parameters | ||
| 147 | // Update the DSI packet header with new information | ||
| 148 | self.config_packet_header(channel_id, packet_type, param1, param2); | ||
| 149 | } | ||
| 150 | |||
| 151 | fn config_packet_header(&mut self, channel_id: u8, packet_type: PacketType, param1: u8, param2: u8) { | ||
| 152 | T::regs().ghcr().write(|w| { | ||
| 153 | w.set_dt(packet_type.into()); | ||
| 154 | w.set_vcid(channel_id); | ||
| 155 | w.set_wclsb(param1); | ||
| 156 | w.set_wcmsb(param2); | ||
| 157 | }); | ||
| 158 | } | ||
| 159 | |||
| 160 | /// Write long DCS or long Generic command. | ||
| 161 | /// | ||
| 162 | /// `params` is expected to contain at least 3 elements. Use [`short_write`] for 2 or less. | ||
| 163 | fn long_write(&mut self, channel_id: u8, packet_type: PacketType, params: &[u8]) { | ||
| 164 | // Must be a long packet if we do the long write, obviously. | ||
| 165 | assert!(matches!( | ||
| 166 | packet_type, | ||
| 167 | PacketType::DcsLongPktWrite | PacketType::GenLongPktWrite | ||
| 168 | )); | ||
| 169 | |||
| 170 | // params needs to have at least 3 elements, otherwise short_write should be used | ||
| 171 | assert!(params.len() >= 3); | ||
| 172 | |||
| 173 | #[cfg(feature = "defmt")] | ||
| 174 | defmt::debug!("long_write: BEGIN wait for command fifo empty"); | ||
| 175 | |||
| 176 | self.wait_command_fifo_empty().unwrap(); | ||
| 177 | |||
| 178 | #[cfg(feature = "defmt")] | ||
| 179 | defmt::debug!("long_write: DONE wait for command fifo empty"); | ||
| 180 | |||
| 181 | let dcs_code = params[params.len() - 1]; // FIXME: DCS Code stored in the last element. Should probably be a separate argument. | ||
| 182 | let data = ¶ms[0..params.len() - 1]; // data is all elements except for the dcs_code | ||
| 183 | |||
| 184 | // Note: CubeMX example "NbParams" is always one LESS than params.len() | ||
| 185 | // DCS code (last element of params) must be on payload byte 1 and if we have only 2 more params, | ||
| 186 | // then they must go into data2 and data3 | ||
| 187 | T::regs().gpdr().write(|w| { | ||
| 188 | // data[2] may or may not exist. | ||
| 189 | if let Some(x) = data.get(2) { | ||
| 190 | w.set_data4(*x); | ||
| 191 | } | ||
| 192 | // data[0] and [1] have to exist if long_write is called. | ||
| 193 | w.set_data3(data[1]); | ||
| 194 | w.set_data2(data[0]); | ||
| 195 | // DCS Code | ||
| 196 | w.set_data1(dcs_code); | ||
| 197 | }); | ||
| 198 | |||
| 199 | // FIXME: This probably should return an error | ||
| 200 | self.wait_command_fifo_empty().unwrap(); | ||
| 201 | |||
| 202 | // These steps are only necessary if more than 1x 4 bytes need to go into the FIFO | ||
| 203 | if data.len() >= 4 { | ||
| 204 | // Generate an iterator that iterates over chunks of exactly 4 bytes | ||
| 205 | let iter = data[3..data.len()].chunks_exact(4); | ||
| 206 | // Obtain remainder before consuming iter | ||
| 207 | let remainder = iter.remainder(); | ||
| 208 | |||
| 209 | // Keep filling the buffer with remaining data | ||
| 210 | for param in iter { | ||
| 211 | T::regs().gpdr().write(|w| { | ||
| 212 | w.set_data4(param[3]); | ||
| 213 | w.set_data3(param[2]); | ||
| 214 | w.set_data2(param[1]); | ||
| 215 | w.set_data1(param[0]); | ||
| 216 | }); | ||
| 217 | |||
| 218 | self.wait_command_fifo_empty().unwrap(); | ||
| 219 | } | ||
| 220 | |||
| 221 | // If the remaining data was not devisible by 4 we get a remainder | ||
| 222 | if remainder.len() >= 1 { | ||
| 223 | T::regs().gpdr().write(|w| { | ||
| 224 | if let Some(x) = remainder.get(2) { | ||
| 225 | w.set_data3(*x); | ||
| 226 | } | ||
| 227 | if let Some(x) = remainder.get(1) { | ||
| 228 | w.set_data2(*x); | ||
| 229 | } | ||
| 230 | w.set_data1(remainder[0]); | ||
| 231 | }); | ||
| 232 | self.wait_command_fifo_empty().unwrap(); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | // Configure the packet to send a long DCS command | ||
| 236 | T::regs().ghcr().write(|w| { | ||
| 237 | w.set_dt(packet_type.into()); | ||
| 238 | w.set_vcid(channel_id); | ||
| 239 | w.set_wclsb((params.len() & 0x00FF) as u8); | ||
| 240 | w.set_wcmsb((params.len() & 0xFF00 >> 8) as u8); | ||
| 241 | }); | ||
| 242 | } | ||
| 243 | |||
| 244 | pub fn read( | ||
| 245 | &mut self, | ||
| 246 | channel_id: u8, | ||
| 247 | packet_type: PacketType, | ||
| 248 | read_size: u16, | ||
| 249 | data: &mut [u8], | ||
| 250 | ) -> Result<(), Error> { | ||
| 251 | if data.len() != read_size as usize { | ||
| 252 | return Err(Error::InvalidReadSize); | ||
| 253 | } | ||
| 254 | |||
| 255 | // Set the maximum return packet size | ||
| 256 | self.short_write( | ||
| 257 | channel_id, | ||
| 258 | PacketType::MaxReturnPktSize, | ||
| 259 | (read_size & 0xFF) as u8, | ||
| 260 | ((read_size >> 8) & 0xFF) as u8, | ||
| 261 | ); | ||
| 262 | |||
| 263 | // Set the packet header according to the packet_type | ||
| 264 | use PacketType::*; | ||
| 265 | match packet_type { | ||
| 266 | DcsShortPktRead(cmd) => self.config_packet_header(channel_id, packet_type, cmd, 0), | ||
| 267 | GenShortPktReadP0 => self.config_packet_header(channel_id, packet_type, 0, 0), | ||
| 268 | GenShortPktReadP1(param1) => self.config_packet_header(channel_id, packet_type, param1, 0), | ||
| 269 | GenShortPktReadP2(param1, param2) => self.config_packet_header(channel_id, packet_type, param1, param2), | ||
| 270 | _ => return Err(Error::InvalidPacketType), | ||
| 271 | } | ||
| 272 | |||
| 273 | // Obtain chunks of 32-bit so the entire FIFO data register can be read | ||
| 274 | for bytes in data.chunks_exact_mut(4) { | ||
| 275 | self.wait_payload_read_fifo_not_empty().unwrap(); | ||
| 276 | |||
| 277 | // Only perform a single read on the entire register to avoid unintended side-effects | ||
| 278 | let gpdr = T::regs().gpdr().read(); | ||
| 279 | bytes[0] = gpdr.data1(); | ||
| 280 | bytes[1] = gpdr.data2(); | ||
| 281 | bytes[2] = gpdr.data3(); | ||
| 282 | bytes[3] = gpdr.data4(); | ||
| 283 | } | ||
| 284 | |||
| 285 | // Collect the remaining chunks and read the corresponding number of bytes from the FIFO | ||
| 286 | let remainder = data.chunks_exact_mut(4).into_remainder(); | ||
| 287 | if !remainder.is_empty() { | ||
| 288 | self.wait_payload_read_fifo_not_empty().unwrap(); | ||
| 289 | // Only perform a single read on the entire register to avoid unintended side-effects | ||
| 290 | let gpdr = T::regs().gpdr().read(); | ||
| 291 | if let Some(x) = remainder.get_mut(0) { | ||
| 292 | *x = gpdr.data1() | ||
| 293 | } | ||
| 294 | if let Some(x) = remainder.get_mut(1) { | ||
| 295 | *x = gpdr.data2() | ||
| 296 | } | ||
| 297 | if let Some(x) = remainder.get_mut(2) { | ||
| 298 | *x = gpdr.data3() | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | Ok(()) | ||
| 303 | } | ||
| 304 | fn wait_command_fifo_empty(&self) -> Result<(), Error> { | ||
| 305 | for _ in 1..1000 { | ||
| 306 | // Wait for Command FIFO empty | ||
| 307 | if T::regs().gpsr().read().cmdfe() { | ||
| 308 | return Ok(()); | ||
| 309 | } | ||
| 310 | blocking_delay_ms(1); | ||
| 311 | } | ||
| 312 | Err(Error::FifoTimeout) | ||
| 313 | } | ||
| 314 | |||
| 315 | fn wait_payload_read_fifo_not_empty(&self) -> Result<(), Error> { | ||
| 316 | for _ in 1..1000 { | ||
| 317 | // Wait for payload read FIFO empty | ||
| 318 | if !T::regs().gpsr().read().prdfe() { | ||
| 319 | return Ok(()); | ||
| 320 | } | ||
| 321 | blocking_delay_ms(1); | ||
| 322 | } | ||
| 323 | Err(Error::FifoTimeout) | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | /// Possible Error Types for DSI HOST | ||
| 328 | #[non_exhaustive] | ||
| 329 | #[derive(Debug)] | ||
| 330 | pub enum Error { | ||
| 331 | /// Waiting for FIFO empty flag timed out | ||
| 332 | FifoTimeout, | ||
| 333 | /// The specified `PacketType` is invalid for the selected operation | ||
| 334 | InvalidPacketType, | ||
| 335 | /// Provided read size does not match the read buffer length | ||
| 336 | InvalidReadSize, | ||
| 337 | } | ||
| 338 | |||
| 339 | impl<'d, T: Instance> Drop for DsiHost<'d, T> { | ||
| 340 | fn drop(&mut self) {} | ||
| 341 | } | ||
| 342 | |||
| 343 | trait SealedInstance: crate::rcc::SealedRccPeripheral { | ||
| 344 | fn regs() -> &'static crate::pac::dsihost::Dsihost; | ||
| 345 | } | ||
| 346 | |||
| 347 | /// DSI instance trait. | ||
| 348 | #[allow(private_bounds)] | ||
| 349 | pub trait Instance: SealedInstance + RccPeripheral + 'static {} | ||
| 350 | |||
| 351 | pin_trait!(TePin, Instance); | ||
| 352 | |||
| 353 | foreach_peripheral!( | ||
| 354 | (dsihost, $inst:ident) => { | ||
| 355 | impl crate::dsihost::SealedInstance for peripherals::$inst { | ||
| 356 | fn regs() -> &'static crate::pac::dsihost::Dsihost { | ||
| 357 | &crate::pac::$inst | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | impl crate::dsihost::Instance for peripherals::$inst {} | ||
| 362 | }; | ||
| 363 | ); | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 1f4e9ab1e..1f92e8b2a 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -66,6 +66,10 @@ pub mod cryp; | |||
| 66 | pub mod dac; | 66 | pub mod dac; |
| 67 | #[cfg(dcmi)] | 67 | #[cfg(dcmi)] |
| 68 | pub mod dcmi; | 68 | pub mod dcmi; |
| 69 | #[cfg(dsihost)] | ||
| 70 | pub mod dsihost; | ||
| 71 | #[cfg(ltdc)] | ||
| 72 | pub mod ltdc; | ||
| 69 | #[cfg(eth)] | 73 | #[cfg(eth)] |
| 70 | pub mod eth; | 74 | pub mod eth; |
| 71 | #[cfg(feature = "exti")] | 75 | #[cfg(feature = "exti")] |
diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs new file mode 100644 index 000000000..1cdf71e7e --- /dev/null +++ b/embassy-stm32/src/ltdc.rs | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | //! LTDC | ||
| 2 | use crate::rcc::RccPeripheral; | ||
| 3 | use crate::{peripherals, Peripheral}; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | |||
| 6 | /// LTDC driver. | ||
| 7 | pub struct Ltdc<'d, T: Instance> { | ||
| 8 | _peri: PhantomData<&'d mut T>, | ||
| 9 | } | ||
| 10 | |||
| 11 | impl<'d, T: Instance> Ltdc<'d, T> { | ||
| 12 | /// Note: Full-Duplex modes are not supported at this time | ||
| 13 | pub fn new( | ||
| 14 | _peri: impl Peripheral<P = T> + 'd, | ||
| 15 | /* | ||
| 16 | clk: impl Peripheral<P = impl ClkPin<T>> + 'd, | ||
| 17 | hsync: impl Peripheral<P = impl HsyncPin<T>> + 'd, | ||
| 18 | vsync: impl Peripheral<P = impl VsyncPin<T>> + 'd, | ||
| 19 | b0: impl Peripheral<P = impl B0Pin<T>> + 'd, | ||
| 20 | b1: impl Peripheral<P = impl B1Pin<T>> + 'd, | ||
| 21 | b2: impl Peripheral<P = impl B2Pin<T>> + 'd, | ||
| 22 | b3: impl Peripheral<P = impl B3Pin<T>> + 'd, | ||
| 23 | b4: impl Peripheral<P = impl B4Pin<T>> + 'd, | ||
| 24 | b5: impl Peripheral<P = impl B5Pin<T>> + 'd, | ||
| 25 | b6: impl Peripheral<P = impl B6Pin<T>> + 'd, | ||
| 26 | b7: impl Peripheral<P = impl B7Pin<T>> + 'd, | ||
| 27 | g0: impl Peripheral<P = impl G0Pin<T>> + 'd, | ||
| 28 | g1: impl Peripheral<P = impl G1Pin<T>> + 'd, | ||
| 29 | g2: impl Peripheral<P = impl G2Pin<T>> + 'd, | ||
| 30 | g3: impl Peripheral<P = impl G3Pin<T>> + 'd, | ||
| 31 | g4: impl Peripheral<P = impl G4Pin<T>> + 'd, | ||
| 32 | g5: impl Peripheral<P = impl G5Pin<T>> + 'd, | ||
| 33 | g6: impl Peripheral<P = impl G6Pin<T>> + 'd, | ||
| 34 | g7: impl Peripheral<P = impl G7Pin<T>> + 'd, | ||
| 35 | r0: impl Peripheral<P = impl R0Pin<T>> + 'd, | ||
| 36 | r1: impl Peripheral<P = impl R1Pin<T>> + 'd, | ||
| 37 | r2: impl Peripheral<P = impl R2Pin<T>> + 'd, | ||
| 38 | r3: impl Peripheral<P = impl R3Pin<T>> + 'd, | ||
| 39 | r4: impl Peripheral<P = impl R4Pin<T>> + 'd, | ||
| 40 | r5: impl Peripheral<P = impl R5Pin<T>> + 'd, | ||
| 41 | r6: impl Peripheral<P = impl R6Pin<T>> + 'd, | ||
| 42 | r7: impl Peripheral<P = impl R7Pin<T>> + 'd, | ||
| 43 | */ | ||
| 44 | ) -> Self { | ||
| 45 | //into_ref!(clk); | ||
| 46 | |||
| 47 | critical_section::with(|_cs| { | ||
| 48 | // RM says the pllsaidivr should only be changed when pllsai is off. But this could have other unintended side effects. So let's just give it a try like this. | ||
| 49 | // According to the debugger, this bit gets set, anyway. | ||
| 50 | stm32_metapac::RCC | ||
| 51 | .dckcfgr() | ||
| 52 | .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2)); | ||
| 53 | }); | ||
| 54 | |||
| 55 | T::enable_and_reset(); | ||
| 56 | |||
| 57 | //new_pin!(clk, AFType::OutputPushPull, Speed::VeryHigh, Pull::None); | ||
| 58 | |||
| 59 | // Set Tearing Enable pin according to CubeMx example | ||
| 60 | //te.set_as_af_pull(te.af_num(), AFType::OutputPushPull, Pull::None); | ||
| 61 | //te.set_speed(Speed::Low); | ||
| 62 | /* | ||
| 63 | T::regs().wcr().modify(|w| { | ||
| 64 | w.set_dsien(true); | ||
| 65 | }); | ||
| 66 | */ | ||
| 67 | Self { _peri: PhantomData } | ||
| 68 | } | ||
| 69 | |||
| 70 | /// Set the enable bit in the control register and assert that it has been enabled | ||
| 71 | pub fn enable(&mut self) { | ||
| 72 | T::regs().gcr().modify(|w| w.set_ltdcen(true)); | ||
| 73 | assert!(T::regs().gcr().read().ltdcen()) | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Unset the enable bit in the control register and assert that it has been disabled | ||
| 77 | pub fn disable(&mut self) { | ||
| 78 | T::regs().gcr().modify(|w| w.set_ltdcen(false)); | ||
| 79 | assert!(!T::regs().gcr().read().ltdcen()) | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | impl<'d, T: Instance> Drop for Ltdc<'d, T> { | ||
| 84 | fn drop(&mut self) {} | ||
| 85 | } | ||
| 86 | |||
| 87 | trait SealedInstance: crate::rcc::SealedRccPeripheral { | ||
| 88 | fn regs() -> &'static crate::pac::ltdc::Ltdc; | ||
| 89 | } | ||
| 90 | |||
| 91 | /// DSI instance trait. | ||
| 92 | #[allow(private_bounds)] | ||
| 93 | pub trait Instance: SealedInstance + RccPeripheral + 'static {} | ||
| 94 | |||
| 95 | pin_trait!(ClkPin, Instance); | ||
| 96 | pin_trait!(HsyncPin, Instance); | ||
| 97 | pin_trait!(VsyncPin, Instance); | ||
| 98 | pin_trait!(DePin, Instance); | ||
| 99 | pin_trait!(R0Pin, Instance); | ||
| 100 | pin_trait!(R1Pin, Instance); | ||
| 101 | pin_trait!(R2Pin, Instance); | ||
| 102 | pin_trait!(R3Pin, Instance); | ||
| 103 | pin_trait!(R4Pin, Instance); | ||
| 104 | pin_trait!(R5Pin, Instance); | ||
| 105 | pin_trait!(R6Pin, Instance); | ||
| 106 | pin_trait!(R7Pin, Instance); | ||
| 107 | pin_trait!(G0Pin, Instance); | ||
| 108 | pin_trait!(G1Pin, Instance); | ||
| 109 | pin_trait!(G2Pin, Instance); | ||
| 110 | pin_trait!(G3Pin, Instance); | ||
| 111 | pin_trait!(G4Pin, Instance); | ||
| 112 | pin_trait!(G5Pin, Instance); | ||
| 113 | pin_trait!(G6Pin, Instance); | ||
| 114 | pin_trait!(G7Pin, Instance); | ||
| 115 | pin_trait!(B0Pin, Instance); | ||
| 116 | pin_trait!(B1Pin, Instance); | ||
| 117 | pin_trait!(B2Pin, Instance); | ||
| 118 | pin_trait!(B3Pin, Instance); | ||
| 119 | pin_trait!(B4Pin, Instance); | ||
| 120 | pin_trait!(B5Pin, Instance); | ||
| 121 | pin_trait!(B6Pin, Instance); | ||
| 122 | pin_trait!(B7Pin, Instance); | ||
| 123 | |||
| 124 | foreach_peripheral!( | ||
| 125 | (ltdc, $inst:ident) => { | ||
| 126 | impl crate::ltdc::SealedInstance for peripherals::$inst { | ||
| 127 | fn regs() -> &'static crate::pac::ltdc::Ltdc { | ||
| 128 | &crate::pac::$inst | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | impl crate::ltdc::Instance for peripherals::$inst {} | ||
| 133 | }; | ||
| 134 | ); | ||
diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index e3ee9a039..640175f1f 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs | |||
| @@ -277,7 +277,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 277 | pclk2_tim: Some(pclk2_tim), | 277 | pclk2_tim: Some(pclk2_tim), |
| 278 | rtc: rtc, | 278 | rtc: rtc, |
| 279 | pll1_q: pll.q, | 279 | pll1_q: pll.q, |
| 280 | pll1_r: None, // TODO | 280 | pll1_r: pll.r, |
| 281 | 281 | ||
| 282 | #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))] | 282 | #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))] |
| 283 | plli2s1_p: plli2s.p, | 283 | plli2s1_p: plli2s.p, |
| @@ -297,6 +297,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 297 | #[cfg(not(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7)))] | 297 | #[cfg(not(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7)))] |
| 298 | pllsai1_q: None, | 298 | pllsai1_q: None, |
| 299 | 299 | ||
| 300 | #[cfg(dsihost)] | ||
| 301 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers | ||
| 302 | |||
| 300 | hsi_div488: hsi.map(|hsi| hsi/488u32), | 303 | hsi_div488: hsi.map(|hsi| hsi/488u32), |
| 301 | hsi_hse: None, | 304 | hsi_hse: None, |
| 302 | afif: None, | 305 | afif: None, |
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 5d47d400c..3cc8a96c8 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs | |||
| @@ -677,6 +677,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 677 | #[cfg(rcc_h50)] | 677 | #[cfg(rcc_h50)] |
| 678 | pll3_r: None, | 678 | pll3_r: None, |
| 679 | 679 | ||
| 680 | #[cfg(dsihost)] | ||
| 681 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers | ||
| 682 | |||
| 680 | #[cfg(stm32h5)] | 683 | #[cfg(stm32h5)] |
| 681 | audioclk: None, | 684 | audioclk: None, |
| 682 | i2s_ckin: None, | 685 | i2s_ckin: None, |
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index c625948fb..1329a03ac 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs | |||
| @@ -413,6 +413,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 413 | #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))] | 413 | #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))] |
| 414 | pllsai2_r: pllsai2.r, | 414 | pllsai2_r: pllsai2.r, |
| 415 | 415 | ||
| 416 | #[cfg(dsihost)] | ||
| 417 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers | ||
| 418 | |||
| 416 | rtc: rtc, | 419 | rtc: rtc, |
| 417 | 420 | ||
| 418 | // TODO | 421 | // TODO |
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 4013d0a46..6ec0911bb 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs | |||
| @@ -289,6 +289,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 289 | pll3_q: pll3.q, | 289 | pll3_q: pll3.q, |
| 290 | pll3_r: pll3.r, | 290 | pll3_r: pll3.r, |
| 291 | 291 | ||
| 292 | #[cfg(dsihost)] | ||
| 293 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers | ||
| 294 | |||
| 292 | // TODO | 295 | // TODO |
| 293 | audioclk: None, | 296 | audioclk: None, |
| 294 | hsi48_div_2: None, | 297 | hsi48_div_2: None, |
