summaryrefslogtreecommitdiffstats
path: root/firmware/src/triac.rs
blob: 54acf1f216fde9c6564d7dd20167affb704be479 (plain)
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
use crate::{
    fixpt::{fixpt, Fixpt},
    mains::{Phase, PhaseUpdate},
    mutex::{MainCtx, MutexCell},
    system::SysPeriph,
    timer::{timer_get_large, LargeTimestamp, RelLargeTimestamp, Timestamp, TIMER_TICK_US},
};

fn time_plus_ms(t: LargeTimestamp, ms: Fixpt) -> LargeTimestamp {
    const TICK_US: i16 = TIMER_TICK_US as i16;
    let ticks = (ms * fixpt!(1000 / TICK_US)).to_int();
    t + RelLargeTimestamp::from_ticks(ticks)
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum TriacState {
    Idle,
    Triggering,
    Triggered,
}

pub struct Triac {
    phi_offs_ms: MutexCell<Fixpt>,
    state: MutexCell<TriacState>,
    trig_time: MutexCell<Timestamp>,
}

impl Triac {
    pub const fn new() -> Self {
        Self {
            phi_offs_ms: MutexCell::new(Fixpt::from_int(20)),
            state: MutexCell::new(TriacState::Idle),
            trig_time: MutexCell::new(Timestamp::new()),
        }
    }

    pub fn set_phi_offs_ms(&self, m: &MainCtx<'_>, ms: Fixpt) {
        self.phi_offs_ms.set(m, ms);
    }

    fn set_trigger(&self, _m: &MainCtx<'_>, sp: &SysPeriph, trigger: bool) {
        let trigger = !trigger; // negative logic at triac gate.
        sp.PORTB.portb.modify(|_, w| w.pb3().bit(trigger));
    }

    pub fn run(&self, m: &MainCtx<'_>, sp: &SysPeriph, phase_update: PhaseUpdate, phase: &Phase) {
        let now = timer_get_large(m);
        let phi_offs_ms = self.phi_offs_ms.get(m);

        let phaseref = match phase {
            Phase::Notsync => {
                return;
            }
            Phase::PosHalfwave(phaseref) => phaseref,
            Phase::NegHalfwave(phaseref) => phaseref,
        };

        match self.state.get(m) {
            TriacState::Idle => {
                let must_trigger = now >= time_plus_ms(*phaseref, phi_offs_ms);
                if must_trigger {
                    self.set_trigger(m, sp, true);
                    self.state.set(m, TriacState::Triggering);
                    self.trig_time.set(m, now.into());
                } else {
                    self.set_trigger(m, sp, false);
                }
            }
            TriacState::Triggering => {
                if self.trig_time.get(m) >= now.into() {
                    self.set_trigger(m, sp, false);
                    self.state.set(m, TriacState::Triggered);
                }
                if phase_update == PhaseUpdate::Changed {
                    self.state.set(m, TriacState::Idle);
                }
            }
            TriacState::Triggered => {
                if phase_update == PhaseUpdate::Changed {
                    self.state.set(m, TriacState::Idle);
                }
            }
        }
    }
}

// vim: ts=4 sw=4 expandtab
bues.ch cgit interface