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}