stm32f446re/
iol_master_actions.rs

1use defmt::*;
2use embassy_time::Timer;
3use embassy_time::Instant;
4use embassy_stm32::i2c::I2c;
5use embassy_stm32::mode::Async;
6
7use iol::master;
8use l6360::{self, L6360, HardwareAccess};
9
10use crate::l6360_hw::{self, L6360_HW};
11use super::IOL_TRANSCEIVER;
12
13#[derive(Copy, Clone)]
14pub struct MasterActions;
15
16impl master::Actions for MasterActions {
17    async fn wait_us(&self, duration: u64) {
18        Timer::after_micros(duration).await;
19    }
20
21    async fn wait_ms(&self, duration: u64) {
22        Timer::after_millis(duration).await;
23    }
24
25    async fn get_cq(&self) -> l6360::PinState {
26        if let Some(l6360) = IOL_TRANSCEIVER.lock().await.as_mut() {
27            l6360.hw.en_cq(l6360::PinState::Low);
28            Timer::after_nanos(500).await; // Typical value is 225ns.
29
30            // The L6360 inverts the pin value
31            match l6360.hw.out_cq() {
32                l6360::PinState::Low => l6360::PinState::High,
33                l6360::PinState::High => l6360::PinState::Low,
34            }
35        }
36        else {
37            crate::panic!("Lock to L6360 failed");
38        }
39    }
40
41    async fn wake_up_pulse(&self, direction: master::WakeUpPulseDirection) {
42        if let Some(l6360) = IOL_TRANSCEIVER.lock().await.as_mut() {
43            fn wait_blocking() {
44                // Busy waiting as we have to be very fast. This could be done nicer.
45                let mut count = 0;
46                while count < 35 {
47                    count += 1;
48                }
49            }
50
51            // Note: The l6360 inverts the state of C/Q.
52            // Execution:
53            // - set cq value
54            // - enable cq stage
55            // - wait
56            // - reset cq value
57            match direction {
58                master::WakeUpPulseDirection::Up => {
59                    l6360.hw.in_cq(l6360::PinState::Low);
60                    l6360.hw.en_cq(l6360::PinState::High);
61                    wait_blocking();
62                    l6360.hw.in_cq(l6360::PinState::High);
63                }
64                master::WakeUpPulseDirection::Down => {
65                    l6360.hw.in_cq(l6360::PinState::High);
66                    l6360.hw.en_cq(l6360::PinState::High);
67                    wait_blocking();
68                    l6360.hw.in_cq(l6360::PinState::Low);
69                }
70            }
71        }
72        else {
73            crate::panic!("Lock to L6360 failed");
74        }
75    }
76
77    async fn port_power_on(&self) {
78        info!("port power on ...");
79        if let Some(l6360) = IOL_TRANSCEIVER.lock().await.as_mut() {
80            l6360.hw.enl_plus(l6360::PinState::High);
81        }
82        info!("done");
83    }
84
85    async fn port_power_off(&self) {
86        info!("port power off ...");
87        if let Some(l6360) = IOL_TRANSCEIVER.lock().await.as_mut() {
88            l6360.hw.enl_plus(l6360::PinState::Low);
89        }
90        info!("done");
91    }
92
93    async fn await_event_with_timeout_ms<F, T>(&self, duration: u64, future: F) -> Option<T>
94    where
95        F: core::future::Future<Output = T> + Send
96    {
97        embassy_time::with_timeout(embassy_time::Duration::from_millis(duration), future).await.ok()
98    }
99
100    async fn await_ready_pulse_with_timeout_ms(&self, duration: u64) -> master::ReadyPulseResult {
101        if let Some(l6360) = IOL_TRANSCEIVER.lock().await.as_mut() {
102            let result = embassy_time::with_timeout(
103                embassy_time::Duration::from_millis(duration),
104                measure_ready_pulse(l6360),
105            ).await;
106
107            match result {
108                Ok(_) => master::ReadyPulseResult::ReadyPulseOk,
109                Err(_) => master::ReadyPulseResult::TimeToReadyElapsed,
110            }
111        }
112        else {
113            crate::panic!("Lock to L6360 failed"); //TODO: why is crate:: necessary here?
114        }
115    }
116
117    async fn exchange_data(&self, data: &[u8], answer: &mut [u8]) {
118        if let Some(l6360) = IOL_TRANSCEIVER.lock().await.as_mut() {
119            if l6360.hw.get_mode() != l6360_hw::Mode::Uart {
120                l6360.hw.switch_to_uart();
121            }
122            l6360.hw.exchange(data, answer).await;
123        }
124        else {
125            crate::panic!("Lock to L6360 failed"); //TODO: why is crate:: necessary here?
126        }
127    }
128}
129
130
131async fn measure_ready_pulse(l6360: &mut L6360<I2c<'static, Async>, L6360_HW<'static>>) {
132    // // Note:
133    // // This implementation of recognizing the Ready-Pulse is not maximaly accurate.
134    // // On high load the pulse would not be measured accurately.
135    // // It would be better to measure the pulse length directly with a timer.
136    // // Currently PA10 is used which does not offer this possibility.
137    // // The STEVAL-IOM001V1 would with minor changes also allow to use PA1 where it should be possible.
138
139    // // Note: This solution is too slow due to context switches. A pulse of 750us is measured as around 915us.
140    // // Incoming signals are inverted by the L6360
141    // info!("waiting for pulse...");
142    // pin.wait_for_falling_edge().await;
143    // let start = Instant::now();
144    // // Note: info! messages here would take too much time.
145    // pin.wait_for_rising_edge().await;
146    // let end = Instant::now();
147    // info!("pulse received");
148
149    // Note: Busy-Waiting for more accuracy. TODO: Better solution.
150    info!("waiting for ready-pulse...");
151    while l6360.hw.out_cq() == l6360::PinState::High {}
152    let start = Instant::now();
153    while l6360.hw.out_cq() == l6360::PinState::Low {}
154    let end = Instant::now();
155    info!("ready-pulse received");
156
157    let high_time_us = (end - start).as_micros();
158    info!("Pin was high for {} us", high_time_us);
159}