aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/build.rs18
-rw-r--r--embassy-stm32/src/lcd.rs176
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--examples/stm32u0/.cargo/config.toml2
-rw-r--r--examples/stm32u0/Cargo.toml2
-rw-r--r--examples/stm32u0/src/bin/lcd.rs76
7 files changed, 275 insertions, 5 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index d06d8af03..79d79c5a3 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -72,7 +72,7 @@ rand_core = "0.6.3"
72sdio-host = "0.5.0" 72sdio-host = "0.5.0"
73critical-section = "1.1" 73critical-section = "1.1"
74#stm32-metapac = { version = "15" } 74#stm32-metapac = { version = "15" }
75stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-cdd0f8e7cb79cbd126e2480f1b747fb01c901910" } 75stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b5202878509d8d82913bd969ba4137b7958139aa" }
76 76
77vcell = "0.1.3" 77vcell = "0.1.3"
78nb = "1.0.0" 78nb = "1.0.0"
@@ -97,7 +97,7 @@ proc-macro2 = "1.0.36"
97quote = "1.0.15" 97quote = "1.0.15"
98 98
99#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} 99#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
100stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-cdd0f8e7cb79cbd126e2480f1b747fb01c901910", default-features = false, features = ["metadata"] } 100stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b5202878509d8d82913bd969ba4137b7958139aa", default-features = false, features = ["metadata"] }
101 101
102[features] 102[features]
103default = ["rt"] 103default = ["rt"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 6aedcc228..b7c00f8f7 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -1072,12 +1072,28 @@ fn main() {
1072 (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), 1072 (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)),
1073 (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), 1073 (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)),
1074 (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), 1074 (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)),
1075 (("lcd", "SEG"), quote!(crate::lcd::SegComPin)),
1076 (("lcd", "COM"), quote!(crate::lcd::SegComPin)),
1075 ].into(); 1077 ].into();
1076 1078
1079 let mut seen_lcd_pins = HashSet::new();
1080
1077 for p in METADATA.peripherals { 1081 for p in METADATA.peripherals {
1078 if let Some(regs) = &p.registers { 1082 if let Some(regs) = &p.registers {
1079 for pin in p.pins { 1083 for pin in p.pins {
1080 let key = (regs.kind, pin.signal); 1084 let mut key = (regs.kind, pin.signal);
1085
1086 // LCD is special
1087 if regs.kind == "lcd" {
1088 key.1 = pin.signal.trim_end_matches(char::is_numeric);
1089
1090 // Some lcd pins have multiple lcd functions
1091 // Dedup so they don't get the trait implemented twice
1092 if !seen_lcd_pins.insert(pin.pin) {
1093 continue;
1094 }
1095 }
1096
1081 if let Some(tr) = signals.get(&key) { 1097 if let Some(tr) = signals.get(&key) {
1082 let mut peri = format_ident!("{}", p.name); 1098 let mut peri = format_ident!("{}", p.name);
1083 1099
diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs
new file mode 100644
index 000000000..89fca416f
--- /dev/null
+++ b/embassy-stm32/src/lcd.rs
@@ -0,0 +1,176 @@
1//! LCD
2use core::marker::PhantomData;
3
4use embassy_hal_internal::{into_ref, PeripheralRef};
5
6use crate::gpio::{AFType, AnyPin, SealedPin};
7use crate::rcc::{self, RccPeripheral};
8use crate::{peripherals, Peripheral};
9
10#[non_exhaustive]
11#[derive(Debug, Default, Clone, Copy)]
12pub struct Config {
13 pub use_voltage_output_buffer: bool,
14 pub use_segment_muxing: bool,
15 pub bias: Bias,
16 pub duty: Duty,
17 pub voltage_source: VoltageSource,
18 pub high_drive: bool,
19}
20
21#[repr(u8)]
22#[derive(Debug, Default, Clone, Copy)]
23pub enum Bias {
24 #[default]
25 Quarter = 0b00,
26 Half = 0b01,
27 Third = 0b10,
28}
29
30#[repr(u8)]
31#[derive(Debug, Default, Clone, Copy)]
32pub enum Duty {
33 #[default]
34 Static = 0b000,
35 Half = 0b001,
36 Third = 0b010,
37 Quarter = 0b011,
38 /// In this mode, `COM[7:4]` outputs are available on `SEG[51:48]`.
39 /// This allows reducing the number of available segments.
40 Eigth = 0b100,
41}
42
43#[repr(u8)]
44#[derive(Debug, Default, Clone, Copy)]
45pub enum VoltageSource {
46 #[default]
47 /// Voltage stepup converter
48 Internal,
49 /// VLCD pin
50 External,
51}
52
53/// LCD driver.
54pub struct Lcd<'d, T: Instance> {
55 _peri: PhantomData<&'d mut T>,
56}
57
58impl<'d, T: Instance> Lcd<'d, T> {
59 /// Initialize the lcd driver
60 pub fn new<const N: usize>(_peri: impl Peripheral<P = T> + 'd, config: Config, pins: [LcdPin<'d, T>; N]) -> Self {
61 rcc::enable_and_reset::<T>();
62
63 for pin in pins {
64 pin.pin.set_as_af(pin.af_num, AFType::OutputPushPull);
65 }
66
67 T::regs().cr().write(|w| {
68 w.set_bufen(config.use_voltage_output_buffer);
69 w.set_mux_seg(config.use_segment_muxing);
70 w.set_bias(config.bias as u8);
71 w.set_duty(config.duty as u8);
72 w.set_vsel(matches!(config.voltage_source, VoltageSource::External));
73 });
74
75 while !T::regs().sr().read().fcrsf() { }
76
77 T::regs().fcr().modify(|w| {
78 w.set_dead(0);
79 w.set_pon(0b111);
80 // w.set_hd(config.high_drive);
81 });
82 while !T::regs().sr().read().fcrsf() { }
83
84 for i in 0..8 {
85 T::regs().ram_com(i).low().write_value(0);
86 T::regs().ram_com(i).high().write_value(0);
87 }
88 T::regs().sr().write(|w| w.set_udr(true));
89
90 while !T::regs().sr().read().fcrsf() { }
91
92 T::regs().fcr().modify(|w| {
93 w.set_ps(2);
94 w.set_div(4);
95 });
96 while !T::regs().sr().read().fcrsf() { }
97
98 T::regs().fcr().modify(|w| {
99 w.set_cc(7);
100 });
101 while !T::regs().sr().read().fcrsf() { }
102
103 T::regs().cr().modify(|w| w.set_lcden(true));
104
105 while !T::regs().sr().read().rdy() { }
106
107 Self { _peri: PhantomData }
108 }
109
110 pub fn write_frame(&mut self, data: &[u32; 16]) {
111 defmt::info!("{:06b}", T::regs().sr().read().0);
112
113 // Wait until the last update is done
114 while T::regs().sr().read().udr() { }
115
116 for i in 0..8 {
117 T::regs().ram_com(i).low().write_value(data[i * 2]);
118 T::regs().ram_com(i).low().write_value(data[i * 2 + 1]);
119 }
120 T::regs().sr().write(|w| w.set_udr(true));
121 }
122}
123
124impl<'d, T: Instance> Drop for Lcd<'d, T> {
125 fn drop(&mut self) {
126 rcc::disable::<T>();
127 }
128}
129
130pub struct LcdPin<'d, T: Instance> {
131 pin: PeripheralRef<'d, AnyPin>,
132 af_num: u8,
133 _phantom: PhantomData<T>,
134}
135
136impl<'d, T: Instance, Pin: Peripheral<P: SegComPin<T>> + 'd> From<Pin> for LcdPin<'d, T> {
137 fn from(value: Pin) -> Self {
138 Self::new(value)
139 }
140}
141
142impl<'d, T: Instance> LcdPin<'d, T> {
143 pub fn new(pin: impl Peripheral<P = impl SegComPin<T>> + 'd) -> Self {
144 into_ref!(pin);
145
146 let af = pin.af_num();
147
148 Self {
149 pin: pin.map_into(),
150 af_num: af,
151 _phantom: PhantomData,
152 }
153 }
154}
155
156trait SealedInstance: crate::rcc::SealedRccPeripheral {
157 fn regs() -> crate::pac::lcd::Lcd;
158}
159
160/// DSI instance trait.
161#[allow(private_bounds)]
162pub trait Instance: SealedInstance + RccPeripheral + 'static {}
163
164pin_trait!(SegComPin, Instance);
165
166foreach_peripheral!(
167 (lcd, $inst:ident) => {
168 impl crate::lcd::SealedInstance for peripherals::$inst {
169 fn regs() -> crate::pac::lcd::Lcd {
170 crate::pac::$inst
171 }
172 }
173
174 impl crate::lcd::Instance for peripherals::$inst {}
175 };
176);
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 95f59360a..334a0d717 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -89,6 +89,8 @@ pub mod i2s;
89pub mod ipcc; 89pub mod ipcc;
90#[cfg(feature = "low-power")] 90#[cfg(feature = "low-power")]
91pub mod low_power; 91pub mod low_power;
92#[cfg(lcd)]
93pub mod lcd;
92#[cfg(ltdc)] 94#[cfg(ltdc)]
93pub mod ltdc; 95pub mod ltdc;
94#[cfg(opamp)] 96#[cfg(opamp)]
diff --git a/examples/stm32u0/.cargo/config.toml b/examples/stm32u0/.cargo/config.toml
index 688347084..06eed6c8f 100644
--- a/examples/stm32u0/.cargo/config.toml
+++ b/examples/stm32u0/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace stm32u083rctx with your chip as listed in `probe-rs chip list` 2# replace stm32u083rctx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs run --chip stm32u083rctx" 3runner = "probe-rs run --chip stm32u083rctx --catch-hardfault"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "thumbv6m-none-eabi"
diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml
index afeb4dc34..6c310b0f6 100644
--- a/examples/stm32u0/Cargo.toml
+++ b/examples/stm32u0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[dependencies] 7[dependencies]
8# Change stm32u083rc to your chip name, if necessary. 8# Change stm32u083rc to your chip name, if necessary.
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083mc", "memory-x", "unstable-pac", "exti", "chrono"] }
10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } 10embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 11embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
12embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs
new file mode 100644
index 000000000..8612c3dfc
--- /dev/null
+++ b/examples/stm32u0/src/bin/lcd.rs
@@ -0,0 +1,76 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::lcd::{Bias, Config, Duty, Lcd, VoltageSource};
7use {defmt_rtt as _, panic_probe as _};
8
9#[embassy_executor::main]
10async fn main(_spawner: Spawner) {
11 let mut config = embassy_stm32::Config::default();
12 // The RTC clock = the LCD clock and must be running
13 {
14 use embassy_stm32::rcc::*;
15 config.rcc.sys = Sysclk::PLL1_R;
16 config.rcc.hsi = true;
17 config.rcc.pll = Some(Pll {
18 source: PllSource::HSI, // 16 MHz
19 prediv: PllPreDiv::DIV1,
20 mul: PllMul::MUL7, // 16 * 7 = 112 MHz
21 divp: None,
22 divq: None,
23 divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz
24 });
25 config.rcc.ls = LsConfig::default();
26 }
27
28 let p = embassy_stm32::init(config);
29 info!("Hello World!");
30
31 let mut config = Config::default();
32 config.bias = Bias::Third;
33 config.duty = Duty::Quarter;
34
35 let mut lcd = Lcd::new(
36 p.LCD,
37 config,
38 [
39 p.PC4.into(),
40 p.PC5.into(),
41 p.PB1.into(),
42 p.PE7.into(),
43 p.PE8.into(),
44 p.PE9.into(),
45 p.PB11.into(),
46 p.PB14.into(),
47 p.PB15.into(),
48 p.PD8.into(),
49 p.PD9.into(),
50 p.PD12.into(),
51 p.PB9.into(),
52 p.PA10.into(),
53 p.PA9.into(),
54 p.PA8.into(),
55 p.PD13.into(),
56 p.PC6.into(),
57 p.PC8.into(),
58 p.PC9.into(),
59 p.PC10.into(),
60 p.PD0.into(),
61 p.PD1.into(),
62 p.PD3.into(),
63 p.PD4.into(),
64 p.PD5.into(),
65 p.PD6.into(),
66 p.PC11.into(),
67 ],
68 );
69
70 loop {
71 defmt::info!("Writing frame");
72 lcd.write_frame(&[0xAAAAAAAA; 16]);
73 defmt::info!("Writing frame");
74 lcd.write_frame(&[!0xAAAAAAAA; 16]);
75 }
76}