{"record":{"id":"e608b26164368b2b","repo":"embassy-rs/embassy","slug":"the-lcd-driver-needs-the-rtc-lcd-clock-to-be-runni","errorCode":null,"errorMessage":"The LCD driver needs the RTC/LCD clock to be running","messagePattern":"The LCD driver needs the RTC/LCD clock to be running","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-stm32/src/lcd.rs","lineNumber":201,"sourceCode":"        );\n\n        // Set the pins\n        for pin in pins.iter_mut() {\n            pin.pin.pin.set_as_af(\n                pin.af_num,\n                AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh),\n            );\n        }\n\n        // Initialize the display ram to 0\n        for i in 0..8 {\n            T::regs().ram_com(i).low().write_value(0);\n            T::regs().ram_com(i).high().write_value(0);\n        }\n\n        // Calculate the clock dividers\n        let Some(lcd_clk) = (unsafe { rcc::get_freqs().rtc.to_hertz() }) else {\n            panic!(\"The LCD driver needs the RTC/LCD clock to be running\");\n        };\n        let duty_divider = match config.duty {\n            Duty::Static => 1,\n            Duty::Half => 2,\n            Duty::Third => 3,\n            Duty::Quarter => 4,\n            Duty::Eigth => 8,\n        };\n        let target_clock = config.target_fps.0 * duty_divider;\n        let target_division = lcd_clk.0 / target_clock;\n\n        let mut ps = 0;\n        let mut div = 0;\n        let mut best_fps_match = u32::MAX;\n\n        for trial_div in 0..0xF {\n            let trial_ps = (target_division / (trial_div + 16))\n                .next_power_of_two()","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-stm32/src/lcd.rs#L183-L219","documentation":"The LCD controller is clocked from the RTC/LCD clock domain. During driver construction the code reads the currently configured RTC frequency via rcc::get_freqs(); if it is None the driver cannot compute the prescaler/divider, so it panics instead of producing garbage timing.","triggerScenarios":"Instantiating Lcd::new() before the RTC peripheral (and its LCD clock source, e.g. LSE/LSI) has been enabled and configured in the RCC setup.","commonSituations":"Forgetting to call the rtc enable/config step in the board init; RCC config that selects no RTC clock source; running LCD init code before clock configuration on boards where LSE startup is deferred.","solutions":["Enable and configure the RTC/LCD clock source (LSE, LSI or HSE/32) in the RCC config before constructing the LCD driver.","Verify rcc::get_freqs().rtc is Some(hz) after your clock setup, then create the Lcd driver.","If LSE is used, account for its startup delay before initializing the LCD."],"exampleFix":"// before\nlet mut lcd = Lcd::new(p.LCD, p.RTC, config); // rtc clock not configured\n// after\nlet rtc = Rtc::new(p.RTC, RtcConfig::default()); // starts LSE/RTC clock\nlet mut lcd = Lcd::new(p.LCD, p.RTC, config);","handlingStrategy":"validation","validationCode":"// assert RTC clock is configured before LCD init\nlet freqs = embassy_stm32::rcc::get_freqs();\nassert!(freqs.rtc.to_hertz().is_some(), \"RTC/LCD clock must run before Lcd::new\");","typeGuard":null,"tryCatchPattern":"// Not catchable in idiomatic embedded Rust; ensure init order:\nlet rtc = Rtc::new(p.RTC, RtcConfig::default());\nlet lcd = Lcd::new(p.LCD, p.RTC, config);","preventionTips":["Always bring up the RTC/LCD clock source before peripheral drivers that consume it.","Add a board-init checklist covering clock domains (RCC -> RTC -> LCD).","Debug-print rcc::get_freqs().rtc during bring-up to confirm it is Some."],"tags":["rust","embedded","stm32","lcd","rcc","clock-config"],"backgroundTag":"missing-required-config","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}