aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32l4/src
diff options
context:
space:
mode:
authorTyler <[email protected]>2023-09-29 20:02:24 -0600
committerGitHub <[email protected]>2023-09-29 20:02:24 -0600
commit2f9b59c5cf21f1e2761a9ccefdfd86f0edea829c (patch)
tree8964744b4fb753cf98f6f413464106c4d2a72976 /examples/stm32l4/src
parentce91fb2bfc846570ef543a09396c428d70675245 (diff)
parent95b3d9eb3b3657de3d7bc9c04f8fb83eae901640 (diff)
Merge branch 'main' into issue-1974-add-sai-driver
Diffstat (limited to 'examples/stm32l4/src')
-rw-r--r--examples/stm32l4/src/bin/rtc.rs3
-rw-r--r--examples/stm32l4/src/bin/spe_adin1110_http_server.rs450
-rw-r--r--examples/stm32l4/src/bin/usart.rs2
-rw-r--r--examples/stm32l4/src/bin/usart_dma.rs2
4 files changed, 454 insertions, 3 deletions
diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs
index 294ea456c..eb1eed012 100644
--- a/examples/stm32l4/src/bin/rtc.rs
+++ b/examples/stm32l4/src/bin/rtc.rs
@@ -23,7 +23,8 @@ async fn main(_spawner: Spawner) {
23 PLLMul::Mul20, 23 PLLMul::Mul20,
24 None, 24 None,
25 ); 25 );
26 config.rcc.rtc_mux = rcc::RtcClockSource::LSE32; 26 config.rcc.lse = Some(Hertz(32_768));
27 config.rcc.rtc_mux = rcc::RtcClockSource::LSE;
27 embassy_stm32::init(config) 28 embassy_stm32::init(config)
28 }; 29 };
29 info!("Hello World!"); 30 info!("Hello World!");
diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
new file mode 100644
index 000000000..287521582
--- /dev/null
+++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs
@@ -0,0 +1,450 @@
1#![deny(clippy::pedantic)]
2#![allow(clippy::doc_markdown)]
3#![no_main]
4#![no_std]
5// Needed unitl https://github.com/rust-lang/rust/issues/63063 is stablised.
6#![feature(type_alias_impl_trait)]
7#![feature(associated_type_bounds)]
8#![allow(clippy::missing_errors_doc)]
9
10// This example works on a ANALOG DEVICE EVAL-ADIN110EBZ board.
11// Settings switch S201 "HW CFG":
12// - Without SPI CRC: OFF-ON-OFF-OFF-OFF
13// - With SPI CRC: ON -ON-OFF-OFF-OFF
14// Settings switch S303 "uC CFG":
15// - CFG0: On = static ip, Off = Dhcp
16// - CFG1: Ethernet `FCS` on TX path: On, Off
17// The webserver shows the actual temperature of the onboard i2c temp sensor.
18
19use core::marker::PhantomData;
20use core::sync::atomic::{AtomicI32, Ordering};
21
22use defmt::{error, info, println, unwrap, Format};
23use defmt_rtt as _; // global logger
24use embassy_executor::Spawner;
25use embassy_futures::select::{select, Either};
26use embassy_futures::yield_now;
27use embassy_net::tcp::TcpSocket;
28use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4};
29use embassy_time::{Delay, Duration, Ticker, Timer};
30use embedded_hal_async::i2c::I2c as I2cBus;
31use embedded_io::Write as bWrite;
32use embedded_io_async::Write;
33use hal::gpio::{Input, Level, Output, Speed};
34use hal::i2c::{self, I2c};
35use hal::rcc::{self};
36use hal::rng::{self, Rng};
37use hal::{bind_interrupts, exti, pac, peripherals};
38use heapless::Vec;
39use rand::RngCore;
40use static_cell::make_static;
41use {embassy_stm32 as hal, panic_probe as _};
42
43bind_interrupts!(struct Irqs {
44 I2C3_EV => i2c::InterruptHandler<peripherals::I2C3>;
45 RNG => rng::InterruptHandler<peripherals::RNG>;
46});
47
48use embassy_net_adin1110::{self, Device, Runner, ADIN1110};
49use embedded_hal_bus::spi::ExclusiveDevice;
50use hal::gpio::Pull;
51use hal::i2c::Config as I2C_Config;
52use hal::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
53use hal::spi::{Config as SPI_Config, Spi};
54use hal::time::Hertz;
55
56// Basic settings
57// MAC-address used by the adin1110
58const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];
59// Static IP settings
60const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24);
61// Listen port for the webserver
62const HTTP_LISTEN_PORT: u16 = 80;
63
64pub type SpeSpi = Spi<'static, peripherals::SPI2, peripherals::DMA1_CH1, peripherals::DMA1_CH2>;
65pub type SpeSpiCs = ExclusiveDevice<SpeSpi, Output<'static, peripherals::PB12>, Delay>;
66pub type SpeInt = exti::ExtiInput<'static, peripherals::PB11>;
67pub type SpeRst = Output<'static, peripherals::PC7>;
68pub type Adin1110T = ADIN1110<SpeSpiCs>;
69pub type TempSensI2c = I2c<'static, peripherals::I2C3, peripherals::DMA1_CH6, peripherals::DMA1_CH7>;
70
71static TEMP: AtomicI32 = AtomicI32::new(0);
72
73#[embassy_executor::main]
74async fn main(spawner: Spawner) {
75 defmt::println!("Start main()");
76
77 let mut config = embassy_stm32::Config::default();
78
79 // 80Mhz clock (Source: 8 / SrcDiv: 1 * PLLMul 20 / ClkDiv 2)
80 // 80MHz highest frequency for flash 0 wait.
81 config.rcc.mux = ClockSrc::PLL(
82 PLLSource::HSE(Hertz(8_000_000)),
83 PLLClkDiv::Div2,
84 PLLSrcDiv::Div1,
85 PLLMul::Mul20,
86 None,
87 );
88 config.rcc.hsi48 = true; // needed for rng
89 config.rcc.rtc_mux = rcc::RtcClockSource::LSI;
90
91 let dp = embassy_stm32::init(config);
92
93 // RM0432rev9, 5.1.2: Independent I/O supply rail
94 // After reset, the I/Os supplied by VDDIO2 are logically and electrically isolated and
95 // therefore are not available. The isolation must be removed before using any I/O from
96 // PG[15:2], by setting the IOSV bit in the PWR_CR2 register, once the VDDIO2 supply is present
97 pac::PWR.cr2().modify(|w| w.set_iosv(true));
98
99 let reset_status = pac::RCC.bdcr().read().0;
100 defmt::println!("bdcr before: 0x{:X}", reset_status);
101
102 defmt::println!("Setup IO pins");
103
104 // Setup LEDs
105 let _led_uc1_green = Output::new(dp.PC13, Level::Low, Speed::Low);
106 let mut led_uc2_red = Output::new(dp.PE2, Level::High, Speed::Low);
107 let led_uc3_yellow = Output::new(dp.PE6, Level::High, Speed::Low);
108 let led_uc4_blue = Output::new(dp.PG15, Level::High, Speed::Low);
109
110 // Read the uc_cfg switches
111 let uc_cfg0 = Input::new(dp.PB2, Pull::None);
112 let uc_cfg1 = Input::new(dp.PF11, Pull::None);
113 let _uc_cfg2 = Input::new(dp.PG6, Pull::None);
114 let _uc_cfg3 = Input::new(dp.PG11, Pull::None);
115
116 // Setup I2C pins
117 let temp_sens_i2c = I2c::new(
118 dp.I2C3,
119 dp.PG7,
120 dp.PG8,
121 Irqs,
122 dp.DMA1_CH6,
123 dp.DMA1_CH7,
124 Hertz(100_000),
125 I2C_Config::default(),
126 );
127
128 // Setup IO and SPI for the SPE chip
129 let spe_reset_n = Output::new(dp.PC7, Level::Low, Speed::Low);
130 let spe_cfg0 = Input::new(dp.PC8, Pull::None);
131 let spe_cfg1 = Input::new(dp.PC9, Pull::None);
132 let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low);
133
134 let spe_int = Input::new(dp.PB11, Pull::None);
135 let spe_int = exti::ExtiInput::new(spe_int, dp.EXTI11);
136
137 let spe_spi_cs_n = Output::new(dp.PB12, Level::High, Speed::High);
138 let spe_spi_sclk = dp.PB13;
139 let spe_spi_miso = dp.PB14;
140 let spe_spi_mosi = dp.PB15;
141
142 // Don't turn the clock to high, clock must fit within the system clock as we get a runtime panic.
143 let mut spi_config = SPI_Config::default();
144 spi_config.frequency = Hertz(25_000_000);
145
146 let spe_spi: SpeSpi = Spi::new(
147 dp.SPI2,
148 spe_spi_sclk,
149 spe_spi_mosi,
150 spe_spi_miso,
151 dp.DMA1_CH1,
152 dp.DMA1_CH2,
153 spi_config,
154 );
155 let spe_spi = SpeSpiCs::new(spe_spi, spe_spi_cs_n, Delay);
156
157 let cfg0_without_crc = spe_cfg0.is_high();
158 let cfg1_spi_mode = spe_cfg1.is_high();
159 let uc_cfg1_fcs_en = uc_cfg1.is_low();
160
161 defmt::println!(
162 "ADIN1110: CFG SPI-MODE 1-{}, CRC-bit 0-{} FCS-{}",
163 cfg1_spi_mode,
164 cfg0_without_crc,
165 uc_cfg1_fcs_en
166 );
167
168 // Check the SPI mode selected with the "HW CFG" dip-switch
169 if !cfg1_spi_mode {
170 error!("Driver doesn´t support SPI Protolcol \"OPEN Alliance\".\nplease use the \"Generic SPI\"! Turn On \"HW CFG\": \"SPI_CFG1\"");
171 loop {
172 led_uc2_red.toggle();
173 Timer::after(Duration::from_hz(10)).await;
174 }
175 };
176
177 let state = make_static!(embassy_net_adin1110::State::<8, 8>::new());
178
179 let (device, runner) = embassy_net_adin1110::new(
180 MAC,
181 state,
182 spe_spi,
183 spe_int,
184 spe_reset_n,
185 !cfg0_without_crc,
186 uc_cfg1_fcs_en,
187 )
188 .await;
189
190 // Start task blink_led
191 unwrap!(spawner.spawn(heartbeat_led(led_uc3_yellow)));
192 // Start task temperature measurement
193 unwrap!(spawner.spawn(temp_task(temp_sens_i2c, led_uc4_blue)));
194 // Start ethernet task
195 unwrap!(spawner.spawn(ethernet_task(runner)));
196
197 let mut rng = Rng::new(dp.RNG, Irqs);
198 // Generate random seed
199 let seed = rng.next_u64();
200
201 let ip_cfg = if uc_cfg0.is_low() {
202 println!("Waiting for DHCP...");
203 let dhcp4_config = embassy_net::DhcpConfig::default();
204 embassy_net::Config::dhcpv4(dhcp4_config)
205 } else {
206 embassy_net::Config::ipv4_static(StaticConfigV4 {
207 address: IP_ADDRESS,
208 gateway: None,
209 dns_servers: Vec::new(),
210 })
211 };
212
213 // Init network stack
214 let stack = &*make_static!(Stack::new(
215 device,
216 ip_cfg,
217 make_static!(StackResources::<2>::new()),
218 seed
219 ));
220
221 // Launch network task
222 unwrap!(spawner.spawn(net_task(stack)));
223
224 let cfg = wait_for_config(stack).await;
225 let local_addr = cfg.address.address();
226
227 // Then we can use it!
228 let mut rx_buffer = [0; 4096];
229 let mut tx_buffer = [0; 4096];
230 let mut mb_buf = [0; 4096];
231 loop {
232 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
233 socket.set_timeout(Some(Duration::from_secs(1)));
234
235 info!("Listening on http://{}:{}...", local_addr, HTTP_LISTEN_PORT);
236 if let Err(e) = socket.accept(HTTP_LISTEN_PORT).await {
237 defmt::error!("accept error: {:?}", e);
238 continue;
239 }
240
241 loop {
242 let _n = match socket.read(&mut mb_buf).await {
243 Ok(0) => {
244 defmt::info!("read EOF");
245 break;
246 }
247 Ok(n) => n,
248 Err(e) => {
249 defmt::error!("{:?}", e);
250 break;
251 }
252 };
253 led_uc2_red.set_low();
254
255 let status_line = "HTTP/1.1 200 OK";
256 let contents = PAGE;
257 let length = contents.len();
258
259 let _ = write!(
260 &mut mb_buf[..],
261 "{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}\r\n\0"
262 );
263 let loc = mb_buf.iter().position(|v| *v == b'#').unwrap();
264
265 let temp = TEMP.load(Ordering::Relaxed);
266 let cel = temp / 1000;
267 let mcel = temp % 1000;
268
269 info!("{}.{}", cel, mcel);
270
271 let _ = write!(&mut mb_buf[loc..loc + 7], "{cel}.{mcel}");
272
273 let n = mb_buf.iter().position(|v| *v == 0).unwrap();
274
275 if let Err(e) = socket.write_all(&mb_buf[..n]).await {
276 error!("write error: {:?}", e);
277 break;
278 }
279
280 led_uc2_red.set_high();
281 }
282 }
283}
284
285async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 {
286 loop {
287 if let Some(config) = stack.config_v4() {
288 return config;
289 }
290 yield_now().await;
291 }
292}
293
294#[embassy_executor::task]
295async fn heartbeat_led(mut led: Output<'static, peripherals::PE6>) {
296 let mut tmr = Ticker::every(Duration::from_hz(3));
297 loop {
298 led.toggle();
299 tmr.next().await;
300 }
301}
302
303// ADT7422
304#[embassy_executor::task]
305async fn temp_task(temp_dev_i2c: TempSensI2c, mut led: Output<'static, peripherals::PG15>) -> ! {
306 let mut tmr = Ticker::every(Duration::from_hz(1));
307 let mut temp_sens = ADT7422::new(temp_dev_i2c, 0x48).unwrap();
308
309 loop {
310 led.set_low();
311 match select(temp_sens.read_temp(), Timer::after(Duration::from_millis(500))).await {
312 Either::First(i2c_ret) => match i2c_ret {
313 Ok(value) => {
314 led.set_high();
315 let temp = i32::from(value);
316 println!("TEMP: {:04x}, {}", temp, temp * 78 / 10);
317 TEMP.store(temp * 78 / 10, Ordering::Relaxed);
318 }
319 Err(e) => defmt::println!("ADT7422: {}", e),
320 },
321 Either::Second(_) => println!("Timeout"),
322 }
323
324 tmr.next().await;
325 }
326}
327
328#[embassy_executor::task]
329async fn ethernet_task(runner: Runner<'static, SpeSpiCs, SpeInt, SpeRst>) -> ! {
330 runner.run().await
331}
332
333#[embassy_executor::task]
334async fn net_task(stack: &'static Stack<Device<'static>>) -> ! {
335 stack.run().await
336}
337
338// same panicking *behavior* as `panic-probe` but doesn't print a panic message
339// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
340#[defmt::panic_handler]
341fn panic() -> ! {
342 cortex_m::asm::udf()
343}
344
345#[allow(non_camel_case_types)]
346#[repr(C)]
347pub enum Registers {
348 Temp_MSB = 0x00,
349 Temp_LSB,
350 Status,
351 Cfg,
352 T_HIGH_MSB,
353 T_HIGH_LSB,
354 T_LOW_MSB,
355 T_LOW_LSB,
356 T_CRIT_MSB,
357 T_CRIT_LSB,
358 T_HYST,
359 ID,
360 SW_RESET = 0x2F,
361}
362
363pub struct ADT7422<'d, BUS: I2cBus> {
364 addr: u8,
365 phantom: PhantomData<&'d ()>,
366 bus: BUS,
367}
368
369#[derive(Debug, Format)]
370pub enum Error<I2cError: Format> {
371 I2c(I2cError),
372 Address,
373}
374
375impl<'d, BUS> ADT7422<'d, BUS>
376where
377 BUS: I2cBus,
378 BUS::Error: Format,
379{
380 pub fn new(bus: BUS, addr: u8) -> Result<Self, Error<BUS::Error>> {
381 if !(0x48..=0x4A).contains(&addr) {
382 return Err(Error::Address);
383 }
384
385 Ok(Self {
386 bus,
387 phantom: PhantomData,
388 addr,
389 })
390 }
391
392 pub async fn init(&mut self) -> Result<(), Error<BUS::Error>> {
393 let mut cfg = 0b000_0000;
394 // if self.int.is_some() {
395 // // Set 1 SPS mode
396 // cfg |= 0b10 << 5;
397 // } else {
398 // One shot mode
399 cfg |= 0b01 << 5;
400 // }
401
402 self.write_cfg(cfg).await
403 }
404
405 pub async fn read(&mut self, reg: Registers) -> Result<u8, Error<BUS::Error>> {
406 let mut buffer = [0u8; 1];
407 self.bus
408 .write_read(self.addr, &[reg as u8], &mut buffer)
409 .await
410 .map_err(Error::I2c)?;
411 Ok(buffer[0])
412 }
413
414 pub async fn write_cfg(&mut self, cfg: u8) -> Result<(), Error<BUS::Error>> {
415 let buf = [Registers::Cfg as u8, cfg];
416 self.bus.write(self.addr, &buf).await.map_err(Error::I2c)
417 }
418
419 pub async fn read_temp(&mut self) -> Result<i16, Error<BUS::Error>> {
420 let mut buffer = [0u8; 2];
421
422 // if let Some(int) = &mut self.int {
423 // // Wait for interrupt
424 // int.wait_for_low().await.unwrap();
425 // } else {
426 // Start: One shot
427 let cfg = 0b01 << 5;
428 self.write_cfg(cfg).await?;
429 Timer::after(Duration::from_millis(250)).await;
430 self.bus
431 .write_read(self.addr, &[Registers::Temp_MSB as u8], &mut buffer)
432 .await
433 .map_err(Error::I2c)?;
434 Ok(i16::from_be_bytes(buffer))
435 }
436}
437
438// Web page
439const PAGE: &str = r#"<!DOCTYPE html>
440<html lang="en">
441 <head>
442 <meta charset="utf-8">
443 <meta http-equiv="refresh" content="1" >
444 <title>ADIN1110 with Rust</title>
445 </head>
446 <body>
447 <p>EVAL-ADIN1110EBZ</p>
448 <table><td>Temp Sensor ADT7422:</td><td> #00.00 &deg;C</td></table>
449 </body>
450</html>"#;
diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs
index beb5ec558..f4da6b5ae 100644
--- a/examples/stm32l4/src/bin/usart.rs
+++ b/examples/stm32l4/src/bin/usart.rs
@@ -19,7 +19,7 @@ fn main() -> ! {
19 let p = embassy_stm32::init(Default::default()); 19 let p = embassy_stm32::init(Default::default());
20 20
21 let config = Config::default(); 21 let config = Config::default();
22 let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, NoDma, NoDma, config); 22 let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, NoDma, NoDma, config).unwrap();
23 23
24 unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); 24 unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n"));
25 info!("wrote Hello, starting echo"); 25 info!("wrote Hello, starting echo");
diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs
index b7d4cb01e..2f3b2a0f0 100644
--- a/examples/stm32l4/src/bin/usart_dma.rs
+++ b/examples/stm32l4/src/bin/usart_dma.rs
@@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) {
22 info!("Hello World!"); 22 info!("Hello World!");
23 23
24 let config = Config::default(); 24 let config = Config::default();
25 let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, NoDma, config); 25 let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, NoDma, config).unwrap();
26 26
27 for n in 0u32.. { 27 for n in 0u32.. {
28 let mut s: String<128> = String::new(); 28 let mut s: String<128> = String::new();