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 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
//! HDMI CEC driver for STM32 microcontrollers
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg), feature(doc_auto_cfg))]
mod reg;
pub use reg::{Cfgr, Cr, Regs};
/// Interrupt flags.
///
/// All interrupts are cleared by writing `1` to the bit field.
pub mod irq {
/// TX-missing acknowledge error interrupt
///
/// In transmission mode, TXACKE is set by hardware to inform application
/// that no acknowledge was received.
/// In case of broadcast transmission, TXACKE informs application that a
/// negative acknowledge was received.
/// TXACKE aborts message transmission and clears TXSOM and TXEOM controls.
pub const TXACK: u32 = 1 << 12;
/// TX-error
///
/// In transmission mode, TXERR is set by hardware if the CEC initiator
/// detects low impedance on the CEC line while it is released.
/// TXERR aborts message transmission and clears TXSOM and TXEOM controls.
pub const TXERR: u32 = 1 << 11;
/// TX-buffer underrun
///
/// In transmission mode, TXUDR is set by hardware if application was not in
/// time to load TXDR before of next byte transmission.
/// TXUDR aborts message transmission and clears TXSOM and TXEOM control
/// bits.
pub const TXUDR: u32 = 1 << 10;
/// End of transmission
///
/// TXEND is set by hardware to inform application that the last byte of the
/// CEC message has been successfully transmitted.
/// TXEND clears the TXSOM and TXEOM control bits.
pub const TXEND: u32 = 1 << 9;
/// TX-byte request
///
/// TXBR is set by hardware to inform application that the next transmission
/// data has to be written to TXDR.
/// TXBR is set when the 4th bit of currently transmitted byte is sent.
/// Application must write the next byte to TXDR within six nominal data-bit
/// periods before transmission underrun error occurs (TXUDR).
pub const TXBR: u32 = 1 << 8;
/// Arbitration lost
///
/// ARBLST is set by hardware to inform application that CEC device is
/// switching to reception due to arbitration lost event following the TXSOM
/// command.
/// ARBLST can be due either to a contending CEC device starting earlier or
/// starting at the same time but with higher HEADER priority.
/// After ARBLST assertion TXSOM bit keeps pending for next transmission
/// attempt.
pub const ARBLST: u32 = 1 << 7;
/// RX-missing acknowledge.
///
/// In receive mode, RXACKE is set by hardware to inform application that no
/// acknowledge was seen on the CEC line.
/// RXACKE applies only for broadcast messages and in listen mode also for
/// not directly addressed messages (destination address not enabled in OAR).
/// RXACKE aborts message reception.
pub const RXACK: u32 = 1 << 6;
/// RX-long bit period error
///
/// LBPE is set by hardware in case a data-bit waveform is detected with
/// long bit period error.
/// LBPE is set at the end of the maximum bit-extension tolerance allowed by
/// RXTOL, in case falling edge is still longing.
/// LBPE always stops reception of the CEC message.
/// LBPE generates an error-bit on the CEC line if LBPEGEN = 1.
/// In case of broadcast, error-bit is generated even in case of LBPEGEN = 0.
pub const LBPE: u32 = 1 << 5;
/// RX-short bit period error
///
/// SBPE is set by hardware in case a data-bit waveform is detected with
/// short bit period error.
/// SBPE is set at the time the anticipated falling edge occurs.
/// SBPE generates an error-bit on the CEC line.
pub const SBPE: u32 = 1 << 4;
/// RX-bit rising error
///
/// BRE is set by hardware in case a data-bit waveform is detected with bit
/// rising error.
/// BRE is set either at the time the misplaced rising edge occurs, or at
/// the end of the maximum BRE tolerance allowed by RXTOL, in case rising
/// edge is still longing.
/// BRE stops message reception if BRESTP = 1.
/// BRE generates an error-bit on the CEC line if BREGEN = 1.
pub const BRE: u32 = 1 << 3;
/// RX-overrun
///
/// RXOVR is set by hardware if RXBR is not yet cleared at the time a new
/// byte is received on the CEC line and stored into RXD.
/// RXOVR assertion stops message reception so that no acknowledge is sent.
/// In case of broadcast, a negative acknowledge is sent.
pub const RXOVR: u32 = 1 << 2;
/// End of reception
///
/// RXEND is set by hardware to inform application that the last byte of a
/// CEC message is received from the CEC line and stored into the RXD buffer.
/// RXEND is set at the same time of RXBR.
pub const RXEND: u32 = 1 << 1;
/// RX-byte received
///
/// The RXBR bit is set by hardware to inform application that a new byte
/// has been received from the CEC line and stored into the RXD buffer.
pub const RXBR: u32 = 1;
/// Bitmask of all interrupts.
pub const ALL: u32 = TXACK
| TXERR
| TXUDR
| TXEND
| TXBR
| ARBLST
| RXACK
| LBPE
| SBPE
| BRE
| RXOVR
| RXEND
| RXBR;
}
/// Physical Address
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct PhysAddr {
addr: u32,
}
#[cfg(feature = "defmt")]
impl defmt::Format for PhysAddr {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(
fmt,
"{:X}.{:X}.{:X}.{:X}",
(self.addr >> 24) as u8,
(self.addr >> 16) as u8,
(self.addr >> 8) as u8,
self.addr as u8,
)
}
}
impl core::fmt::Display for PhysAddr {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
"{:X}.{:X}.{:X}.{:X}",
(self.addr >> 24) as u8,
(self.addr >> 16) as u8,
(self.addr >> 8) as u8,
self.addr as u8,
)
}
}
/// Logical Address
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum LogiAddr {
/// Television
Tv = 0x0,
/// Recording device 1
RecDev1 = 0x1,
/// Recording device 2
RecDev2 = 0x2,
/// Tuner 1
Tuner1 = 0x3,
/// Playback device 1
PlaybackDev = 0x4,
// Audio system
AudioSys = 0x5,
/// Tuner 2
Tuner2 = 0x6,
/// Tuner 3
Tuner3 = 0x7,
/// Playback Device 2
PlaybackDev2 = 0x8,
/// Recording Device 3
RecDev3 = 0x9,
/// Tuner 4
Tuner4 = 0xA,
/// Playback Device 3
PlaybackDev3 = 0xB,
/// Reserved 1
Rsvd1 = 0xC,
/// Reserved 2
Rsvd2 = 0xD,
/// Free use
FreeUse = 0xE,
/// Unregistered (as source address) or Broadcast (as destination address).
Broadcast = 0xF,
}
impl From<LogiAddr> for u8 {
#[inline]
fn from(addr: LogiAddr) -> Self {
addr as u8
}
}
impl TryFrom<u8> for LogiAddr {
type Error = u8;
fn try_from(addr: u8) -> Result<Self, Self::Error> {
match addr {
x if x == Self::Tv as u8 => Ok(Self::Tv),
x if x == Self::RecDev1 as u8 => Ok(Self::RecDev1),
x if x == Self::RecDev2 as u8 => Ok(Self::RecDev2),
x if x == Self::Tuner1 as u8 => Ok(Self::Tuner1),
x if x == Self::PlaybackDev as u8 => Ok(Self::PlaybackDev),
x if x == Self::AudioSys as u8 => Ok(Self::AudioSys),
x if x == Self::Tuner2 as u8 => Ok(Self::Tuner2),
x if x == Self::Tuner3 as u8 => Ok(Self::Tuner3),
x if x == Self::PlaybackDev2 as u8 => Ok(Self::PlaybackDev2),
x if x == Self::RecDev3 as u8 => Ok(Self::RecDev3),
x if x == Self::Tuner4 as u8 => Ok(Self::Tuner4),
x if x == Self::PlaybackDev3 as u8 => Ok(Self::PlaybackDev3),
x if x == Self::Rsvd1 as u8 => Ok(Self::Rsvd1),
x if x == Self::Rsvd2 as u8 => Ok(Self::Rsvd2),
x if x == Self::FreeUse as u8 => Ok(Self::FreeUse),
x if x == Self::Broadcast as u8 => Ok(Self::Broadcast),
_ => Err(addr),
}
}
}
/// HDMI-CEC driver.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Cec<const BASE: usize> {
regs: Regs<BASE>,
}
impl<const BASE: usize> Cec<BASE> {
/// Create a new HDMI CEC driver.
///
/// # Safety
///
/// 1. The HDMI CEC source clock must be enabled.
/// 2. The HDMI CEC pin must be configured.
/// 3. The HDMI CEC peripheral should be reset before.
/// 4. The HDMI CEC registers provided by the PAC should be dropped.
/// 5. The generic BASE parameter must be correct or bad things will happen.
///
/// # Panics
///
/// Panics if reading CFGR does not return the value written.
/// This occurs when the HDMI CEC peripheral clocks are not configured
/// correctly.
///
/// # Example
///
/// ```no_run
/// use stm32_cec::Cec;
///
/// // device specific setup occurs here
/// // ...
///
/// // valid address for the STM32H7
/// let mut cec: Cec<0x40006C00> = unsafe { Cec::<0x40006C00>::new() };
/// ```
#[inline]
pub unsafe fn new() -> Cec<BASE> {
let mut regs: Regs<BASE> = Regs { _priv: () };
regs.set_cr(Cr::DEFAULT);
const MY_CFGR: Cfgr = Cfgr::DEFAULT
.set_lstn(true)
.set_oar(0x8)
.set_lbpegen(true)
.set_bregen(true)
.set_brestp(true);
regs.set_cfgr(MY_CFGR);
assert_eq!(Regs::<BASE>::cfgr().oar(), 0x8);
regs.set_ier(irq::ALL);
regs.set_cr(Cr::EN);
Cec { regs }
}
fn poll_isr() -> u32 {
// TODO: timeout
loop {
let isr: u32 = Regs::<BASE>::isr();
if isr != 0 {
Regs::<BASE>::set_isr(isr);
return isr;
}
}
}
fn send_byte(&mut self, src: LogiAddr, dst: LogiAddr, data: u8) -> Result<(), u32> {
self.regs.set_txdr(u8::from(src) << 4 | u8::from(dst));
self.regs.set_cr(Cr::SOM);
let isr: u32 = Self::poll_isr();
if isr & irq::TXBR == irq::TXBR {
self.regs.set_txdr(data);
self.regs.set_cr(Cr::EOM);
let isr: u32 = Self::poll_isr();
if isr & irq::TXEND == irq::TXEND {
Ok(())
} else {
Err(isr)
}
} else {
Err(isr)
}
}
/// Power off devices.
///
/// # Example
///
/// Turn everything off.
///
/// ```no_run
/// use stm32_cec::{Cec, LogiAddr};
///
/// let mut cec = unsafe { stm32_cec::Cec::<0x40006C00>::new() };
/// cec.set_standby(LogiAddr::Broadcast, LogiAddr::Broadcast)?;
/// # Ok::<(), u32>(())
/// ```
pub fn set_standby(&mut self, src: LogiAddr, dst: LogiAddr) -> Result<(), u32> {
self.send_byte(src, dst, 0x36)
}
/// Power on the TV.
///
/// # Example
///
/// Turn everything on.
///
/// ```no_run
/// use stm32_cec::{Cec, LogiAddr};
///
/// let mut cec = unsafe { stm32_cec::Cec::<0x40006C00>::new() };
/// cec.set_image_view_on(LogiAddr::Broadcast, LogiAddr::Broadcast)?;
/// # Ok::<(), u32>(())
/// ```
pub fn set_image_view_on(&mut self, src: LogiAddr, dst: LogiAddr) -> Result<(), u32> {
self.send_byte(src, dst, 0x04)
}
}