Skip to main content

rmk_q6_he_ansi/
main.rs

1#![no_main]
2#![no_std]
3#![deny(warnings)]
4#![warn(clippy::all, clippy::pedantic, clippy::restriction, clippy::nursery)]
5#![feature(
6    const_convert,
7    const_trait_impl,
8    const_cmp,
9    const_index,
10    const_option_ops,
11    const_result_trait_fn,
12    optimize_attribute,
13    likely_unlikely
14)]
15#![expect(
16    clippy::blanket_clippy_restriction_lints,
17    clippy::future_not_send,
18    clippy::implicit_return,
19    clippy::separated_literal_suffix,
20    clippy::single_call_fn,
21    clippy::self_named_module_files,
22    clippy::pub_with_shorthand,
23    clippy::pub_use,
24    reason = "Implementation specific ignored lints"
25)]
26/// Backlight driver integration.
27mod backlight;
28mod eeprom;
29/// Flash storage wrapper types.
30mod flash_wrapper_async;
31/// Default keymap definitions.
32mod keymap;
33/// Matrix scanning components.
34mod matrix;
35/// Vial configuration constants.
36mod vial;
37
38use crate::{
39    backlight::{init::backlight_runner, led_processor::LedIndicatorProcessor},
40    eeprom::Ft24c64,
41    flash_wrapper_async::Flash16K,
42    keymap::{COL, ROW},
43    matrix::{
44        analog_matrix::{AdcPart, AnalogHallMatrix, HallCfg},
45        encoder_switch,
46        hc164_cols::Hc164Cols,
47        layer_toggle::{LayerToggle, MatrixPos},
48    },
49    vial::VIAL_SERIAL,
50};
51use embassy_executor::{Spawner, main};
52use embassy_stm32::{
53    Config,
54    adc::{Adc, AdcChannel as _, AnyAdcChannel, SampleTime},
55    bind_interrupts,
56    dma,
57    exti::{self, ExtiInput},
58    flash,
59    flash::Flash,
60    gpio::{Flex, Input, Level, Output, Pull, Speed},
61    i2c,
62    i2c::I2c,
63    init,
64    interrupt::typelevel,
65    pac,
66    peripherals::{self, ADC1},
67    rcc::{
68        AHBPrescaler,
69        APBPrescaler,
70        Hse,
71        HseMode,
72        Pll,
73        PllMul,
74        PllPDiv,
75        PllPreDiv,
76        PllQDiv,
77        PllSource,
78        Sysclk,
79        mux::Clk48sel,
80    },
81    spi::{self},
82    time::Hertz,
83    usb::{self, Driver},
84};
85use encoder_switch::EncoderSwitch;
86use pac::SYSCFG;
87use rmk::{
88    KeymapData,
89    config::{BehaviorConfig, DeviceConfig, PositionalConfig, RmkConfig, StorageConfig, VialConfig},
90    futures::future::join4,
91    initialize_keymap_and_storage,
92    input_device::{Runnable as _, rotary_encoder::RotaryEncoder},
93    keyboard::Keyboard,
94    run_all,
95    run_rmk,
96};
97use static_cell::ConstStaticCell;
98use vial::{VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID};
99
100bind_interrupts!(struct Irqs {
101    DMA2_STREAM0 => dma::InterruptHandler<peripherals::DMA2_CH0>;
102    DMA2_STREAM3 => dma::InterruptHandler<peripherals::DMA2_CH3>;
103    DMA2_STREAM2 => dma::InterruptHandler<peripherals::DMA2_CH2>;
104    DMA1_STREAM4 => dma::InterruptHandler<peripherals::DMA1_CH4>;
105    DMA1_STREAM2 => dma::InterruptHandler<peripherals::DMA1_CH2>;
106    EXTI3 => exti::InterruptHandler<typelevel::EXTI3>;
107    EXTI15_10 => exti::InterruptHandler<typelevel::EXTI15_10>;
108    FLASH => flash::InterruptHandler;
109    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;
110    I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;
111    I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;
112});
113
114/// Entry point for the firmware.
115#[main]
116async fn main(spawner: Spawner) {
117    // Tasks run inline via join4; spawner is unused.
118    let _: Spawner = spawner;
119    // Initialize peripherals
120    let mut peripheral = init({
121        let mut config = Config::default();
122        config.rcc.hse = Some(Hse { freq: Hertz(16_000_000), mode: HseMode::Oscillator });
123        config.rcc.hsi = false;
124        config.rcc.pll_src = PllSource::Hse;
125        config.rcc.pll = Some(Pll {
126            prediv: PllPreDiv::Div8,   // 16/8 = 2 MHz
127            mul: PllMul::Mul168,       // 2*168 = 336 MHz (VCO)
128            divp: Some(PllPDiv::Div4), // 336/4 = 84 MHz (SYSCLK)
129            divq: Some(PllQDiv::Div7), // 336/7 = 48 MHz
130            divr: None,
131        });
132        config.rcc.ahb_pre = AHBPrescaler::Div1; // 84 MHz
133        config.rcc.apb1_pre = APBPrescaler::Div2; // 42 MHz
134        config.rcc.apb2_pre = APBPrescaler::Div1; // 84 MHz
135        config.rcc.sys = Sysclk::Pll1P;
136        config.rcc.mux.clk48sel = Clk48sel::Pll1Q;
137        config
138    });
139
140    // Usb config
141    static EP_OUT_BUFFER: ConstStaticCell<[u8; 1024]> = ConstStaticCell::new([0; 1024]);
142    let usb_config = {
143        let mut usb_config = usb::Config::default();
144        usb_config.vbus_detection = false;
145        usb_config
146    };
147    let driver = Driver::new_fs(
148        peripheral.USB_OTG_FS,
149        Irqs,
150        peripheral.PA12,
151        peripheral.PA11,
152        &mut EP_OUT_BUFFER.take()[..],
153        usb_config,
154    );
155
156    // Use internal flash to emulate eeprom
157    let storage_config = StorageConfig {
158        // Start at sector 1, 0x4000 from the start of the FLASH region
159        start_addr: 0x4000,
160        num_sectors: 2,
161        ..Default::default()
162    };
163    let flash = Flash16K(Flash::new(peripheral.FLASH, Irqs));
164
165    // Keyboard config
166    let rmk_config = RmkConfig {
167        vial_config: VialConfig::new(VIAL_KEYBOARD_ID, VIAL_KEYBOARD_DEF, &[(0, 0), (4, 20)]),
168        device_config: DeviceConfig {
169            manufacturer: "Keychron",
170            product_name: "Q6 HE",
171            vid: 0x3434,
172            pid: 0x0B60,
173            serial_number: VIAL_SERIAL,
174        },
175        ..Default::default()
176    };
177    // PC13 must be driven high for the duration of main to keep the analog
178    // matrix powered. Dropping this binding would return the pin to its reset
179    // state and cut power to the hall-effect sensors.
180    let _analog_matrix_power = Output::new(peripheral.PC13, Level::High, Speed::Low);
181    // PC5 must be held low via pull-down so it does not float and trigger a
182    // spurious wakeup from the MCU's wakeup-pin logic.
183    let _analog_matrix_wakeup = Input::new(peripheral.PC5, Pull::Down);
184
185    // HC164 columns
186    let ds = Output::new(peripheral.PB3, Level::Low, Speed::VeryHigh);
187    let cp = Output::new(peripheral.PB5, Level::Low, Speed::VeryHigh);
188    let mr = Output::new(peripheral.PD2, Level::Low, Speed::VeryHigh);
189    let cols = Hc164Cols::new(ds, cp, mr, HallCfg::default().shifter_delay_cycles);
190
191    // ADC matrix (rows are ADC pins)
192    let adc: Adc<'_, ADC1> = Adc::new(peripheral.ADC1);
193    // Apply STM32 AN4073 Option 2 workaround to reduce ADC noise coupling from USB
194    // activity on STM32F401.
195    SYSCFG.pmc().modify(|w| w.set_adc1dc2(true));
196    let row_channels: [AnyAdcChannel<'_, ADC1>; ROW] = [
197        peripheral.PC0.degrade_adc(),
198        peripheral.PC1.degrade_adc(),
199        peripheral.PC2.degrade_adc(),
200        peripheral.PC3.degrade_adc(),
201        peripheral.PA0.degrade_adc(),
202        peripheral.PA1.degrade_adc(),
203    ];
204
205    let i2c_config = {
206        let mut cfg = i2c::Config::default();
207        cfg.frequency = Hertz(850_000);
208        cfg
209    };
210    let i2c3 = I2c::new(
211        peripheral.I2C3,
212        peripheral.PA8,      // SCL (PA8 = I2C3_SCL)
213        peripheral.PC9,      // SDA (PC9 = I2C3_SDA)
214        peripheral.DMA1_CH4, // TX DMA
215        peripheral.DMA1_CH2, // RX DMA
216        Irqs,
217        i2c_config,
218    );
219    let eeprom_wp = Flex::new(peripheral.PB10);
220    let eeprom = Ft24c64::new(i2c3, eeprom_wp);
221    let adc_part = AdcPart::new(adc, row_channels, peripheral.DMA2_CH0, SampleTime::Cycles56);
222    let mut matrix = AnalogHallMatrix::<_, _, _, _, ROW, COL>::new(adc_part, Irqs, cols, HallCfg::default(), eeprom);
223    // Rotary encoder
224    let pin_a = ExtiInput::new(peripheral.PB14, peripheral.EXTI14, Pull::None, Irqs);
225    let pin_b = ExtiInput::new(peripheral.PB15, peripheral.EXTI15, Pull::None, Irqs);
226    let mut encoder = RotaryEncoder::with_resolution(pin_a, pin_b, 4, true, 0);
227    let enc_sw_pin = ExtiInput::new(peripheral.PA3, peripheral.EXTI3, Pull::Up, Irqs);
228    let mut enc_switch = EncoderSwitch::new(enc_sw_pin, 0, 13);
229
230    // Layer Toggle Switch
231    let layer_toggle_pin = ExtiInput::new(peripheral.PB12, peripheral.EXTI12, Pull::Up, Irqs);
232    let mut layer_toggle = LayerToggle::new_with_default_debounce(
233        layer_toggle_pin,
234        MatrixPos { row: 5, col: 7 }, // HIGH taps this
235        MatrixPos { row: 5, col: 8 }, // LOW taps this
236    );
237
238    // Initialize the storage and keymap
239    let mut keymap_data = KeymapData::new_with_encoder(keymap::get_default_keymap(), keymap::get_default_encoder_map());
240    let mut behavior_config = BehaviorConfig::default();
241    let key_config = PositionalConfig::default();
242    let (keymap, mut storage) =
243        initialize_keymap_and_storage(&mut keymap_data, flash, &storage_config, &mut behavior_config, &key_config)
244            .await;
245
246    // Initialize the keyboard
247    let mut keyboard = Keyboard::new(&keymap);
248
249    // LED backlight
250    let spi_config = {
251        let mut spi_config = spi::Config::default();
252        spi_config.frequency = Hertz(3_500_000);
253        spi_config.mode = spi::MODE_0;
254        spi_config
255    };
256    let spi_backlight = spi::Spi::new(
257        peripheral.SPI1,     // SPI1
258        peripheral.PA5,      // SCK
259        peripheral.PA7,      // MOSI
260        peripheral.PA6,      // MISO
261        peripheral.DMA2_CH3, // TX DMA
262        peripheral.DMA2_CH2, // RX DMA
263        Irqs,
264        spi_config,
265    );
266    let cs0 = Output::new(peripheral.PB8, Level::High, Speed::VeryHigh);
267    let cs1 = Output::new(peripheral.PB9, Level::High, Speed::VeryHigh);
268    let sdb = Output::new(peripheral.PB7, Level::Low, Speed::VeryHigh);
269    let mut led_indicator = LedIndicatorProcessor::new();
270
271    // Start
272    join4(
273        run_all!(matrix, encoder, enc_switch, layer_toggle, led_indicator),
274        keyboard.run(),
275        run_rmk(&keymap, driver, &mut storage, rmk_config),
276        backlight_runner(spi_backlight, cs0, cs1, sdb),
277    )
278    .await;
279}