1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
#![no_std] pub mod commands; pub mod config; pub mod display_list; pub mod graphics; pub mod interface; pub mod low_level; pub mod memory; pub mod models; mod error; pub use error::CoprocessorError; pub use error::Error; /// Constructs a [`Message`](crate::strfmt::Message) value for use with EVE /// coprocessor commands that support string formatting. /// /// This macro understands the format syntax just enough to automatically /// infer the types of any given arguments and thus produce a valid pairing /// of format string and arguments. However, it achieves that by parsing the /// format string at compile tLowLevelthe format string must always be /// a quoted string constant. /// /// The coprocessor's formatter serves a similar purpose as Rust's own /// format functionality, but since the actual formatting operation happens /// inside the EVE coprocessor we can avoid including the potentially-large /// formatting code in memory-constrained systems. The EVE formatter can also /// interpolate strings already stored in the EVE RAM, via the `%s` verb, which /// Rust's own formatter doesn't have direct access to. pub use evegfx_macros::eve_format as format; // For more convenient use elsewhere in the crate, because we make a lot of // use of these internally even though they are not a significant part of // the main public interface. pub(crate) use low_level::{host_commands, registers}; /// Model type representing the BT815 and BT816 chips. #[doc(inline)] pub use models::bt815::BT815; use interface::Interface; /// An alias for [`BT815`](BT815), because both models belong to the same /// generation and thus share a common API. #[doc(inline)] pub type BT816 = BT815; use models::Model; /// The main type for this crate, providing a high-level API to an EVE chip /// in terms of a low-level, platform-specific interface. /// /// In order to interact with a real EVE chip you'll need to first select /// an implementation of [`Interface`](interface::Interface) which you'll /// access the chip through. This will typically be an adapter to the API /// hardware for your platform. You can pass that interface object, along /// with a selected model, to this type's constructor. /// /// After instantiating an `EVE` object, the first step would typically /// be to initialize it using its various initialization functions. /// /// Since there are no real interface implementations in this create, the /// following example just supposes there's already an interface in scope /// as the variable name `ei`: /// /// ```rust /// # evegfx::interface::fake::interface_example(|mut ei| { /// use evegfx::EVE; /// let eve = EVE::new(evegfx::BT815, ei); /// # }) /// ``` pub struct EVE<M: Model, I: Interface> { pub(crate) ll: low_level::LowLevel<M, I>, } impl<M: Model, I: Interface> EVE<M, I> { /// Construct a new `EVE` object for the given EVE model, communicating /// via the given interface. /// /// Models are represented by empty struct types in this crate, such /// as [`BT815`](BT815) for the BT815 and BT816 models. The different /// models all have a broadly-compatible API but later generations have /// additional functionality. #[allow(unused_variables)] pub fn new(m: M, ei: I) -> Self { Self::new_internal(ei) } // This is an internal version of `new` for situations where type inference // already implies a particular model type. pub(crate) fn new_internal(ei: I) -> Self { Self { ll: low_level::LowLevel::new(ei), } } /// Consumes the `EVE` object and returns its underlying interface. pub fn take_interface(self) -> I { self.ll.take_interface() } pub fn borrow_interface<'a>(&'a mut self) -> &'a mut I { self.ll.borrow_interface() } /// Consume the `EVE` object and returns an instance of `LowLevel` /// that uses the same interface. pub fn take_low_level(self) -> low_level::LowLevel<M, I> { self.ll } pub fn borrow_low_level<'a>(&'a mut self) -> &'a mut low_level::LowLevel<M, I> { &mut self.ll } /// Sends commands to the device to configure and then activate the system /// clock. /// /// If this function succeeds then the system clock will be activated and /// the device will have begun (but not necessarily completed) its boot /// process. You could use the `poll_for_boot` method as an easy way to /// wait for the device to become ready, albeit via busy-waiting. /// /// The typical next steps are to first call `configure_video_pins` in /// order to configure the physical characteristics of the Parallel RGB /// interface, and then to call `start_video` to configure the video mode /// and activate the pixel clock. pub fn start_system_clock( &mut self, source: config::ClockSource, video: &config::VideoTimings, ) -> Result<(), Error<I>> { config::activate_system_clock(self, source, video) } /// Busy-waits while polling the EVE ID for its ID register. Once it /// returns the expected value that indicates that the boot process /// is complete and this function will return. /// /// If the connected device isn't an EVE, or if the chip isn't connected /// correctly, or if it's failing boot in some other way then this /// function will poll forever. pub fn poll_for_boot(&mut self, poll_limit: u32) -> Result<bool, Error<I>> { config::poll_for_boot(self, poll_limit) } pub fn configure_video_pins( &mut self, mode: &config::RGBElectricalMode, ) -> Result<(), Error<I>> { config::configure_video_pins(self, mode) } /// Configures registers to achieve a particular graphics mode wLowLevel /// given timings. /// /// You should typically call `start_system_clock` first, using the /// same timing value, in order to activate the system clock. Calling /// `start_video` afterwards will then activate the graphics engine with /// a pixel clock derived from the system clock. /// /// If you call `start_video` with a different timings value than you most /// recently passed to `start_system_clock` then this is likely to /// produce an invalid video signal. /// /// If this function succeeds then the display will be active before it /// returns, assuming that the chip itself was already activated. pub fn start_video(&mut self, c: &config::VideoTimings) -> Result<(), Error<I>> { config::activate_pixel_clock(self, c) } pub fn new_display_list< F: FnOnce( &mut display_list::JustBuilder<low_level::LowLevel<M, I>>, ) -> Result<(), error::Error<I>>, >( &mut self, f: F, ) -> Result<(), error::Error<I>> { self.ll.dl_reset(); { let mut builder = display_list::just_builder(&mut self.ll); f(&mut builder)?; } let dlswap_ptr = M::reg_ptr(registers::Register::DLSWAP); self.ll.wr8(dlswap_ptr, 0b00000010) } /// Consumes the main EVE object and returns an interface to the /// coprocessor component of the chip, using the given waiter to pause /// when the command buffer becomes too full. /// /// The typical way to use an EVE device is to initialize it via direct /// register writes and then do all of the main application activities /// via the coprocessor, which exposes all of the system's capabilities /// either directly or indirectly. pub fn coprocessor<W: commands::waiter::Waiter<M, I>>( self, waiter: W, ) -> commands::Result<commands::Coprocessor<M, I, W>, M, I, W> { let ei = self.ll.take_interface(); commands::Coprocessor::new(ei, waiter) } /// A wrapper around `coprocessor` which automatically provides a /// busy-polling waiter. This can be a good choice particularly if your /// application typically generates coprocessor commands slow enough that /// the buffer will rarely fill, and thus busy waiting will not be /// typical. /// /// However, this will use more CPU and cause more SPI traffic than an /// interrupt-based waiter for applications that frequently need to wait /// for command processing, such as those which attempt to synchronize /// with the display refresh rate and thus could often end up waiting for /// the scanout to "catch up". pub fn coprocessor_polling( self, ) -> commands::Result< commands::Coprocessor<M, I, commands::waiter::PollingWaiter<M, I>>, M, I, commands::waiter::PollingWaiter<M, I>, > { let ei = self.ll.take_interface(); commands::Coprocessor::new_polling(ei) } }