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