1use crate::db::*;
2use crate::gpio::*;
3use crate::i2c::*;
4use crate::rcc::*;
5use crate::spi::*;
6use crate::usart::*;
7use crate::utils::*;
8use crate::{Config, MCUFamily};
9
10pub fn generate_main(config: &Config) -> anyhow::Result<String> {
11 let hal = match config.mcu_family {
12 MCUFamily::STM32F0 => "stm32f0xx_hal",
13 _ => todo!("Only STM32F0 supported for now"),
14 };
15
16 let mut imports = GeneratedString::new();
17
18 imports.line("#![no_std]");
19 imports.line("#![no_main]");
20 imports.empty_line();
21 imports.line("use crate::hal::{prelude::*, stm32};");
22 imports.line("use cortex_m::interrupt;");
23 imports.line("use cortex_m_rt::entry;");
24 imports.line("use panic_halt as _;");
25 imports.line(f!("use {hal} as hal;"));
26 imports.empty_line();
27
28 let mut main_func = GeneratedString::new();
29
30 main_func.line("#[entry]");
31 main_func.line("fn main() -> ! {");
32 main_func.indent_right();
33
34 main_func.line("let mut p = stm32::Peripherals::take().unwrap();");
35
36 add_rcc(&mut main_func, &config);
37
38 add_ports(&mut main_func, &config);
39
40 add_gpios(&mut main_func, &config);
41
42 for spi in config.spis.iter() {
43 add_spi(&mut main_func, &mut imports, spi);
44 }
45
46 for usart in config.usarts.iter() {
47 add_usart(&mut main_func, &mut imports, usart);
48 }
49
50 for i2c in config.i2cs.iter() {
51 add_i2c(&mut main_func, &mut imports, i2c);
52 }
53
54 main_func.line("loop {}");
55
56 main_func.indent_left();
57 main_func.line("}");
58
59 Ok(imports.string + "\n" + &main_func.string)
60}
61
62fn add_rcc(string: &mut GeneratedString, config: &Config) {
63 string.line("let mut rcc = p");
64 string.indent_right();
65 string.line(".RCC");
66 string.line(".configure()");
67 match config.rcc.clock_source {
68 ClockSource::HSI => {}
69 ClockSource::HSI48 => string.line(".hsi48()"),
70 ClockSource::HSE(HSEMode::NotBypassed(freq)) => string.line(f!(
71 ".hse({freq}.hz(), hal::rcc::HSEBypassMode::NotBypassed)"
72 )),
73 ClockSource::HSE(HSEMode::Bypassed(freq)) => {
74 string.line(f!(".hse({freq}.hz(), hal::rcc::HSEBypassMode::Bypassed)"))
75 }
76 }
77
78 if let Some(sysclk_freq) = config.rcc.sysclk_freq {
79 string.line(f!(".sysclk({sysclk_freq}.hz())"));
80 }
81 if let Some(hclk_freq) = config.rcc.hclk_freq {
82 string.line(f!(".hclk({hclk_freq}.hz())"));
83 }
84 if let Some(apb1_freq) = config.rcc.apb1_freq {
85 string.line(f!(".pclk({apb1_freq}.hz())"));
86 }
87 string.line(".freeze(&mut p.FLASH);");
88 string.indent_left();
89 string.empty_line();
90}
91
92fn add_ports(string: &mut GeneratedString, config: &Config) {
93 for port in config.ports.iter() {
94 let port_lower = port.to_ascii_lowercase();
95
96 let gpio_names: Vec<_> = config
97 .gpios
98 .iter()
99 .filter(|gpio| gpio.port.ends_with(*port))
100 .map(|gpio| gpio.register.clone())
101 .collect();
102
103 let registers: Vec<_> = gpio_names
104 .iter()
105 .map(|name| f!("_{port_lower}.{name}"))
106 .collect();
107
108 let gpio_names = gpio_names.join(", ");
109 let registers = registers.join(", ");
110
111 string.line(f!("let _{port_lower} = p.GPIO{port}.split(&mut rcc);"));
112 string.line(f!("let ({gpio_names}) = ({registers});"));
113 }
114 string.empty_line();
115}
116
117fn add_gpios(string: &mut GeneratedString, config: &Config) {
118 for gpio in config.gpios.iter() {
119 let pin_name = gpio.get_name();
120 let pin_configuration = configure_gpio(gpio, config.mcu_family);
121
122 let mutable = if !matches!(&gpio.signal, SignalType::Peripheral(_)) {
123 "mut "
124 } else {
125 ""
126 };
127
128 string.line(f!("let {mutable}{pin_name} = {pin_configuration};"));
129 }
130
131 string.empty_line();
132}
133
134fn configure_gpio(gpio: &GpioPin, mcu_family: MCUFamily) -> String {
135 let name = gpio.get_name();
136
137 let func = match gpio.signal {
138 SignalType::AdcInput => f!("into_analog"),
139 SignalType::GpioInput => match gpio.pu_pd.unwrap_or_default() {
140 PullType::GPIO_NOPULL => f!("into_floating_input"),
141 PullType::GPIO_PULLUP => f!("into_pull_up_input"),
142 PullType::GPIO_PULLDOWN => f!("into_pull_down_input"),
143 },
144 SignalType::GpioOutput => match gpio.mode_default_output_pp.unwrap_or_default() {
145 ModeOutputType::GPIO_MODE_OUTPUT_OD => match gpio.speed.unwrap_or_default() {
146 SpeedType::GPIO_SPEED_FREQ_LOW => f!("into_open_drain_output"),
147 _ => todo!("{} higher speeds for", name),
148 },
149 ModeOutputType::GPIO_MODE_OUTPUT_PP => match gpio.speed.unwrap_or_default() {
150 SpeedType::GPIO_SPEED_FREQ_LOW => f!("into_push_pull_output"),
151 SpeedType::GPIO_SPEED_FREQ_MEDIUM => f!("into_push_pull_output_hs"),
152
153 _ => todo!("{} higher speeds for", name),
154 },
155 },
156 SignalType::Peripheral(ref name) => {
157 let af = get_alternate_function(mcu_family, gpio, name);
158 f!("into_alternate_af{af}")
159 }
160 };
161 f!("interrupt::free(|cs| {gpio.register}.{func}(cs))")
162}
163
164fn add_spi(main_func: &mut GeneratedString, imports: &mut GeneratedString, spi: &SPI) {
165 let polarity = match spi.polarity.unwrap_or_default() {
166 CLKPolarity::SPI_POLARITY_LOW => "IdleLow",
167 CLKPolarity::SPI_POLARITY_HIGH => "IdleHigh",
168 };
169
170 let phase = match spi.phase.unwrap_or_default() {
171 CLKPhase::SPI_PHASE_1EDGE => "CaptureOnFirstTransition",
172 CLKPhase::SPI_PHASE_2EDGE => "CaptureOnSecondTransition",
173 };
174
175 imports.line("use hal::spi::{Spi, Mode, Phase, Polarity};");
176
177 main_func.line(f!("let mut {spi.name_lower} = Spi::{spi.name_lower}("));
178 main_func.indent_right();
179 main_func.line(f!("p.{spi.name_upper},"));
180 main_func.line(f!(
181 "({spi.name_lower}_sck, {spi.name_lower}_miso, {spi.name_lower}_mosi),"
182 ));
183 main_func.line("Mode {");
184 main_func.indent_right();
185 main_func.line(f!("polarity: Polarity::{polarity},"));
186 main_func.line(f!("phase: Phase::{phase},"));
187 main_func.indent_left();
188 main_func.line("},");
189 main_func.line(f!("{spi.baudrate.0}.hz(),"));
190 main_func.line("&mut rcc");
191 main_func.indent_left();
192 main_func.line(");");
193 main_func.empty_line();
194}
195
196fn add_usart(main_func: &mut GeneratedString, imports: &mut GeneratedString, usart: &USART) {
197 let baudrate = usart.baudrate.unwrap_or(38400);
198
199 imports.line("use hal::serial::Serial;");
200
201 main_func.line(f!(
202 "let mut {usart.name_lower} = Serial::{usart.name_lower}("
203 ));
204 main_func.indent_right();
205 main_func.line(f!("p.{usart.name_upper},"));
206 main_func.line(f!("({usart.name_lower}_tx, {usart.name_lower}_rx),"));
207 main_func.line(f!("{baudrate}.bps(),"));
208 main_func.line("&mut rcc");
209 main_func.indent_left();
210 main_func.line(");");
211 main_func.empty_line();
212}
213
214fn add_i2c(main_func: &mut GeneratedString, imports: &mut GeneratedString, i2c: &I2C) {
215 imports.line("use hal::i2c::I2c;");
216
217 let speed: u32 = match i2c.mode.unwrap_or_default() {
218 Mode::I2C_Standard => 100,
219 Mode::I2C_Fast => 400,
220 Mode::I2C_Fast_Plus => 1000,
221 };
222
223 main_func.line(f!("let mut {i2c.name_lower} = I2c::{i2c.name_lower}("));
224 main_func.indent_right();
225 main_func.line(f!("p.{i2c.name_upper},"));
226 main_func.line(f!("({i2c.name_lower}_scl, {i2c.name_lower}_sda),"));
227 main_func.line(f!("{speed}.khz(),"));
228 main_func.line("&mut rcc");
229 main_func.indent_left();
230 main_func.line(");");
231 main_func.empty_line();
232}
233
234pub fn generate_cargo_config(config: &Config) -> String {
235 let mut file_content = String::from(
236 r#"[target.'cfg(all(target_arch = "arm", target_os = "none"))']
237# uncomment ONE of these three option to make `cargo run` start a GDB session
238# which option to pick depends on your system
239# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
240# runner = "gdb-multiarch -q -x openocd.gdb"
241# runner = "gdb -q -x openocd.gdb"
242
243rustflags = [
244 # LLD (shipped with the Rust toolchain) is used as the default linker
245 "-C", "link-arg=-Tlink.x",
246
247 # if you run into problems with LLD switch to the GNU linker by commenting out
248 # this line
249 # "-C", "linker=arm-none-eabi-ld",
250
251 # if you need to link to pre-compiled C libraries provided by a C toolchain
252 # use GCC as the linker by commenting out both lines above and then
253 # uncommenting the three lines below
254 # "-C", "linker=arm-none-eabi-gcc",
255 # "-C", "link-arg=-Wl,-Tlink.x",
256 # "-C", "link-arg=-nostartfiles",
257]
258
259[build]
260"#,
261 );
262
263 let target = match config.mcu_family {
264 MCUFamily::STM32F0 | MCUFamily::STM32L0 | MCUFamily::STM32G0 => "thumbv6m-none-eabi",
265 MCUFamily::STM32F1 | MCUFamily::STM32F2 | MCUFamily::STM32L1 => "thumbv7m-none-eabi",
266 MCUFamily::STM32F3 | MCUFamily::STM32F4 => "thumbv7em-none-eabihf",
267 _ => todo!("find out if it has FPU"),
268 };
269
270 file_content.push_str(&f!("target = \"{target}\"\n"));
271 file_content
272}
273
274pub fn generate_dependencies(config: &Config) -> anyhow::Result<String> {
275 let hal_crate = match config.mcu_family {
276 MCUFamily::STM32F0 => "stm32f0xx-hal",
277 _ => todo!("other hal crates"),
278 };
279
280 let feature = get_feature(config)?;
281
282 let mut filecontent =
283 f!("{hal_crate} = {{version = \"*\", features = [\"{feature}\", \"rt\"]}}");
284
285 filecontent.push_str(
286 r#"
287cortex-m = "*"
288cortex-m-rt = "*"
289panic-halt = "*"
290
291[profile.dev.package."*"]
292# opt-level = "z"
293
294[profile.release]
295# lto = true
296"#,
297 );
298
299 Ok(filecontent)
300}
301
302pub fn generate_memory_x(config: &Config) -> anyhow::Result<String> {
303 let mem_size = get_mem_size(config);
304
305 Ok(f!("\
306MEMORY
307{{
308 FLASH : ORIGIN = 0x00000000, LENGTH = {mem_size.flash}K
309 RAM : ORIGIN = 0x20000000, LENGTH = {mem_size.ram}K
310}}
311"))
312}