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
use crate::{ payload::{EsbHeader, PayloadR, PayloadW}, peripherals::{Interrupt, NVIC}, Error, }; use bbqueue::{ framed::{FrameConsumer, FrameProducer}, ArrayLength, Error as BbqError, }; use core::default::Default; /// This is the primary Application-side interface. /// /// It is intended to be used outside of the `RADIO` interrupt, /// and allows for sending or receiving frames from the ESB Radio /// hardware. pub struct EsbApp<OutgoingLen, IncomingLen> where OutgoingLen: ArrayLength<u8>, IncomingLen: ArrayLength<u8>, { // TODO(AJM): Make a constructor for this so we don't // need to make these fields pub(crate) pub(crate) prod_to_radio: FrameProducer<'static, OutgoingLen>, pub(crate) cons_from_radio: FrameConsumer<'static, IncomingLen>, pub(crate) maximum_payload: u8, } impl<OutgoingLen, IncomingLen> EsbApp<OutgoingLen, IncomingLen> where OutgoingLen: ArrayLength<u8>, IncomingLen: ArrayLength<u8>, { /// Obtain a grant for an outgoing packet to be sent over the Radio /// /// When space is available, this function will return a [`PayloadW`], /// which can be written into for data to be sent over the radio. If /// the given parameters are incorrect, or if no space is available, /// or if a grant is already in progress, an error will be returned. /// /// ## Notes /// /// Once a grant has been created, the maximum size of the grant can not /// be increased, only shrunk. If a larger grant is needed, you must /// `drop` the old grant, and create a new one. /// /// Only one grant may be active at a time. pub fn grant_packet(&mut self, header: EsbHeader) -> Result<PayloadW<OutgoingLen>, Error> { // Check we have not exceeded the configured packet max if header.length > self.maximum_payload { return Err(Error::MaximumPacketExceeded); } let grant_result = self .prod_to_radio .grant(header.payload_len() + EsbHeader::header_size()); let grant = grant_result.map_err(|err| match err { BbqError::GrantInProgress => Error::GrantInProgress, BbqError::InsufficientSize => Error::OutgoingQueueFull, _ => Error::InternalError, })?; Ok(PayloadW::new_from_app(grant, header)) } /// Starts the radio sending all packets in the queue. /// /// The radio will send until the queue has been drained. This method must be called again if /// the queue is completely drained before the user commits new packets. #[inline] pub fn start_tx(&mut self) { // TODO(AJM): Is this appropriate for PRX? Or is this a PTX-only // sort of interface? // Do we need to do anything other than pend the interrupt? NVIC::pend(Interrupt::RADIO) } /// Is there a received message that is ready to be read? /// /// Returns `true` if a call to `read_packet` would return `Some`. pub fn msg_ready(&mut self) -> bool { // Dropping the grant does not release it. self.cons_from_radio.read().is_some() } /// Attempt to read a packet that has been received via the radio. /// /// Returns `Some(PayloadR)` if a packet is ready to be read, /// otherwise `None`. pub fn read_packet(&mut self) -> Option<PayloadR<IncomingLen>> { self.cons_from_radio.read().map(PayloadR::new) } /// Gets the maximum payload size (in bytes) that the driver was configured to use. #[inline] pub fn maximum_payload_size(&self) -> usize { self.maximum_payload.into() } } /// Addresses used for communication. /// /// ESB uses up to eight pipes to address communication, each pipe has an unique address which is /// composed by the base address and the prefix. Pipe 0 has an unique base and prefix, while the /// other pipes share a base address but have different prefixes. /// /// Default values: /// /// | Field | Default Value | /// | :--- | :--- | /// | base0 | [0xE7, 0xE7, 0xE7, 0xE7] | /// | base1 | [0xC2, 0xC2, 0xC2, 0xC2] | /// | prefixes0 | [0xE7, 0xC2, 0xC3, 0xC4] | /// | prefixes1 | [0xC5, 0xC6, 0xC7, 0xC8] | /// | rf_channel | 2 | /// pub struct Addresses { /// Base address for pipe 0 pub(crate) base0: [u8; 4], /// Base address for pipe 1-7 pub(crate) base1: [u8; 4], /// Prefixes for pipes 0-3, in order pub(crate) prefixes0: [u8; 4], /// `prefixes1` - Prefixes for pipes 4-7, in order pub(crate) prefixes1: [u8; 4], /// Channel to be used by the radio hardware (must be between 0 and 100) pub(crate) rf_channel: u8, } impl Addresses { /// Creates a new instance of `Addresses` /// /// * `base0` - Base address for pipe 0. /// * `base1` - Base address for pipe 1-7. /// * `prefixes0` - Prefixes for pipes 0-3, in order. /// * `prefixes1` - Prefixes for pipes 4-7, in order. /// * `rf_channel` - Channel to be used by the radio hardware (must be between 0 and 100). /// /// # Panics /// /// This function will panic if `rf_channel` is bigger than 100. pub fn new( base0: [u8; 4], base1: [u8; 4], prefixes0: [u8; 4], prefixes1: [u8; 4], rf_channel: u8, ) -> Result<Self, Error> { // TODO(AJM): Move to a builder pattern here? if rf_channel > 100 { return Err(Error::InvalidParameters); } Ok(Self { base0, base1, prefixes0, prefixes1, rf_channel, }) } } impl Default for Addresses { fn default() -> Self { Self { base0: [0xE7, 0xE7, 0xE7, 0xE7], base1: [0xC2, 0xC2, 0xC2, 0xC2], prefixes0: [0xE7, 0xC2, 0xC3, 0xC4], prefixes1: [0xC5, 0xC6, 0xC7, 0xC8], rf_channel: 2, } } }