Skip to main content

rmk_q1_pro_iso/matrix/
hc595_cols.rs

1use cortex_m::asm::delay;
2use embassy_stm32::gpio::Output;
3
4/// Driver for a 74HC595 shift register used to drive keyboard matrix columns.
5///
6/// Controls three GPIO output pins to clock serial data into the shift register
7/// and latch it to the parallel outputs, selecting the active matrix column.
8pub struct Hc595Cols<'peripherals> {
9    /// Clock pin (SHCP / SRCLK), used to shift data on each rising edge.
10    clk: Output<'peripherals>,
11    /// Serial data pin (DS / SER), used to shift bits into the register.
12    data: Output<'peripherals>,
13    /// Latch pin (STCP / RCLK), used to transfer the shift register to the
14    /// output register.
15    latch: Output<'peripherals>,
16    /// CPU cycles between shift register pin transitions.
17    shifter_delay_cycles: u32,
18}
19
20impl<'peripherals> Hc595Cols<'peripherals> {
21    /// Creates a new [`Hc595Cols`] instance from the given GPIO output pins.
22    pub const fn new(
23        data: Output<'peripherals>,
24        clk: Output<'peripherals>,
25        latch: Output<'peripherals>,
26        shifter_delay_cycles: u32,
27    ) -> Self {
28        Self { clk, data, latch, shifter_delay_cycles }
29    }
30
31    /// Pulses the clock pin high then low to advance the shift register by one
32    /// bit.
33    #[inline]
34    fn pulse(&mut self) {
35        self.clk.set_high();
36        delay(self.shifter_delay_cycles);
37        self.clk.set_low();
38        delay(self.shifter_delay_cycles);
39    }
40
41    /// Selects a single column by driving its output low (active-low logic).
42    ///
43    /// All other columns are driven high. The column index is mapped MSB-first,
44    /// so `col = 0` drives bit 15 low.
45    #[optimize(speed)]
46    pub fn select_col(&mut self, col: usize) {
47        let col_u32 = u32::try_from(col).unwrap_or(u32::MAX);
48        let shift = 15_u32.saturating_sub(col_u32);
49        let mask = 1_u16.checked_shl(shift).unwrap_or(0_u16);
50        self.write_u16_lsb_first(!mask);
51    }
52
53    /// Deselects all columns by driving all outputs high (active-low logic).
54    pub fn unselect_all(&mut self) { self.write_u16_lsb_first(0xFFFF); }
55
56    /// Shifts out 16 bits LSB-first into the shift register, then latches the
57    /// outputs.
58    ///
59    /// Pulls the latch pin low before shifting and high then low again after
60    /// all bits have been clocked in, transferring the shift register
61    /// contents to the parallel outputs.
62    #[optimize(speed)]
63    pub fn write_u16_lsb_first(&mut self, mut value: u16) {
64        self.latch.set_low();
65        for _ in 0..16_u8 {
66            match value & 1 {
67                0 => self.data.set_low(),
68                _ => self.data.set_high(),
69            }
70            self.pulse();
71            delay(self.shifter_delay_cycles);
72            value >>= 1_u16;
73        }
74        delay(self.shifter_delay_cycles);
75        self.latch.set_high();
76        delay(self.shifter_delay_cycles);
77        self.latch.set_low();
78    }
79}