From ce9ae48f2af313b9c7b705f6168a30ecd5a36dbc Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Sat, 18 Apr 2020 10:54:55 +0200 Subject: [PATCH 01/40] Adding WIP update to use HAL --- .cargo/config | 2 +- Cargo.toml | 2 +- src/eeprom.rs | 2 +- src/eth.rs | 2 +- src/i2c.rs | 2 +- src/main.rs | 4 ++-- stabilizer.cfg | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.cargo/config b/.cargo/config index 382c36e..a765d53 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "gdb-multiarch -q -x openocd.gdb" +runner = "gdb-multiarch -q -x bmp.gdb" rustflags = ["-C", "link-arg=-Tlink.x"] [build] diff --git a/Cargo.toml b/Cargo.toml index c62c914..66e5451 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ panic-halt = "0.2" serde = { version = "1.0", features = ["derive"], default-features = false } heapless = "0.5" serde-json-core = "0.1" -stm32h7 = { version = "0.10", features = ["stm32h743", "rt"] } +stm32h7 = { version = "0.10", features = ["stm32h743v", "rt"] } cortex-m-rtfm = "0.5" smoltcp = { version = "0.6", features = ["ethernet", "proto-ipv4", "socket-tcp"], default-features = false } diff --git a/src/eeprom.rs b/src/eeprom.rs index fe30680..a50dd69 100644 --- a/src/eeprom.rs +++ b/src/eeprom.rs @@ -1,5 +1,5 @@ use super::i2c; -use stm32h7::stm32h743 as pac; +use stm32h7::stm32h743v as pac; const I2C_ADDR: u8 = 0xa0; diff --git a/src/eth.rs b/src/eth.rs index 9cd1c9c..d108982 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -3,7 +3,7 @@ use smoltcp::phy; use smoltcp::time::Instant; use smoltcp::wire::EthernetAddress; use smoltcp::Result; -use stm32h7::stm32h743 as pac; +use stm32h7::stm32h743v as pac; #[allow(dead_code)] mod phy_consts { diff --git a/src/i2c.rs b/src/i2c.rs index 9c431ca..2118194 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -1,4 +1,4 @@ -use stm32h7::stm32h743 as pac; +use stm32h7::stm32h743v as pac; // Adapted from stm32h7xx embedded-hal diff --git a/src/main.rs b/src/main.rs index f7e9f94..5cc2497 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,7 @@ use core::fmt::Write; use cortex_m_rt::exception; use heapless::{consts::*, String, Vec}; use rtfm::cyccnt::{Instant, U32Ext as _}; -use stm32h7::stm32h743 as pac; +use stm32h7::stm32h743v as pac; use smoltcp as net; @@ -94,7 +94,7 @@ macro_rules! create_socket { }; } -#[rtfm::app(device = stm32h7::stm32h743, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] +#[rtfm::app(device = stm32h7::stm32h743v, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { spi: (pac::SPI1, pac::SPI2, pac::SPI4, pac::SPI5), diff --git a/stabilizer.cfg b/stabilizer.cfg index 51d7c6c..ea01d24 100644 --- a/stabilizer.cfg +++ b/stabilizer.cfg @@ -1,4 +1,4 @@ -source [find interface/stlink.cfg] +source [find interface/stlink-v2.cfg] transport select hla_swd source [find target/stm32h7x_dual_bank.cfg] From 820a37a62549bedc1f8056c20cae0568f896cd72 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Sun, 19 Apr 2020 13:37:03 +0200 Subject: [PATCH 02/40] Refactoring branches --- Cargo.lock | 99 ++++++--- Cargo.toml | 7 +- src/board.rs | 566 +------------------------------------------------- src/eeprom.rs | 11 +- src/eth.rs | 46 +--- src/i2c.rs | 145 ------------- src/main.rs | 344 +++++++++++++++++++++++++++--- 7 files changed, 401 insertions(+), 817 deletions(-) delete mode 100644 src/i2c.rs diff --git a/Cargo.lock b/Cargo.lock index c6f11f8..7b47d33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,14 @@ name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cast" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -80,7 +88,7 @@ name = "cortex-m-rt-macros" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -93,7 +101,7 @@ dependencies = [ "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapless 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -102,7 +110,7 @@ name = "cortex-m-rtfm-macros" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", @@ -116,12 +124,21 @@ dependencies = [ "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "embedded-hal" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -129,7 +146,7 @@ name = "generic-array" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -142,12 +159,13 @@ dependencies = [ [[package]] name = "heapless" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", "hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -171,6 +189,11 @@ name = "managed" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nb" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "panic-halt" version = "0.2.0" @@ -187,7 +210,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -198,7 +221,7 @@ name = "quote" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -217,7 +240,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -244,10 +267,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -255,16 +278,16 @@ name = "serde-json-core" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "heapless 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -287,14 +310,15 @@ dependencies = [ "cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heapless 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", "serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)", ] [[package]] @@ -313,19 +337,34 @@ dependencies = [ "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "stm32h7xx-hal" +version = "0.4.0" +source = "git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi#a65de87263d87d5a730d21b5ebeea4bb1886233a" +dependencies = [ + "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "typenum" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -338,6 +377,11 @@ name = "vcell" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "volatile-register" version = "0.2.0" @@ -353,6 +397,7 @@ dependencies = [ "checksum bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" "checksum cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "978caafe65d1023d38b00c76b83564788fc351d954a5005fb72cf992c0d61458" @@ -361,16 +406,18 @@ dependencies = [ "checksum cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf0b9fd3f042cb3793d15daf3cea201b2f25c99b0b5b936a551bb6909c3ae5b" "checksum cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62092f6ff344e9b0adb748f0302ed69889ba2fae1fce446e3788d4726ea73bb" "checksum cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa" +"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" "checksum hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" -"checksum heapless 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10b591a0032f114b7a77d4fbfab452660c553055515b7d7ece355db080d19087" +"checksum heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ffa511365b12346c5fbe759d82f80d3aa70d9f1ba01955594f84a1a6bbab985" "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" +"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" "checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" "checksum panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c" -"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" "checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493" @@ -378,14 +425,16 @@ dependencies = [ "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" +"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" "checksum serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf406405ada9ef326ca78677324ac66994ff348fc48a16030be08caeed29825" -"checksum serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" +"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" "checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b0045066e082648e8a7ab1dd45c92efa8d7bec2beedf72ac7b62563911f82a" +"checksum stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)" = "" "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" diff --git a/Cargo.toml b/Cargo.toml index 66e5451..87e1ffe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,9 +35,14 @@ panic-halt = "0.2" serde = { version = "1.0", features = ["derive"], default-features = false } heapless = "0.5" serde-json-core = "0.1" -stm32h7 = { version = "0.10", features = ["stm32h743v", "rt"] } cortex-m-rtfm = "0.5" smoltcp = { version = "0.6", features = ["ethernet", "proto-ipv4", "socket-tcp"], default-features = false } +embedded-hal = "0.2.3" + +[dependencies.stm32h7xx-hal] +git = "https://github.com/quartiq/stm32h7xx-hal.git" +branch = "feature/quad-spi" +features = ["stm32h743v", "rt"] [features] semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] diff --git a/src/board.rs b/src/board.rs index 572b1c2..eb72b54 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,460 +1,6 @@ -use super::eth; -use super::i2c; use super::pac; -fn pwr_setup(pwr: &pac::PWR) { - // go to VOS1 voltage scale for high perf - pwr.cr3 - .write(|w| w.scuen().set_bit().ldoen().set_bit().bypass().clear_bit()); - while pwr.csr1.read().actvosrdy().bit_is_clear() {} - pwr.d3cr.write(|w| unsafe { w.vos().bits(0b11) }); // vos1 - while pwr.d3cr.read().vosrdy().bit_is_clear() {} -} - -fn rcc_reset(rcc: &pac::RCC) { - // Reset all peripherals - rcc.ahb1rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - rcc.ahb1rstr.write(|w| unsafe { w.bits(0) }); - rcc.apb1lrstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - rcc.apb1lrstr.write(|w| unsafe { w.bits(0) }); - rcc.apb1hrstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - rcc.apb1hrstr.write(|w| unsafe { w.bits(0) }); - - rcc.ahb2rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - rcc.ahb2rstr.write(|w| unsafe { w.bits(0) }); - rcc.apb2rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - rcc.apb2rstr.write(|w| unsafe { w.bits(0) }); - - // do not reset the cpu - rcc.ahb3rstr.write(|w| unsafe { w.bits(0x7FFF_FFFF) }); - rcc.ahb3rstr.write(|w| unsafe { w.bits(0) }); - rcc.apb3rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - rcc.apb3rstr.write(|w| unsafe { w.bits(0) }); - - rcc.ahb4rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - rcc.ahb4rstr.write(|w| unsafe { w.bits(0) }); - rcc.apb4rstr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - rcc.apb4rstr.write(|w| unsafe { w.bits(0) }); -} - -fn rcc_pll_setup(rcc: &pac::RCC, flash: &pac::FLASH) { - // Switch to HSI to mess with HSE - rcc.cr.modify(|_, w| w.hsion().on()); - while rcc.cr.read().hsirdy().is_not_ready() {} - rcc.cfgr.modify(|_, w| w.sw().hsi()); - while !rcc.cfgr.read().sws().is_hsi() {} - rcc.cr.write(|w| w.hsion().on()); - rcc.cfgr.reset(); - - // Ensure HSE is on and stable - rcc.cr.modify(|_, w| w.hseon().on().hsebyp().not_bypassed()); - while !rcc.cr.read().hserdy().is_ready() {} - - rcc.pllckselr.modify( - |_, w| { - w.pllsrc() - .hse() - .divm1() - .bits(1) // ref prescaler - .divm2() - .bits(1) - }, // ref prescaler - ); - // Configure PLL1: 8MHz /1 *100 /2 = 400 MHz - rcc.pllcfgr.modify(|_, w| { - w.pll1vcosel() - .wide_vco() // 192-836 MHz VCO - .pll1rge() - .range8() // 8-16 MHz PFD - .pll1fracen() - .reset() - .divp1en() - .enabled() - .pll2vcosel() - .medium_vco() // 150-420 MHz VCO - .pll2rge() - .range8() // 8-16 MHz PFD - .pll2fracen() - .reset() - .divp2en() - .enabled() - .divq2en() - .enabled() - }); - rcc.pll1divr.write(|w| unsafe { - w.divn1() - .bits(100 - 1) // feebdack divider - .divp1() - .div2() // p output divider - }); - rcc.cr.modify(|_, w| w.pll1on().on()); - while !rcc.cr.read().pll1rdy().is_ready() {} - - // Configure PLL2: 8MHz /1 *25 / 2 = 100 MHz - rcc.pll2divr.write(|w| unsafe { - w.divn2() - .bits(25 - 1) // feebdack divider - .divp2() - .bits(2 - 1) // p output divider - .divq2() - .bits(2 - 1) // q output divider - }); - rcc.cr.modify(|_, w| w.pll2on().on()); - while !rcc.cr.read().pll2rdy().is_ready() {} - - // hclk 200 MHz, pclk 100 MHz - rcc.d1cfgr.write( - |w| { - w.d1cpre() - .div1() // sys_ck not divided - .hpre() - .div2() // rcc_hclk3 = sys_d1cpre_ck / 2 - .d1ppre() - .div2() - }, // rcc_pclk3 = rcc_hclk3 / 2 - ); - rcc.d2cfgr.write( - |w| { - w.d2ppre1() - .div2() // rcc_pclk1 = rcc_hclk3 / 2 - .d2ppre2() - .div2() - }, // rcc_pclk2 = rcc_hclk3 / 2 - ); - rcc.d3cfgr.write( - |w| w.d3ppre().div2(), // rcc_pclk4 = rcc_hclk3 / 2 - ); - - // 2 wait states, 0b10 programming delay - // 185-210 MHz - flash - .acr - .write(|w| unsafe { w.wrhighfreq().bits(2).latency().bits(2) }); - while flash.acr.read().latency().bits() != 2 {} - - // CSI for I/O compensationc ell - rcc.cr.modify(|_, w| w.csion().on()); - while !rcc.cr.read().csirdy().is_ready() {} - - // Set system clock to pll1_p - rcc.cfgr.modify(|_, w| w.sw().pll1()); - while !rcc.cfgr.read().sws().is_pll1() {} - - rcc.d1ccipr.write(|w| w.ckpersel().hse()); - rcc.d2ccip1r - .modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); - rcc.d3ccipr.modify(|_, w| w.spi6sel().pll2_q()); -} - -fn io_compensation_setup(syscfg: &pac::SYSCFG) { - syscfg - .cccsr - .modify(|_, w| w.en().set_bit().cs().clear_bit().hslv().clear_bit()); - while syscfg.cccsr.read().ready().bit_is_clear() {} -} - -fn gpio_setup( - gpioa: &pac::GPIOA, - gpiob: &pac::GPIOB, - gpiod: &pac::GPIOD, - gpioe: &pac::GPIOE, - gpiof: &pac::GPIOF, - gpiog: &pac::GPIOG, -) { - // FP_LED0 - gpiod.otyper.modify(|_, w| w.ot5().push_pull()); - gpiod.moder.modify(|_, w| w.moder5().output()); - gpiod.odr.modify(|_, w| w.odr5().low()); - - // FP_LED1 - gpiod.otyper.modify(|_, w| w.ot6().push_pull()); - gpiod.moder.modify(|_, w| w.moder6().output()); - gpiod.odr.modify(|_, w| w.odr6().low()); - - // LED_FP2 - gpiog.otyper.modify(|_, w| w.ot4().push_pull()); - gpiog.moder.modify(|_, w| w.moder4().output()); - gpiog.odr.modify(|_, w| w.odr4().low()); - - // LED_FP3 - gpiod.otyper.modify(|_, w| w.ot12().push_pull()); - gpiod.moder.modify(|_, w| w.moder12().output()); - gpiod.odr.modify(|_, w| w.odr12().low()); - - // AFE0_A0,1: PG2,PG3 - gpiog - .otyper - .modify(|_, w| w.ot2().push_pull().ot3().push_pull()); - gpiog - .moder - .modify(|_, w| w.moder2().output().moder3().output()); - gpiog.odr.modify(|_, w| w.odr2().low().odr3().low()); - - // ADC0 - // SCK: PG11 - gpiog.moder.modify(|_, w| w.moder11().alternate()); - gpiog.otyper.modify(|_, w| w.ot11().push_pull()); - gpiog.ospeedr.modify(|_, w| w.ospeedr11().very_high_speed()); - gpiog.afrh.modify(|_, w| w.afr11().af5()); - // MOSI: PD7 - // MISO: PA6 - gpioa.moder.modify(|_, w| w.moder6().alternate()); - gpioa.afrl.modify(|_, w| w.afr6().af5()); - // NSS: PG10 - gpiog.moder.modify(|_, w| w.moder10().alternate()); - gpiog.otyper.modify(|_, w| w.ot10().push_pull()); - gpiog.ospeedr.modify(|_, w| w.ospeedr10().very_high_speed()); - gpiog.afrh.modify(|_, w| w.afr10().af5()); - - // DAC0 - // SCK: PB10 - gpiob.moder.modify(|_, w| w.moder10().alternate()); - gpiob.otyper.modify(|_, w| w.ot10().push_pull()); - gpiob.ospeedr.modify(|_, w| w.ospeedr10().very_high_speed()); - gpiob.afrh.modify(|_, w| w.afr10().af5()); - // MOSI: PB15 - gpiob.moder.modify(|_, w| w.moder15().alternate()); - gpiob.otyper.modify(|_, w| w.ot15().push_pull()); - gpiob.ospeedr.modify(|_, w| w.ospeedr15().very_high_speed()); - gpiob.afrh.modify(|_, w| w.afr15().af5()); - // MISO: PB14 - // NSS: PB9 - gpiob.moder.modify(|_, w| w.moder9().alternate()); - gpiob.otyper.modify(|_, w| w.ot9().push_pull()); - gpiob.ospeedr.modify(|_, w| w.ospeedr9().very_high_speed()); - gpiob.afrh.modify(|_, w| w.afr9().af5()); - - // DAC0_LDAC: PE11 - gpioe.moder.modify(|_, w| w.moder11().output()); - gpioe.otyper.modify(|_, w| w.ot11().push_pull()); - gpioe.odr.modify(|_, w| w.odr11().low()); - - // DAC_CLR: PE12 - gpioe.moder.modify(|_, w| w.moder12().output()); - gpioe.otyper.modify(|_, w| w.ot12().push_pull()); - gpioe.odr.modify(|_, w| w.odr12().high()); - - // AFE1_A0,1: PD14,PD15 - gpiod - .otyper - .modify(|_, w| w.ot14().push_pull().ot15().push_pull()); - gpiod - .moder - .modify(|_, w| w.moder14().output().moder15().output()); - gpiod.odr.modify(|_, w| w.odr14().low().odr15().low()); - - // I2C2: SDA,SCL: PF0,PF1 - gpiof - .moder - .modify(|_, w| w.moder0().alternate().moder1().alternate()); - gpiof.afrl.modify(|_, w| w.afr0().af4().afr1().af4()); - gpiof - .otyper - .modify(|_, w| w.ot0().open_drain().ot1().open_drain()); - - // ADC1 - // SCK: PF6 - gpiof.moder.modify(|_, w| w.moder7().alternate()); - gpiof.otyper.modify(|_, w| w.ot7().push_pull()); - gpiof.ospeedr.modify(|_, w| w.ospeedr7().very_high_speed()); - gpiof.afrl.modify(|_, w| w.afr7().af5()); - // MOSI: PF9 - // MISO: PF7 - gpiof.moder.modify(|_, w| w.moder8().alternate()); - gpiof.afrh.modify(|_, w| w.afr8().af5()); - // NSS: PF8 - gpiof.moder.modify(|_, w| w.moder6().alternate()); - gpiof.otyper.modify(|_, w| w.ot6().push_pull()); - gpiof.ospeedr.modify(|_, w| w.ospeedr6().very_high_speed()); - gpiof.afrl.modify(|_, w| w.afr6().af5()); - - // DAC1 - // SCK: PE2 - gpioe.moder.modify(|_, w| w.moder2().alternate()); - gpioe.otyper.modify(|_, w| w.ot2().push_pull()); - gpioe.ospeedr.modify(|_, w| w.ospeedr2().very_high_speed()); - gpioe.afrl.modify(|_, w| w.afr2().af5()); - // MOSI: PE6 - gpioe.moder.modify(|_, w| w.moder6().alternate()); - gpioe.otyper.modify(|_, w| w.ot6().push_pull()); - gpioe.ospeedr.modify(|_, w| w.ospeedr6().very_high_speed()); - gpioe.afrl.modify(|_, w| w.afr6().af5()); - // MISO: PE5 - // NSS: PE4 - gpioe.moder.modify(|_, w| w.moder4().alternate()); - gpioe.otyper.modify(|_, w| w.ot4().push_pull()); - gpioe.ospeedr.modify(|_, w| w.ospeedr4().very_high_speed()); - gpioe.afrl.modify(|_, w| w.afr4().af5()); - - // DAC1_LDAC: PE15 - gpioe.moder.modify(|_, w| w.moder15().output()); - gpioe.otyper.modify(|_, w| w.ot15().push_pull()); - gpioe.odr.modify(|_, w| w.odr15().low()); -} - -// ADC0 -fn spi1_setup(spi1: &pac::SPI1) { - spi1.cfg1 - .modify(|_, w| w.mbr().div4().dsize().bits(16 - 1).fthlv().one_frame()); - spi1.cfg2.modify(|_, w| { - w.afcntr() - .controlled() - .ssom() - .not_asserted() - .ssoe() - .enabled() - .ssiop() - .active_low() - .ssm() - .disabled() - .cpol() - .idle_high() - .cpha() - .second_edge() - .lsbfrst() - .msbfirst() - .master() - .master() - .sp() - .motorola() - .comm() - .receiver() - .ioswp() - .disabled() - .midi() - .bits(0) - .mssi() - .bits(6) - }); - spi1.cr2.modify(|_, w| w.tsize().bits(1)); - spi1.cr1.write(|w| w.spe().enabled()); -} - -// ADC1 -fn spi5_setup(spi5: &pac::SPI5) { - spi5.cfg1 - .modify(|_, w| w.mbr().div4().dsize().bits(16 - 1).fthlv().one_frame()); - spi5.cfg2.modify(|_, w| { - w.afcntr() - .controlled() - .ssom() - .not_asserted() - .ssoe() - .enabled() - .ssiop() - .active_low() - .ssm() - .disabled() - .cpol() - .idle_high() - .cpha() - .second_edge() - .lsbfrst() - .msbfirst() - .master() - .master() - .sp() - .motorola() - .comm() - .receiver() - .ioswp() - .disabled() - .midi() - .bits(0) - .mssi() - .bits(6) - }); - spi5.cr2.modify(|_, w| w.tsize().bits(1)); - spi5.cr1.write(|w| w.spe().enabled()); -} - -// DAC0 -fn spi2_setup(spi2: &pac::SPI2) { - spi2.cfg1 - .modify(|_, w| w.mbr().div2().dsize().bits(16 - 1).fthlv().one_frame()); - spi2.cfg2.modify(|_, w| { - w.afcntr() - .controlled() - .ssom() - .not_asserted() - .ssoe() - .enabled() - .ssiop() - .active_low() - .ssm() - .disabled() - .cpol() - .idle_low() - .cpha() - .first_edge() - .lsbfrst() - .msbfirst() - .master() - .master() - .sp() - .motorola() - .comm() - .transmitter() - .ioswp() - .disabled() - .midi() - .bits(0) - .mssi() - .bits(0) - }); - spi2.cr2.modify(|_, w| w.tsize().bits(0)); - spi2.cr1.write(|w| w.spe().enabled()); - spi2.cr1.modify(|_, w| w.cstart().started()); -} - -// DAC1 -fn spi4_setup(spi4: &pac::SPI4) { - spi4.cfg1 - .modify(|_, w| w.mbr().div2().dsize().bits(16 - 1).fthlv().one_frame()); - spi4.cfg2.modify(|_, w| { - w.afcntr() - .controlled() - .ssom() - .not_asserted() - .ssoe() - .enabled() - .ssiop() - .active_low() - .ssm() - .disabled() - .cpol() - .idle_low() - .cpha() - .first_edge() - .lsbfrst() - .msbfirst() - .master() - .master() - .sp() - .motorola() - .comm() - .transmitter() - .ioswp() - .disabled() - .midi() - .bits(0) - .mssi() - .bits(0) - }); - spi4.cr2.modify(|_, w| w.tsize().bits(0)); - spi4.cr1.write(|w| w.spe().enabled()); - spi4.cr1.modify(|_, w| w.cstart().started()); -} - -fn tim2_setup(tim2: &pac::TIM2) { - tim2.psc.write(|w| w.psc().bits(200 - 1)); // from 200 MHz - tim2.arr.write(|w| unsafe { w.bits(2 - 1) }); // 2 µs - tim2.dier.write(|w| w.ude().set_bit()); - tim2.egr.write(|w| w.ug().set_bit()); - tim2.cr1.modify(|_, w| w.dir().clear_bit()); // up -} - -fn dma1_setup( +pub fn dma1_setup( dma1: &pac::DMA1, dmamux1: &pac::DMAMUX1, ma: usize, @@ -529,113 +75,3 @@ fn dma1_setup( dma1.st[1].fcr.modify(|_, w| w.dmdis().clear_bit()); dma1.st[1].cr.modify(|_, w| w.en().set_bit()); } - -#[link_section = ".sram1.datspi"] -static mut DAT: u32 = 0x201; // EN | CSTART - -pub fn init() { - let mut cp = unsafe { cortex_m::Peripherals::steal() }; - let dp = unsafe { pac::Peripherals::steal() }; - - let rcc = dp.RCC; - rcc_reset(&rcc); - pwr_setup(&dp.PWR); - rcc_pll_setup(&rcc, &dp.FLASH); - rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit()); - io_compensation_setup(&dp.SYSCFG); - - cp.SCB.enable_icache(); - // TODO: ETH DMA coherence issues - // cp.SCB.enable_dcache(&mut cp.CPUID); - cp.DWT.enable_cycle_counter(); // japaric/cortex-m-rtfm#184 - - rcc.ahb4enr.modify(|_, w| { - w.gpioaen() - .set_bit() - .gpioben() - .set_bit() - .gpiocen() - .set_bit() - .gpioden() - .set_bit() - .gpioeen() - .set_bit() - .gpiofen() - .set_bit() - .gpiogen() - .set_bit() - }); - gpio_setup( - &dp.GPIOA, &dp.GPIOB, &dp.GPIOD, &dp.GPIOE, &dp.GPIOF, &dp.GPIOG, - ); - - rcc.apb1lenr.modify(|_, w| w.spi2en().set_bit()); - let spi2 = dp.SPI2; - spi2_setup(&spi2); - - rcc.apb2enr.modify(|_, w| w.spi4en().set_bit()); - let spi4 = dp.SPI4; - spi4_setup(&spi4); - - rcc.apb2enr.modify(|_, w| w.spi1en().set_bit()); - let spi1 = dp.SPI1; - spi1_setup(&spi1); - spi1.ier.write(|w| w.eotie().set_bit()); - - rcc.apb2enr.modify(|_, w| w.spi5en().set_bit()); - let spi5 = dp.SPI5; - spi5_setup(&spi5); - // spi5.ier.write(|w| w.eotie().set_bit()); - - rcc.ahb2enr.modify(|_, w| { - w.sram1en() - .set_bit() - .sram2en() - .set_bit() - .sram3en() - .set_bit() - }); - rcc.ahb1enr.modify(|_, w| w.dma1en().set_bit()); - // init SRAM1 rodata can't load with sram1 disabled - unsafe { DAT = 0x201 }; // EN | CSTART - cortex_m::asm::dsb(); - let dat_addr = unsafe { &DAT as *const _ } as usize; - cp.SCB.clean_dcache_by_address(dat_addr, 4); - - dma1_setup( - &dp.DMA1, - &dp.DMAMUX1, - dat_addr, - &spi1.cr1 as *const _ as usize, - &spi5.cr1 as *const _ as usize, - ); - - rcc.apb1lenr.modify(|_, w| w.tim2en().set_bit()); - - // work around the SPI stall erratum - let dbgmcu = dp.DBGMCU; - dbgmcu.apb1lfz1.modify(|_, w| w.tim2().set_bit()); - - tim2_setup(&dp.TIM2); - - rcc.apb1lenr.modify(|_, w| w.i2c2en().set_bit()); - i2c::setup(&dp.I2C2); - - rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit()); - rcc.ahb1enr.modify(|_, w| { - w.eth1macen() - .set_bit() - .eth1txen() - .set_bit() - .eth1rxen() - .set_bit() - }); - dp.SYSCFG - .pmcr - .modify(|_, w| unsafe { w.epis().bits(0b100) }); // RMII - eth::setup_pins(&dp.GPIOA, &dp.GPIOB, &dp.GPIOC, &dp.GPIOG); - - // enable TIM2 this must be late to be able to handle the first ADC SPI - // interrupt in time - dp.TIM2.cr1.modify(|_, w| w.cen().set_bit()); -} diff --git a/src/eeprom.rs b/src/eeprom.rs index a50dd69..784c0b6 100644 --- a/src/eeprom.rs +++ b/src/eeprom.rs @@ -1,10 +1,11 @@ -use super::i2c; -use stm32h7::stm32h743v as pac; +use embedded_hal::blocking::i2c::WriteRead; -const I2C_ADDR: u8 = 0xa0; +const I2C_ADDR: u8 = 0x50; -pub fn read_eui48(i2c: &pac::I2C2) -> Result<[u8; 6], i2c::Error> { +pub fn read_eui48(i2c: &mut T) -> Result<[u8; 6], T::Error> +where T: WriteRead +{ let mut buffer = [0u8; 6]; - i2c::write_read(i2c, I2C_ADDR, &[0xFAu8], &mut buffer)?; + i2c.write_read(I2C_ADDR, &[0xFA_u8], &mut buffer)?; Ok(buffer) } diff --git a/src/eth.rs b/src/eth.rs index d108982..267a098 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -3,7 +3,7 @@ use smoltcp::phy; use smoltcp::time::Instant; use smoltcp::wire::EthernetAddress; use smoltcp::Result; -use stm32h7::stm32h743v as pac; +use super::{pac}; #[allow(dead_code)] mod phy_consts { @@ -85,50 +85,6 @@ use self::cr_consts::*; // 200 MHz AHB clock = eth_hclk const CLOCK_RANGE: u8 = ETH_MACMIIAR_CR_HCLK_DIV_102; -pub fn setup_pins( - gpioa: &pac::GPIOA, - gpiob: &pac::GPIOB, - gpioc: &pac::GPIOC, - gpiog: &pac::GPIOG, -) { - // PA1 RMII_REF_CLK - gpioa.moder.modify(|_, w| w.moder1().alternate()); - gpioa.afrl.modify(|_, w| w.afr1().af11()); - gpioa.ospeedr.modify(|_, w| w.ospeedr1().very_high_speed()); - // PA2 RMII_MDIO - gpioa.moder.modify(|_, w| w.moder2().alternate()); - gpioa.afrl.modify(|_, w| w.afr2().af11()); - gpioa.ospeedr.modify(|_, w| w.ospeedr2().very_high_speed()); - // PC1 RMII_MDC - gpioc.moder.modify(|_, w| w.moder1().alternate()); - gpioc.afrl.modify(|_, w| w.afr1().af11()); - gpioc.ospeedr.modify(|_, w| w.ospeedr1().very_high_speed()); - // PA7 RMII_CRS_DV - gpioa.moder.modify(|_, w| w.moder7().alternate()); - gpioa.afrl.modify(|_, w| w.afr7().af11()); - gpioa.ospeedr.modify(|_, w| w.ospeedr7().very_high_speed()); - // PC4 RMII_RXD0 - gpioc.moder.modify(|_, w| w.moder4().alternate()); - gpioc.afrl.modify(|_, w| w.afr4().af11()); - gpioc.ospeedr.modify(|_, w| w.ospeedr4().very_high_speed()); - // PC5 RMII_RXD1 - gpioc.moder.modify(|_, w| w.moder5().alternate()); - gpioc.afrl.modify(|_, w| w.afr5().af11()); - gpioc.ospeedr.modify(|_, w| w.ospeedr5().very_high_speed()); - // PB11 RMII_TX_EN - gpiob.moder.modify(|_, w| w.moder11().alternate()); - gpiob.afrh.modify(|_, w| w.afr11().af11()); - gpiob.ospeedr.modify(|_, w| w.ospeedr11().very_high_speed()); - // PB12 RXII_TXD0 - gpiob.moder.modify(|_, w| w.moder12().alternate()); - gpiob.afrh.modify(|_, w| w.afr12().af11()); - gpiob.ospeedr.modify(|_, w| w.ospeedr12().very_high_speed()); - // PG14 RMII TXD1 - gpiog.moder.modify(|_, w| w.moder14().alternate()); - gpiog.afrh.modify(|_, w| w.afr14().af11()); - gpiog.ospeedr.modify(|_, w| w.ospeedr14().very_high_speed()); -} - const PHY_ADDR: u8 = 0; fn phy_read(reg_addr: u8, mac: &pac::ETHERNET_MAC) -> u16 { diff --git a/src/i2c.rs b/src/i2c.rs deleted file mode 100644 index 2118194..0000000 --- a/src/i2c.rs +++ /dev/null @@ -1,145 +0,0 @@ -use stm32h7::stm32h743v as pac; - -// Adapted from stm32h7xx embedded-hal - -// I2C error -#[derive(Debug)] -pub enum Error { - // Bus error - Bus, - // Arbitration loss - Arbitration, - // Address not ACKd within a reasonable time (no device present?) - Timeout, - // Unexpected NACK during transfer - NAck, -} - -// Maximum number of times to retry NACKed address phase before timing out -// Note that many devices indicate a busy condition by NACKing (e.g. 24xx -// EEPROMs during write) -const N_RETRY: usize = 100; // ~ 10ms @ 100 kHz bus clock - -pub fn setup(i2c: &pac::I2C2) { - // Disable the peripheral before setting timings - i2c.cr1.modify(|_, w| w.pe().clear_bit()); - - // Values from STM32MXCube for 100 kHz I2C clock with 100 MHz peripheral clock - i2c.timingr.modify(|_, w| { - w.presc() - .bits(1) - .scldel() - .bits(0x12) - .sdadel() - .bits(0) - .sclh() - .bits(0xec) - .scll() - .bits(0xff) - }); - - // Enable the peripheral - i2c.cr1.write(|w| w.pe().set_bit()); -} - -// Busy-wait for a flag to be asserted, erroring out on unrecoverable problems -macro_rules! busy_wait_errors { - ($i2c:expr, $flag:ident) => { - loop { - let isr = $i2c.isr.read(); - - if isr.berr().bit_is_set() { - return Err(Error::Bus); - } else if isr.arlo().bit_is_set() { - return Err(Error::Arbitration); - } else if isr.nackf().bit_is_set() { - return Err(Error::NAck); - } else if isr.$flag().bit_is_set() { - break; - } - } - }; -} - -fn poll_for_start_ack( - i2c: &pac::I2C2, - addr: u8, - r_wn: bool, - data_len: usize, - autoend: bool, - start: bool, -) -> Result<(), Error> { - for _i in 0..N_RETRY { - // START and prepare to send `data_len` - i2c.cr2.write(|w| { - w.start() - .bit(start) - .sadd() - .bits(addr as u16) - .add10() - .clear_bit() - .rd_wrn() - .bit(r_wn) - .nbytes() - .bits(data_len as u8) - .autoend() - .bit(autoend) - }); - - loop { - let isr = i2c.isr.read(); - - if isr.berr().bit_is_set() { - return Err(Error::Bus); - } else if isr.arlo().bit_is_set() { - return Err(Error::Arbitration); - } else if isr.nackf().bit_is_set() { - i2c.icr.write(|w| w.nackcf().set_bit()); - // Wait to finish handling NACK-STOP - loop { - if i2c.isr.read().busy().bit_is_clear() { - break; - } - } - break; - } else if isr.txis().bit_is_set() || isr.rxne().bit_is_set() { - return Ok(()); - } - } - } - - Err(Error::Timeout) -} - -pub fn write_read( - i2c: &pac::I2C2, - addr: u8, - bytes: &[u8], - buffer: &mut [u8], -) -> Result<(), Error> { - assert!(bytes.len() < 256 && !bytes.is_empty()); - assert!(buffer.len() < 256 && !buffer.is_empty()); - - poll_for_start_ack(i2c, addr, false, bytes.len(), false, true)?; - - for byte in bytes { - // Wait until we are allowed to send data (START has been ACKed or last - // byte when through) - busy_wait_errors!(i2c, txis); - i2c.txdr.write(|w| w.txdata().bits(*byte)); - } - - // Wait until the last transmission is finished - busy_wait_errors!(i2c, tc); - - poll_for_start_ack(i2c, addr | 1, true, buffer.len(), true, true)?; - - for byte in buffer { - // Wait until we have received something - busy_wait_errors!(i2c, rxne); - *byte = i2c.rxdr.read().rxdata().bits(); - } - - // automatic STOP - Ok(()) -} diff --git a/src/main.rs b/src/main.rs index 5cc2497..1050e2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,13 +27,20 @@ extern crate panic_halt; #[macro_use] extern crate log; -use core::ptr; // use core::sync::atomic::{AtomicU32, AtomicBool, Ordering}; use core::fmt::Write; use cortex_m_rt::exception; use heapless::{consts::*, String, Vec}; use rtfm::cyccnt::{Instant, U32Ext as _}; -use stm32h7::stm32h743v as pac; +use stm32h7xx_hal as hal; +use stm32h7xx_hal::{ + prelude::*, + stm32 as pac, +}; + +use embedded_hal::{ + digital::v2::OutputPin, +}; use smoltcp as net; @@ -47,7 +54,6 @@ use iir::*; mod board; mod eeprom; -mod i2c; #[cfg(not(feature = "semihosting"))] fn init_log() {} @@ -94,11 +100,69 @@ macro_rules! create_socket { }; } -#[rtfm::app(device = stm32h7::stm32h743v, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] +type Adc1Spi = hal::spi::Spi< + hal::stm32::SPI2, + ( + hal::gpio::gpiob::PB10>, + hal::gpio::gpiob::PB14>, + hal::spi::NoMosi, + //hal::gpio::gpiob::PB9>, + ) +>; + +type Adc2Spi = hal::spi::Spi< + hal::stm32::SPI3, + ( + hal::gpio::gpioc::PC10>, + hal::gpio::gpiob::PB4>, + hal::spi::NoMosi, + //hal::gpio::gpioa::PA15>, + ) +>; + +type Dac1Spi = hal::spi::Spi< + hal::stm32::SPI4, + ( + hal::gpio::gpioe::PE2>, + hal::gpio::gpioe::PE5>, + hal::spi::NoMosi, + //hal::gpio::gpioe::PE4>, + ) +>; + +type Dac2Spi = hal::spi::Spi< + hal::stm32::SPI5, + ( + hal::gpio::gpiof::PF7>, + hal::gpio::gpiof::PF8>, + hal::spi::NoMosi, + //hal::gpio::gpiof::PF6>, + ) +>; + +type EepromI2c = hal::i2c::I2c< + hal::stm32::I2C2, + ( + hal::gpio::gpiof::PF1>, + hal::gpio::gpiof::PF0>, + ) +>; + +static DAT: u32 = 0x30; // EN | CSTART + +#[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { - spi: (pac::SPI1, pac::SPI2, pac::SPI4, pac::SPI5), - i2c: pac::I2C2, + adc1: Adc1Spi, + dac1: Dac1Spi, + + adc2: Adc2Spi, + dac2: Dac2Spi, + + i2c: EepromI2c, + + // TODO: Add in pounder hardware resources. + ethernet_periph: (pac::ETHERNET_MAC, pac::ETHERNET_DMA, pac::ETHERNET_MTL), #[init([[0.; 5]; 2])] @@ -112,7 +176,213 @@ const APP: () = { #[init(schedule = [tick])] fn init(c: init::Context) -> init::LateResources { - board::init(); + let dp = c.device; + let mut cp = cortex_m::Peripherals::take().unwrap(); + + let pwr = dp.PWR.constrain(); + let vos = pwr.freeze(); + + let rcc = dp.RCC.constrain(); + let mut clocks = rcc + .use_hse(8.mhz()) + .sysclk(400.mhz()) + .hclk(200.mhz()) + .per_ck(100.mhz()) + .pll2_p_ck(100.mhz()) + .pll2_q_ck(100.mhz()) + .freeze(vos, &dp.SYSCFG); + + clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); + + let gpioa = dp.GPIOA.split(&mut clocks.ahb4); + let gpiob = dp.GPIOB.split(&mut clocks.ahb4); + let gpioc = dp.GPIOC.split(&mut clocks.ahb4); + let gpiod = dp.GPIOD.split(&mut clocks.ahb4); + let gpioe = dp.GPIOE.split(&mut clocks.ahb4); + let gpiof = dp.GPIOF.split(&mut clocks.ahb4); + let gpiog = dp.GPIOG.split(&mut clocks.ahb4); + + // Configure the SPI interfaces to the ADCs and DACs. + let adc1_spi = { + let spi_miso = gpiob.pb14.into_alternate_af5(); + let spi_sck = gpiob.pb10.into_alternate_af5(); + let _spi_nss = gpiob.pb9.into_alternate_af5(); + + let config = hal::spi::Config::new() + .set_mode(hal::spi::Mode{ + polarity: hal::spi::Polarity::IdleHigh, + phase: hal::spi::Phase::CaptureOnSecondTransition}) + .cs_delay(220e-9) + .frame_size(16) + .freeze(); + + let spi = hal::spi::Spi::spi2( + dp.SPI2, + (spi_sck, spi_miso, hal::spi::NoMosi), + config, + 25.mhz(), + &clocks); + + //spi.listen(hal::spi::Event::Rxp); + + spi + }; + + let adc2_spi = { + let spi_miso = gpiob.pb4.into_alternate_af6(); + let spi_sck = gpioc.pc10.into_alternate_af6(); + let _spi_nss = gpioa.pa15.into_alternate_af6(); + + let config = hal::spi::Config::new() + .set_mode(hal::spi::Mode{ + polarity: hal::spi::Polarity::IdleHigh, + phase: hal::spi::Phase::CaptureOnSecondTransition}) + .frame_size(16) + .cs_delay(220e-9) + .freeze(); + + let spi = hal::spi::Spi::spi3( + dp.SPI3, + (spi_sck, spi_miso, hal::spi::NoMosi), + config, + 25.mhz(), + &clocks); + + //spi.listen(hal::spi::Event::Rxp); + + spi + }; + + let dac1_spi = { + let spi_miso = gpioe.pe5.into_alternate_af5(); + let spi_sck = gpioe.pe2.into_alternate_af5(); + let _spi_nss = gpioe.pe4.into_alternate_af5(); + + let config = hal::spi::Config::new() + .set_mode(hal::spi::Mode{ + polarity: hal::spi::Polarity::IdleHigh, + phase: hal::spi::Phase::CaptureOnSecondTransition}) + .frame_size(16) + .swap_mosi_miso() + .freeze(); + + let spi = hal::spi::Spi::spi4( + dp.SPI4, + (spi_sck, spi_miso, hal::spi::NoMosi), + config, + 25.mhz(), + &clocks); + + spi + }; + + let dac2_spi = { + let spi_miso = gpiof.pf8.into_alternate_af5(); + let spi_sck = gpiof.pf7.into_alternate_af5(); + let _spi_nss = gpiof.pf6.into_alternate_af5(); + + let config = hal::spi::Config::new() + .set_mode(hal::spi::Mode{ + polarity: hal::spi::Polarity::IdleHigh, + phase: hal::spi::Phase::CaptureOnSecondTransition}) + .frame_size(16) + .swap_mosi_miso() + .freeze(); + + let spi = hal::spi::Spi::spi5( + dp.SPI5, + (spi_sck, spi_miso, hal::spi::NoMosi), + config, + 25.mhz(), + &clocks); + + spi + }; + + // TODO: Configure the DMA + //let mut dma = Dma::new(dp.DMA1, dp.DMAMUX1); + //dma.configure_transfer(DmaTransfer::MemoryToPeripheral, DmaSource::Timer2, trigger_word, + // &adc1_spi.spi.ptr().cr1.register) + //dma.configure_transfer(DmaTransfer::MemoryToPeripheral, DmaSource::Timer2, trigger_word, + // &adc2_spi.spi.ptr().cr1.register) + + // TODO: Configure timer 2 to trigger conversions for the ADC + let _timer2 = hal::timer::Timer::tim2(dp.TIM2, 500.khz(), &mut clocks); + + cortex_m::asm::dsb(); + let dat_addr = &DAT as *const _ as usize; + cp.SCB.clean_dcache_by_address(dat_addr, 4); + + board::dma1_setup( + &dp.DMA1, + &dp.DMAMUX1, + dat_addr, + &adc1_spi.spi.cr1 as *const _ as usize, + &adc2_spi.spi.cr1 as *const _ as usize, + ); + //timer2.enable_event(hal::timer::Event::DmaTransfer); + + // TODO: Configure the ethernet controller + + // Instantiate the QUADSPI pins and peripheral interface. + + // TODO: Place these into a pins structure that is provided to the QSPI constructor. + let _qspi_clk = gpiob.pb2.into_alternate_af9(); + let _qspi_ncs = gpioc.pc11.into_alternate_af9(); + let _qspi_io0 = gpioe.pe7.into_alternate_af10(); + let _qspi_io1 = gpioe.pe8.into_alternate_af10(); + let _qspi_io2 = gpioe.pe9.into_alternate_af10(); + let _qspi_io3 = gpioe.pe10.into_alternate_af10(); + + let mut fp_led_0 = gpiod.pd5.into_push_pull_output(); + let mut fp_led_1 = gpiod.pd6.into_push_pull_output(); + let mut fp_led_2 = gpiod.pd12.into_push_pull_output(); + let mut fp_led_3 = gpiog.pg4.into_push_pull_output(); + + fp_led_0.set_low().unwrap(); + fp_led_1.set_low().unwrap(); + fp_led_2.set_low().unwrap(); + fp_led_3.set_low().unwrap(); + + let _i2c1 = { + let sda = gpiob.pb7.into_alternate_af4().set_open_drain(); + let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); + hal::i2c::I2c::i2c1(dp.I2C1, (scl, sda), 100.khz(), &clocks) + }; + + let i2c2 = { + let sda = gpiof.pf0.into_alternate_af4().set_open_drain(); + let scl = gpiof.pf1.into_alternate_af4().set_open_drain(); + hal::i2c::I2c::i2c2(dp.I2C2, (scl, sda), 100.khz(), &clocks) + }; + + // Configure ethernet pins. + let _rmii_ref_clk = gpioa.pa1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_mdio = gpioa.pa2.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_mdc = gpioc.pc1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_crs_dv = gpioa.pa7.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_rxd0 = gpioc.pc4.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_rxd1 = gpioc.pc5.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_tx_en = gpiob.pb11.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_txd0 = gpiob.pb12.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + + // TODO: This configuration seems to cause a system reset? + //let _rmii_txd1 = gpiog.pg14.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + + // Enable the ethernet peripheral. + clocks.apb4.enr().modify(|_, w| w.syscfgen().set_bit()); + clocks.ahb1.enr().modify(|_, w| { + w.eth1macen().set_bit() + .eth1txen().set_bit() + .eth1rxen().set_bit() + }); + + dp.SYSCFG.pmcr.modify(|_, w| unsafe { w.epis().bits(0b100) }); // RMII + + cp.SCB.enable_icache(); + + // The cycle counter is used for RTFM scheduling. + cp.DWT.enable_cycle_counter(); init_log(); // info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap()); @@ -121,10 +391,13 @@ const APP: () = { // c.schedule.tick(Instant::now()).unwrap(); - let dp = c.device; init::LateResources { - spi: (dp.SPI1, dp.SPI2, dp.SPI4, dp.SPI5), - i2c: dp.I2C2, + adc1: adc1_spi, + dac1: dac1_spi, + adc2: adc2_spi, + dac2: dac2_spi, + + i2c: i2c2, ethernet_periph: ( dp.ETHERNET_MAC, dp.ETHERNET_DMA, @@ -244,40 +517,49 @@ const APP: () = { // seems to slow it down // #[link_section = ".data.spi1"] - #[task(binds = SPI1, resources = [spi, iir_state, iir_ch], priority = 2)] - fn spi1(c: spi1::Context) { + #[task(binds = SPI2, resources = [adc1, dac1, iir_state, iir_ch], priority = 2)] + fn spi2(c: spi2::Context) { #[cfg(feature = "bkpt")] cortex_m::asm::bkpt(); - let (spi1, spi2, spi4, spi5) = c.resources.spi; + let adc = c.resources.adc1; + let dac = c.resources.dac1; let iir_ch = c.resources.iir_ch; let iir_state = c.resources.iir_state; - let sr = spi1.sr.read(); - if sr.eot().bit_is_set() { - spi1.ifcr.write(|w| w.eotc().set_bit()); - } - if sr.rxp().bit_is_set() { - let rxdr = &spi1.rxdr as *const _ as *const u16; - let a = unsafe { ptr::read_volatile(rxdr) }; + // TODO: Doesn't make sense if RXP is unset. + if adc.is_rxp() { + let a: u16 = adc.read().unwrap(); + let x0 = f32::from(a as i16); let y0 = iir_ch[0].update(&mut iir_state[0], x0); let d = y0 as i16 as u16 ^ 0x8000; - let txdr = &spi2.txdr as *const _ as *mut u16; - unsafe { ptr::write_volatile(txdr, d) }; - } - let sr = spi5.sr.read(); - if sr.eot().bit_is_set() { - spi5.ifcr.write(|w| w.eotc().set_bit()); + // TODO: Handle errors. + dac.send(d).unwrap(); } - if sr.rxp().bit_is_set() { - let rxdr = &spi5.rxdr as *const _ as *const u16; - let a = unsafe { ptr::read_volatile(rxdr) }; + #[cfg(feature = "bkpt")] + cortex_m::asm::bkpt(); + } + + #[task(binds = SPI3, resources = [adc2, dac2, iir_state, iir_ch], priority = 2)] + fn spi3(c: spi3::Context) { + #[cfg(feature = "bkpt")] + cortex_m::asm::bkpt(); + let adc = c.resources.adc2; + let dac = c.resources.dac2; + let iir_ch = c.resources.iir_ch; + let iir_state = c.resources.iir_state; + + // TODO: Doesn't make sense if RXP is unset. + if adc.is_rxp() { + let a: u16 = adc.read().unwrap(); + let x0 = f32::from(a as i16); let y0 = iir_ch[1].update(&mut iir_state[1], x0); let d = y0 as i16 as u16 ^ 0x8000; - let txdr = &spi4.txdr as *const _ as *mut u16; - unsafe { ptr::write_volatile(txdr, d) }; + + // TODO: Handle errors. + dac.send(d).unwrap(); } #[cfg(feature = "bkpt")] cortex_m::asm::bkpt(); From d700935246e52bfa2d11909034a85633bd4f5446 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 21 Apr 2020 19:02:52 +0200 Subject: [PATCH 03/40] Adding WIP updates to HAL API --- src/eeprom.rs | 1 + src/main.rs | 251 ++++++++++++++++++++++---------------------------- 2 files changed, 109 insertions(+), 143 deletions(-) diff --git a/src/eeprom.rs b/src/eeprom.rs index 784c0b6..a31a4ff 100644 --- a/src/eeprom.rs +++ b/src/eeprom.rs @@ -2,6 +2,7 @@ use embedded_hal::blocking::i2c::WriteRead; const I2C_ADDR: u8 = 0x50; +#[allow(dead_code)] pub fn read_eui48(i2c: &mut T) -> Result<[u8; 6], T::Error> where T: WriteRead { diff --git a/src/main.rs b/src/main.rs index 1050e2d..47ae262 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,10 +28,8 @@ extern crate panic_halt; extern crate log; // use core::sync::atomic::{AtomicU32, AtomicBool, Ordering}; -use core::fmt::Write; use cortex_m_rt::exception; -use heapless::{consts::*, String, Vec}; -use rtfm::cyccnt::{Instant, U32Ext as _}; +use rtfm::cyccnt::{U32Ext as _}; use stm32h7xx_hal as hal; use stm32h7xx_hal::{ prelude::*, @@ -42,10 +40,13 @@ use embedded_hal::{ digital::v2::OutputPin, }; +/* +use core::fmt::Write; +use heapless::{consts::*, String, Vec}; use smoltcp as net; - use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json_core::{de::from_slice, ser::to_string}; +*/ mod eth; @@ -83,6 +84,7 @@ const SCALE: f32 = ((1 << 15) - 1) as f32; // static ETHERNET_PENDING: AtomicBool = AtomicBool::new(true); +/* const TCP_RX_BUFFER_SIZE: usize = 8192; const TCP_TX_BUFFER_SIZE: usize = 8192; @@ -99,79 +101,32 @@ macro_rules! create_socket { let $target = $set.add(tcp_socket); }; } - -type Adc1Spi = hal::spi::Spi< - hal::stm32::SPI2, - ( - hal::gpio::gpiob::PB10>, - hal::gpio::gpiob::PB14>, - hal::spi::NoMosi, - //hal::gpio::gpiob::PB9>, - ) ->; - -type Adc2Spi = hal::spi::Spi< - hal::stm32::SPI3, - ( - hal::gpio::gpioc::PC10>, - hal::gpio::gpiob::PB4>, - hal::spi::NoMosi, - //hal::gpio::gpioa::PA15>, - ) ->; - -type Dac1Spi = hal::spi::Spi< - hal::stm32::SPI4, - ( - hal::gpio::gpioe::PE2>, - hal::gpio::gpioe::PE5>, - hal::spi::NoMosi, - //hal::gpio::gpioe::PE4>, - ) ->; - -type Dac2Spi = hal::spi::Spi< - hal::stm32::SPI5, - ( - hal::gpio::gpiof::PF7>, - hal::gpio::gpiof::PF8>, - hal::spi::NoMosi, - //hal::gpio::gpiof::PF6>, - ) ->; - -type EepromI2c = hal::i2c::I2c< - hal::stm32::I2C2, - ( - hal::gpio::gpiof::PF1>, - hal::gpio::gpiof::PF0>, - ) ->; +*/ static DAT: u32 = 0x30; // EN | CSTART #[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { - adc1: Adc1Spi, - dac1: Dac1Spi, + adc1: hal::spi::Spi, + dac1: hal::spi::Spi, - adc2: Adc2Spi, - dac2: Dac2Spi, + adc2: hal::spi::Spi, + dac2: hal::spi::Spi, - i2c: EepromI2c, + _eeprom_i2c: hal::i2c::I2c, // TODO: Add in pounder hardware resources. - ethernet_periph: - (pac::ETHERNET_MAC, pac::ETHERNET_DMA, pac::ETHERNET_MTL), + //ethernet_periph: + // (pac::ETHERNET_MAC, pac::ETHERNET_DMA, pac::ETHERNET_MTL), #[init([[0.; 5]; 2])] iir_state: [IIRState; 2], #[init([IIR { ba: [1., 0., 0., 0., 0.], y_offset: 0., y_min: -SCALE - 1., y_max: SCALE }; 2])] iir_ch: [IIR; 2], - #[link_section = ".sram3.eth"] - #[init(eth::Device::new())] - ethernet: eth::Device, + //#[link_section = ".sram3.eth"] + //#[init(eth::Device::new())] + //ethernet: eth::Device, } #[init(schedule = [tick])] @@ -184,7 +139,7 @@ const APP: () = { let rcc = dp.RCC.constrain(); let mut clocks = rcc - .use_hse(8.mhz()) +// .use_hse(8.mhz()) .sysclk(400.mhz()) .hclk(200.mhz()) .per_ck(100.mhz()) @@ -192,6 +147,8 @@ const APP: () = { .pll2_q_ck(100.mhz()) .freeze(vos, &dp.SYSCFG); + clocks.rb.rsr.write(|w| w.rmvf().set_bit()); + clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); let gpioa = dp.GPIOA.split(&mut clocks.ahb4); @@ -203,54 +160,61 @@ const APP: () = { let gpiog = dp.GPIOG.split(&mut clocks.ahb4); // Configure the SPI interfaces to the ADCs and DACs. - let adc1_spi = { + let (adc1_spi, adc1_cr_address) = { let spi_miso = gpiob.pb14.into_alternate_af5(); let spi_sck = gpiob.pb10.into_alternate_af5(); let _spi_nss = gpiob.pb9.into_alternate_af5(); - let config = hal::spi::Config::new() - .set_mode(hal::spi::Mode{ + let _config = hal::spi::Config::new(hal::spi::Mode{ polarity: hal::spi::Polarity::IdleHigh, - phase: hal::spi::Phase::CaptureOnSecondTransition}) + phase: hal::spi::Phase::CaptureOnSecondTransition, + }) + .manage_cs() .cs_delay(220e-9) - .frame_size(16) - .freeze(); + .frame_size(16); - let spi = hal::spi::Spi::spi2( - dp.SPI2, + let cr_address = &dp.SPI2.cr1 as *const _ as usize; + + let mut spi = dp.SPI2.spi( (spi_sck, spi_miso, hal::spi::NoMosi), - config, + hal::spi::MODE_0, //config, 25.mhz(), &clocks); - //spi.listen(hal::spi::Event::Rxp); + spi.listen(hal::spi::Event::Rxp); - spi + (spi, cr_address) }; - let adc2_spi = { + let (adc2_spi, adc2_cr_address) = { let spi_miso = gpiob.pb4.into_alternate_af6(); let spi_sck = gpioc.pc10.into_alternate_af6(); - let _spi_nss = gpioa.pa15.into_alternate_af6(); - let config = hal::spi::Config::new() - .set_mode(hal::spi::Mode{ + let mut cs = gpioa.pa15.into_push_pull_output(); + cs.set_high().unwrap(); + let _spi_nss = cs.into_alternate_af6(); + + let config = hal::spi::Config::new(hal::spi::Mode{ polarity: hal::spi::Polarity::IdleHigh, - phase: hal::spi::Phase::CaptureOnSecondTransition}) + phase: hal::spi::Phase::CaptureOnSecondTransition, + }) + .manage_cs() .frame_size(16) - .cs_delay(220e-9) - .freeze(); + .cs_delay(220e-9); - let spi = hal::spi::Spi::spi3( - dp.SPI3, + let cr_address = &dp.SPI3.cr1 as *const _ as usize; + + let mut spi = dp.SPI3.spi( (spi_sck, spi_miso, hal::spi::NoMosi), config, 25.mhz(), &clocks); - //spi.listen(hal::spi::Event::Rxp); + spi.send(8_u8).unwrap(); - spi + spi.listen(hal::spi::Event::Rxp); + + (spi, cr_address) }; let dac1_spi = { @@ -258,22 +222,15 @@ const APP: () = { let spi_sck = gpioe.pe2.into_alternate_af5(); let _spi_nss = gpioe.pe4.into_alternate_af5(); - let config = hal::spi::Config::new() - .set_mode(hal::spi::Mode{ + let config = hal::spi::Config::new(hal::spi::Mode{ polarity: hal::spi::Polarity::IdleHigh, - phase: hal::spi::Phase::CaptureOnSecondTransition}) + phase: hal::spi::Phase::CaptureOnSecondTransition, + }) + .manage_cs() .frame_size(16) - .swap_mosi_miso() - .freeze(); + .swap_mosi_miso(); - let spi = hal::spi::Spi::spi4( - dp.SPI4, - (spi_sck, spi_miso, hal::spi::NoMosi), - config, - 25.mhz(), - &clocks); - - spi + dp.SPI4.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 25.mhz(), &clocks) }; let dac2_spi = { @@ -281,48 +238,36 @@ const APP: () = { let spi_sck = gpiof.pf7.into_alternate_af5(); let _spi_nss = gpiof.pf6.into_alternate_af5(); - let config = hal::spi::Config::new() - .set_mode(hal::spi::Mode{ + let config = hal::spi::Config::new(hal::spi::Mode{ polarity: hal::spi::Polarity::IdleHigh, - phase: hal::spi::Phase::CaptureOnSecondTransition}) + phase: hal::spi::Phase::CaptureOnSecondTransition, + }) + .manage_cs() .frame_size(16) - .swap_mosi_miso() - .freeze(); + .swap_mosi_miso(); - let spi = hal::spi::Spi::spi5( - dp.SPI5, - (spi_sck, spi_miso, hal::spi::NoMosi), - config, - 25.mhz(), - &clocks); - - spi + dp.SPI5.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 25.mhz(), &clocks) }; - // TODO: Configure the DMA - //let mut dma = Dma::new(dp.DMA1, dp.DMAMUX1); - //dma.configure_transfer(DmaTransfer::MemoryToPeripheral, DmaSource::Timer2, trigger_word, - // &adc1_spi.spi.ptr().cr1.register) - //dma.configure_transfer(DmaTransfer::MemoryToPeripheral, DmaSource::Timer2, trigger_word, - // &adc2_spi.spi.ptr().cr1.register) - - // TODO: Configure timer 2 to trigger conversions for the ADC - let _timer2 = hal::timer::Timer::tim2(dp.TIM2, 500.khz(), &mut clocks); + // Configure timer 2 to trigger conversions for the ADC + let mut timer2 = hal::timer::Timer::tim2(dp.TIM2, 500.khz(), &mut clocks); + //timer2.listen(hal::timer::Event::TimeOut); + timer2.listen(hal::timer::Event::DmaRequest); cortex_m::asm::dsb(); let dat_addr = &DAT as *const _ as usize; cp.SCB.clean_dcache_by_address(dat_addr, 4); + // Enable the DMA. + // TODO: Refactor the DMA API to be a bit cleaner. + clocks.ahb1.enr().modify(|_, w| w.dma1en().set_bit()); board::dma1_setup( &dp.DMA1, &dp.DMAMUX1, dat_addr, - &adc1_spi.spi.cr1 as *const _ as usize, - &adc2_spi.spi.cr1 as *const _ as usize, + adc1_cr_address, + adc2_cr_address, ); - //timer2.enable_event(hal::timer::Event::DmaTransfer); - - // TODO: Configure the ethernet controller // Instantiate the QUADSPI pins and peripheral interface. @@ -347,16 +292,22 @@ const APP: () = { let _i2c1 = { let sda = gpiob.pb7.into_alternate_af4().set_open_drain(); let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); - hal::i2c::I2c::i2c1(dp.I2C1, (scl, sda), 100.khz(), &clocks) + dp.I2C1.i2c((scl, sda), 100.khz(), &clocks) }; let i2c2 = { let sda = gpiof.pf0.into_alternate_af4().set_open_drain(); let scl = gpiof.pf1.into_alternate_af4().set_open_drain(); - hal::i2c::I2c::i2c2(dp.I2C2, (scl, sda), 100.khz(), &clocks) + dp.I2C2.i2c((scl, sda), 100.khz(), &clocks) }; // Configure ethernet pins. + + // Reset the PHY before configuring pins. + let mut eth_phy_nrst = gpioe.pe3.into_push_pull_output(); + eth_phy_nrst.set_high().unwrap(); + eth_phy_nrst.set_low().unwrap(); + eth_phy_nrst.set_high().unwrap(); let _rmii_ref_clk = gpioa.pa1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); let _rmii_mdio = gpioa.pa2.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); let _rmii_mdc = gpioc.pc1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); @@ -365,19 +316,18 @@ const APP: () = { let _rmii_rxd1 = gpioc.pc5.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); let _rmii_tx_en = gpiob.pb11.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); let _rmii_txd0 = gpiob.pb12.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_txd1 = gpiog.pg14.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - // TODO: This configuration seems to cause a system reset? - //let _rmii_txd1 = gpiog.pg14.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - + // TODO: Configure the ethernet controller // Enable the ethernet peripheral. - clocks.apb4.enr().modify(|_, w| w.syscfgen().set_bit()); - clocks.ahb1.enr().modify(|_, w| { - w.eth1macen().set_bit() - .eth1txen().set_bit() - .eth1rxen().set_bit() - }); + //clocks.apb4.enr().modify(|_, w| w.syscfgen().set_bit()); + //clocks.ahb1.enr().modify(|_, w| { + // w.eth1macen().set_bit() + // .eth1txen().set_bit() + // .eth1rxen().set_bit() + //}); - dp.SYSCFG.pmcr.modify(|_, w| unsafe { w.epis().bits(0b100) }); // RMII + //dp.SYSCFG.pmcr.modify(|_, w| unsafe { w.epis().bits(0b100) }); // RMII cp.SCB.enable_icache(); @@ -397,15 +347,22 @@ const APP: () = { adc2: adc2_spi, dac2: dac2_spi, - i2c: i2c2, - ethernet_periph: ( - dp.ETHERNET_MAC, - dp.ETHERNET_DMA, - dp.ETHERNET_MTL, - ), + _eeprom_i2c: i2c2, +// ethernet_periph: ( +// dp.ETHERNET_MAC, +// dp.ETHERNET_DMA, +// dp.ETHERNET_MTL, +// ), } } + #[idle] + fn idle(_c: idle::Context) -> ! { + // TODO Implement and poll ethernet interface. + loop {} + } + + /* #[idle(resources = [ethernet, ethernet_periph, iir_state, iir_ch, i2c])] fn idle(c: idle::Context) -> ! { let (MAC, DMA, MTL) = c.resources.ethernet_periph; @@ -506,6 +463,7 @@ const APP: () = { } } } + */ #[task(priority = 1, schedule = [tick])] fn tick(c: tick::Context) { @@ -515,6 +473,11 @@ const APP: () = { c.schedule.tick(c.scheduled + PERIOD.cycles()).unwrap(); } + #[task(binds = TIM2)] + fn tim2(_c: tim2::Context) { + info!("TIM2 interrupt"); + } + // seems to slow it down // #[link_section = ".data.spi1"] #[task(binds = SPI2, resources = [adc1, dac1, iir_state, iir_ch], priority = 2)] @@ -583,6 +546,7 @@ const APP: () = { } }; +/* #[derive(Deserialize, Serialize)] struct Request { channel: u8, @@ -692,6 +656,7 @@ impl Server { None } } +*/ #[exception] fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! { From 3962f7eb68a10720597e44c6e320e607c9850be4 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 22 Apr 2020 13:36:51 +0200 Subject: [PATCH 04/40] Adding WIP updates to SPI functionality --- src/board.rs | 77 -------------------------------------- src/main.rs | 103 +++++++++++++++++++++++++-------------------------- 2 files changed, 51 insertions(+), 129 deletions(-) delete mode 100644 src/board.rs diff --git a/src/board.rs b/src/board.rs deleted file mode 100644 index eb72b54..0000000 --- a/src/board.rs +++ /dev/null @@ -1,77 +0,0 @@ -use super::pac; - -pub fn dma1_setup( - dma1: &pac::DMA1, - dmamux1: &pac::DMAMUX1, - ma: usize, - pa0: usize, - pa1: usize, -) { - dma1.st[0].cr.modify(|_, w| w.en().clear_bit()); - while dma1.st[0].cr.read().en().bit_is_set() {} - - dma1.st[0].par.write(|w| unsafe { w.bits(pa0 as u32) }); - dma1.st[0].m0ar.write(|w| unsafe { w.bits(ma as u32) }); - dma1.st[0].ndtr.write(|w| w.ndt().bits(1)); - dmamux1.ccr[0].modify(|_, w| w.dmareq_id().tim2_up()); - dma1.st[0].cr.modify(|_, w| { - w.pl() - .medium() - .circ() - .enabled() - .msize() - .bits32() - .minc() - .fixed() - .mburst() - .single() - .psize() - .bits32() - .pinc() - .fixed() - .pburst() - .single() - .dbm() - .disabled() - .dir() - .memory_to_peripheral() - .pfctrl() - .dma() - }); - dma1.st[0].fcr.modify(|_, w| w.dmdis().clear_bit()); - dma1.st[0].cr.modify(|_, w| w.en().set_bit()); - - dma1.st[1].cr.modify(|_, w| w.en().clear_bit()); - while dma1.st[1].cr.read().en().bit_is_set() {} - - dma1.st[1].par.write(|w| unsafe { w.bits(pa1 as u32) }); - dma1.st[1].m0ar.write(|w| unsafe { w.bits(ma as u32) }); - dma1.st[1].ndtr.write(|w| w.ndt().bits(1)); - dmamux1.ccr[1].modify(|_, w| w.dmareq_id().tim2_up()); - dma1.st[1].cr.modify(|_, w| { - w.pl() - .medium() - .circ() - .enabled() - .msize() - .bits32() - .minc() - .fixed() - .mburst() - .single() - .psize() - .bits32() - .pinc() - .fixed() - .pburst() - .single() - .dbm() - .disabled() - .dir() - .memory_to_peripheral() - .pfctrl() - .dma() - }); - dma1.st[1].fcr.modify(|_, w| w.dmdis().clear_bit()); - dma1.st[1].cr.modify(|_, w| w.en().set_bit()); -} diff --git a/src/main.rs b/src/main.rs index 47ae262..601c369 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,6 @@ extern crate log; // use core::sync::atomic::{AtomicU32, AtomicBool, Ordering}; use cortex_m_rt::exception; -use rtfm::cyccnt::{U32Ext as _}; use stm32h7xx_hal as hal; use stm32h7xx_hal::{ prelude::*, @@ -53,7 +52,6 @@ mod eth; mod iir; use iir::*; -mod board; mod eeprom; #[cfg(not(feature = "semihosting"))] @@ -103,9 +101,7 @@ macro_rules! create_socket { } */ -static DAT: u32 = 0x30; // EN | CSTART - -#[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] +#[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true)] const APP: () = { struct Resources { adc1: hal::spi::Spi, @@ -116,6 +112,10 @@ const APP: () = { _eeprom_i2c: hal::i2c::I2c, + dbg_pin: hal::gpio::gpioc::PC6>, + dac_pin: hal::gpio::gpiob::PB15>, + timer: hal::timer::Timer, + // TODO: Add in pounder hardware resources. //ethernet_periph: @@ -129,7 +129,7 @@ const APP: () = { //ethernet: eth::Device, } - #[init(schedule = [tick])] + #[init] fn init(c: init::Context) -> init::LateResources { let dp = c.device; let mut cp = cortex_m::Peripherals::take().unwrap(); @@ -139,6 +139,7 @@ const APP: () = { let rcc = dp.RCC.constrain(); let mut clocks = rcc + //TODO: Re-enable HSE for Stabilizer platform. // .use_hse(8.mhz()) .sysclk(400.mhz()) .hclk(200.mhz()) @@ -160,7 +161,7 @@ const APP: () = { let gpiog = dp.GPIOG.split(&mut clocks.ahb4); // Configure the SPI interfaces to the ADCs and DACs. - let (adc1_spi, adc1_cr_address) = { + let adc1_spi = { let spi_miso = gpiob.pb14.into_alternate_af5(); let spi_sck = gpiob.pb10.into_alternate_af5(); let _spi_nss = gpiob.pb9.into_alternate_af5(); @@ -169,12 +170,11 @@ const APP: () = { polarity: hal::spi::Polarity::IdleHigh, phase: hal::spi::Phase::CaptureOnSecondTransition, }) + .communication_mode(hal::spi::CommunicationMode::Receiver) .manage_cs() .cs_delay(220e-9) .frame_size(16); - let cr_address = &dp.SPI2.cr1 as *const _ as usize; - let mut spi = dp.SPI2.spi( (spi_sck, spi_miso, hal::spi::NoMosi), hal::spi::MODE_0, //config, @@ -183,38 +183,33 @@ const APP: () = { spi.listen(hal::spi::Event::Rxp); - (spi, cr_address) + spi }; - let (adc2_spi, adc2_cr_address) = { + let adc2_spi = { let spi_miso = gpiob.pb4.into_alternate_af6(); let spi_sck = gpioc.pc10.into_alternate_af6(); + let _spi_nss = gpioa.pa15.into_alternate_af6(); - let mut cs = gpioa.pa15.into_push_pull_output(); - cs.set_high().unwrap(); - let _spi_nss = cs.into_alternate_af6(); let config = hal::spi::Config::new(hal::spi::Mode{ polarity: hal::spi::Polarity::IdleHigh, phase: hal::spi::Phase::CaptureOnSecondTransition, }) + .communication_mode(hal::spi::CommunicationMode::Receiver) .manage_cs() .frame_size(16) .cs_delay(220e-9); - let cr_address = &dp.SPI3.cr1 as *const _ as usize; - let mut spi = dp.SPI3.spi( (spi_sck, spi_miso, hal::spi::NoMosi), config, 25.mhz(), &clocks); - spi.send(8_u8).unwrap(); - spi.listen(hal::spi::Event::Rxp); - (spi, cr_address) + spi }; let dac1_spi = { @@ -226,6 +221,7 @@ const APP: () = { polarity: hal::spi::Polarity::IdleHigh, phase: hal::spi::Phase::CaptureOnSecondTransition, }) + .communication_mode(hal::spi::CommunicationMode::Transmitter) .manage_cs() .frame_size(16) .swap_mosi_miso(); @@ -242,6 +238,7 @@ const APP: () = { polarity: hal::spi::Polarity::IdleHigh, phase: hal::spi::Phase::CaptureOnSecondTransition, }) + .communication_mode(hal::spi::CommunicationMode::Transmitter) .manage_cs() .frame_size(16) .swap_mosi_miso(); @@ -249,26 +246,6 @@ const APP: () = { dp.SPI5.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 25.mhz(), &clocks) }; - // Configure timer 2 to trigger conversions for the ADC - let mut timer2 = hal::timer::Timer::tim2(dp.TIM2, 500.khz(), &mut clocks); - //timer2.listen(hal::timer::Event::TimeOut); - timer2.listen(hal::timer::Event::DmaRequest); - - cortex_m::asm::dsb(); - let dat_addr = &DAT as *const _ as usize; - cp.SCB.clean_dcache_by_address(dat_addr, 4); - - // Enable the DMA. - // TODO: Refactor the DMA API to be a bit cleaner. - clocks.ahb1.enr().modify(|_, w| w.dma1en().set_bit()); - board::dma1_setup( - &dp.DMA1, - &dp.DMAMUX1, - dat_addr, - adc1_cr_address, - adc2_cr_address, - ); - // Instantiate the QUADSPI pins and peripheral interface. // TODO: Place these into a pins structure that is provided to the QSPI constructor. @@ -332,14 +309,24 @@ const APP: () = { cp.SCB.enable_icache(); // The cycle counter is used for RTFM scheduling. - cp.DWT.enable_cycle_counter(); + //cp.DWT.enable_cycle_counter(); init_log(); // info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap()); // info!("Built on {}", build_info::BUILT_TIME_UTC); // info!("{} {}", build_info::RUSTC_VERSION, build_info::TARGET); - // c.schedule.tick(Instant::now()).unwrap(); + let mut debug_pin = gpioc.pc6.into_push_pull_output(); + debug_pin.set_low().unwrap(); + + let mut dac_pin = gpiob.pb15.into_push_pull_output(); + dac_pin.set_low().unwrap(); + + // Configure timer 2 to trigger conversions for the ADC + let mut timer2 = dp.TIM2.timer(1.khz(), &mut clocks); + timer2.clear_uif_bit(); + + timer2.listen(hal::timer::Event::TimeOut); init::LateResources { adc1: adc1_spi, @@ -347,6 +334,10 @@ const APP: () = { adc2: adc2_spi, dac2: dac2_spi, + dbg_pin: debug_pin, + dac_pin: dac_pin, + timer: timer2, + _eeprom_i2c: i2c2, // ethernet_periph: ( // dp.ETHERNET_MAC, @@ -465,17 +456,19 @@ const APP: () = { } */ - #[task(priority = 1, schedule = [tick])] - fn tick(c: tick::Context) { - static mut TIME: u32 = 0; - *TIME += 1; - const PERIOD: u32 = 200_000_000; - c.schedule.tick(c.scheduled + PERIOD.cycles()).unwrap(); - } + #[task(binds = TIM2, resources = [dbg_pin, timer, adc1, adc2])] + fn tim2(mut c: tim2::Context) { + c.resources.timer.clear_uif_bit(); - #[task(binds = TIM2)] - fn tim2(_c: tim2::Context) { - info!("TIM2 interrupt"); + c.resources.dbg_pin.set_high().unwrap(); + c.resources.dbg_pin.set_low().unwrap(); + c.resources.dbg_pin.set_high().unwrap(); + c.resources.dbg_pin.set_low().unwrap(); + + // Start a SPI transaction on ADC0 and ADC1 + // TODO: Stagger these requests. + c.resources.adc1.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); + c.resources.adc2.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); } // seems to slow it down @@ -500,11 +493,13 @@ const APP: () = { // TODO: Handle errors. dac.send(d).unwrap(); } + + adc.spi.ifcr.write(|w| w.eotc().set_bit()); #[cfg(feature = "bkpt")] cortex_m::asm::bkpt(); } - #[task(binds = SPI3, resources = [adc2, dac2, iir_state, iir_ch], priority = 2)] + #[task(binds = SPI3, resources = [adc2, dac2, iir_state, iir_ch, dac_pin], priority = 2)] fn spi3(c: spi3::Context) { #[cfg(feature = "bkpt")] cortex_m::asm::bkpt(); @@ -512,6 +507,7 @@ const APP: () = { let dac = c.resources.dac2; let iir_ch = c.resources.iir_ch; let iir_state = c.resources.iir_state; + c.resources.dac_pin.set_high().unwrap(); // TODO: Doesn't make sense if RXP is unset. if adc.is_rxp() { @@ -524,6 +520,9 @@ const APP: () = { // TODO: Handle errors. dac.send(d).unwrap(); } + adc.spi.ifcr.write(|w| w.eotc().set_bit()); + + c.resources.dac_pin.set_low().unwrap(); #[cfg(feature = "bkpt")] cortex_m::asm::bkpt(); } From c6eb4d17579cceded414cc909db7c7abfb4fd99a Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 22 Apr 2020 15:50:07 +0200 Subject: [PATCH 05/40] Adding functional baseline HAL conversion --- Cargo.lock | 5 +- Cargo.toml | 7 ++- src/main.rs | 148 +++++++++++++++++++++------------------------------- 3 files changed, 66 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b47d33..829e231 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -313,12 +313,13 @@ dependencies = [ "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", "serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)", + "stm32h7xx-hal 0.4.0", ] [[package]] @@ -340,7 +341,6 @@ dependencies = [ [[package]] name = "stm32h7xx-hal" version = "0.4.0" -source = "git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi#a65de87263d87d5a730d21b5ebeea4bb1886233a" dependencies = [ "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -431,7 +431,6 @@ dependencies = [ "checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b0045066e082648e8a7ab1dd45c92efa8d7bec2beedf72ac7b62563911f82a" -"checksum stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)" = "" "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/Cargo.toml b/Cargo.toml index 87e1ffe..209a2b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,10 +38,12 @@ serde-json-core = "0.1" cortex-m-rtfm = "0.5" smoltcp = { version = "0.6", features = ["ethernet", "proto-ipv4", "socket-tcp"], default-features = false } embedded-hal = "0.2.3" +nb = "0.1.2" [dependencies.stm32h7xx-hal] -git = "https://github.com/quartiq/stm32h7xx-hal.git" -branch = "feature/quad-spi" +#git = "https://github.com/quartiq/stm32h7xx-hal.git" +#branch = "feature/quad-spi" +path = "../stm32h7xx-hal/" features = ["stm32h743v", "rt"] [features] @@ -52,6 +54,7 @@ nightly = ["cortex-m/inline-asm"] [profile.dev] codegen-units = 1 incremental = false +opt-level = 3 [profile.release] debug = true diff --git a/src/main.rs b/src/main.rs index 601c369..c9d3098 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,8 +27,11 @@ extern crate panic_halt; #[macro_use] extern crate log; +use nb; + // use core::sync::atomic::{AtomicU32, AtomicBool, Ordering}; use cortex_m_rt::exception; +use cortex_m::asm; use stm32h7xx_hal as hal; use stm32h7xx_hal::{ prelude::*, @@ -162,11 +165,11 @@ const APP: () = { // Configure the SPI interfaces to the ADCs and DACs. let adc1_spi = { - let spi_miso = gpiob.pb14.into_alternate_af5(); - let spi_sck = gpiob.pb10.into_alternate_af5(); + let spi_miso = gpiob.pb14.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); + let spi_sck = gpiob.pb10.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); let _spi_nss = gpiob.pb9.into_alternate_af5(); - let _config = hal::spi::Config::new(hal::spi::Mode{ + let config = hal::spi::Config::new(hal::spi::Mode{ polarity: hal::spi::Polarity::IdleHigh, phase: hal::spi::Phase::CaptureOnSecondTransition, }) @@ -177,8 +180,8 @@ const APP: () = { let mut spi = dp.SPI2.spi( (spi_sck, spi_miso, hal::spi::NoMosi), - hal::spi::MODE_0, //config, - 25.mhz(), + config, + 50.mhz(), &clocks); spi.listen(hal::spi::Event::Rxp); @@ -187,8 +190,8 @@ const APP: () = { }; let adc2_spi = { - let spi_miso = gpiob.pb4.into_alternate_af6(); - let spi_sck = gpioc.pc10.into_alternate_af6(); + let spi_miso = gpiob.pb4.into_alternate_af6().set_speed(hal::gpio::Speed::VeryHigh); + let spi_sck = gpioc.pc10.into_alternate_af6().set_speed(hal::gpio::Speed::VeryHigh); let _spi_nss = gpioa.pa15.into_alternate_af6(); @@ -201,14 +204,12 @@ const APP: () = { .frame_size(16) .cs_delay(220e-9); - let mut spi = dp.SPI3.spi( + let spi = dp.SPI3.spi( (spi_sck, spi_miso, hal::spi::NoMosi), config, - 25.mhz(), + 50.mhz(), &clocks); - spi.listen(hal::spi::Event::Rxp); - spi }; @@ -308,9 +309,6 @@ const APP: () = { cp.SCB.enable_icache(); - // The cycle counter is used for RTFM scheduling. - //cp.DWT.enable_cycle_counter(); - init_log(); // info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap()); // info!("Built on {}", build_info::BUILT_TIME_UTC); @@ -323,9 +321,7 @@ const APP: () = { dac_pin.set_low().unwrap(); // Configure timer 2 to trigger conversions for the ADC - let mut timer2 = dp.TIM2.timer(1.khz(), &mut clocks); - timer2.clear_uif_bit(); - + let mut timer2 = dp.TIM2.timer(500.khz(), &mut clocks); timer2.listen(hal::timer::Event::TimeOut); init::LateResources { @@ -347,10 +343,55 @@ const APP: () = { } } + #[task(binds = TIM2, resources = [dbg_pin, timer, adc1, adc2])] + fn tim2(mut c: tim2::Context) { + c.resources.timer.clear_uif_bit(); + c.resources.dbg_pin.set_high().unwrap(); + + // Start a SPI transaction on ADC0 and ADC1 + c.resources.adc1.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); + c.resources.adc2.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); + + c.resources.dbg_pin.set_low().unwrap(); + } + + #[task(binds = SPI2, resources = [adc1, dac1, adc2, dac2, iir_state, iir_ch, dac_pin], priority = 2)] + fn adc_spi(c: spi2::Context) { + #[cfg(feature = "bkpt")] + cortex_m::asm::bkpt(); + + c.resources.dac_pin.set_high().unwrap(); + + let output_ch1 = { + let a: u16 = c.resources.adc1.read().unwrap(); + let x0 = f32::from(a as i16); + let y0 = c.resources.iir_ch[0].update(&mut c.resources.iir_state[0], x0); + y0 as i16 as u16 ^ 0x8000 + }; + c.resources.adc1.spi.ifcr.write(|w| w.eotc().set_bit()); + + let output_ch2 = { + let a: u16 = nb::block!(c.resources.adc2.read()).unwrap(); + let x0 = f32::from(a as i16); + let y0 = c.resources.iir_ch[1].update(&mut c.resources.iir_state[1], x0); + y0 as i16 as u16 ^ 0x8000 + }; + c.resources.adc2.spi.ifcr.write(|w| w.eotc().set_bit()); + + c.resources.dac1.send(output_ch1).unwrap(); + c.resources.dac2.send(output_ch2).unwrap(); + + c.resources.dac_pin.set_low().unwrap(); + #[cfg(feature = "bkpt")] + cortex_m::asm::bkpt(); + } + #[idle] fn idle(_c: idle::Context) -> ! { // TODO Implement and poll ethernet interface. - loop {} + loop { + asm::nop(); + } } /* @@ -456,77 +497,6 @@ const APP: () = { } */ - #[task(binds = TIM2, resources = [dbg_pin, timer, adc1, adc2])] - fn tim2(mut c: tim2::Context) { - c.resources.timer.clear_uif_bit(); - - c.resources.dbg_pin.set_high().unwrap(); - c.resources.dbg_pin.set_low().unwrap(); - c.resources.dbg_pin.set_high().unwrap(); - c.resources.dbg_pin.set_low().unwrap(); - - // Start a SPI transaction on ADC0 and ADC1 - // TODO: Stagger these requests. - c.resources.adc1.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); - c.resources.adc2.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); - } - - // seems to slow it down - // #[link_section = ".data.spi1"] - #[task(binds = SPI2, resources = [adc1, dac1, iir_state, iir_ch], priority = 2)] - fn spi2(c: spi2::Context) { - #[cfg(feature = "bkpt")] - cortex_m::asm::bkpt(); - let adc = c.resources.adc1; - let dac = c.resources.dac1; - let iir_ch = c.resources.iir_ch; - let iir_state = c.resources.iir_state; - - // TODO: Doesn't make sense if RXP is unset. - if adc.is_rxp() { - let a: u16 = adc.read().unwrap(); - - let x0 = f32::from(a as i16); - let y0 = iir_ch[0].update(&mut iir_state[0], x0); - let d = y0 as i16 as u16 ^ 0x8000; - - // TODO: Handle errors. - dac.send(d).unwrap(); - } - - adc.spi.ifcr.write(|w| w.eotc().set_bit()); - #[cfg(feature = "bkpt")] - cortex_m::asm::bkpt(); - } - - #[task(binds = SPI3, resources = [adc2, dac2, iir_state, iir_ch, dac_pin], priority = 2)] - fn spi3(c: spi3::Context) { - #[cfg(feature = "bkpt")] - cortex_m::asm::bkpt(); - let adc = c.resources.adc2; - let dac = c.resources.dac2; - let iir_ch = c.resources.iir_ch; - let iir_state = c.resources.iir_state; - c.resources.dac_pin.set_high().unwrap(); - - // TODO: Doesn't make sense if RXP is unset. - if adc.is_rxp() { - let a: u16 = adc.read().unwrap(); - - let x0 = f32::from(a as i16); - let y0 = iir_ch[1].update(&mut iir_state[1], x0); - let d = y0 as i16 as u16 ^ 0x8000; - - // TODO: Handle errors. - dac.send(d).unwrap(); - } - adc.spi.ifcr.write(|w| w.eotc().set_bit()); - - c.resources.dac_pin.set_low().unwrap(); - #[cfg(feature = "bkpt")] - cortex_m::asm::bkpt(); - } - /* #[task(binds = ETH, resources = [ethernet_periph], priority = 1)] fn eth(c: eth::Context) { From 87858a6e0a12faf41ff71e4cfaa2c6aecd4df075 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 22 Apr 2020 15:59:08 +0200 Subject: [PATCH 06/40] Fixing changes --- .cargo/config | 2 +- Cargo.lock | 4 +++- Cargo.toml | 5 ++--- src/main.rs | 2 +- stabilizer.cfg | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.cargo/config b/.cargo/config index a765d53..382c36e 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "gdb-multiarch -q -x bmp.gdb" +runner = "gdb-multiarch -q -x openocd.gdb" rustflags = ["-C", "link-arg=-Tlink.x"] [build] diff --git a/Cargo.lock b/Cargo.lock index 829e231..3f6481a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,7 +319,7 @@ dependencies = [ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", "serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7xx-hal 0.4.0", + "stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)", ] [[package]] @@ -341,6 +341,7 @@ dependencies = [ [[package]] name = "stm32h7xx-hal" version = "0.4.0" +source = "git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi#4e67198d881a77aa825b4777e8dc270599a45242" dependencies = [ "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -431,6 +432,7 @@ dependencies = [ "checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b0045066e082648e8a7ab1dd45c92efa8d7bec2beedf72ac7b62563911f82a" +"checksum stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)" = "" "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/Cargo.toml b/Cargo.toml index 209a2b9..8d3bc45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,9 +41,8 @@ embedded-hal = "0.2.3" nb = "0.1.2" [dependencies.stm32h7xx-hal] -#git = "https://github.com/quartiq/stm32h7xx-hal.git" -#branch = "feature/quad-spi" -path = "../stm32h7xx-hal/" +git = "https://github.com/quartiq/stm32h7xx-hal.git" +branch = "feature/quad-spi" features = ["stm32h743v", "rt"] [features] diff --git a/src/main.rs b/src/main.rs index c9d3098..cc07801 100644 --- a/src/main.rs +++ b/src/main.rs @@ -356,7 +356,7 @@ const APP: () = { } #[task(binds = SPI2, resources = [adc1, dac1, adc2, dac2, iir_state, iir_ch, dac_pin], priority = 2)] - fn adc_spi(c: spi2::Context) { + fn adc_spi(c: adc_spi::Context) { #[cfg(feature = "bkpt")] cortex_m::asm::bkpt(); diff --git a/stabilizer.cfg b/stabilizer.cfg index ea01d24..51d7c6c 100644 --- a/stabilizer.cfg +++ b/stabilizer.cfg @@ -1,4 +1,4 @@ -source [find interface/stlink-v2.cfg] +source [find interface/stlink.cfg] transport select hla_swd source [find target/stm32h7x_dual_bank.cfg] From abf22676ce1617a6e7d22dd6b7df8eab99f1b283 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 28 Apr 2020 19:07:19 +0200 Subject: [PATCH 07/40] Adding refactor to support pounder hardware abstractions --- Cargo.lock | 94 ++++++++++++ Cargo.toml | 18 ++- src/main.rs | 302 +++++++++++++++++++++++++++---------- src/pounder/attenuators.rs | 54 +++++++ src/pounder/error.rs | 9 ++ src/pounder/mod.rs | 148 ++++++++++++++++++ 6 files changed, 544 insertions(+), 81 deletions(-) create mode 100644 src/pounder/attenuators.rs create mode 100644 src/pounder/error.rs create mode 100644 src/pounder/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 3f6481a..2d9234b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,19 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "ad9959" +version = "0.1.0" +source = "git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver#71f96bab751048aa944490b4b56463551025a21f" +dependencies = [ + "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aligned" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "aligned" version = "0.3.2" @@ -18,6 +32,16 @@ dependencies = [ "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "asm-delay" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitrate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "autocfg" version = "1.0.0" @@ -31,11 +55,21 @@ dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bitrate" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.3.4" @@ -54,6 +88,17 @@ name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cortex-m" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cortex-m" version = "0.6.2" @@ -189,6 +234,17 @@ name = "managed" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "mcp23017" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nb" version = "0.1.2" @@ -299,6 +355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -306,6 +363,8 @@ dependencies = [ name = "stabilizer" version = "0.3.0" dependencies = [ + "ad9959 0.1.0 (git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver)", + "asm-delay 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -313,12 +372,14 @@ dependencies = [ "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mcp23017 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", "serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7-ethernet 0.1.1", "stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)", ] @@ -338,6 +399,16 @@ dependencies = [ "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "stm32h7-ethernet" +version = "0.1.1" +dependencies = [ + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7xx-hal 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "stm32h7xx-hal" version = "0.4.0" @@ -353,6 +424,21 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "stm32h7xx-hal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "1.0.17" @@ -392,14 +478,20 @@ dependencies = [ ] [metadata] +"checksum ad9959 0.1.0 (git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver)" = "" +"checksum aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" "checksum aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" "checksum as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" +"checksum asm-delay 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e0c8eec73de29ae94b2aff405a272304bc286204ddb1cdf20d7e2249078ae20" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum bitrate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0b159a1e8306949579de3698c841dba58058197b65c60807194e4fa1e7a554" "checksum cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" "checksum cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "978caafe65d1023d38b00c76b83564788fc351d954a5005fb72cf992c0d61458" "checksum cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28" @@ -415,6 +507,7 @@ dependencies = [ "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" +"checksum mcp23017 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4854484a74b626165c3f3cf5e15bfee2898c6b4d6eccc3ed5ee634850af4dd" "checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" "checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" "checksum panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c" @@ -433,6 +526,7 @@ dependencies = [ "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b0045066e082648e8a7ab1dd45c92efa8d7bec2beedf72ac7b62563911f82a" "checksum stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)" = "" +"checksum stm32h7xx-hal 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "689aff61a1cf43e03cf821c5540bb540e6bd04b374e486be23b035c0afc3c15f" "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/Cargo.toml b/Cargo.toml index 8d3bc45..e05c09a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,9 +36,25 @@ serde = { version = "1.0", features = ["derive"], default-features = false } heapless = "0.5" serde-json-core = "0.1" cortex-m-rtfm = "0.5" -smoltcp = { version = "0.6", features = ["ethernet", "proto-ipv4", "socket-tcp"], default-features = false } embedded-hal = "0.2.3" nb = "0.1.2" +asm-delay = "0.7.0" +mcp23017 = "0.1.1" + +[dependencies.smoltcp] +version = "0.6" +features = ["ethernet", "proto-ipv4", "socket-tcp", "proto-ipv6"] +default-features = false + +[dependencies.ad9959] +git = "https://github.com/quartiq/ad9959.git" +branch = "feature/basic-driver" + +[dependencies.stm32h7-ethernet] +#git = "https://github.com/quartiq/stm32h7-ethernet.git" +#branch = "feature/device-dependency" +path = "../stm32h7-ethernet" +features = ["stm32h743v"] [dependencies.stm32h7xx-hal] git = "https://github.com/quartiq/stm32h7xx-hal.git" diff --git a/src/main.rs b/src/main.rs index cc07801..1d24d19 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,8 +30,10 @@ extern crate log; use nb; // use core::sync::atomic::{AtomicU32, AtomicBool, Ordering}; +use asm_delay; +use rtfm::cyccnt::{Instant, U32Ext}; use cortex_m_rt::exception; -use cortex_m::asm; +use cortex_m; use stm32h7xx_hal as hal; use stm32h7xx_hal::{ prelude::*, @@ -42,15 +44,31 @@ use embedded_hal::{ digital::v2::OutputPin, }; -/* -use core::fmt::Write; -use heapless::{consts::*, String, Vec}; +use stm32h7_ethernet as ethernet; use smoltcp as net; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json_core::{de::from_slice, ser::to_string}; -*/ + +use core::fmt::Write; +use heapless::{ + consts::*, + String, + //Vec +}; + +use serde::{ + //de::DeserializeOwned, + Deserialize, + Serialize +}; +use serde_json_core::{ + //de::from_slice, + ser::to_string +}; + +#[link_section = ".sram3.eth"] +static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); mod eth; +mod pounder; mod iir; use iir::*; @@ -81,30 +99,26 @@ mod build_info { // include!(concat!(env!("OUT_DIR"), "/built.rs")); } +pub struct NetStorage { + ip_addrs: [net::wire::IpCidr; 1], + neighbor_cache: [Option<(net::wire::IpAddress, net::iface::Neighbor)>; 8], +} + +static mut NET_STORE: NetStorage = NetStorage { + // Placeholder for the real IP address, which is initialized at runtime. + ip_addrs: [net::wire::IpCidr::Ipv6(net::wire::Ipv6Cidr::SOLICITED_NODE_PREFIX)], + + neighbor_cache: [None; 8], +}; + const SCALE: f32 = ((1 << 15) - 1) as f32; // static ETHERNET_PENDING: AtomicBool = AtomicBool::new(true); -/* const TCP_RX_BUFFER_SIZE: usize = 8192; const TCP_TX_BUFFER_SIZE: usize = 8192; -macro_rules! create_socket { - ($set:ident, $rx_storage:ident, $tx_storage:ident, $target:ident) => { - let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE]; - let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE]; - let tcp_rx_buffer = - net::socket::TcpSocketBuffer::new(&mut $rx_storage[..]); - let tcp_tx_buffer = - net::socket::TcpSocketBuffer::new(&mut $tx_storage[..]); - let tcp_socket = - net::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); - let $target = $set.add(tcp_socket); - }; -} -*/ - -#[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true)] +#[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { adc1: hal::spi::Spi, @@ -113,23 +127,22 @@ const APP: () = { adc2: hal::spi::Spi, dac2: hal::spi::Spi, - _eeprom_i2c: hal::i2c::I2c, + eeprom_i2c: hal::i2c::I2c, dbg_pin: hal::gpio::gpioc::PC6>, dac_pin: hal::gpio::gpiob::PB15>, timer: hal::timer::Timer, + net_interface: net::iface::EthernetInterface<'static, 'static, 'static, + ethernet::EthernetDMA<'static>>, + _eth_mac: ethernet::EthernetMAC, + mac_addr: net::wire::EthernetAddress, - // TODO: Add in pounder hardware resources. + pounder: pounder::PounderDevices, - //ethernet_periph: - // (pac::ETHERNET_MAC, pac::ETHERNET_DMA, pac::ETHERNET_MTL), #[init([[0.; 5]; 2])] iir_state: [IIRState; 2], #[init([IIR { ba: [1., 0., 0., 0., 0.], y_offset: 0., y_min: -SCALE - 1., y_max: SCALE }; 2])] iir_ch: [IIR; 2], - //#[link_section = ".sram3.eth"] - //#[init(eth::Device::new())] - //ethernet: eth::Device, } #[init] @@ -151,6 +164,9 @@ const APP: () = { .pll2_q_ck(100.mhz()) .freeze(vos, &dp.SYSCFG); + // Enable SRAM3 for the ethernet descriptor ring. + clocks.rb.ahb2enr.modify(|_, w| w.sram3en().set_bit()); + clocks.rb.rsr.write(|w| w.rmvf().set_bit()); clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); @@ -247,15 +263,63 @@ const APP: () = { dp.SPI5.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 25.mhz(), &clocks) }; - // Instantiate the QUADSPI pins and peripheral interface. + let pounder_devices = { + let ad9959 = { + let qspi_interface = { + // Instantiate the QUADSPI pins and peripheral interface. + // TODO: Place these into a pins structure that is provided to the QSPI + // constructor. + let _qspi_clk = gpiob.pb2.into_alternate_af9(); + let _qspi_ncs = gpioc.pc11.into_alternate_af9(); + let _qspi_io0 = gpioe.pe7.into_alternate_af10(); + let _qspi_io1 = gpioe.pe8.into_alternate_af10(); + let _qspi_io2 = gpioe.pe9.into_alternate_af10(); + let _qspi_io3 = gpioe.pe10.into_alternate_af10(); - // TODO: Place these into a pins structure that is provided to the QSPI constructor. - let _qspi_clk = gpiob.pb2.into_alternate_af9(); - let _qspi_ncs = gpioc.pc11.into_alternate_af9(); - let _qspi_io0 = gpioe.pe7.into_alternate_af10(); - let _qspi_io1 = gpioe.pe8.into_alternate_af10(); - let _qspi_io2 = gpioe.pe9.into_alternate_af10(); - let _qspi_io3 = gpioe.pe10.into_alternate_af10(); + let qspi = hal::qspi::Qspi::new(dp.QUADSPI, &mut clocks, 10.mhz()).unwrap(); + pounder::QspiInterface {qspi} + }; + + let mut reset_pin = gpioa.pa0.into_push_pull_output(); + let io_update = gpiog.pg7.into_push_pull_output(); + + + let delay = { + let frequency_hz = clocks.clocks.c_ck().0; + asm_delay::AsmDelay::new(asm_delay::bitrate::Hertz (frequency_hz)) + }; + + ad9959::Ad9959::new(qspi_interface, + &mut reset_pin, + io_update, + delay, + ad9959::Mode::FourBitSerial, + 100_000_000).unwrap() + }; + + let io_expander = { + let sda = gpiob.pb7.into_alternate_af4().set_open_drain(); + let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); + let i2c1 = dp.I2C1.i2c((scl, sda), 100.khz(), &clocks); + mcp23017::MCP23017::default(i2c1).unwrap() + }; + + let spi = { + let spi_mosi = gpiod.pd7.into_alternate_af5(); + let spi_miso = gpioa.pa6.into_alternate_af5(); + let spi_sck = gpiog.pg11.into_alternate_af5(); + + let config = hal::spi::Config::new(hal::spi::Mode{ + polarity: hal::spi::Polarity::IdleHigh, + phase: hal::spi::Phase::CaptureOnSecondTransition, + }) + .frame_size(8); + + dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 25.mhz(), &clocks) + }; + + pounder::PounderDevices::new(io_expander, ad9959, spi).unwrap() + }; let mut fp_led_0 = gpiod.pd5.into_push_pull_output(); let mut fp_led_1 = gpiod.pd6.into_push_pull_output(); @@ -267,45 +331,63 @@ const APP: () = { fp_led_2.set_low().unwrap(); fp_led_3.set_low().unwrap(); - let _i2c1 = { - let sda = gpiob.pb7.into_alternate_af4().set_open_drain(); - let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); - dp.I2C1.i2c((scl, sda), 100.khz(), &clocks) - }; - - let i2c2 = { + let mut eeprom_i2c = { let sda = gpiof.pf0.into_alternate_af4().set_open_drain(); let scl = gpiof.pf1.into_alternate_af4().set_open_drain(); dp.I2C2.i2c((scl, sda), 100.khz(), &clocks) }; // Configure ethernet pins. + { + // Reset the PHY before configuring pins. + let mut eth_phy_nrst = gpioe.pe3.into_push_pull_output(); + eth_phy_nrst.set_high().unwrap(); + eth_phy_nrst.set_low().unwrap(); + eth_phy_nrst.set_high().unwrap(); + let _rmii_ref_clk = gpioa.pa1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_mdio = gpioa.pa2.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_mdc = gpioc.pc1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_crs_dv = gpioa.pa7.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_rxd0 = gpioc.pc4.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_rxd1 = gpioc.pc5.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_tx_en = gpiob.pb11.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_txd0 = gpiob.pb12.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let _rmii_txd1 = gpiog.pg14.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + } - // Reset the PHY before configuring pins. - let mut eth_phy_nrst = gpioe.pe3.into_push_pull_output(); - eth_phy_nrst.set_high().unwrap(); - eth_phy_nrst.set_low().unwrap(); - eth_phy_nrst.set_high().unwrap(); - let _rmii_ref_clk = gpioa.pa1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - let _rmii_mdio = gpioa.pa2.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - let _rmii_mdc = gpioc.pc1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - let _rmii_crs_dv = gpioa.pa7.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - let _rmii_rxd0 = gpioc.pc4.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - let _rmii_rxd1 = gpioc.pc5.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - let _rmii_tx_en = gpiob.pb11.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - let _rmii_txd0 = gpiob.pb12.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); - let _rmii_txd1 = gpiog.pg14.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); + let mac_addr = match eeprom::read_eui48(&mut eeprom_i2c) { + Err(_) => { + info!("Could not read EEPROM, using default MAC address"); + net::wire::EthernetAddress([0x10, 0xE2, 0xD5, 0x00, 0x03, 0x00]) + } + Ok(raw_mac) => net::wire::EthernetAddress(raw_mac), + }; - // TODO: Configure the ethernet controller - // Enable the ethernet peripheral. - //clocks.apb4.enr().modify(|_, w| w.syscfgen().set_bit()); - //clocks.ahb1.enr().modify(|_, w| { - // w.eth1macen().set_bit() - // .eth1txen().set_bit() - // .eth1rxen().set_bit() - //}); + let (network_interface, eth_mac) = { + // Configure the ethernet controller + let (eth_dma, eth_mac) = unsafe { + ethernet::ethernet_init( + dp.ETHERNET_MAC, + dp.ETHERNET_MTL, + dp.ETHERNET_DMA, + &mut DES_RING, + mac_addr.clone()) + }; - //dp.SYSCFG.pmcr.modify(|_, w| unsafe { w.epis().bits(0b100) }); // RMII + let store = unsafe { &mut NET_STORE }; + + store.ip_addrs[0] = net::wire::IpCidr::new(net::wire::IpAddress::v4(10, 0, 16, 99), 24); + + let neighbor_cache = net::iface::NeighborCache::new(&mut store.neighbor_cache[..]); + + let interface = net::iface::EthernetInterfaceBuilder::new(eth_dma) + .ethernet_addr(mac_addr) + .neighbor_cache(neighbor_cache) + .ip_addrs(&mut store.ip_addrs[..]) + .finalize(); + + (interface, eth_mac) + }; cp.SCB.enable_icache(); @@ -314,6 +396,9 @@ const APP: () = { // info!("Built on {}", build_info::BUILT_TIME_UTC); // info!("{} {}", build_info::RUSTC_VERSION, build_info::TARGET); + // Utilize the cycle counter for RTFM scheduling. + cp.DWT.enable_cycle_counter(); + let mut debug_pin = gpioc.pc6.into_push_pull_output(); debug_pin.set_low().unwrap(); @@ -333,13 +418,12 @@ const APP: () = { dbg_pin: debug_pin, dac_pin: dac_pin, timer: timer2, + pounder: pounder_devices, - _eeprom_i2c: i2c2, -// ethernet_periph: ( -// dp.ETHERNET_MAC, -// dp.ETHERNET_DMA, -// dp.ETHERNET_MTL, -// ), + eeprom_i2c: eeprom_i2c, + net_interface: network_interface, + _eth_mac: eth_mac, + mac_addr: mac_addr, } } @@ -386,11 +470,69 @@ const APP: () = { cortex_m::asm::bkpt(); } - #[idle] - fn idle(_c: idle::Context) -> ! { - // TODO Implement and poll ethernet interface. + #[idle(resources=[net_interface, mac_addr, iir_state])] + fn idle(mut c: idle::Context) -> ! { + + let interface = c.resources.net_interface; + let mut socket_set_entries: [_; 8] = Default::default(); + let mut sockets = net::socket::SocketSet::new(&mut socket_set_entries[..]); + + let mut rx_storage = [0; TCP_RX_BUFFER_SIZE]; + let mut tx_storage = [0; TCP_TX_BUFFER_SIZE]; + let tcp_handle0 = { + let tcp_rx_buffer = net::socket::TcpSocketBuffer::new(&mut rx_storage[..]); + let tcp_tx_buffer = net::socket::TcpSocketBuffer::new(&mut tx_storage[..]); + let tcp_socket = net::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); + sockets.add(tcp_socket) + }; + + let mut time = 0u32; + let mut next_ms = Instant::now(); + + // TODO: Replace with reference to CPU clock from CCDR. + next_ms += 400_000.cycles(); + loop { - asm::nop(); + let tick = Instant::now() > next_ms; + + if tick { + next_ms += 400_000.cycles(); + time += 1; + } + + { + let mut socket = sockets.get::(tcp_handle0); + if socket.state() == net::socket::TcpState::CloseWait { + socket.close(); + } else if !(socket.is_open() || socket.is_listening()) { + socket + .listen(1234) + .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); + } else if tick && socket.can_send() { + let s = c.resources.iir_state.lock(|iir_state| Status { + t: time, + x0: iir_state[0][0], + y0: iir_state[0][2], + x1: iir_state[1][0], + y1: iir_state[1][2], + }); + json_reply(&mut socket, &s); + } + } + + let sleep = match interface.poll(&mut sockets, + net::time::Instant::from_millis(time as i64)) { + Ok(changed) => changed, + Err(net::Error::Unrecognized) => true, + Err(e) => { + info!("iface poll error: {:?}", e); + true + } + }; + + if sleep { + cortex_m::asm::wfi(); + } } } @@ -515,7 +657,6 @@ const APP: () = { } }; -/* #[derive(Deserialize, Serialize)] struct Request { channel: u8, @@ -543,6 +684,7 @@ fn json_reply(socket: &mut net::socket::TcpSocket, msg: &T) { socket.write_str(&u).unwrap(); } +/* struct Server { data: Vec, discard: bool, diff --git a/src/pounder/attenuators.rs b/src/pounder/attenuators.rs new file mode 100644 index 0000000..b8569d8 --- /dev/null +++ b/src/pounder/attenuators.rs @@ -0,0 +1,54 @@ +use super::error::Error; + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +pub enum Channel { + One = 0, + Two = 1, + Three = 2, + Four = 3, +} + +pub trait AttenuatorInterface { + fn modify(&mut self, attenuation: f32, channel: Channel) -> Result { + if attenuation > 31.5 { + return Err(Error::Bounds); + } + + // Calculate the attenuation code to program into the attenuator. + let attenuation_code = (attenuation * 2.0) as u8; + + // Read all the channels, modify the channel of interest, and write all the channels back. + // This ensures the staging register and the output register are always in sync. + let mut channels = [0_u8; 4]; + self.read_all(&mut channels)?; + channels[channel as usize] = attenuation_code; + self.write_all(&channels)?; + + // Finally, latch the output of the updated channel to force it into an active state. + self.latch(channel)?; + + Ok(attenuation_code as f32 / 2.0) + } + + fn read(&mut self, channel: Channel) -> Result { + let mut channels = [0_u8; 4]; + + // Reading the data always shifts data out of the staging registers, so we perform a + // duplicate write-back to ensure the staging register is always equal to the output + // register. + self.read_all(&mut channels)?; + self.write_all(&channels)?; + + // Convert the desired channel code into dB of attenuation. + let attenuation_code = channels[channel as usize]; + + Ok(attenuation_code as f32 / 2.0) + } + + fn reset(&mut self) -> Result<(), Error>; + + fn latch(&mut self, channel: Channel) -> Result<(), Error>; + fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>; + fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error>; +} diff --git a/src/pounder/error.rs b/src/pounder/error.rs new file mode 100644 index 0000000..56a9e09 --- /dev/null +++ b/src/pounder/error.rs @@ -0,0 +1,9 @@ +#[derive(Debug)] +pub enum Error { + Spi, + I2c, + DDS, + Qspi, + Bounds, + InvalidAddress, +} diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs new file mode 100644 index 0000000..bcd24bd --- /dev/null +++ b/src/pounder/mod.rs @@ -0,0 +1,148 @@ +use mcp23017; +use ad9959; + +pub mod error; +pub mod attenuators; + +use super::hal; + +use error::Error; +use attenuators::{AttenuatorInterface, Channel}; + +use embedded_hal::blocking::spi::Transfer; + +#[allow(dead_code)] +const OSC_EN_N_PIN: u8 = 8 + 7; + +const EXT_CLK_SEL_PIN: u8 = 8 + 6; + +const ATT_RST_N_PIN: u8 = 8 + 5; + +const ATT_LE0_PIN: u8 = 8 + 0; +const ATT_LE1_PIN: u8 = 8 + 1; +const ATT_LE2_PIN: u8 = 8 + 2; +const ATT_LE3_PIN: u8 = 8 + 3; + +pub struct QspiInterface { + pub qspi: hal::qspi::Qspi, +} + +impl ad9959::Interface for QspiInterface { + type Error = Error; + + fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> { + let result = match mode { + ad9959::Mode::SingleBitTwoWire | ad9959::Mode::SingleBitThreeWire => + self.qspi.configure_mode(hal::qspi::QspiMode::OneBit), + ad9959::Mode::TwoBitSerial => self.qspi.configure_mode(hal::qspi::QspiMode::TwoBit), + ad9959::Mode::FourBitSerial => self.qspi.configure_mode(hal::qspi::QspiMode::FourBit), + }; + + result.map_err(|_| Error::Qspi) + } + + fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> { + if (addr & 0x80) != 0 { + return Err(Error::InvalidAddress); + } + + self.qspi.write(addr, &data).map_err(|_| Error::Qspi) + } + + fn read(&mut self, addr: u8, mut dest: &mut [u8]) -> Result<(), Error> { + if (addr & 0x80) != 0 { + return Err(Error::InvalidAddress); + } + self.qspi.read(0x80_u8 | addr, &mut dest).map_err(|_| Error::Qspi) + } +} + +pub struct PounderDevices { + pub ad9959: ad9959::Ad9959>>, + mcp23017: mcp23017::MCP23017>, + attenuator_spi: hal::spi::Spi +} + +impl PounderDevices +where + DELAY: embedded_hal::blocking::delay::DelayMs, +{ + pub fn new(mcp23017: mcp23017::MCP23017>, + ad9959: ad9959::Ad9959>>, + attenuator_spi: hal::spi::Spi) -> Result { + let mut devices = Self { + mcp23017, + ad9959, + attenuator_spi + }; + + // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator + // selected, attenuators out of reset. + devices.mcp23017.write_gpioa(0xF).map_err(|_| Error::I2c)?; + devices.mcp23017.write_gpiob(1_u8.wrapping_shl(5)).map_err(|_| Error::I2c)?; + devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?; + + devices.select_onboard_clock()?; + + Ok(devices) + } + + pub fn select_external_clock(&mut self, frequency: u32) -> Result<(), Error>{ + self.mcp23017.digital_write(EXT_CLK_SEL_PIN, 1).map_err(|_| Error::I2c)?; + self.ad9959.set_clock_frequency(frequency).map_err(|_| Error::DDS)?; + + Ok(()) + } + + pub fn select_onboard_clock(&mut self) -> Result<(), Error> { + self.mcp23017.digital_write(EXT_CLK_SEL_PIN, 0).map_err(|_| Error::I2c)?; + self.ad9959.set_clock_frequency(100_000_000).map_err(|_| Error::DDS)?; + + Ok(()) + } +} + +impl AttenuatorInterface for PounderDevices +{ + fn reset(&mut self) -> Result<(), Error> { + self.mcp23017.digital_write(ATT_RST_N_PIN, 1).map_err(|_| Error::I2c)?; + // TODO: Delay here. + self.mcp23017.digital_write(ATT_RST_N_PIN, 0).map_err(|_| Error::I2c)?; + + Ok(()) + } + + fn latch(&mut self, channel: Channel) -> Result<(), Error> { + let pin = match channel { + Channel::One => ATT_LE0_PIN, + Channel::Two => ATT_LE1_PIN, + Channel::Three => ATT_LE2_PIN, + Channel::Four => ATT_LE3_PIN, + }; + + self.mcp23017.digital_write(pin, 1).map_err(|_| Error::I2c)?; + // TODO: Delay here. + self.mcp23017.digital_write(pin, 0).map_err(|_| Error::I2c)?; + + Ok(()) + } + + fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> { + self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?; + + Ok(()) + } + + fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error> { + let mut result = [0_u8; 4]; + result.clone_from_slice(channels); + self.attenuator_spi.transfer(&mut result).map_err(|_| Error::Spi)?; + + Ok(()) + } +} From b52a09e58b5b032493d12e62f4f453d6ab6e9566 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 28 Apr 2020 19:08:12 +0200 Subject: [PATCH 08/40] Fixing Toml file --- Cargo.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e05c09a..725e36f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,10 +51,8 @@ git = "https://github.com/quartiq/ad9959.git" branch = "feature/basic-driver" [dependencies.stm32h7-ethernet] -#git = "https://github.com/quartiq/stm32h7-ethernet.git" -#branch = "feature/device-dependency" -path = "../stm32h7-ethernet" -features = ["stm32h743v"] +git = "https://github.com/quartiq/stm32h7-ethernet.git" +branch = "feature/device-dependency" [dependencies.stm32h7xx-hal] git = "https://github.com/quartiq/stm32h7xx-hal.git" From b49596c96cca90e80eaa026d70b33072fb176c84 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 28 Apr 2020 19:15:00 +0200 Subject: [PATCH 09/40] Adding JSON API --- Cargo.lock | 4 +- Cargo.toml | 1 + src/main.rs | 147 +++++++++++++--------------------------------------- 3 files changed, 40 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d9234b..d3952f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -379,7 +379,7 @@ dependencies = [ "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", "serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7-ethernet 0.1.1", + "stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git?branch=feature/device-dependency)", "stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)", ] @@ -402,6 +402,7 @@ dependencies = [ [[package]] name = "stm32h7-ethernet" version = "0.1.1" +source = "git+https://github.com/quartiq/stm32h7-ethernet.git?branch=feature/device-dependency#acb5bbb23d278e614f5ea4a3cd8a28f19540fc2c" dependencies = [ "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -525,6 +526,7 @@ dependencies = [ "checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b0045066e082648e8a7ab1dd45c92efa8d7bec2beedf72ac7b62563911f82a" +"checksum stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git?branch=feature/device-dependency)" = "" "checksum stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)" = "" "checksum stm32h7xx-hal 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "689aff61a1cf43e03cf821c5540bb540e6bd04b374e486be23b035c0afc3c15f" "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" diff --git a/Cargo.toml b/Cargo.toml index 725e36f..6a0e10a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ branch = "feature/basic-driver" [dependencies.stm32h7-ethernet] git = "https://github.com/quartiq/stm32h7-ethernet.git" +features = ["stm32h743v"] branch = "feature/device-dependency" [dependencies.stm32h7xx-hal] diff --git a/src/main.rs b/src/main.rs index 1d24d19..3d09731 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,16 +51,16 @@ use core::fmt::Write; use heapless::{ consts::*, String, - //Vec + Vec }; use serde::{ - //de::DeserializeOwned, + de::DeserializeOwned, Deserialize, Serialize }; use serde_json_core::{ - //de::from_slice, + de::from_slice, ser::to_string }; @@ -470,10 +470,9 @@ const APP: () = { cortex_m::asm::bkpt(); } - #[idle(resources=[net_interface, mac_addr, iir_state])] + #[idle(resources=[net_interface, mac_addr, iir_state, iir_ch])] fn idle(mut c: idle::Context) -> ! { - let interface = c.resources.net_interface; let mut socket_set_entries: [_; 8] = Default::default(); let mut sockets = net::socket::SocketSet::new(&mut socket_set_entries[..]); @@ -486,6 +485,17 @@ const APP: () = { sockets.add(tcp_socket) }; + let mut rx_storage2 = [0; TCP_RX_BUFFER_SIZE]; + let mut tx_storage2 = [0; TCP_TX_BUFFER_SIZE]; + let tcp_handle1 = { + let tcp_rx_buffer = net::socket::TcpSocketBuffer::new(&mut rx_storage2[..]); + let tcp_tx_buffer = net::socket::TcpSocketBuffer::new(&mut tx_storage2[..]); + let tcp_socket = net::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); + sockets.add(tcp_socket) + }; + + let mut server = Server::new(); + let mut time = 0u32; let mut next_ms = Instant::now(); @@ -520,7 +530,27 @@ const APP: () = { } } - let sleep = match interface.poll(&mut sockets, + { + let socket = + &mut *sockets.get::(tcp_handle1); + if socket.state() == net::socket::TcpState::CloseWait { + socket.close(); + } else if !(socket.is_open() || socket.is_listening()) { + socket + .listen(1235) + .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); + } else { + server.poll(socket, |req: &Request| { + if req.channel < 2 { + c.resources.iir_ch.lock(|iir_ch| { + iir_ch[req.channel as usize] = req.iir + }); + } + }); + } + } + + let sleep = match c.resources.net_interface.poll(&mut sockets, net::time::Instant::from_millis(time as i64)) { Ok(changed) => changed, Err(net::Error::Unrecognized) => true, @@ -536,109 +566,6 @@ const APP: () = { } } - /* - #[idle(resources = [ethernet, ethernet_periph, iir_state, iir_ch, i2c])] - fn idle(c: idle::Context) -> ! { - let (MAC, DMA, MTL) = c.resources.ethernet_periph; - - let hardware_addr = match eeprom::read_eui48(c.resources.i2c) { - Err(_) => { - info!("Could not read EEPROM, using default MAC address"); - net::wire::EthernetAddress([0x10, 0xE2, 0xD5, 0x00, 0x03, 0x00]) - } - Ok(raw_mac) => net::wire::EthernetAddress(raw_mac), - }; - info!("MAC: {}", hardware_addr); - - unsafe { c.resources.ethernet.init(hardware_addr, MAC, DMA, MTL) }; - let mut neighbor_cache_storage = [None; 8]; - let neighbor_cache = - net::iface::NeighborCache::new(&mut neighbor_cache_storage[..]); - let local_addr = net::wire::IpAddress::v4(10, 0, 16, 99); - let mut ip_addrs = [net::wire::IpCidr::new(local_addr, 24)]; - let mut iface = - net::iface::EthernetInterfaceBuilder::new(c.resources.ethernet) - .ethernet_addr(hardware_addr) - .neighbor_cache(neighbor_cache) - .ip_addrs(&mut ip_addrs[..]) - .finalize(); - let mut socket_set_entries: [_; 8] = Default::default(); - let mut sockets = - net::socket::SocketSet::new(&mut socket_set_entries[..]); - create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, tcp_handle0); - create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, tcp_handle1); - - // unsafe { eth::enable_interrupt(DMA); } - let mut time = 0u32; - let mut next_ms = Instant::now(); - next_ms += 400_000.cycles(); - let mut server = Server::new(); - let mut iir_state: resources::iir_state = c.resources.iir_state; - let mut iir_ch: resources::iir_ch = c.resources.iir_ch; - loop { - // if ETHERNET_PENDING.swap(false, Ordering::Relaxed) { } - let tick = Instant::now() > next_ms; - if tick { - next_ms += 400_000.cycles(); - time += 1; - } - { - let socket = - &mut *sockets.get::(tcp_handle0); - if socket.state() == net::socket::TcpState::CloseWait { - socket.close(); - } else if !(socket.is_open() || socket.is_listening()) { - socket - .listen(1234) - .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); - } else if tick && socket.can_send() { - let s = iir_state.lock(|iir_state| Status { - t: time, - x0: iir_state[0][0], - y0: iir_state[0][2], - x1: iir_state[1][0], - y1: iir_state[1][2], - }); - json_reply(socket, &s); - } - } - { - let socket = - &mut *sockets.get::(tcp_handle1); - if socket.state() == net::socket::TcpState::CloseWait { - socket.close(); - } else if !(socket.is_open() || socket.is_listening()) { - socket - .listen(1235) - .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); - } else { - server.poll(socket, |req: &Request| { - if req.channel < 2 { - iir_ch.lock(|iir_ch| { - iir_ch[req.channel as usize] = req.iir - }); - } - }); - } - } - - if !match iface.poll( - &mut sockets, - net::time::Instant::from_millis(time as i64), - ) { - Ok(changed) => changed, - Err(net::Error::Unrecognized) => true, - Err(e) => { - info!("iface poll error: {:?}", e); - true - } - } { - // cortex_m::asm::wfi(); - } - } - } - */ - /* #[task(binds = ETH, resources = [ethernet_periph], priority = 1)] fn eth(c: eth::Context) { @@ -684,7 +611,6 @@ fn json_reply(socket: &mut net::socket::TcpSocket, msg: &T) { socket.write_str(&u).unwrap(); } -/* struct Server { data: Vec, discard: bool, @@ -767,7 +693,6 @@ impl Server { None } } -*/ #[exception] fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! { From 41f4960b93fcf7486e4b4160414f744afd6b458a Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 28 Apr 2020 19:26:43 +0200 Subject: [PATCH 10/40] Refactoring server into separate file --- src/main.rs | 142 +++----------------------------------------------- src/server.rs | 133 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 134 deletions(-) create mode 100644 src/server.rs diff --git a/src/main.rs b/src/main.rs index 3d09731..3da7786 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,28 +47,12 @@ use embedded_hal::{ use stm32h7_ethernet as ethernet; use smoltcp as net; -use core::fmt::Write; -use heapless::{ - consts::*, - String, - Vec -}; - -use serde::{ - de::DeserializeOwned, - Deserialize, - Serialize -}; -use serde_json_core::{ - de::from_slice, - ser::to_string -}; - #[link_section = ".sram3.eth"] static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); mod eth; mod pounder; +mod server; mod iir; use iir::*; @@ -494,7 +478,7 @@ const APP: () = { sockets.add(tcp_socket) }; - let mut server = Server::new(); + let mut server = server::Server::new(); let mut time = 0u32; let mut next_ms = Instant::now(); @@ -519,14 +503,14 @@ const APP: () = { .listen(1234) .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); } else if tick && socket.can_send() { - let s = c.resources.iir_state.lock(|iir_state| Status { + let s = c.resources.iir_state.lock(|iir_state| server::Status { t: time, x0: iir_state[0][0], y0: iir_state[0][2], x1: iir_state[1][0], y1: iir_state[1][2], }); - json_reply(&mut socket, &s); + server::json_reply(&mut socket, &s); } } @@ -540,7 +524,7 @@ const APP: () = { .listen(1235) .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); } else { - server.poll(socket, |req: &Request| { + server.poll(socket, |req: &server::Request| { if req.channel < 2 { c.resources.iir_ch.lock(|iir_ch| { iir_ch[req.channel as usize] = req.iir @@ -567,11 +551,11 @@ const APP: () = { } /* - #[task(binds = ETH, resources = [ethernet_periph], priority = 1)] + #[task(binds = ETH, resources = [net_interface], priority = 1)] fn eth(c: eth::Context) { - let dma = &c.resources.ethernet_periph.1; + let dma = &c.resources.net_interface.device(); ETHERNET_PENDING.store(true, Ordering::Relaxed); - unsafe { eth::interrupt_handler(dma) } + dma.interrupt_handler() } */ @@ -584,116 +568,6 @@ const APP: () = { } }; -#[derive(Deserialize, Serialize)] -struct Request { - channel: u8, - iir: IIR, -} - -#[derive(Serialize)] -struct Response<'a> { - code: i32, - message: &'a str, -} - -#[derive(Serialize)] -struct Status { - t: u32, - x0: f32, - y0: f32, - x1: f32, - y1: f32, -} - -fn json_reply(socket: &mut net::socket::TcpSocket, msg: &T) { - let mut u: String = to_string(msg).unwrap(); - u.push('\n').unwrap(); - socket.write_str(&u).unwrap(); -} - -struct Server { - data: Vec, - discard: bool, -} - -impl Server { - fn new() -> Self { - Self { - data: Vec::new(), - discard: false, - } - } - - fn poll( - &mut self, - socket: &mut net::socket::TcpSocket, - f: F, - ) -> Option - where - T: DeserializeOwned, - F: FnOnce(&T) -> R, - { - while socket.can_recv() { - let found = socket - .recv(|buf| { - let (len, found) = - match buf.iter().position(|&c| c as char == '\n') { - Some(end) => (end + 1, true), - None => (buf.len(), false), - }; - if self.data.len() + len >= self.data.capacity() { - self.discard = true; - self.data.clear(); - } else if !self.discard && len > 0 { - self.data.extend_from_slice(&buf[..len]).unwrap(); - } - (len, found) - }) - .unwrap(); - if found { - if self.discard { - self.discard = false; - json_reply( - socket, - &Response { - code: 520, - message: "command buffer overflow", - }, - ); - self.data.clear(); - } else { - let r = from_slice::(&self.data[..self.data.len() - 1]); - self.data.clear(); - match r { - Ok(res) => { - let r = f(&res); - json_reply( - socket, - &Response { - code: 200, - message: "ok", - }, - ); - return Some(r); - } - Err(err) => { - warn!("parse error {:?}", err); - json_reply( - socket, - &Response { - code: 550, - message: "parse error", - }, - ); - } - } - } - } - } - None - } -} - #[exception] fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! { panic!("HardFault at {:#?}", ef); diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..8cd48ce --- /dev/null +++ b/src/server.rs @@ -0,0 +1,133 @@ +use heapless::{ + consts::*, + String, + Vec +}; + +use core::fmt::Write; + + +use serde::{ + de::DeserializeOwned, + Deserialize, + Serialize +}; + +use serde_json_core::{ + de::from_slice, + ser::to_string +}; + +use super::net; +use super::iir::IIR; + +#[derive(Deserialize, Serialize)] +pub struct Request { + pub channel: u8, + pub iir: IIR, +} + +#[derive(Serialize)] +pub struct Response<'a> { + code: i32, + message: &'a str, +} + +#[derive(Serialize)] +pub struct Status { + pub t: u32, + pub x0: f32, + pub y0: f32, + pub x1: f32, + pub y1: f32, +} + +pub fn json_reply(socket: &mut net::socket::TcpSocket, msg: &T) { + let mut u: String = to_string(msg).unwrap(); + u.push('\n').unwrap(); + socket.write_str(&u).unwrap(); +} + +pub struct Server { + data: Vec, + discard: bool, +} + +impl Server { + pub fn new() -> Self { + Self { + data: Vec::new(), + discard: false, + } + } + + pub fn poll( + &mut self, + socket: &mut net::socket::TcpSocket, + f: F, + ) -> Option + where + T: DeserializeOwned, + F: FnOnce(&T) -> R, + { + while socket.can_recv() { + let found = socket + .recv(|buf| { + let (len, found) = + match buf.iter().position(|&c| c as char == '\n') { + Some(end) => (end + 1, true), + None => (buf.len(), false), + }; + if self.data.len() + len >= self.data.capacity() { + self.discard = true; + self.data.clear(); + } else if !self.discard && len > 0 { + self.data.extend_from_slice(&buf[..len]).unwrap(); + } + (len, found) + }) + .unwrap(); + if found { + if self.discard { + self.discard = false; + json_reply( + socket, + &Response { + code: 520, + message: "command buffer overflow", + }, + ); + self.data.clear(); + } else { + let r = from_slice::(&self.data[..self.data.len() - 1]); + self.data.clear(); + match r { + Ok(res) => { + let r = f(&res); + json_reply( + socket, + &Response { + code: 200, + message: "ok", + }, + ); + return Some(r); + } + Err(err) => { + warn!("parse error {:?}", err); + json_reply( + socket, + &Response { + code: 550, + message: "parse error", + }, + ); + } + } + } + } + } + None + } +} + From 6f7bb0569c015381c30391880d0e7e94f1e64b68 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 29 Apr 2020 11:59:04 +0200 Subject: [PATCH 11/40] Adding stabilizer AFE gain amplifier controls --- src/afe.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 25 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/afe.rs diff --git a/src/afe.rs b/src/afe.rs new file mode 100644 index 0000000..9238393 --- /dev/null +++ b/src/afe.rs @@ -0,0 +1,71 @@ +use embedded_hal; + +#[derive(Copy, Clone, Debug)] +pub enum Gain { + G1 = 0b00, + G2 = 0b01, + G5 = 0b10, + G10 = 0b11 +} + +pub struct ProgrammableGainAmplifier { + a0: A0, + a1: A1 +} + +impl ProgrammableGainAmplifier +where + A0: embedded_hal::digital::v2::StatefulOutputPin, + A0::Error: core::fmt::Debug, + A1: embedded_hal::digital::v2::StatefulOutputPin, + A1::Error: core::fmt::Debug, +{ + pub fn new(a0: A0, a1: A1) -> Self + { + let mut afe = Self { a0: a0, a1: a1}; + + afe.set_gain(Gain::G1); + + afe + } + + pub fn set_gain(&mut self, gain: Gain) { + match gain { + Gain::G1 => { + self.a0.set_low().unwrap(); + self.a1.set_low().unwrap(); + }, + Gain::G2 => { + self.a0.set_high().unwrap(); + self.a1.set_low().unwrap(); + }, + Gain::G5 => { + self.a0.set_low().unwrap(); + self.a1.set_high().unwrap(); + }, + Gain::G10 => { + self.a0.set_high().unwrap(); + self.a1.set_high().unwrap(); + }, + } + } + + pub fn get_gain(&self) -> Gain { + let lsb_set = self.a0.is_set_high().unwrap(); + let msb_set = self.a1.is_set_high().unwrap(); + + if msb_set { + if lsb_set { + Gain::G10 + } else { + Gain::G5 + } + } else { + if lsb_set { + Gain::G2 + } else { + Gain::G1 + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 3da7786..d67ed37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,6 +53,7 @@ static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); mod eth; mod pounder; mod server; +mod afe; mod iir; use iir::*; @@ -102,14 +103,24 @@ const SCALE: f32 = ((1 << 15) - 1) as f32; const TCP_RX_BUFFER_SIZE: usize = 8192; const TCP_TX_BUFFER_SIZE: usize = 8192; +type AFE1 = afe::ProgrammableGainAmplifier< + hal::gpio::gpiof::PF2>, + hal::gpio::gpiof::PF5>>; + +type AFE2 = afe::ProgrammableGainAmplifier< + hal::gpio::gpiod::PD14>, + hal::gpio::gpiod::PD15>>; + #[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { adc1: hal::spi::Spi, dac1: hal::spi::Spi, + _afe1: AFE1, adc2: hal::spi::Spi, dac2: hal::spi::Spi, + _afe2: AFE2, eeprom_i2c: hal::i2c::I2c, @@ -163,6 +174,18 @@ const APP: () = { let gpiof = dp.GPIOF.split(&mut clocks.ahb4); let gpiog = dp.GPIOG.split(&mut clocks.ahb4); + let afe1 = { + let a0_pin = gpiof.pf2.into_push_pull_output(); + let a1_pin = gpiof.pf5.into_push_pull_output(); + afe::ProgrammableGainAmplifier::new(a0_pin, a1_pin) + }; + + let afe2 = { + let a0_pin = gpiod.pd14.into_push_pull_output(); + let a1_pin = gpiod.pd15.into_push_pull_output(); + afe::ProgrammableGainAmplifier::new(a0_pin, a1_pin) + }; + // Configure the SPI interfaces to the ADCs and DACs. let adc1_spi = { let spi_miso = gpiob.pb14.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); @@ -398,6 +421,8 @@ const APP: () = { dac1: dac1_spi, adc2: adc2_spi, dac2: dac2_spi, + _afe1: afe1, + _afe2: afe2, dbg_pin: debug_pin, dac_pin: dac_pin, From 6792ab5469d95716489017415f0ed9280f139151 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 29 Apr 2020 13:00:29 +0200 Subject: [PATCH 12/40] Adding input power measurement support --- Cargo.toml | 2 +- src/main.rs | 31 ++++++++++++++++++-- src/pounder/attenuators.rs | 16 +++-------- src/pounder/error.rs | 2 ++ src/pounder/mod.rs | 58 +++++++++++++++++++++++++++++++------- src/pounder/rf_power.rs | 13 +++++++++ src/pounder/types.rs | 16 +++++++++++ 7 files changed, 112 insertions(+), 26 deletions(-) create mode 100644 src/pounder/rf_power.rs create mode 100644 src/pounder/types.rs diff --git a/Cargo.toml b/Cargo.toml index 6a0e10a..619b198 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ branch = "feature/device-dependency" [dependencies.stm32h7xx-hal] git = "https://github.com/quartiq/stm32h7xx-hal.git" branch = "feature/quad-spi" -features = ["stm32h743v", "rt"] +features = ["stm32h743v", "rt", "unproven"] [features] semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] diff --git a/src/main.rs b/src/main.rs index d67ed37..38d122d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -166,6 +166,8 @@ const APP: () = { clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); + let mut delay = hal::delay::Delay::new(cp.SYST, clocks.clocks); + let gpioa = dp.GPIOA.split(&mut clocks.ahb4); let gpiob = dp.GPIOB.split(&mut clocks.ahb4); let gpioc = dp.GPIOC.split(&mut clocks.ahb4); @@ -291,7 +293,7 @@ const APP: () = { let io_update = gpiog.pg7.into_push_pull_output(); - let delay = { + let asm_delay = { let frequency_hz = clocks.clocks.c_ck().0; asm_delay::AsmDelay::new(asm_delay::bitrate::Hertz (frequency_hz)) }; @@ -299,7 +301,7 @@ const APP: () = { ad9959::Ad9959::new(qspi_interface, &mut reset_pin, io_update, - delay, + asm_delay, ad9959::Mode::FourBitSerial, 100_000_000).unwrap() }; @@ -325,7 +327,30 @@ const APP: () = { dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 25.mhz(), &clocks) }; - pounder::PounderDevices::new(io_expander, ad9959, spi).unwrap() + let adc1 = { + let mut adc = dp.ADC1.adc(&mut delay, &mut clocks); + adc.calibrate(); + + adc.enable() + }; + + let adc2 = { + let mut adc = dp.ADC2.adc(&mut delay, &mut clocks); + adc.calibrate(); + + adc.enable() + }; + + let adc1_in_p = gpiof.pf11.into_analog(); + let adc2_in_p = gpiof.pf14.into_analog(); + + pounder::PounderDevices::new(io_expander, + ad9959, + spi, + adc1, + adc2, + adc1_in_p, + adc2_in_p).unwrap() }; let mut fp_led_0 = gpiod.pd5.into_push_pull_output(); diff --git a/src/pounder/attenuators.rs b/src/pounder/attenuators.rs index b8569d8..cff8337 100644 --- a/src/pounder/attenuators.rs +++ b/src/pounder/attenuators.rs @@ -1,16 +1,8 @@ use super::error::Error; - -#[allow(dead_code)] -#[derive(Debug, Clone, Copy)] -pub enum Channel { - One = 0, - Two = 1, - Three = 2, - Four = 3, -} +use super::DdsChannel; pub trait AttenuatorInterface { - fn modify(&mut self, attenuation: f32, channel: Channel) -> Result { + fn modify(&mut self, attenuation: f32, channel: DdsChannel) -> Result { if attenuation > 31.5 { return Err(Error::Bounds); } @@ -31,7 +23,7 @@ pub trait AttenuatorInterface { Ok(attenuation_code as f32 / 2.0) } - fn read(&mut self, channel: Channel) -> Result { + fn read(&mut self, channel: DdsChannel) -> Result { let mut channels = [0_u8; 4]; // Reading the data always shifts data out of the staging registers, so we perform a @@ -48,7 +40,7 @@ pub trait AttenuatorInterface { fn reset(&mut self) -> Result<(), Error>; - fn latch(&mut self, channel: Channel) -> Result<(), Error>; + fn latch(&mut self, channel: DdsChannel) -> Result<(), Error>; fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>; fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error>; } diff --git a/src/pounder/error.rs b/src/pounder/error.rs index 56a9e09..b3cc596 100644 --- a/src/pounder/error.rs +++ b/src/pounder/error.rs @@ -6,4 +6,6 @@ pub enum Error { Qspi, Bounds, InvalidAddress, + InvalidChannel, + Adc, } diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs index bcd24bd..7946100 100644 --- a/src/pounder/mod.rs +++ b/src/pounder/mod.rs @@ -3,13 +3,20 @@ use ad9959; pub mod error; pub mod attenuators; +mod rf_power; +pub mod types; use super::hal; use error::Error; -use attenuators::{AttenuatorInterface, Channel}; +use attenuators::AttenuatorInterface; +use types::{DdsChannel, InputChannel}; +use rf_power::PowerMeasurementInterface; -use embedded_hal::blocking::spi::Transfer; +use embedded_hal::{ + blocking::spi::Transfer, + adc::OneShot +}; #[allow(dead_code)] const OSC_EN_N_PIN: u8 = 8 + 7; @@ -62,7 +69,11 @@ pub struct PounderDevices { DELAY, hal::gpio::gpiog::PG7>>, mcp23017: mcp23017::MCP23017>, - attenuator_spi: hal::spi::Spi + attenuator_spi: hal::spi::Spi, + adc1: hal::adc::Adc, + adc2: hal::adc::Adc, + adc1_in_p: hal::gpio::gpiof::PF11, + adc2_in_p: hal::gpio::gpiof::PF14, } impl PounderDevices @@ -74,11 +85,20 @@ where DELAY, hal::gpio::gpiog::PG7< hal::gpio::Output>>, - attenuator_spi: hal::spi::Spi) -> Result { + attenuator_spi: hal::spi::Spi, + adc1: hal::adc::Adc, + adc2: hal::adc::Adc, + adc1_in_p: hal::gpio::gpiof::PF11, + adc2_in_p: hal::gpio::gpiof::PF14, + ) -> Result { let mut devices = Self { mcp23017, ad9959, - attenuator_spi + attenuator_spi, + adc1, + adc2, + adc1_in_p, + adc2_in_p, }; // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator @@ -117,12 +137,12 @@ impl AttenuatorInterface for PounderDevices Ok(()) } - fn latch(&mut self, channel: Channel) -> Result<(), Error> { + fn latch(&mut self, channel: DdsChannel) -> Result<(), Error> { let pin = match channel { - Channel::One => ATT_LE0_PIN, - Channel::Two => ATT_LE1_PIN, - Channel::Three => ATT_LE2_PIN, - Channel::Four => ATT_LE3_PIN, + DdsChannel::Zero => ATT_LE0_PIN, + DdsChannel::One => ATT_LE1_PIN, + DdsChannel::Two => ATT_LE2_PIN, + DdsChannel::Three => ATT_LE3_PIN, }; self.mcp23017.digital_write(pin, 1).map_err(|_| Error::I2c)?; @@ -146,3 +166,21 @@ impl AttenuatorInterface for PounderDevices Ok(()) } } + +impl PowerMeasurementInterface for PounderDevices { + fn sample_converter(&mut self, channel: InputChannel) -> Result { + let adc_scale = match channel { + InputChannel::Zero => { + let adc_reading: u32 = self.adc1.read(&mut self.adc1_in_p).map_err(|_| Error::Adc)?; + adc_reading as f32 / self.adc1.max_sample() as f32 + }, + InputChannel::One => { + let adc_reading: u32 = self.adc2.read(&mut self.adc2_in_p).map_err(|_| Error::Adc)?; + adc_reading as f32 / self.adc2.max_sample() as f32 + }, + }; + + // Convert analog percentage to voltage. + Ok(adc_scale * 3.3) + } +} diff --git a/src/pounder/rf_power.rs b/src/pounder/rf_power.rs new file mode 100644 index 0000000..cc3f654 --- /dev/null +++ b/src/pounder/rf_power.rs @@ -0,0 +1,13 @@ +use super::Error; +use super::InputChannel; + +pub trait PowerMeasurementInterface { + fn sample_converter(&mut self, channel: InputChannel) -> Result; + + fn measure_power(&mut self, channel: InputChannel) -> Result { + let analog_measurement = self.sample_converter(channel)?; + + // The AD8363 with VSET connected to VOUT provides an output voltage of 52mV / dB. + Ok(analog_measurement / 0.052) + } +} diff --git a/src/pounder/types.rs b/src/pounder/types.rs new file mode 100644 index 0000000..9938bfe --- /dev/null +++ b/src/pounder/types.rs @@ -0,0 +1,16 @@ + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum DdsChannel { + Zero, + One, + Two, + Three, +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum InputChannel { + Zero, + One, +} From 13cd0ad6368cf1d27548a7d7618d9a0a90e04a73 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Jun 2020 10:36:35 +0200 Subject: [PATCH 13/40] Updating code after review feedback --- Cargo.lock | 9 +++------ Cargo.toml | 4 +++- src/afe.rs | 27 ++++++++++----------------- src/pounder/attenuators.rs | 21 ++++++++++++++++----- src/pounder/mod.rs | 31 +++++++++++++++++-------------- src/pounder/rf_power.rs | 5 +++-- 6 files changed, 52 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3952f9..7fb8489 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,12 +237,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "mcp23017" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/mrd0ll4r/mcp23017.git#a3d072754abca60a92ece820f7cfb767a0c11669" dependencies = [ - "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", - "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -372,7 +369,7 @@ dependencies = [ "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mcp23017 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)", "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -508,7 +505,7 @@ dependencies = [ "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" -"checksum mcp23017 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4854484a74b626165c3f3cf5e15bfee2898c6b4d6eccc3ed5ee634850af4dd" +"checksum mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)" = "" "checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" "checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" "checksum panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c" diff --git a/Cargo.toml b/Cargo.toml index 619b198..33f7302 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,9 @@ cortex-m-rtfm = "0.5" embedded-hal = "0.2.3" nb = "0.1.2" asm-delay = "0.7.0" -mcp23017 = "0.1.1" + +[dependencies.mcp23017] +git = "https://github.com/mrd0ll4r/mcp23017.git" [dependencies.smoltcp] version = "0.6" diff --git a/src/afe.rs b/src/afe.rs index 9238393..c3d9f3c 100644 --- a/src/afe.rs +++ b/src/afe.rs @@ -30,23 +30,16 @@ where } pub fn set_gain(&mut self, gain: Gain) { - match gain { - Gain::G1 => { - self.a0.set_low().unwrap(); - self.a1.set_low().unwrap(); - }, - Gain::G2 => { - self.a0.set_high().unwrap(); - self.a1.set_low().unwrap(); - }, - Gain::G5 => { - self.a0.set_low().unwrap(); - self.a1.set_high().unwrap(); - }, - Gain::G10 => { - self.a0.set_high().unwrap(); - self.a1.set_high().unwrap(); - }, + if (gain as u8 & 0b01) != 0 { + self.a0.set_high().unwrap(); + } else { + self.a0.set_low().unwrap(); + } + + if (gain as u8 & 0b10) != 0 { + self.a1.set_high().unwrap() + } else { + self.a1.set_low().unwrap(); } } diff --git a/src/pounder/attenuators.rs b/src/pounder/attenuators.rs index cff8337..d9785f8 100644 --- a/src/pounder/attenuators.rs +++ b/src/pounder/attenuators.rs @@ -3,18 +3,23 @@ use super::DdsChannel; pub trait AttenuatorInterface { fn modify(&mut self, attenuation: f32, channel: DdsChannel) -> Result { - if attenuation > 31.5 { + if attenuation > 31.5 || attenuation < 0.0 { return Err(Error::Bounds); } - // Calculate the attenuation code to program into the attenuator. + // Calculate the attenuation code to program into the attenuator. The attenuator uses a + // code where the LSB is 0.5 dB. let attenuation_code = (attenuation * 2.0) as u8; // Read all the channels, modify the channel of interest, and write all the channels back. // This ensures the staging register and the output register are always in sync. let mut channels = [0_u8; 4]; self.read_all(&mut channels)?; - channels[channel as usize] = attenuation_code; + + // The lowest 2 bits of the 8-bit shift register on the attenuator are ignored. Shift the + // attenuator code into the upper 6 bits of the register value. Note that the attenuator + // treats inputs as active-low, so the code is inverted before writing. + channels[channel as usize] = !attenuation_code.wrapping_shl(2); self.write_all(&channels)?; // Finally, latch the output of the updated channel to force it into an active state. @@ -32,9 +37,15 @@ pub trait AttenuatorInterface { self.read_all(&mut channels)?; self.write_all(&channels)?; - // Convert the desired channel code into dB of attenuation. - let attenuation_code = channels[channel as usize]; + // The attenuation code is stored in the upper 6 bits of the register, where each LSB + // represents 0.5 dB. The attenuator stores the code as active-low, so inverting the result + // (before the shift) has the affect of transforming the bits of interest (and the + // dont-care bits) into an active-high state and then masking off the don't care bits. If + // the shift occurs before the inversion, the upper 2 bits (which would then be don't + // care) would contain erroneous data. + let attenuation_code = (!channels[channel as usize]).wrapping_shr(2); + // Convert the desired channel code into dB of attenuation. Ok(attenuation_code as f32 / 2.0) } diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs index 7946100..c192b67 100644 --- a/src/pounder/mod.rs +++ b/src/pounder/mod.rs @@ -103,8 +103,9 @@ where // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator // selected, attenuators out of reset. - devices.mcp23017.write_gpioa(0xF).map_err(|_| Error::I2c)?; - devices.mcp23017.write_gpiob(1_u8.wrapping_shl(5)).map_err(|_| Error::I2c)?; + devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0xF).map_err(|_| Error::I2c)?; + devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, + 1_u8.wrapping_shl(5)).map_err(|_| Error::I2c)?; devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?; devices.select_onboard_clock()?; @@ -113,14 +114,14 @@ where } pub fn select_external_clock(&mut self, frequency: u32) -> Result<(), Error>{ - self.mcp23017.digital_write(EXT_CLK_SEL_PIN, 1).map_err(|_| Error::I2c)?; + self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?; self.ad9959.set_clock_frequency(frequency).map_err(|_| Error::DDS)?; Ok(()) } pub fn select_onboard_clock(&mut self) -> Result<(), Error> { - self.mcp23017.digital_write(EXT_CLK_SEL_PIN, 0).map_err(|_| Error::I2c)?; + self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?; self.ad9959.set_clock_frequency(100_000_000).map_err(|_| Error::DDS)?; Ok(()) @@ -130,24 +131,26 @@ where impl AttenuatorInterface for PounderDevices { fn reset(&mut self) -> Result<(), Error> { - self.mcp23017.digital_write(ATT_RST_N_PIN, 1).map_err(|_| Error::I2c)?; - // TODO: Delay here. - self.mcp23017.digital_write(ATT_RST_N_PIN, 0).map_err(|_| Error::I2c)?; + self.mcp23017.digital_write(ATT_RST_N_PIN, true).map_err(|_| Error::I2c)?; + // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is + // sufficient. Document the delay here. + self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?; Ok(()) } fn latch(&mut self, channel: DdsChannel) -> Result<(), Error> { let pin = match channel { - DdsChannel::Zero => ATT_LE0_PIN, - DdsChannel::One => ATT_LE1_PIN, - DdsChannel::Two => ATT_LE2_PIN, - DdsChannel::Three => ATT_LE3_PIN, + DdsChannel::Zero => ATT_LE1_PIN, + DdsChannel::One => ATT_LE0_PIN, + DdsChannel::Two => ATT_LE3_PIN, + DdsChannel::Three => ATT_LE2_PIN, }; - self.mcp23017.digital_write(pin, 1).map_err(|_| Error::I2c)?; - // TODO: Delay here. - self.mcp23017.digital_write(pin, 0).map_err(|_| Error::I2c)?; + self.mcp23017.digital_write(pin, true).map_err(|_| Error::I2c)?; + // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is + // sufficient. Document the delay here. + self.mcp23017.digital_write(pin, false).map_err(|_| Error::I2c)?; Ok(()) } diff --git a/src/pounder/rf_power.rs b/src/pounder/rf_power.rs index cc3f654..dd7a3ec 100644 --- a/src/pounder/rf_power.rs +++ b/src/pounder/rf_power.rs @@ -7,7 +7,8 @@ pub trait PowerMeasurementInterface { fn measure_power(&mut self, channel: InputChannel) -> Result { let analog_measurement = self.sample_converter(channel)?; - // The AD8363 with VSET connected to VOUT provides an output voltage of 52mV / dB. - Ok(analog_measurement / 0.052) + // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at + // 100MHz. + Ok(analog_measurement / 0.0517) } } From b39de7f414c02b4ee8181cf9fb29b406ac47e557 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Jun 2020 15:44:34 +0200 Subject: [PATCH 14/40] Adding support for QSPI operating continuously in 4-bit mode --- Cargo.lock | 128 +++++++++++++++++++++++++++------------------ Cargo.toml | 6 +-- src/main.rs | 2 +- src/pounder/mod.rs | 91 +++++++++++++++++++++++++++++--- 4 files changed, 165 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7fb8489..44886bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,7 +3,7 @@ [[package]] name = "ad9959" version = "0.1.0" -source = "git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver#71f96bab751048aa944490b4b56463551025a21f" +source = "git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver#b45bce3f0dd7a58b5a272b778001dc9422c2c160" dependencies = [ "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -133,9 +133,9 @@ name = "cortex-m-rt-macros" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -146,7 +146,7 @@ dependencies = [ "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -155,10 +155,10 @@ name = "cortex-m-rtfm-macros" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -204,7 +204,7 @@ dependencies = [ [[package]] name = "heapless" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -215,7 +215,7 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -261,9 +261,34 @@ dependencies = [ "cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "paste" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste-impl" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" -version = "1.0.10" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -271,10 +296,10 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -292,9 +317,9 @@ name = "rtfm-syntax" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -320,10 +345,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.106" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -331,18 +356,18 @@ name = "serde-json-core" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.106" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -367,17 +392,17 @@ dependencies = [ "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)", "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", "serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git?branch=feature/device-dependency)", - "stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)", + "stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git)", + "stm32h7xx-hal 0.5.0", ] [[package]] @@ -387,7 +412,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "stm32h7" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -399,18 +424,17 @@ dependencies = [ [[package]] name = "stm32h7-ethernet" version = "0.1.1" -source = "git+https://github.com/quartiq/stm32h7-ethernet.git?branch=feature/device-dependency#acb5bbb23d278e614f5ea4a3cd8a28f19540fc2c" +source = "git+https://github.com/quartiq/stm32h7-ethernet.git#cf9b8bb2e1b440d8ada6ac6048f48dc4ed9c269a" dependencies = [ "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7xx-hal 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "stm32h7xx-hal" -version = "0.4.0" -source = "git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi#4e67198d881a77aa825b4777e8dc270599a45242" +version = "0.5.0" dependencies = [ "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -418,13 +442,14 @@ dependencies = [ "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "stm32h7xx-hal" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -433,17 +458,18 @@ dependencies = [ "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "1.0.17" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -501,32 +527,34 @@ dependencies = [ "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" "checksum hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" -"checksum heapless 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ffa511365b12346c5fbe759d82f80d3aa70d9f1ba01955594f84a1a6bbab985" -"checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" +"checksum heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "73a8a2391a3bc70b31f60e7a90daa5755a360559c0b6b9c5cfc0fee482362dc0" +"checksum indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" "checksum mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)" = "" "checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" "checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" "checksum panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c" -"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" -"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +"checksum paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" +"checksum paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" +"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" +"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +"checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493" "checksum rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4455e23c34df3d66454e7e218a4d76a7f83321d04a806be614463341cec4116e" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +"checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" "checksum serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf406405ada9ef326ca78677324ac66994ff348fc48a16030be08caeed29825" -"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +"checksum serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" "checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum stm32h7 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b0045066e082648e8a7ab1dd45c92efa8d7bec2beedf72ac7b62563911f82a" -"checksum stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git?branch=feature/device-dependency)" = "" -"checksum stm32h7xx-hal 0.4.0 (git+https://github.com/quartiq/stm32h7xx-hal.git?branch=feature/quad-spi)" = "" -"checksum stm32h7xx-hal 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "689aff61a1cf43e03cf821c5540bb540e6bd04b374e486be23b035c0afc3c15f" -"checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +"checksum stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9beb5e2a223c82f263c3051bba4614aebc6e98bd40217df3cd8817c83ac7bd8" +"checksum stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git)" = "" +"checksum stm32h7xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "987c66628f30012ed9a41cc738421c5caece03292c0cc8fd1e99956f122735bd" +"checksum syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" diff --git a/Cargo.toml b/Cargo.toml index 33f7302..acaad2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,11 +55,11 @@ branch = "feature/basic-driver" [dependencies.stm32h7-ethernet] git = "https://github.com/quartiq/stm32h7-ethernet.git" features = ["stm32h743v"] -branch = "feature/device-dependency" [dependencies.stm32h7xx-hal] -git = "https://github.com/quartiq/stm32h7xx-hal.git" -branch = "feature/quad-spi" +#git = "https://github.com/quartiq/stm32h7xx-hal.git" +#branch = "feature/quad-spi" +path = "../stm32h7xx-hal/" features = ["stm32h743v", "rt", "unproven"] [features] diff --git a/src/main.rs b/src/main.rs index 38d122d..4401bfe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -286,7 +286,7 @@ const APP: () = { let _qspi_io3 = gpioe.pe10.into_alternate_af10(); let qspi = hal::qspi::Qspi::new(dp.QUADSPI, &mut clocks, 10.mhz()).unwrap(); - pounder::QspiInterface {qspi} + pounder::QspiInterface::new(qspi).unwrap() }; let mut reset_pin = gpioa.pa0.into_push_pull_output(); diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs index c192b67..b3ce19f 100644 --- a/src/pounder/mod.rs +++ b/src/pounder/mod.rs @@ -32,20 +32,23 @@ const ATT_LE3_PIN: u8 = 8 + 3; pub struct QspiInterface { pub qspi: hal::qspi::Qspi, + mode: ad9959::Mode, +} + +impl QspiInterface { + pub fn new(mut qspi: hal::qspi::Qspi) -> Result { + qspi.configure_mode(hal::qspi::QspiMode::FourBit).map_err(|_| Error::Qspi)?; + Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire }) + } } impl ad9959::Interface for QspiInterface { type Error = Error; fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> { - let result = match mode { - ad9959::Mode::SingleBitTwoWire | ad9959::Mode::SingleBitThreeWire => - self.qspi.configure_mode(hal::qspi::QspiMode::OneBit), - ad9959::Mode::TwoBitSerial => self.qspi.configure_mode(hal::qspi::QspiMode::TwoBit), - ad9959::Mode::FourBitSerial => self.qspi.configure_mode(hal::qspi::QspiMode::FourBit), - }; + self.mode = mode; - result.map_err(|_| Error::Qspi) + Ok(()) } fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> { @@ -53,13 +56,85 @@ impl ad9959::Interface for QspiInterface { return Err(Error::InvalidAddress); } - self.qspi.write(addr, &data).map_err(|_| Error::Qspi) + // The QSPI interface implementation always operates in 4-bit mode because the AD9959 uses + // IO3 as SYNC_IO in some output modes. In order for writes to be successful, SYNC_IO must + // be driven low. However, the QSPI peripheral forces IO3 high when operating in 1 or 2 bit + // modes. As a result, any writes while in single- or dual-bit modes has to instead write + // the data encoded into 4-bit QSPI data so that IO3 can be driven low. + match self.mode { + ad9959::Mode::SingleBitTwoWire => { + // Encode the data into a 4-bit QSPI pattern. + + // In 4-bit mode, we can send 2 bits of address and data per byte transfer. As + // such, we need at least 4x more bytes than the length of data. To avoid dynamic + // allocation, we assume the maximum transaction length for single-bit-two-wire is + // 2 bytes. + let mut encoded_data: [u8; 12] = [0; 12]; + + if (data.len() * 4) > (encoded_data.len() - 4) { + return Err(Error::Bounds); + } + + // Encode the address into the first 4 bytes. + for address_bit in 0..8 { + let offset: u8 = { + if address_bit % 2 == 0 { + 4 + } else { + 0 + } + }; + + if addr & address_bit != 0 { + encoded_data[(address_bit >> 1) as usize] |= 1 << offset; + } + } + + // Encode the data into the remaining bytes. + for byte_index in 0..data.len() { + let byte = data[byte_index]; + for address_bit in 0..8 { + let offset: u8 = { + if address_bit % 2 == 0 { + 4 + } else { + 0 + } + }; + + if byte & address_bit != 0 { + encoded_data[(byte_index + 1) * 4 + (address_bit >> 1) as usize] |= 1 << offset; + } + } + } + + let (encoded_address, encoded_payload) = { + let end_index = (1 + data.len()) * 4; + (encoded_data[0], &encoded_data[1..end_index]) + }; + + self.qspi.write(encoded_address, &encoded_payload).map_err(|_| Error::Qspi) + }, + ad9959::Mode::FourBitSerial => { + self.qspi.write(addr, &data).map_err(|_| Error::Qspi) + }, + _ => { + Err(Error::Qspi) + } + } } fn read(&mut self, addr: u8, mut dest: &mut [u8]) -> Result<(), Error> { if (addr & 0x80) != 0 { return Err(Error::InvalidAddress); } + + // It is not possible to read data from the AD9959 in single bit two wire mode because the + // QSPI interface assumes that data is always received on IO1. + if self.mode == ad9959::Mode::SingleBitTwoWire { + return Err(Error::Qspi); + } + self.qspi.read(0x80_u8 | addr, &mut dest).map_err(|_| Error::Qspi) } } From d7bdafeff248a6aef6d06cc585ad55142ab0908b Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Jun 2020 15:46:18 +0200 Subject: [PATCH 15/40] Adding wip changes to ethernet interface --- .cargo/config | 2 +- Cargo.toml | 2 +- src/afe.rs | 3 +- src/main.rs | 82 ++++++++++++++++++++++++++++++---- src/pounder/mod.rs | 4 +- src/server.rs | 109 ++++++++++++++++++++++++--------------------- 6 files changed, 136 insertions(+), 66 deletions(-) diff --git a/.cargo/config b/.cargo/config index 382c36e..a765d53 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "gdb-multiarch -q -x openocd.gdb" +runner = "gdb-multiarch -q -x bmp.gdb" rustflags = ["-C", "link-arg=-Tlink.x"] [build] diff --git a/Cargo.toml b/Cargo.toml index acaad2f..7522e17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ log = "0.4" panic-semihosting = { version = "0.5", optional = true } panic-halt = "0.2" serde = { version = "1.0", features = ["derive"], default-features = false } -heapless = "0.5" +heapless = "0.5.4" serde-json-core = "0.1" cortex-m-rtfm = "0.5" embedded-hal = "0.2.3" diff --git a/src/afe.rs b/src/afe.rs index c3d9f3c..8dabf06 100644 --- a/src/afe.rs +++ b/src/afe.rs @@ -1,6 +1,7 @@ use embedded_hal; +use serde::{Serialize, Deserialize}; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub enum Gain { G1 = 0b00, G2 = 0b01, diff --git a/src/main.rs b/src/main.rs index 4401bfe..e909af7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,6 +39,12 @@ use stm32h7xx_hal::{ prelude::*, stm32 as pac, }; +use pounder::Error; + +use heapless::{ + String, + consts::*, +}; use embedded_hal::{ digital::v2::OutputPin, @@ -111,12 +117,64 @@ type AFE2 = afe::ProgrammableGainAmplifier< hal::gpio::gpiod::PD14>, hal::gpio::gpiod::PD15>>; +macro_rules! route_request { + ($request:ident, $buffer:ident, + readable_attributes: [$(($read_attribute:tt, $getter:tt)),*], + modifiable_attributes: [$(($write_attribute:tt, $TYPE:ty, $setter:tt)),*]) => { + match $request { + server::Request::Read{attribute} => { + match attribute { + $( + &$read_attribute => { + let value = match $getter() { + Ok(data) => data, + Err(_) => return server::Response::error(attribute, + "Failed to set attribute"), + }; + + $buffer = match serde_json_core::to_string(&value) { + Ok(data) => data, + Err(_) => return server::Response::error(attribute, + "Failed to encode attribute value"), + }; + + server::Response::success(attribute, &$buffer) + }, + )* + _ => server::Response::error(attribute, "Unknown attribute") + } + }, + server::Request::Write{attribute, value} => { + match attribute { + $( + &$write_attribute => { + let new_value = match serde_json_core::from_str::<$TYPE>(value) { + Ok(data) => data, + Err(_) => return server::Response::error(attribute, + "Failed to decode value"), + }; + + match $setter(new_value) { + Ok(_) => server::Response::success(attribute, value), + Err(_) => server::Response::error(attribute, + "Failed to set attribute"), + } + } + )* + _ => server::Response::error(attribute, "Unknown attribute") + } + } + } + } +} + + #[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { adc1: hal::spi::Spi, dac1: hal::spi::Spi, - _afe1: AFE1, + afe1: AFE1, adc2: hal::spi::Spi, dac2: hal::spi::Spi, @@ -446,7 +504,7 @@ const APP: () = { dac1: dac1_spi, adc2: adc2_spi, dac2: dac2_spi, - _afe1: afe1, + afe1: afe1, _afe2: afe2, dbg_pin: debug_pin, @@ -504,7 +562,7 @@ const APP: () = { cortex_m::asm::bkpt(); } - #[idle(resources=[net_interface, mac_addr, iir_state, iir_ch])] + #[idle(resources=[net_interface, mac_addr, iir_state, iir_ch, afe1])] fn idle(mut c: idle::Context) -> ! { let mut socket_set_entries: [_; 8] = Default::default(); @@ -536,6 +594,8 @@ const APP: () = { // TODO: Replace with reference to CPU clock from CCDR. next_ms += 400_000.cycles(); + let buffer: String = String::new(); + loop { let tick = Instant::now() > next_ms; @@ -574,12 +634,16 @@ const APP: () = { .listen(1235) .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); } else { - server.poll(socket, |req: &server::Request| { - if req.channel < 2 { - c.resources.iir_ch.lock(|iir_ch| { - iir_ch[req.channel as usize] = req.iir - }); - } + server.poll(socket, |req| { + info!("Got request: {:?}", req); + route_request!(req, buffer, + readable_attributes: [ + ("stabilizer/afe0/gain", (|| Ok::(c.resources.afe1.get_gain()))) + ], + modifiable_attributes: [ + ("stabilizer/afe0/gain", afe::Gain, (|gain| Ok::<(), ()>(c.resources.afe1.set_gain(gain)))) + ] + ) }); } } diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs index b3ce19f..d1f445b 100644 --- a/src/pounder/mod.rs +++ b/src/pounder/mod.rs @@ -1,14 +1,14 @@ use mcp23017; use ad9959; -pub mod error; +mod error; pub mod attenuators; mod rf_power; pub mod types; use super::hal; -use error::Error; +pub use error::Error; use attenuators::AttenuatorInterface; use types::{DdsChannel, InputChannel}; use rf_power::PowerMeasurementInterface; diff --git a/src/server.rs b/src/server.rs index 8cd48ce..e2bf5e1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -8,7 +8,6 @@ use core::fmt::Write; use serde::{ - de::DeserializeOwned, Deserialize, Serialize }; @@ -19,18 +18,43 @@ use serde_json_core::{ }; use super::net; -use super::iir::IIR; -#[derive(Deserialize, Serialize)] -pub struct Request { - pub channel: u8, - pub iir: IIR, +#[derive(Deserialize, Serialize, Debug)] +pub enum Request<'a, 'b> { + Read{attribute: &'a str}, + Write{attribute: &'a str, value: &'b str}, } #[derive(Serialize)] -pub struct Response<'a> { +pub struct Response<'a, 'b> { code: i32, - message: &'a str, + attribute: &'a str, + value: &'b str, +} + +impl<'a, 'b> Response<'a, 'b> { + pub fn success<'c, 'd>(attribute: &'c str, value: &'d str) -> Self + where + 'c: 'a, + 'd: 'b, + { + Self { code: 200, attribute: attribute, value: value} + } + + pub fn error<'c, 'd>(attribute: &'c str, message: &'d str) -> Self + where + 'c: 'a, + 'd: 'b, + { + Self { code: 400, attribute: attribute, value: message} + } + + pub fn custom<'c>(code: i32, message : &'c str) -> Self + where + 'c: 'b, + { + Self { code: code, attribute: "", value: message} + } } #[derive(Serialize)] @@ -61,73 +85,54 @@ impl Server { } } - pub fn poll( + pub fn poll<'a, 'b, F>( &mut self, socket: &mut net::socket::TcpSocket, f: F, - ) -> Option + ) where - T: DeserializeOwned, - F: FnOnce(&T) -> R, + F: FnOnce(&Request) -> Response<'a, 'b> { while socket.can_recv() { - let found = socket - .recv(|buf| { - let (len, found) = - match buf.iter().position(|&c| c as char == '\n') { - Some(end) => (end + 1, true), - None => (buf.len(), false), - }; - if self.data.len() + len >= self.data.capacity() { - self.discard = true; - self.data.clear(); - } else if !self.discard && len > 0 { - self.data.extend_from_slice(&buf[..len]).unwrap(); - } - (len, found) - }) - .unwrap(); + let found = socket.recv(|buf| { + let (len, found) = + match buf.iter().position(|&c| c as char == '\n') { + Some(end) => (end + 1, true), + None => (buf.len(), false), + }; + if self.data.len() + len >= self.data.capacity() { + self.discard = true; + self.data.clear(); + } else if !self.discard && len > 0 { + self.data.extend_from_slice(&buf[..len]).unwrap(); + } + (len, found) + }).unwrap(); + if found { if self.discard { self.discard = false; - json_reply( - socket, - &Response { - code: 520, - message: "command buffer overflow", - }, + json_reply(socket, &Response::custom(520, "command buffer overflow"), ); self.data.clear(); } else { - let r = from_slice::(&self.data[..self.data.len() - 1]); + let r = from_slice::(&self.data[..self.data.len() - 1]); self.data.clear(); match r { Ok(res) => { - let r = f(&res); - json_reply( - socket, - &Response { - code: 200, - message: "ok", - }, - ); - return Some(r); - } + let response = f(&res); + json_reply(socket, &response); + return; + }, Err(err) => { warn!("parse error {:?}", err); - json_reply( - socket, - &Response { - code: 550, - message: "parse error", - }, + json_reply(socket, &Response::custom(550, "parse error"), ); } } } } } - None } } From fa5b1aa152c9720c57c662e74b73df8ba222d9e5 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Jun 2020 16:53:25 +0200 Subject: [PATCH 16/40] Adding prototype ethernet interface --- Cargo.lock | 1 + Cargo.toml | 2 +- src/main.rs | 71 +++++++++++++++++++++++++-------------------------- src/server.rs | 44 ++++++++++++------------------- 4 files changed, 53 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44886bb..13e1a8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,6 +210,7 @@ dependencies = [ "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", "hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index 7522e17..31e0403 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ log = "0.4" panic-semihosting = { version = "0.5", optional = true } panic-halt = "0.2" serde = { version = "1.0", features = ["derive"], default-features = false } -heapless = "0.5.4" +heapless = { version = "0.5.4", features = ["serde"] } serde-json-core = "0.1" cortex-m-rtfm = "0.5" embedded-hal = "0.2.3" diff --git a/src/main.rs b/src/main.rs index e909af7..fbe8d07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,7 +39,6 @@ use stm32h7xx_hal::{ prelude::*, stm32 as pac, }; -use pounder::Error; use heapless::{ String, @@ -109,16 +108,16 @@ const SCALE: f32 = ((1 << 15) - 1) as f32; const TCP_RX_BUFFER_SIZE: usize = 8192; const TCP_TX_BUFFER_SIZE: usize = 8192; -type AFE1 = afe::ProgrammableGainAmplifier< +type AFE0 = afe::ProgrammableGainAmplifier< hal::gpio::gpiof::PF2>, hal::gpio::gpiof::PF5>>; -type AFE2 = afe::ProgrammableGainAmplifier< +type AFE1 = afe::ProgrammableGainAmplifier< hal::gpio::gpiod::PD14>, hal::gpio::gpiod::PD15>>; macro_rules! route_request { - ($request:ident, $buffer:ident, + ($request:ident, readable_attributes: [$(($read_attribute:tt, $getter:tt)),*], modifiable_attributes: [$(($write_attribute:tt, $TYPE:ty, $setter:tt)),*]) => { match $request { @@ -132,13 +131,13 @@ macro_rules! route_request { "Failed to set attribute"), }; - $buffer = match serde_json_core::to_string(&value) { + let encoded_data: String = match serde_json_core::to_string(&value) { Ok(data) => data, Err(_) => return server::Response::error(attribute, "Failed to encode attribute value"), }; - server::Response::success(attribute, &$buffer) + server::Response::success(attribute, &encoded_data) }, )* _ => server::Response::error(attribute, "Unknown attribute") @@ -172,13 +171,13 @@ macro_rules! route_request { #[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { - adc1: hal::spi::Spi, - dac1: hal::spi::Spi, - afe1: AFE1, + adc0: hal::spi::Spi, + dac0: hal::spi::Spi, + afe0: AFE0, - adc2: hal::spi::Spi, - dac2: hal::spi::Spi, - _afe2: AFE2, + adc1: hal::spi::Spi, + dac1: hal::spi::Spi, + afe1: AFE1, eeprom_i2c: hal::i2c::I2c, @@ -234,20 +233,20 @@ const APP: () = { let gpiof = dp.GPIOF.split(&mut clocks.ahb4); let gpiog = dp.GPIOG.split(&mut clocks.ahb4); - let afe1 = { + let afe0 = { let a0_pin = gpiof.pf2.into_push_pull_output(); let a1_pin = gpiof.pf5.into_push_pull_output(); afe::ProgrammableGainAmplifier::new(a0_pin, a1_pin) }; - let afe2 = { + let afe1 = { let a0_pin = gpiod.pd14.into_push_pull_output(); let a1_pin = gpiod.pd15.into_push_pull_output(); afe::ProgrammableGainAmplifier::new(a0_pin, a1_pin) }; // Configure the SPI interfaces to the ADCs and DACs. - let adc1_spi = { + let adc0_spi = { let spi_miso = gpiob.pb14.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); let spi_sck = gpiob.pb10.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); let _spi_nss = gpiob.pb9.into_alternate_af5(); @@ -272,7 +271,7 @@ const APP: () = { spi }; - let adc2_spi = { + let adc1_spi = { let spi_miso = gpiob.pb4.into_alternate_af6().set_speed(hal::gpio::Speed::VeryHigh); let spi_sck = gpioc.pc10.into_alternate_af6().set_speed(hal::gpio::Speed::VeryHigh); let _spi_nss = gpioa.pa15.into_alternate_af6(); @@ -296,7 +295,7 @@ const APP: () = { spi }; - let dac1_spi = { + let dac0_spi = { let spi_miso = gpioe.pe5.into_alternate_af5(); let spi_sck = gpioe.pe2.into_alternate_af5(); let _spi_nss = gpioe.pe4.into_alternate_af5(); @@ -313,7 +312,7 @@ const APP: () = { dp.SPI4.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 25.mhz(), &clocks) }; - let dac2_spi = { + let dac1_spi = { let spi_miso = gpiof.pf8.into_alternate_af5(); let spi_sck = gpiof.pf7.into_alternate_af5(); let _spi_nss = gpiof.pf6.into_alternate_af5(); @@ -500,12 +499,12 @@ const APP: () = { timer2.listen(hal::timer::Event::TimeOut); init::LateResources { + adc0: adc0_spi, + dac0: dac0_spi, adc1: adc1_spi, dac1: dac1_spi, - adc2: adc2_spi, - dac2: dac2_spi, + afe0: afe0, afe1: afe1, - _afe2: afe2, dbg_pin: debug_pin, dac_pin: dac_pin, @@ -519,19 +518,19 @@ const APP: () = { } } - #[task(binds = TIM2, resources = [dbg_pin, timer, adc1, adc2])] + #[task(binds = TIM2, resources = [dbg_pin, timer, adc0, adc1])] fn tim2(mut c: tim2::Context) { c.resources.timer.clear_uif_bit(); c.resources.dbg_pin.set_high().unwrap(); // Start a SPI transaction on ADC0 and ADC1 + c.resources.adc0.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); c.resources.adc1.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); - c.resources.adc2.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); c.resources.dbg_pin.set_low().unwrap(); } - #[task(binds = SPI2, resources = [adc1, dac1, adc2, dac2, iir_state, iir_ch, dac_pin], priority = 2)] + #[task(binds = SPI2, resources = [adc0, dac0, adc1, dac1, iir_state, iir_ch, dac_pin], priority = 2)] fn adc_spi(c: adc_spi::Context) { #[cfg(feature = "bkpt")] cortex_m::asm::bkpt(); @@ -539,30 +538,30 @@ const APP: () = { c.resources.dac_pin.set_high().unwrap(); let output_ch1 = { - let a: u16 = c.resources.adc1.read().unwrap(); + let a: u16 = c.resources.adc0.read().unwrap(); let x0 = f32::from(a as i16); let y0 = c.resources.iir_ch[0].update(&mut c.resources.iir_state[0], x0); y0 as i16 as u16 ^ 0x8000 }; - c.resources.adc1.spi.ifcr.write(|w| w.eotc().set_bit()); + c.resources.adc0.spi.ifcr.write(|w| w.eotc().set_bit()); let output_ch2 = { - let a: u16 = nb::block!(c.resources.adc2.read()).unwrap(); + let a: u16 = nb::block!(c.resources.adc1.read()).unwrap(); let x0 = f32::from(a as i16); let y0 = c.resources.iir_ch[1].update(&mut c.resources.iir_state[1], x0); y0 as i16 as u16 ^ 0x8000 }; - c.resources.adc2.spi.ifcr.write(|w| w.eotc().set_bit()); + c.resources.adc1.spi.ifcr.write(|w| w.eotc().set_bit()); - c.resources.dac1.send(output_ch1).unwrap(); - c.resources.dac2.send(output_ch2).unwrap(); + c.resources.dac0.send(output_ch1).unwrap(); + c.resources.dac1.send(output_ch2).unwrap(); c.resources.dac_pin.set_low().unwrap(); #[cfg(feature = "bkpt")] cortex_m::asm::bkpt(); } - #[idle(resources=[net_interface, mac_addr, iir_state, iir_ch, afe1])] + #[idle(resources=[net_interface, mac_addr, iir_state, iir_ch, afe0, afe1])] fn idle(mut c: idle::Context) -> ! { let mut socket_set_entries: [_; 8] = Default::default(); @@ -594,8 +593,6 @@ const APP: () = { // TODO: Replace with reference to CPU clock from CCDR. next_ms += 400_000.cycles(); - let buffer: String = String::new(); - loop { let tick = Instant::now() > next_ms; @@ -636,12 +633,14 @@ const APP: () = { } else { server.poll(socket, |req| { info!("Got request: {:?}", req); - route_request!(req, buffer, + route_request!(req, readable_attributes: [ - ("stabilizer/afe0/gain", (|| Ok::(c.resources.afe1.get_gain()))) + ("stabilizer/afe0/gain", (|| Ok::(c.resources.afe0.get_gain()))), + ("stabilizer/afe1/gain", (|| Ok::(c.resources.afe1.get_gain()))) ], modifiable_attributes: [ - ("stabilizer/afe0/gain", afe::Gain, (|gain| Ok::<(), ()>(c.resources.afe1.set_gain(gain)))) + ("stabilizer/afe0/gain", afe::Gain, (|gain| Ok::<(), ()>(c.resources.afe0.set_gain(gain)))), + ("stabilizer/afe1/gain", afe::Gain, (|gain| Ok::<(), ()>(c.resources.afe1.set_gain(gain)))) ] ) }); diff --git a/src/server.rs b/src/server.rs index e2bf5e1..c0e46cd 100644 --- a/src/server.rs +++ b/src/server.rs @@ -26,34 +26,26 @@ pub enum Request<'a, 'b> { } #[derive(Serialize)] -pub struct Response<'a, 'b> { +pub struct Response { code: i32, - attribute: &'a str, - value: &'b str, + attribute: String, + value: String, } -impl<'a, 'b> Response<'a, 'b> { - pub fn success<'c, 'd>(attribute: &'c str, value: &'d str) -> Self - where - 'c: 'a, - 'd: 'b, +impl Response { + pub fn success<'a, 'b>(attribute: &'a str, value: &'b str) -> Self { - Self { code: 200, attribute: attribute, value: value} + Self { code: 200, attribute: String::from(attribute), value: String::from(value)} } - pub fn error<'c, 'd>(attribute: &'c str, message: &'d str) -> Self - where - 'c: 'a, - 'd: 'b, + pub fn error<'a, 'b>(attribute: &'a str, message: &'b str) -> Self { - Self { code: 400, attribute: attribute, value: message} + Self { code: 400, attribute: String::from(attribute), value: String::from(message)} } - pub fn custom<'c>(code: i32, message : &'c str) -> Self - where - 'c: 'b, + pub fn custom<'a>(code: i32, message : &'a str) -> Self { - Self { code: code, attribute: "", value: message} + Self { code: code, attribute: String::from(""), value: String::from(message)} } } @@ -85,13 +77,13 @@ impl Server { } } - pub fn poll<'a, 'b, F>( + pub fn poll( &mut self, socket: &mut net::socket::TcpSocket, - f: F, + mut f: F, ) where - F: FnOnce(&Request) -> Response<'a, 'b> + F: FnMut(&Request) -> Response { while socket.can_recv() { let found = socket.recv(|buf| { @@ -112,25 +104,21 @@ impl Server { if found { if self.discard { self.discard = false; - json_reply(socket, &Response::custom(520, "command buffer overflow"), - ); - self.data.clear(); + json_reply(socket, &Response::custom(520, "command buffer overflow")); } else { let r = from_slice::(&self.data[..self.data.len() - 1]); - self.data.clear(); match r { Ok(res) => { let response = f(&res); json_reply(socket, &response); - return; }, Err(err) => { warn!("parse error {:?}", err); - json_reply(socket, &Response::custom(550, "parse error"), - ); + json_reply(socket, &Response::custom(550, "parse error")); } } } + self.data.clear(); } } } From 7cb18cf19782038796a2dda580d055aab0782d8a Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Jun 2020 17:04:09 +0200 Subject: [PATCH 17/40] Simplifying TCP sockets --- src/main.rs | 60 +++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/src/main.rs b/src/main.rs index fbe8d07..06f99e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -576,15 +576,6 @@ const APP: () = { sockets.add(tcp_socket) }; - let mut rx_storage2 = [0; TCP_RX_BUFFER_SIZE]; - let mut tx_storage2 = [0; TCP_TX_BUFFER_SIZE]; - let tcp_handle1 = { - let tcp_rx_buffer = net::socket::TcpSocketBuffer::new(&mut rx_storage2[..]); - let tcp_tx_buffer = net::socket::TcpSocketBuffer::new(&mut tx_storage2[..]); - let tcp_socket = net::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer); - sockets.add(tcp_socket) - }; - let mut server = server::Server::new(); let mut time = 0u32; @@ -601,29 +592,9 @@ const APP: () = { time += 1; } - { - let mut socket = sockets.get::(tcp_handle0); - if socket.state() == net::socket::TcpState::CloseWait { - socket.close(); - } else if !(socket.is_open() || socket.is_listening()) { - socket - .listen(1234) - .unwrap_or_else(|e| warn!("TCP listen error: {:?}", e)); - } else if tick && socket.can_send() { - let s = c.resources.iir_state.lock(|iir_state| server::Status { - t: time, - x0: iir_state[0][0], - y0: iir_state[0][2], - x1: iir_state[1][0], - y1: iir_state[1][2], - }); - server::json_reply(&mut socket, &s); - } - } - { let socket = - &mut *sockets.get::(tcp_handle1); + &mut *sockets.get::(tcp_handle0); if socket.state() == net::socket::TcpState::CloseWait { socket.close(); } else if !(socket.is_open() || socket.is_listening()) { @@ -635,12 +606,33 @@ const APP: () = { info!("Got request: {:?}", req); route_request!(req, readable_attributes: [ - ("stabilizer/afe0/gain", (|| Ok::(c.resources.afe0.get_gain()))), - ("stabilizer/afe1/gain", (|| Ok::(c.resources.afe1.get_gain()))) + ("stabilizer/iir/state", (|| { + let state = c.resources.iir_state.lock(|iir_state| + server::Status { + t: time, + x0: iir_state[0][0], + y0: iir_state[0][2], + x1: iir_state[1][0], + y1: iir_state[1][2], + }); + + Ok::(state) + })), + ("stabilizer/afe0/gain", (|| { + Ok::(c.resources.afe0.get_gain()) + })), + ("stabilizer/afe1/gain", (|| { + Ok::(c.resources.afe1.get_gain()) + })) ], + modifiable_attributes: [ - ("stabilizer/afe0/gain", afe::Gain, (|gain| Ok::<(), ()>(c.resources.afe0.set_gain(gain)))), - ("stabilizer/afe1/gain", afe::Gain, (|gain| Ok::<(), ()>(c.resources.afe1.set_gain(gain)))) + ("stabilizer/afe0/gain", afe::Gain, (|gain| { + Ok::<(), ()>(c.resources.afe0.set_gain(gain)) + })), + ("stabilizer/afe1/gain", afe::Gain, (|gain| { + Ok::<(), ()>(c.resources.afe1.set_gain(gain)) + })) ] ) }); From df5dcb3dc1e004a6baa297429819932848d56968 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Jun 2020 17:15:57 +0200 Subject: [PATCH 18/40] Adding support for modifying IIR state --- src/main.rs | 22 ++++++++++++++++++++++ src/server.rs | 7 +++++++ 2 files changed, 29 insertions(+) diff --git a/src/main.rs b/src/main.rs index 06f99e4..ce36e86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -627,6 +627,28 @@ const APP: () = { ], modifiable_attributes: [ + ("stabilizer/iir0/state", server::IirRequest, (|req: server::IirRequest| { + c.resources.iir_ch.lock(|iir_ch| { + if req.channel > 1 { + return Err(()); + } + + iir_ch[req.channel as usize] = req.iir; + + Ok::(req) + }) + })), + ("stabilizer/iir1/state", server::IirRequest, (|req: server::IirRequest| { + c.resources.iir_ch.lock(|iir_ch| { + if req.channel > 1 { + return Err(()); + } + + iir_ch[req.channel as usize] = req.iir; + + Ok::(req) + }) + })), ("stabilizer/afe0/gain", afe::Gain, (|gain| { Ok::<(), ()>(c.resources.afe0.set_gain(gain)) })), diff --git a/src/server.rs b/src/server.rs index c0e46cd..68edafe 100644 --- a/src/server.rs +++ b/src/server.rs @@ -18,6 +18,7 @@ use serde_json_core::{ }; use super::net; +use super::iir; #[derive(Deserialize, Serialize, Debug)] pub enum Request<'a, 'b> { @@ -25,6 +26,12 @@ pub enum Request<'a, 'b> { Write{attribute: &'a str, value: &'b str}, } +#[derive(Serialize, Deserialize)] +pub struct IirRequest { + pub channel: u8, + pub iir: iir::IIR, +} + #[derive(Serialize)] pub struct Response { code: i32, From beecbe3efce987da790c62575a9c7501c1f126d1 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 3 Jun 2020 17:36:43 +0200 Subject: [PATCH 19/40] Refactoring AFE code --- Cargo.lock | 21 +++++++++++++++++++++ Cargo.toml | 1 + src/afe.rs | 43 ++++++++++++++++++++++++++----------------- src/main.rs | 7 +++---- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44886bb..f6e7f56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,6 +178,24 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "enum-iterator" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "enum-iterator-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "generic-array" version = "0.12.3" @@ -392,6 +410,7 @@ dependencies = [ "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-iterator 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)", @@ -524,6 +543,8 @@ dependencies = [ "checksum cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62092f6ff344e9b0adb748f0302ed69889ba2fae1fce446e3788d4726ea73bb" "checksum cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa" "checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" +"checksum enum-iterator 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c79a6321a1197d7730510c7e3f6cb80432dfefecb32426de8cea0aa19b4bb8d7" +"checksum enum-iterator-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" "checksum hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" diff --git a/Cargo.toml b/Cargo.toml index acaad2f..f82a952 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ cortex-m-rtfm = "0.5" embedded-hal = "0.2.3" nb = "0.1.2" asm-delay = "0.7.0" +enum-iterator = "0.6.0" [dependencies.mcp23017] git = "https://github.com/mrd0ll4r/mcp23017.git" diff --git a/src/afe.rs b/src/afe.rs index c3d9f3c..dfe0b00 100644 --- a/src/afe.rs +++ b/src/afe.rs @@ -1,6 +1,8 @@ use embedded_hal; +use core::convert::TryFrom; +use enum_iterator::IntoEnumIterator; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, IntoEnumIterator)] pub enum Gain { G1 = 0b00, G2 = 0b01, @@ -13,6 +15,20 @@ pub struct ProgrammableGainAmplifier { a1: A1 } +impl TryFrom for Gain { + type Error = (); + + fn try_from(value: u8) -> Result { + for gain in Gain::into_enum_iter() { + if value == gain as u8 { + return Ok(gain) + } + } + + Err(()) + } +} + impl ProgrammableGainAmplifier where A0: embedded_hal::digital::v2::StatefulOutputPin, @@ -43,22 +59,15 @@ where } } - pub fn get_gain(&self) -> Gain { - let lsb_set = self.a0.is_set_high().unwrap(); - let msb_set = self.a1.is_set_high().unwrap(); - - if msb_set { - if lsb_set { - Gain::G10 - } else { - Gain::G5 - } - } else { - if lsb_set { - Gain::G2 - } else { - Gain::G1 - } + pub fn get_gain(&self) -> Result { + let mut code: u8 = 0; + if self.a0.is_set_high().unwrap() { + code |= 0b1; } + if self.a1.is_set_high().unwrap() { + code |= 0b10; + } + + Gain::try_from(code) } } diff --git a/src/main.rs b/src/main.rs index 4401bfe..f6018c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -252,7 +252,7 @@ const APP: () = { .frame_size(16) .swap_mosi_miso(); - dp.SPI4.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 25.mhz(), &clocks) + dp.SPI4.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 50.mhz(), &clocks) }; let dac2_spi = { @@ -269,7 +269,7 @@ const APP: () = { .frame_size(16) .swap_mosi_miso(); - dp.SPI5.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 25.mhz(), &clocks) + dp.SPI5.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 50.mhz(), &clocks) }; let pounder_devices = { @@ -487,6 +487,7 @@ const APP: () = { y0 as i16 as u16 ^ 0x8000 }; c.resources.adc1.spi.ifcr.write(|w| w.eotc().set_bit()); + c.resources.dac1.send(output_ch1).unwrap(); let output_ch2 = { let a: u16 = nb::block!(c.resources.adc2.read()).unwrap(); @@ -495,8 +496,6 @@ const APP: () = { y0 as i16 as u16 ^ 0x8000 }; c.resources.adc2.spi.ifcr.write(|w| w.eotc().set_bit()); - - c.resources.dac1.send(output_ch1).unwrap(); c.resources.dac2.send(output_ch2).unwrap(); c.resources.dac_pin.set_low().unwrap(); From ade06cbcb874bb9318c08102fcc2e88e63314cec Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 4 Jun 2020 16:56:04 +0200 Subject: [PATCH 20/40] Updating AD9959 api --- src/main.rs | 3 ++- src/pounder/mod.rs | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index f6018c8..99a8c5f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -303,7 +303,8 @@ const APP: () = { io_update, asm_delay, ad9959::Mode::FourBitSerial, - 100_000_000).unwrap() + 100_000_000, + 5).unwrap() }; let io_expander = { diff --git a/src/pounder/mod.rs b/src/pounder/mod.rs index b3ce19f..d29e3bd 100644 --- a/src/pounder/mod.rs +++ b/src/pounder/mod.rs @@ -183,21 +183,22 @@ where 1_u8.wrapping_shl(5)).map_err(|_| Error::I2c)?; devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?; - devices.select_onboard_clock()?; + // Select the on-board clock with a 5x prescaler (500MHz). + devices.select_onboard_clock(5u8)?; Ok(devices) } - pub fn select_external_clock(&mut self, frequency: u32) -> Result<(), Error>{ + pub fn select_external_clock(&mut self, frequency: u32, prescaler: u8) -> Result<(), Error>{ self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?; - self.ad9959.set_clock_frequency(frequency).map_err(|_| Error::DDS)?; + self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::DDS)?; Ok(()) } - pub fn select_onboard_clock(&mut self) -> Result<(), Error> { + pub fn select_onboard_clock(&mut self, prescaler: u8) -> Result<(), Error> { self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?; - self.ad9959.set_clock_frequency(100_000_000).map_err(|_| Error::DDS)?; + self.ad9959.configure_system_clock(100_000_000, prescaler).map_err(|_| Error::DDS)?; Ok(()) } From 4dcf2b57bdc540501238b238e0590d064034b88f Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 8 Jun 2020 09:36:28 +0200 Subject: [PATCH 21/40] Updating project structure --- .gitmodules | 3 + Cargo.lock | 390 ++++++------ Cargo.toml | 84 +-- ad9959/.gitignore | 2 + ad9959/Cargo.toml | 11 + ad9959/src/lib.rs | 350 +++++++++++ stabilizer/Cargo.lock | 583 ++++++++++++++++++ stabilizer/Cargo.toml | 76 +++ openocd.gdb => stabilizer/openocd.gdb | 0 {src => stabilizer/src}/afe.rs | 0 {src => stabilizer/src}/eeprom.rs | 0 {src => stabilizer/src}/eth.rs | 0 {src => stabilizer/src}/iir.rs | 0 {src => stabilizer/src}/main.rs | 88 +++ .../src}/pounder/attenuators.rs | 0 {src => stabilizer/src}/pounder/error.rs | 0 {src => stabilizer/src}/pounder/mod.rs | 0 {src => stabilizer/src}/pounder/rf_power.rs | 0 {src => stabilizer/src}/pounder/types.rs | 0 {src => stabilizer/src}/server.rs | 0 stm32h7xx-hal | 1 + 21 files changed, 1309 insertions(+), 279 deletions(-) create mode 100644 .gitmodules create mode 100644 ad9959/.gitignore create mode 100644 ad9959/Cargo.toml create mode 100644 ad9959/src/lib.rs create mode 100644 stabilizer/Cargo.lock create mode 100644 stabilizer/Cargo.toml rename openocd.gdb => stabilizer/openocd.gdb (100%) rename {src => stabilizer/src}/afe.rs (100%) rename {src => stabilizer/src}/eeprom.rs (100%) rename {src => stabilizer/src}/eth.rs (100%) rename {src => stabilizer/src}/iir.rs (100%) rename {src => stabilizer/src}/main.rs (90%) rename {src => stabilizer/src}/pounder/attenuators.rs (100%) rename {src => stabilizer/src}/pounder/error.rs (100%) rename {src => stabilizer/src}/pounder/mod.rs (100%) rename {src => stabilizer/src}/pounder/rf_power.rs (100%) rename {src => stabilizer/src}/pounder/types.rs (100%) rename {src => stabilizer/src}/server.rs (100%) create mode 160000 stm32h7xx-hal diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5340aad --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "stm32h7xx-hal"] + path = stm32h7xx-hal + url = https://github.com/quartiq/stm32h7xx-hal.git diff --git a/Cargo.lock b/Cargo.lock index f6e7f56..bb74654 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,424 +3,462 @@ [[package]] name = "ad9959" version = "0.1.0" -source = "git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver#b45bce3f0dd7a58b5a272b778001dc9422c2c160" dependencies = [ - "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field", + "embedded-hal", ] -[[package]] -name = "aligned" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "aligned" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" dependencies = [ - "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "as-slice", ] [[package]] name = "as-slice" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3", + "generic-array 0.13.2", + "stable_deref_trait", ] [[package]] name = "asm-delay" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc4896200a8422e15fbb1899cb436889d215954d376bf32d311a2330ac13440" dependencies = [ - "bitrate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitrate", + "cortex-m", + "embedded-hal", ] [[package]] name = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "bare-metal" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version", ] [[package]] name = "bit_field" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitrate" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d" [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cast" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version", ] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cortex-m" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cortex-m" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" dependencies = [ - "aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aligned", + "bare-metal", + "volatile-register", ] [[package]] name = "cortex-m-log" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978caafe65d1023d38b00c76b83564788fc351d954a5005fb72cf992c0d61458" dependencies = [ - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m", + "cortex-m-semihosting", + "log", ] [[package]] name = "cortex-m-rt" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28" dependencies = [ - "cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt-macros", + "r0", ] [[package]] name = "cortex-m-rt-macros" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "cortex-m-rtfm" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf0b9fd3f042cb3793d15daf3cea201b2f25c99b0b5b936a551bb6909c3ae5b" dependencies = [ - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m", + "cortex-m-rt", + "cortex-m-rtfm-macros", + "heapless", + "rtfm-core", ] [[package]] name = "cortex-m-rtfm-macros" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62092f6ff344e9b0adb748f0302ed69889ba2fae1fce446e3788d4726ea73bb" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "rtfm-syntax", + "syn", ] [[package]] name = "cortex-m-semihosting" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa" dependencies = [ - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m", ] [[package]] name = "embedded-hal" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" dependencies = [ - "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nb", + "void", ] [[package]] name = "enum-iterator" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79a6321a1197d7730510c7e3f6cb80432dfefecb32426de8cea0aa19b4bb8d7" dependencies = [ - "enum-iterator-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-iterator-derive", ] [[package]] name = "enum-iterator-derive" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] name = "generic-array" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" dependencies = [ - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] name = "hash32" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "heapless" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73a8a2391a3bc70b31f60e7a90daa5755a360559c0b6b9c5cfc0fee482362dc0" dependencies = [ - "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "as-slice", + "generic-array 0.13.2", + "hash32", + "stable_deref_trait", ] [[package]] name = "indexmap" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] name = "managed" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" [[package]] name = "mcp23017" version = "0.1.1" source = "git+https://github.com/mrd0ll4r/mcp23017.git#a3d072754abca60a92ece820f7cfb767a0c11669" dependencies = [ - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal", ] [[package]] name = "nb" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" [[package]] name = "panic-halt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" + +[[package]] +name = "panic-itm" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98830d17a95587207e41edaa3009b143d326ce134b0e3538ac98246a67d66cc3" +dependencies = [ + "cortex-m", +] [[package]] name = "panic-semihosting" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c" dependencies = [ - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m", + "cortex-m-semihosting", ] [[package]] name = "paste" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" dependencies = [ - "paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", + "paste-impl", + "proc-macro-hack", ] [[package]] name = "paste-impl" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" dependencies = [ - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "proc-macro-hack" version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" [[package]] name = "proc-macro2" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quote" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "r0" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" [[package]] name = "rtfm-core" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493" [[package]] name = "rtfm-syntax" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4455e23c34df3d66454e7e218a4d76a7f83321d04a806be614463341cec4116e" dependencies = [ - "indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap", + "proc-macro2", + "syn", ] [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", ] [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" dependencies = [ - "serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde-json-core" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf406405ada9ef326ca78677324ac66994ff348fc48a16030be08caeed29825" dependencies = [ - "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless", + "serde", ] [[package]] name = "serde_derive" version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "smoltcp" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "byteorder", + "log", + "managed", ] [[package]] name = "stabilizer" version = "0.3.0" dependencies = [ - "ad9959 0.1.0 (git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver)", - "asm-delay 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "enum-iterator 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)", - "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", - "serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git)", + "ad9959", + "asm-delay", + "cortex-m", + "cortex-m-log", + "cortex-m-rt", + "cortex-m-rtfm", + "embedded-hal", + "enum-iterator", + "heapless", + "log", + "mcp23017", + "nb", + "panic-halt", + "panic-semihosting", + "serde", + "serde-json-core", + "smoltcp", + "stm32h7-ethernet", "stm32h7xx-hal 0.5.0", ] @@ -428,16 +466,18 @@ dependencies = [ name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" [[package]] name = "stm32h7" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9beb5e2a223c82f263c3051bba4614aebc6e98bd40217df3cd8817c83ac7bd8" dependencies = [ - "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bare-metal", + "cortex-m", + "cortex-m-rt", + "vcell", ] [[package]] @@ -445,9 +485,9 @@ name = "stm32h7-ethernet" version = "0.1.1" source = "git+https://github.com/quartiq/stm32h7-ethernet.git#cf9b8bb2e1b440d8ada6ac6048f48dc4ed9c269a" dependencies = [ - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m", + "log", + "smoltcp", "stm32h7xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -455,129 +495,77 @@ dependencies = [ name = "stm32h7xx-hal" version = "0.5.0" dependencies = [ - "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bare-metal", + "cast", + "cortex-m", + "cortex-m-log", + "cortex-m-rt", + "cortex-m-rtfm", + "embedded-hal", + "nb", + "panic-itm", + "paste", + "stm32h7", + "void", ] [[package]] name = "stm32h7xx-hal" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "987c66628f30012ed9a41cc738421c5caece03292c0cc8fd1e99956f122735bd" dependencies = [ - "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bare-metal", + "cast", + "cortex-m", + "cortex-m-rt", + "embedded-hal", + "nb", + "paste", + "stm32h7", + "void", ] [[package]] name = "syn" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "vcell" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "volatile-register" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" dependencies = [ - "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "vcell", ] - -[metadata] -"checksum ad9959 0.1.0 (git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver)" = "" -"checksum aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" -"checksum aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" -"checksum as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" -"checksum asm-delay 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e0c8eec73de29ae94b2aff405a272304bc286204ddb1cdf20d7e2249078ae20" -"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" -"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum bitrate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0b159a1e8306949579de3698c841dba58058197b65c60807194e4fa1e7a554" -"checksum cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" -"checksum cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "978caafe65d1023d38b00c76b83564788fc351d954a5005fb72cf992c0d61458" -"checksum cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28" -"checksum cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647" -"checksum cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf0b9fd3f042cb3793d15daf3cea201b2f25c99b0b5b936a551bb6909c3ae5b" -"checksum cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62092f6ff344e9b0adb748f0302ed69889ba2fae1fce446e3788d4726ea73bb" -"checksum cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa" -"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" -"checksum enum-iterator 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c79a6321a1197d7730510c7e3f6cb80432dfefecb32426de8cea0aa19b4bb8d7" -"checksum enum-iterator-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" -"checksum hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" -"checksum heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "73a8a2391a3bc70b31f60e7a90daa5755a360559c0b6b9c5cfc0fee482362dc0" -"checksum indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" -"checksum mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)" = "" -"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" -"checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" -"checksum panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c" -"checksum paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" -"checksum paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" -"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" -"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" -"checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" -"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" -"checksum rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493" -"checksum rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4455e23c34df3d66454e7e218a4d76a7f83321d04a806be614463341cec4116e" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" -"checksum serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf406405ada9ef326ca78677324ac66994ff348fc48a16030be08caeed29825" -"checksum serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" -"checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9beb5e2a223c82f263c3051bba4614aebc6e98bd40217df3cd8817c83ac7bd8" -"checksum stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git)" = "" -"checksum stm32h7xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "987c66628f30012ed9a41cc738421c5caece03292c0cc8fd1e99956f122735bd" -"checksum syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" -"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" diff --git a/Cargo.toml b/Cargo.toml index f82a952..de36a31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,79 +1,7 @@ -[package] -name = "stabilizer" -version = "0.3.0" -authors = ["Robert Jördens "] -description = "Firmware for the Sinara Stabilizer board (stm32h743, eth, poe, 2 adc, 2 dac)" -categories = ["embedded", "no-std", "hardware-support", "science"] -license = "GPL-3.0-or-later" -keywords = ["ethernet", "stm32h7", "adc", "dac", "physics"] -repository = "https://github.com/quartiq/stabilizer" -readme = "README.md" -documentation = "https://docs.rs/stabilizer/" -edition = "2018" -exclude = [ - ".travis.yml", - ".gitignore", - "doc/", - "doc/*" +[workspace] + +members = [ + "stabilizer", + "stm32h7xx-hal", + "ad9959", ] - -[badges] -travis-ci = { repository = "quartiq/stabilizer", branch = "master" } -maintenance = { status = "experimental" } - -[package.metadata.docs.rs] -features = [] -default-target = "thumbv7em-none-eabihf" - -[dependencies] -cortex-m = { version = "0.6", features = ["const-fn"] } -cortex-m-rt = { version = "0.6", features = ["device"] } -cortex-m-log = { version = "0.6", features = ["log-integration"] } -log = "0.4" -panic-semihosting = { version = "0.5", optional = true } -panic-halt = "0.2" -serde = { version = "1.0", features = ["derive"], default-features = false } -heapless = "0.5" -serde-json-core = "0.1" -cortex-m-rtfm = "0.5" -embedded-hal = "0.2.3" -nb = "0.1.2" -asm-delay = "0.7.0" -enum-iterator = "0.6.0" - -[dependencies.mcp23017] -git = "https://github.com/mrd0ll4r/mcp23017.git" - -[dependencies.smoltcp] -version = "0.6" -features = ["ethernet", "proto-ipv4", "socket-tcp", "proto-ipv6"] -default-features = false - -[dependencies.ad9959] -git = "https://github.com/quartiq/ad9959.git" -branch = "feature/basic-driver" - -[dependencies.stm32h7-ethernet] -git = "https://github.com/quartiq/stm32h7-ethernet.git" -features = ["stm32h743v"] - -[dependencies.stm32h7xx-hal] -#git = "https://github.com/quartiq/stm32h7xx-hal.git" -#branch = "feature/quad-spi" -path = "../stm32h7xx-hal/" -features = ["stm32h743v", "rt", "unproven"] - -[features] -semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] -bkpt = [ ] -nightly = ["cortex-m/inline-asm"] - -[profile.dev] -codegen-units = 1 -incremental = false -opt-level = 3 - -[profile.release] -debug = true -lto = true -codegen-units = 1 diff --git a/ad9959/.gitignore b/ad9959/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/ad9959/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/ad9959/Cargo.toml b/ad9959/Cargo.toml new file mode 100644 index 0000000..f234470 --- /dev/null +++ b/ad9959/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ad9959" +version = "0.1.0" +authors = ["Ryan Summers "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +embedded-hal = {version = "0.2.3", features = ["unproven"]} +bit_field = "0.10.0" diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs new file mode 100644 index 0000000..5346bdc --- /dev/null +++ b/ad9959/src/lib.rs @@ -0,0 +1,350 @@ +#![no_std] + +use bit_field::BitField; +use embedded_hal::{ + digital::v2::OutputPin, + blocking::delay::DelayMs, +}; + +/// A device driver for the AD9959 direct digital synthesis (DDS) chip. +/// +/// This chip provides four independently controllable digital-to-analog output sinusoids with +/// configurable phase, amplitude, and frequency. All channels are inherently synchronized as they +/// are derived off a common system clock. +/// +/// The chip contains a configurable PLL and supports system clock frequencies up to 500 MHz. +/// +/// The chip supports a number of serial interfaces to improve data throughput, including normal, +/// dual, and quad SPI configurations. +pub struct Ad9959 { + interface: INTERFACE, + delay: DELAY, + reference_clock_frequency: u32, + system_clock_multiplier: u8, + io_update: UPDATE, +} + +pub trait Interface { + type Error; + + fn configure_mode(&mut self, mode: Mode) -> Result<(), Self::Error>; + + fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Self::Error>; + + fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>; +} + +#[derive(Copy, Clone, PartialEq)] +pub enum Mode { + SingleBitTwoWire = 0b00, + SingleBitThreeWire = 0b01, + TwoBitSerial = 0b10, + FourBitSerial = 0b11, +} + +/// The configuration registers within the AD9959 DDS device. The values of each register are +/// equivalent to the address. +pub enum Register { + CSR = 0x00, + FR1 = 0x01, + FR2 = 0x02, + CFR = 0x03, + CFTW0 = 0x04, + CPOW0 = 0x05, + ACR = 0x06, + LSRR = 0x07, + RDW = 0x08, + FDW = 0x09, + CW1 = 0x0a, + CW2 = 0x0b, + CW3 = 0x0c, + CW4 = 0x0d, + CW5 = 0x0e, + CW6 = 0x0f, + CW7 = 0x10, + CW8 = 0x11, + CW9 = 0x12, + CW10 = 0x13, + CW11 = 0x14, + CW12 = 0x15, + CW13 = 0x16, + CW14 = 0x17, + CW15 = 0x18, +} + +/// Specifies an output channel of the AD9959 DDS chip. +pub enum Channel { + One = 0, + Two = 1, + Three = 2, + Four = 3, +} + +/// Possible errors generated by the AD9959 driver. +#[derive(Debug)] +pub enum Error { + Interface(InterfaceE), + Bounds, + Pin, + Frequency, +} + +impl From for Error { + fn from(interface_error: InterfaceE) -> Self { + Error::Interface(interface_error) + } +} + +impl Ad9959 +where + INTERFACE: Interface, + DELAY: DelayMs, + UPDATE: OutputPin, + +{ + pub fn new(interface: INTERFACE, + reset_pin: &mut RST, + io_update: UPDATE, + delay: DELAY, + desired_mode: Mode, + clock_frequency: u32, + multiplier: u8) -> Result> + where + RST: OutputPin, + { + let mut ad9959 = Ad9959 { + interface: interface, + io_update: io_update, + delay: delay, + reference_clock_frequency: clock_frequency, + system_clock_multiplier: 1, + }; + + ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; + + // Reset the AD9959 + reset_pin.set_high().or_else(|_| Err(Error::Pin))?; + + // Delay for a clock cycle to allow the device to reset. + ad9959.delay.delay_ms((1000.0 / clock_frequency as f32) as u8); + + reset_pin.set_low().or_else(|_| Err(Error::Pin))?; + + ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?; + + // Program the interface configuration in the AD9959. Default to all channels enabled. + let mut csr: [u8; 1] = [0xF0]; + csr[0].set_bits(1..3, desired_mode as u8); + ad9959.interface.write(0, &csr)?; + + // Configure the interface to the desired mode. + ad9959.interface.configure_mode(Mode::FourBitSerial)?; + + // Latch the configuration registers to make them active. + ad9959.latch_configuration()?; + + ad9959.interface.configure_mode(desired_mode)?; + + // Set the clock frequency to configure the device as necessary. + ad9959.configure_system_clock(clock_frequency, multiplier)?; + Ok(ad9959) + } + + fn latch_configuration(&mut self) -> Result<(), Error> { + self.io_update.set_high().or_else(|_| Err(Error::Pin))?; + // The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one + // full SYNC_CLK pulse to register. For safety, we latch for 5 here. + self.delay.delay_ms((5000.0 / self.system_clock_frequency()) as u8); + self.io_update.set_low().or_else(|_| Err(Error::Pin))?; + + Ok(()) + } + + /// Configure the internal system clock of the chip. + /// + /// Arguments: + /// * `reference_clock_frequency` - The reference clock frequency provided to the AD9959 core. + /// * `prescaler` - The frequency prescaler of the system clock. Must be 1 or 4-20. + /// + /// Returns: + /// The actual frequency configured for the internal system clock. + pub fn configure_system_clock(&mut self, + reference_clock_frequency: u32, + prescaler: u8) -> Result> + { + self.reference_clock_frequency = reference_clock_frequency; + + if prescaler != 1 && (prescaler > 20 || prescaler < 4) { + return Err(Error::Bounds); + } + + let frequency = prescaler as f64 * self.reference_clock_frequency as f64; + if frequency > 500_000_000.0f64 { + return Err(Error::Frequency); + } + + // TODO: Update / disable any enabled channels? + let mut fr1: [u8; 3] = [0, 0, 0]; + self.interface.read(Register::FR1 as u8, &mut fr1)?; + fr1[0].set_bits(2..=6, prescaler); + + let vco_range = frequency > 255e6; + fr1[0].set_bit(7, vco_range); + + self.interface.write(Register::FR1 as u8, &fr1)?; + self.system_clock_multiplier = prescaler; + + Ok(self.system_clock_frequency()) + } + + /// Perform a self-test of the communication interface. + /// + /// Note: + /// This modifies the existing channel enables. They are restored upon exit. + /// + /// Returns: + /// True if the self test succeeded. False otherwise. + pub fn self_test(&mut self) -> Result> { + let mut csr: [u8; 1] = [0]; + self.interface.read(Register::CSR as u8, &mut csr)?; + let old_csr = csr[0]; + + // Enable all channels. + csr[0].set_bits(4..8, 0xF); + self.interface.write(Register::CSR as u8, &csr)?; + + // Read back the enable. + csr[0] = 0; + self.interface.read(Register::CSR as u8, &mut csr)?; + if csr[0].get_bits(4..8) != 0xF { + return Ok(false); + } + + // Clear all channel enables. + csr[0].set_bits(4..8, 0x0); + self.interface.write(Register::CSR as u8, &csr)?; + + // Read back the enable. + csr[0] = 0xFF; + self.interface.read(Register::CSR as u8, &mut csr)?; + if csr[0].get_bits(4..8) != 0 { + return Ok(false); + } + + // Restore the CSR. + csr[0] = old_csr; + self.interface.write(Register::CSR as u8, &csr)?; + + Ok(true) + } + + fn system_clock_frequency(&self) -> f64 { + self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64 + } + + /// Enable an output channel. + pub fn enable_channel(&mut self, channel: Channel) -> Result<(), Error> { + let mut csr: [u8; 1] = [0]; + self.interface.read(Register::CSR as u8, &mut csr)?; + csr[0].set_bit(channel as usize + 4, true); + self.interface.write(Register::CSR as u8, &csr)?; + + Ok(()) + } + + /// Disable an output channel. + pub fn disable_channel(&mut self, channel: Channel) -> Result<(), Error> { + let mut csr: [u8; 1] = [0]; + self.interface.read(Register::CSR as u8, &mut csr)?; + csr[0].set_bit(channel as usize + 4, false); + self.interface.write(Register::CSR as u8, &csr)?; + + Ok(()) + } + + fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> { + let mut csr: [u8; 1] = [0]; + self.interface.read(Register::CSR as u8, &mut csr)?; + + let mut new_csr = csr; + new_csr[0].set_bits(4..8, 0); + new_csr[0].set_bit(4 + channel as usize, true); + + self.interface.write(Register::CSR as u8, &new_csr)?; + + self.interface.write(register as u8, &data)?; + + // Latch the configuration and restore the previous CSR. Note that the re-enable of the + // channel happens immediately, so the CSR update does not need to be latched. + self.latch_configuration()?; + self.interface.write(Register::CSR as u8, &csr)?; + + Ok(()) + } + + /// Configure the phase of a specified channel. + /// + /// Arguments: + /// * `channel` - The channel to configure the frequency of. + /// * `phase_turns` - The desired phase offset in normalized turns. + /// + /// Returns: + /// The actual programmed phase offset of the channel in degrees. + pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result> { + if phase_turns > 1.0 || phase_turns < 0.0 { + return Err(Error::Bounds); + } + + let phase_offset: u16 = (phase_turns * 1u32.wrapping_shl(14) as f32) as u16; + self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?; + Ok((phase_offset as f32 / 1u32.wrapping_shl(14) as f32) * 360.0) + } + + /// Configure the amplitude of a specified channel. + /// + /// Arguments: + /// * `channel` - The channel to configure the frequency of. + /// * `amplitude` - A normalized amplitude setting [0, 1]. + /// + /// Returns: + /// The actual normalized amplitude of the channel relative to full-scale range. + pub fn set_amplitude(&mut self, channel: Channel, amplitude: f32) -> Result> { + if amplitude < 0.0 || amplitude > 1.0 { + return Err(Error::Bounds); + } + + let amplitude_control: u16 = (amplitude / 1u16.wrapping_shl(10) as f32) as u16; + let mut acr: [u8; 3] = [0, amplitude_control.to_be_bytes()[0], amplitude_control.to_be_bytes()[1]]; + + // Enable the amplitude multiplier for the channel if required. The amplitude control has + // full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever + // full-scale is used. + acr[1].set_bit(4, amplitude_control >= 1u16.wrapping_shl(10)); + + self.modify_channel(channel, Register::ACR, &acr)?; + + Ok(amplitude_control as f32 / 1_u16.wrapping_shl(10) as f32) + } + + /// Configure the frequency of a specified channel. + /// + /// Arguments: + /// * `channel` - The channel to configure the frequency of. + /// * `frequency` - The desired output frequency in Hz. + /// + /// Returns: + /// The actual programmed frequency of the channel. + pub fn set_frequency(&mut self, channel: Channel, frequency: f64) -> Result> { + if frequency < 0.0 || frequency > self.system_clock_frequency() { + return Err(Error::Bounds); + } + + // The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the + // frequency tuning word and f_s is the system clock rate. + let tuning_word: u32 = ((frequency as f64 / self.system_clock_frequency()) + * 1u64.wrapping_shl(32) as f64) as u32; + + self.modify_channel(channel, Register::CFTW0, &tuning_word.to_be_bytes())?; + Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency()) + } +} diff --git a/stabilizer/Cargo.lock b/stabilizer/Cargo.lock new file mode 100644 index 0000000..f6e7f56 --- /dev/null +++ b/stabilizer/Cargo.lock @@ -0,0 +1,583 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ad9959" +version = "0.1.0" +source = "git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver#b45bce3f0dd7a58b5a272b778001dc9422c2c160" +dependencies = [ + "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "aligned" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "aligned" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "as-slice" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "asm-delay" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitrate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitrate" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cast" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cortex-m" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-log" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-rt" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-rtfm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-rtfm-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cortex-m-semihosting" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "embedded-hal" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "enum-iterator" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "enum-iterator-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hash32" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "heapless" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "indexmap" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "managed" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mcp23017" +version = "0.1.1" +source = "git+https://github.com/mrd0ll4r/mcp23017.git#a3d072754abca60a92ece820f7cfb767a0c11669" +dependencies = [ + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nb" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "panic-halt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "panic-semihosting" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste-impl" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "r0" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rtfm-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rtfm-syntax" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde-json-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "smoltcp" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stabilizer" +version = "0.3.0" +dependencies = [ + "ad9959 0.1.0 (git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver)", + "asm-delay 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-iterator 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)", + "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git)", + "stm32h7xx-hal 0.5.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "stm32h7" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stm32h7-ethernet" +version = "0.1.1" +source = "git+https://github.com/quartiq/stm32h7-ethernet.git#cf9b8bb2e1b440d8ada6ac6048f48dc4ed9c269a" +dependencies = [ + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stm32h7xx-hal" +version = "0.5.0" +dependencies = [ + "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stm32h7xx-hal" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vcell" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "volatile-register" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum ad9959 0.1.0 (git+https://github.com/quartiq/ad9959.git?branch=feature/basic-driver)" = "" +"checksum aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" +"checksum aligned 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" +"checksum as-slice 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" +"checksum asm-delay 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e0c8eec73de29ae94b2aff405a272304bc286204ddb1cdf20d7e2249078ae20" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum bare-metal 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum bitrate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c147d86912d04bef727828fda769a76ca81629a46d8ba311a8d58a26aa91473d" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0b159a1e8306949579de3698c841dba58058197b65c60807194e4fa1e7a554" +"checksum cortex-m 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" +"checksum cortex-m-log 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "978caafe65d1023d38b00c76b83564788fc351d954a5005fb72cf992c0d61458" +"checksum cortex-m-rt 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28" +"checksum cortex-m-rt-macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647" +"checksum cortex-m-rtfm 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf0b9fd3f042cb3793d15daf3cea201b2f25c99b0b5b936a551bb6909c3ae5b" +"checksum cortex-m-rtfm-macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62092f6ff344e9b0adb748f0302ed69889ba2fae1fce446e3788d4726ea73bb" +"checksum cortex-m-semihosting 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "113ef0ecffee2b62b58f9380f4469099b30e9f9cbee2804771b4203ba1762cfa" +"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" +"checksum enum-iterator 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c79a6321a1197d7730510c7e3f6cb80432dfefecb32426de8cea0aa19b4bb8d7" +"checksum enum-iterator-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" +"checksum hash32 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" +"checksum heapless 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "73a8a2391a3bc70b31f60e7a90daa5755a360559c0b6b9c5cfc0fee482362dc0" +"checksum indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" +"checksum mcp23017 0.1.1 (git+https://github.com/mrd0ll4r/mcp23017.git)" = "" +"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" +"checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" +"checksum panic-semihosting 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c03864ac862876c16a308f5286f4aa217f1a69ac45df87ad3cd2847f818a642c" +"checksum paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" +"checksum paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" +"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" +"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +"checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +"checksum rtfm-core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493" +"checksum rtfm-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4455e23c34df3d66454e7e218a4d76a7f83321d04a806be614463341cec4116e" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" +"checksum serde-json-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf406405ada9ef326ca78677324ac66994ff348fc48a16030be08caeed29825" +"checksum serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" +"checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" +"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum stm32h7 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9beb5e2a223c82f263c3051bba4614aebc6e98bd40217df3cd8817c83ac7bd8" +"checksum stm32h7-ethernet 0.1.1 (git+https://github.com/quartiq/stm32h7-ethernet.git)" = "" +"checksum stm32h7xx-hal 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "987c66628f30012ed9a41cc738421c5caece03292c0cc8fd1e99956f122735bd" +"checksum syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" +"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" diff --git a/stabilizer/Cargo.toml b/stabilizer/Cargo.toml new file mode 100644 index 0000000..d614c54 --- /dev/null +++ b/stabilizer/Cargo.toml @@ -0,0 +1,76 @@ +[package] +name = "stabilizer" +version = "0.3.0" +authors = ["Robert Jördens "] +description = "Firmware for the Sinara Stabilizer board (stm32h743, eth, poe, 2 adc, 2 dac)" +categories = ["embedded", "no-std", "hardware-support", "science"] +license = "GPL-3.0-or-later" +keywords = ["ethernet", "stm32h7", "adc", "dac", "physics"] +repository = "https://github.com/quartiq/stabilizer" +readme = "README.md" +documentation = "https://docs.rs/stabilizer/" +edition = "2018" +exclude = [ + ".travis.yml", + ".gitignore", + "doc/", + "doc/*" +] + +[badges] +travis-ci = { repository = "quartiq/stabilizer", branch = "master" } +maintenance = { status = "experimental" } + +[package.metadata.docs.rs] +features = [] +default-target = "thumbv7em-none-eabihf" + +[dependencies] +cortex-m = { version = "0.6", features = ["const-fn"] } +cortex-m-rt = { version = "0.6", features = ["device"] } +cortex-m-log = { version = "0.6", features = ["log-integration"] } +log = "0.4" +panic-semihosting = { version = "0.5", optional = true } +panic-halt = "0.2" +serde = { version = "1.0", features = ["derive"], default-features = false } +heapless = "0.5" +serde-json-core = "0.1" +cortex-m-rtfm = "0.5" +embedded-hal = "0.2.3" +nb = "0.1.2" +asm-delay = "0.7.0" +enum-iterator = "0.6.0" + +[dependencies.mcp23017] +git = "https://github.com/mrd0ll4r/mcp23017.git" + +[dependencies.smoltcp] +version = "0.6" +features = ["ethernet", "proto-ipv4", "socket-tcp", "proto-ipv6"] +default-features = false + +[dependencies.ad9959] +path = "../ad9959" + +[dependencies.stm32h7-ethernet] +git = "https://github.com/quartiq/stm32h7-ethernet.git" +features = ["stm32h743v"] + +[dependencies.stm32h7xx-hal] +path = "../stm32h7xx-hal/" +features = ["stm32h743v", "rt", "unproven"] + +[features] +semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] +bkpt = [ ] +nightly = ["cortex-m/inline-asm"] + +[profile.dev] +codegen-units = 1 +incremental = false +opt-level = 3 + +[profile.release] +debug = true +lto = true +codegen-units = 1 diff --git a/openocd.gdb b/stabilizer/openocd.gdb similarity index 100% rename from openocd.gdb rename to stabilizer/openocd.gdb diff --git a/src/afe.rs b/stabilizer/src/afe.rs similarity index 100% rename from src/afe.rs rename to stabilizer/src/afe.rs diff --git a/src/eeprom.rs b/stabilizer/src/eeprom.rs similarity index 100% rename from src/eeprom.rs rename to stabilizer/src/eeprom.rs diff --git a/src/eth.rs b/stabilizer/src/eth.rs similarity index 100% rename from src/eth.rs rename to stabilizer/src/eth.rs diff --git a/src/iir.rs b/stabilizer/src/iir.rs similarity index 100% rename from src/iir.rs rename to stabilizer/src/iir.rs diff --git a/src/main.rs b/stabilizer/src/main.rs similarity index 90% rename from src/main.rs rename to stabilizer/src/main.rs index 99a8c5f..07f11b6 100644 --- a/src/main.rs +++ b/stabilizer/src/main.rs @@ -98,6 +98,8 @@ static mut NET_STORE: NetStorage = NetStorage { const SCALE: f32 = ((1 << 15) - 1) as f32; +const SPI_START_CODE: u32 = 0x201; + // static ETHERNET_PENDING: AtomicBool = AtomicBool::new(true); const TCP_RX_BUFFER_SIZE: usize = 8192; @@ -111,6 +113,86 @@ type AFE2 = afe::ProgrammableGainAmplifier< hal::gpio::gpiod::PD14>, hal::gpio::gpiod::PD15>>; + + + + +fn dma1_setup( + dma1: &pac::DMA1, + dmamux1: &pac::DMAMUX1, + ma: usize, + pa0: usize, + pa1: usize, +) { + dma1.st[0].cr.modify(|_, w| w.en().clear_bit()); + while dma1.st[0].cr.read().en().bit_is_set() {} + + dma1.st[0].par.write(|w| unsafe { w.bits(pa0 as u32) }); + dma1.st[0].m0ar.write(|w| unsafe { w.bits(ma as u32) }); + dma1.st[0].ndtr.write(|w| w.ndt().bits(1)); + dmamux1.ccr[0].modify(|_, w| w.dmareq_id().tim2_up()); + dma1.st[0].cr.modify(|_, w| { + w.pl() + .medium() + .circ() + .enabled() + .msize() + .bits32() + .minc() + .fixed() + .mburst() + .single() + .psize() + .bits32() + .pinc() + .fixed() + .pburst() + .single() + .dbm() + .disabled() + .dir() + .memory_to_peripheral() + .pfctrl() + .dma() + }); + dma1.st[0].fcr.modify(|_, w| w.dmdis().clear_bit()); + dma1.st[0].cr.modify(|_, w| w.en().set_bit()); + + dma1.st[1].cr.modify(|_, w| w.en().clear_bit()); + while dma1.st[1].cr.read().en().bit_is_set() {} + + dma1.st[1].par.write(|w| unsafe { w.bits(pa1 as u32) }); + dma1.st[1].m0ar.write(|w| unsafe { w.bits(ma as u32) }); + dma1.st[1].ndtr.write(|w| w.ndt().bits(1)); + dmamux1.ccr[1].modify(|_, w| w.dmareq_id().tim2_up()); + dma1.st[1].cr.modify(|_, w| { + w.pl() + .medium() + .circ() + .enabled() + .msize() + .bits32() + .minc() + .fixed() + .mburst() + .single() + .psize() + .bits32() + .pinc() + .fixed() + .pburst() + .single() + .dbm() + .disabled() + .dir() + .memory_to_peripheral() + .pfctrl() + .dma() + }); + dma1.st[1].fcr.modify(|_, w| w.dmdis().clear_bit()); + dma1.st[1].cr.modify(|_, w| w.en().set_bit()); +} + #[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { @@ -442,6 +524,12 @@ const APP: () = { let mut timer2 = dp.TIM2.timer(500.khz(), &mut clocks); timer2.listen(hal::timer::Event::TimeOut); + dma1_setup(&dp.DMA1, + &dp.DMAMUX1, + &SPI_START_CODE as *const _ as usize, + &adc1_spi.spi.cr1 as *const _ as usize, + &adc2_spi.spi.cr1 as *const _ as usize); + init::LateResources { adc1: adc1_spi, dac1: dac1_spi, diff --git a/src/pounder/attenuators.rs b/stabilizer/src/pounder/attenuators.rs similarity index 100% rename from src/pounder/attenuators.rs rename to stabilizer/src/pounder/attenuators.rs diff --git a/src/pounder/error.rs b/stabilizer/src/pounder/error.rs similarity index 100% rename from src/pounder/error.rs rename to stabilizer/src/pounder/error.rs diff --git a/src/pounder/mod.rs b/stabilizer/src/pounder/mod.rs similarity index 100% rename from src/pounder/mod.rs rename to stabilizer/src/pounder/mod.rs diff --git a/src/pounder/rf_power.rs b/stabilizer/src/pounder/rf_power.rs similarity index 100% rename from src/pounder/rf_power.rs rename to stabilizer/src/pounder/rf_power.rs diff --git a/src/pounder/types.rs b/stabilizer/src/pounder/types.rs similarity index 100% rename from src/pounder/types.rs rename to stabilizer/src/pounder/types.rs diff --git a/src/server.rs b/stabilizer/src/server.rs similarity index 100% rename from src/server.rs rename to stabilizer/src/server.rs diff --git a/stm32h7xx-hal b/stm32h7xx-hal new file mode 160000 index 0000000..ba7d21a --- /dev/null +++ b/stm32h7xx-hal @@ -0,0 +1 @@ +Subproject commit ba7d21a018752c2cf9303470badfa8dbf14dc3aa From 0815a4cff5aec39df0d529e936f8bf4c2394500f Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 8 Jun 2020 18:11:14 +0200 Subject: [PATCH 22/40] Updating structure, adding DMA triggered transfers --- Cargo.toml | 10 + stabilizer/openocd.gdb => openocd.gdb | 3 +- stabilizer/Cargo.toml | 9 - stabilizer/src/main.rs | 319 ++++++++++---------------- stm32h7xx-hal | 2 +- 5 files changed, 130 insertions(+), 213 deletions(-) rename stabilizer/openocd.gdb => openocd.gdb (95%) diff --git a/Cargo.toml b/Cargo.toml index de36a31..05df738 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,13 @@ members = [ "stm32h7xx-hal", "ad9959", ] + +[profile.dev] +codegen-units = 1 +incremental = false +opt-level = 3 + +[profile.release] +debug = true +lto = true +codegen-units = 1 diff --git a/stabilizer/openocd.gdb b/openocd.gdb similarity index 95% rename from stabilizer/openocd.gdb rename to openocd.gdb index 1728d7e..e903a33 100644 --- a/stabilizer/openocd.gdb +++ b/openocd.gdb @@ -16,7 +16,7 @@ break rust_begin_unwind load # tbreak cortex_m_rt::reset_handler -# monitor reset halt +monitor reset halt # cycle counter delta tool, place two bkpts around the section set var $cc=0xe0001004 @@ -26,4 +26,3 @@ set var $t0=*$cc continue end #set var $t0=*$cc -continue diff --git a/stabilizer/Cargo.toml b/stabilizer/Cargo.toml index d614c54..ffbd1e4 100644 --- a/stabilizer/Cargo.toml +++ b/stabilizer/Cargo.toml @@ -65,12 +65,3 @@ semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] bkpt = [ ] nightly = ["cortex-m/inline-asm"] -[profile.dev] -codegen-units = 1 -incremental = false -opt-level = 3 - -[profile.release] -debug = true -lto = true -codegen-units = 1 diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 07f11b6..1840360 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -1,4 +1,4 @@ -#![deny(warnings)] +//#![deny(warnings)] #![allow(clippy::missing_safety_doc)] #![no_std] #![no_main] @@ -114,85 +114,6 @@ type AFE2 = afe::ProgrammableGainAmplifier< hal::gpio::gpiod::PD15>>; - - - -fn dma1_setup( - dma1: &pac::DMA1, - dmamux1: &pac::DMAMUX1, - ma: usize, - pa0: usize, - pa1: usize, -) { - dma1.st[0].cr.modify(|_, w| w.en().clear_bit()); - while dma1.st[0].cr.read().en().bit_is_set() {} - - dma1.st[0].par.write(|w| unsafe { w.bits(pa0 as u32) }); - dma1.st[0].m0ar.write(|w| unsafe { w.bits(ma as u32) }); - dma1.st[0].ndtr.write(|w| w.ndt().bits(1)); - dmamux1.ccr[0].modify(|_, w| w.dmareq_id().tim2_up()); - dma1.st[0].cr.modify(|_, w| { - w.pl() - .medium() - .circ() - .enabled() - .msize() - .bits32() - .minc() - .fixed() - .mburst() - .single() - .psize() - .bits32() - .pinc() - .fixed() - .pburst() - .single() - .dbm() - .disabled() - .dir() - .memory_to_peripheral() - .pfctrl() - .dma() - }); - dma1.st[0].fcr.modify(|_, w| w.dmdis().clear_bit()); - dma1.st[0].cr.modify(|_, w| w.en().set_bit()); - - dma1.st[1].cr.modify(|_, w| w.en().clear_bit()); - while dma1.st[1].cr.read().en().bit_is_set() {} - - dma1.st[1].par.write(|w| unsafe { w.bits(pa1 as u32) }); - dma1.st[1].m0ar.write(|w| unsafe { w.bits(ma as u32) }); - dma1.st[1].ndtr.write(|w| w.ndt().bits(1)); - dmamux1.ccr[1].modify(|_, w| w.dmareq_id().tim2_up()); - dma1.st[1].cr.modify(|_, w| { - w.pl() - .medium() - .circ() - .enabled() - .msize() - .bits32() - .minc() - .fixed() - .mburst() - .single() - .psize() - .bits32() - .pinc() - .fixed() - .pburst() - .single() - .dbm() - .disabled() - .dir() - .memory_to_peripheral() - .pfctrl() - .dma() - }); - dma1.st[1].fcr.modify(|_, w| w.dmdis().clear_bit()); - dma1.st[1].cr.modify(|_, w| w.en().set_bit()); -} - #[rtfm::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)] const APP: () = { struct Resources { @@ -206,15 +127,13 @@ const APP: () = { eeprom_i2c: hal::i2c::I2c, - dbg_pin: hal::gpio::gpioc::PC6>, - dac_pin: hal::gpio::gpiob::PB15>, timer: hal::timer::Timer, net_interface: net::iface::EthernetInterface<'static, 'static, 'static, ethernet::EthernetDMA<'static>>, _eth_mac: ethernet::EthernetMAC, mac_addr: net::wire::EthernetAddress, - pounder: pounder::PounderDevices, + //pounder: pounder::PounderDevices, #[init([[0.; 5]; 2])] iir_state: [IIRState; 2], @@ -282,8 +201,9 @@ const APP: () = { }) .communication_mode(hal::spi::CommunicationMode::Receiver) .manage_cs() - .cs_delay(220e-9) - .frame_size(16); + .transfer_size(1) + .frame_size(16) + .cs_delay(220e-9); let mut spi = dp.SPI2.spi( (spi_sck, spi_miso, hal::spi::NoMosi), @@ -291,7 +211,7 @@ const APP: () = { 50.mhz(), &clocks); - spi.listen(hal::spi::Event::Rxp); + spi.listen(hal::spi::Event::Eot); spi }; @@ -308,21 +228,24 @@ const APP: () = { }) .communication_mode(hal::spi::CommunicationMode::Receiver) .manage_cs() + .transfer_size(1) .frame_size(16) .cs_delay(220e-9); - let spi = dp.SPI3.spi( + let mut spi = dp.SPI3.spi( (spi_sck, spi_miso, hal::spi::NoMosi), config, 50.mhz(), &clocks); + spi.listen(hal::spi::Event::Eot); + spi }; let dac1_spi = { - let spi_miso = gpioe.pe5.into_alternate_af5(); - let spi_sck = gpioe.pe2.into_alternate_af5(); + let spi_miso = gpioe.pe5.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); + let spi_sck = gpioe.pe2.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); let _spi_nss = gpioe.pe4.into_alternate_af5(); let config = hal::spi::Config::new(hal::spi::Mode{ @@ -331,15 +254,17 @@ const APP: () = { }) .communication_mode(hal::spi::CommunicationMode::Transmitter) .manage_cs() + .transfer_size(1) .frame_size(16) .swap_mosi_miso(); - dp.SPI4.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 50.mhz(), &clocks) + let spi = dp.SPI4.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 50.mhz(), &clocks); + spi }; let dac2_spi = { - let spi_miso = gpiof.pf8.into_alternate_af5(); - let spi_sck = gpiof.pf7.into_alternate_af5(); + let spi_miso = gpiof.pf8.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); + let spi_sck = gpiof.pf7.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); let _spi_nss = gpiof.pf6.into_alternate_af5(); let config = hal::spi::Config::new(hal::spi::Mode{ @@ -348,93 +273,96 @@ const APP: () = { }) .communication_mode(hal::spi::CommunicationMode::Transmitter) .manage_cs() + .transfer_size(1) .frame_size(16) .swap_mosi_miso(); - dp.SPI5.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 50.mhz(), &clocks) + let spi = dp.SPI5.spi((spi_sck, spi_miso, hal::spi::NoMosi), config, 50.mhz(), &clocks); + + spi }; - let pounder_devices = { - let ad9959 = { - let qspi_interface = { - // Instantiate the QUADSPI pins and peripheral interface. - // TODO: Place these into a pins structure that is provided to the QSPI - // constructor. - let _qspi_clk = gpiob.pb2.into_alternate_af9(); - let _qspi_ncs = gpioc.pc11.into_alternate_af9(); - let _qspi_io0 = gpioe.pe7.into_alternate_af10(); - let _qspi_io1 = gpioe.pe8.into_alternate_af10(); - let _qspi_io2 = gpioe.pe9.into_alternate_af10(); - let _qspi_io3 = gpioe.pe10.into_alternate_af10(); + // let pounder_devices = { + // let ad9959 = { + // let qspi_interface = { + // // Instantiate the QUADSPI pins and peripheral interface. + // // TODO: Place these into a pins structure that is provided to the QSPI + // // constructor. + // let _qspi_clk = gpiob.pb2.into_alternate_af9(); + // let _qspi_ncs = gpioc.pc11.into_alternate_af9(); + // let _qspi_io0 = gpioe.pe7.into_alternate_af10(); + // let _qspi_io1 = gpioe.pe8.into_alternate_af10(); + // let _qspi_io2 = gpioe.pe9.into_alternate_af10(); + // let _qspi_io3 = gpioe.pe10.into_alternate_af10(); - let qspi = hal::qspi::Qspi::new(dp.QUADSPI, &mut clocks, 10.mhz()).unwrap(); - pounder::QspiInterface::new(qspi).unwrap() - }; + // let qspi = hal::qspi::Qspi::new(dp.QUADSPI, &mut clocks, 10.mhz()).unwrap(); + // pounder::QspiInterface::new(qspi).unwrap() + // }; - let mut reset_pin = gpioa.pa0.into_push_pull_output(); - let io_update = gpiog.pg7.into_push_pull_output(); + // let mut reset_pin = gpioa.pa0.into_push_pull_output(); + // let io_update = gpiog.pg7.into_push_pull_output(); - let asm_delay = { - let frequency_hz = clocks.clocks.c_ck().0; - asm_delay::AsmDelay::new(asm_delay::bitrate::Hertz (frequency_hz)) - }; + // let asm_delay = { + // let frequency_hz = clocks.clocks.c_ck().0; + // asm_delay::AsmDelay::new(asm_delay::bitrate::Hertz (frequency_hz)) + // }; - ad9959::Ad9959::new(qspi_interface, - &mut reset_pin, - io_update, - asm_delay, - ad9959::Mode::FourBitSerial, - 100_000_000, - 5).unwrap() - }; + // ad9959::Ad9959::new(qspi_interface, + // &mut reset_pin, + // io_update, + // asm_delay, + // ad9959::Mode::FourBitSerial, + // 100_000_000, + // 5).unwrap() + // }; - let io_expander = { - let sda = gpiob.pb7.into_alternate_af4().set_open_drain(); - let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); - let i2c1 = dp.I2C1.i2c((scl, sda), 100.khz(), &clocks); - mcp23017::MCP23017::default(i2c1).unwrap() - }; + // let io_expander = { + // let sda = gpiob.pb7.into_alternate_af4().set_open_drain(); + // let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); + // let i2c1 = dp.I2C1.i2c((scl, sda), 100.khz(), &clocks); + // mcp23017::MCP23017::default(i2c1).unwrap() + // }; - let spi = { - let spi_mosi = gpiod.pd7.into_alternate_af5(); - let spi_miso = gpioa.pa6.into_alternate_af5(); - let spi_sck = gpiog.pg11.into_alternate_af5(); + // let spi = { + // let spi_mosi = gpiod.pd7.into_alternate_af5(); + // let spi_miso = gpioa.pa6.into_alternate_af5(); + // let spi_sck = gpiog.pg11.into_alternate_af5(); - let config = hal::spi::Config::new(hal::spi::Mode{ - polarity: hal::spi::Polarity::IdleHigh, - phase: hal::spi::Phase::CaptureOnSecondTransition, - }) - .frame_size(8); + // let config = hal::spi::Config::new(hal::spi::Mode{ + // polarity: hal::spi::Polarity::IdleHigh, + // phase: hal::spi::Phase::CaptureOnSecondTransition, + // }) + // .frame_size(8); - dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 25.mhz(), &clocks) - }; + // dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 25.mhz(), &clocks) + // }; - let adc1 = { - let mut adc = dp.ADC1.adc(&mut delay, &mut clocks); - adc.calibrate(); + // let adc1 = { + // let mut adc = dp.ADC1.adc(&mut delay, &mut clocks); + // adc.calibrate(); - adc.enable() - }; + // adc.enable() + // }; - let adc2 = { - let mut adc = dp.ADC2.adc(&mut delay, &mut clocks); - adc.calibrate(); + // let adc2 = { + // let mut adc = dp.ADC2.adc(&mut delay, &mut clocks); + // adc.calibrate(); - adc.enable() - }; + // adc.enable() + // }; - let adc1_in_p = gpiof.pf11.into_analog(); - let adc2_in_p = gpiof.pf14.into_analog(); + // let adc1_in_p = gpiof.pf11.into_analog(); + // let adc2_in_p = gpiof.pf14.into_analog(); - pounder::PounderDevices::new(io_expander, - ad9959, - spi, - adc1, - adc2, - adc1_in_p, - adc2_in_p).unwrap() - }; + // pounder::PounderDevices::new(io_expander, + // ad9959, + // spi, + // adc1, + // adc2, + // adc1_in_p, + // adc2_in_p).unwrap() + // }; let mut fp_led_0 = gpiod.pd5.into_push_pull_output(); let mut fp_led_1 = gpiod.pd6.into_push_pull_output(); @@ -514,21 +442,24 @@ const APP: () = { // Utilize the cycle counter for RTFM scheduling. cp.DWT.enable_cycle_counter(); - let mut debug_pin = gpioc.pc6.into_push_pull_output(); - debug_pin.set_low().unwrap(); + let mut dma = hal::dma::Dma::dma(dp.DMA1, dp.DMAMUX1, &clocks); + dma.configure_m2p_stream(hal::dma::Stream::One, + &SPI_START_CODE as *const _ as u32, + &adc1_spi.spi.cr1 as *const _ as u32, + hal::dma::DMAREQ_ID::TIM2_CH1); - let mut dac_pin = gpiob.pb15.into_push_pull_output(); - dac_pin.set_low().unwrap(); + dma.configure_m2p_stream(hal::dma::Stream::Two, + &SPI_START_CODE as *const _ as u32, + &adc2_spi.spi.cr1 as *const _ as u32, + hal::dma::DMAREQ_ID::TIM2_CH2); // Configure timer 2 to trigger conversions for the ADC let mut timer2 = dp.TIM2.timer(500.khz(), &mut clocks); - timer2.listen(hal::timer::Event::TimeOut); + timer2.configure_channel(hal::timer::Channel::One, 0.25); + timer2.configure_channel(hal::timer::Channel::Two, 0.75); - dma1_setup(&dp.DMA1, - &dp.DMAMUX1, - &SPI_START_CODE as *const _ as usize, - &adc1_spi.spi.cr1 as *const _ as usize, - &adc2_spi.spi.cr1 as *const _ as usize); + timer2.listen(hal::timer::Event::ChannelOneDma); + timer2.listen(hal::timer::Event::ChannelTwoDma); init::LateResources { adc1: adc1_spi, @@ -538,10 +469,8 @@ const APP: () = { _afe1: afe1, _afe2: afe2, - dbg_pin: debug_pin, - dac_pin: dac_pin, timer: timer2, - pounder: pounder_devices, + //pounder: pounder_devices, eeprom_i2c: eeprom_i2c, net_interface: network_interface, @@ -550,46 +479,34 @@ const APP: () = { } } - #[task(binds = TIM2, resources = [dbg_pin, timer, adc1, adc2])] - fn tim2(mut c: tim2::Context) { - c.resources.timer.clear_uif_bit(); - c.resources.dbg_pin.set_high().unwrap(); + #[task(binds = SPI3, resources = [adc2, dac2, iir_state, iir_ch], priority = 2)] + fn spi3(c: spi3::Context) { + c.resources.adc2.spi.ifcr.write(|w| w.eotc().set_bit()); - // Start a SPI transaction on ADC0 and ADC1 - c.resources.adc1.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); - c.resources.adc2.lock(|adc| adc.spi.cr1.modify(|_, w| w.cstart().set_bit())); + let output: u16 = { + let a: u16 = c.resources.adc2.read().unwrap(); + let x0 = f32::from(a as i16); + let y0 = c.resources.iir_ch[1].update(&mut c.resources.iir_state[1], x0); + y0 as i16 as u16 ^ 0x8000 + }; - c.resources.dbg_pin.set_low().unwrap(); + c.resources.dac2.spi.ifcr.write(|w| w.eotc().set_bit().txtfc().set_bit()); + c.resources.dac2.send(output).unwrap(); } - #[task(binds = SPI2, resources = [adc1, dac1, adc2, dac2, iir_state, iir_ch, dac_pin], priority = 2)] - fn adc_spi(c: adc_spi::Context) { - #[cfg(feature = "bkpt")] - cortex_m::asm::bkpt(); + #[task(binds = SPI2, resources = [adc1, dac1, iir_state, iir_ch], priority = 2)] + fn spi2(c: spi2::Context) { + c.resources.adc1.spi.ifcr.write(|w| w.eotc().set_bit()); - c.resources.dac_pin.set_high().unwrap(); - - let output_ch1 = { + let output: u16 = { let a: u16 = c.resources.adc1.read().unwrap(); let x0 = f32::from(a as i16); let y0 = c.resources.iir_ch[0].update(&mut c.resources.iir_state[0], x0); y0 as i16 as u16 ^ 0x8000 }; - c.resources.adc1.spi.ifcr.write(|w| w.eotc().set_bit()); - c.resources.dac1.send(output_ch1).unwrap(); - let output_ch2 = { - let a: u16 = nb::block!(c.resources.adc2.read()).unwrap(); - let x0 = f32::from(a as i16); - let y0 = c.resources.iir_ch[1].update(&mut c.resources.iir_state[1], x0); - y0 as i16 as u16 ^ 0x8000 - }; - c.resources.adc2.spi.ifcr.write(|w| w.eotc().set_bit()); - c.resources.dac2.send(output_ch2).unwrap(); - - c.resources.dac_pin.set_low().unwrap(); - #[cfg(feature = "bkpt")] - cortex_m::asm::bkpt(); + c.resources.dac1.spi.ifcr.write(|w| w.eotc().set_bit().txtfc().set_bit()); + c.resources.dac1.send(output).unwrap(); } #[idle(resources=[net_interface, mac_addr, iir_state, iir_ch])] diff --git a/stm32h7xx-hal b/stm32h7xx-hal index ba7d21a..2236b57 160000 --- a/stm32h7xx-hal +++ b/stm32h7xx-hal @@ -1 +1 @@ -Subproject commit ba7d21a018752c2cf9303470badfa8dbf14dc3aa +Subproject commit 2236b578b48aa195679dd65515f595f491f88513 From 86c4c1ea5e4315a0f35d8b0d53799069f6bd210f Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 8 Jun 2020 18:17:51 +0200 Subject: [PATCH 23/40] Removing pounder-specific code --- Cargo.lock | 15 -- Cargo.toml | 1 - ad9959/.gitignore | 2 - ad9959/Cargo.toml | 11 - ad9959/src/lib.rs | 350 -------------------------- stabilizer/Cargo.toml | 3 - stabilizer/src/main.rs | 93 +------ stabilizer/src/pounder/attenuators.rs | 57 ----- stabilizer/src/pounder/error.rs | 11 - stabilizer/src/pounder/mod.rs | 265 ------------------- stabilizer/src/pounder/rf_power.rs | 14 -- stabilizer/src/pounder/types.rs | 16 -- 12 files changed, 1 insertion(+), 837 deletions(-) delete mode 100644 ad9959/.gitignore delete mode 100644 ad9959/Cargo.toml delete mode 100644 ad9959/src/lib.rs delete mode 100644 stabilizer/src/pounder/attenuators.rs delete mode 100644 stabilizer/src/pounder/error.rs delete mode 100644 stabilizer/src/pounder/mod.rs delete mode 100644 stabilizer/src/pounder/rf_power.rs delete mode 100644 stabilizer/src/pounder/types.rs diff --git a/Cargo.lock b/Cargo.lock index bb74654..3957ff6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,13 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "ad9959" -version = "0.1.0" -dependencies = [ - "bit_field", - "embedded-hal", -] - [[package]] name = "aligned" version = "0.3.2" @@ -54,12 +46,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "bit_field" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" - [[package]] name = "bitflags" version = "1.2.1" @@ -441,7 +427,6 @@ dependencies = [ name = "stabilizer" version = "0.3.0" dependencies = [ - "ad9959", "asm-delay", "cortex-m", "cortex-m-log", diff --git a/Cargo.toml b/Cargo.toml index 05df738..ac00002 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "stabilizer", "stm32h7xx-hal", - "ad9959", ] [profile.dev] diff --git a/ad9959/.gitignore b/ad9959/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/ad9959/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/ad9959/Cargo.toml b/ad9959/Cargo.toml deleted file mode 100644 index f234470..0000000 --- a/ad9959/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "ad9959" -version = "0.1.0" -authors = ["Ryan Summers "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -embedded-hal = {version = "0.2.3", features = ["unproven"]} -bit_field = "0.10.0" diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs deleted file mode 100644 index 5346bdc..0000000 --- a/ad9959/src/lib.rs +++ /dev/null @@ -1,350 +0,0 @@ -#![no_std] - -use bit_field::BitField; -use embedded_hal::{ - digital::v2::OutputPin, - blocking::delay::DelayMs, -}; - -/// A device driver for the AD9959 direct digital synthesis (DDS) chip. -/// -/// This chip provides four independently controllable digital-to-analog output sinusoids with -/// configurable phase, amplitude, and frequency. All channels are inherently synchronized as they -/// are derived off a common system clock. -/// -/// The chip contains a configurable PLL and supports system clock frequencies up to 500 MHz. -/// -/// The chip supports a number of serial interfaces to improve data throughput, including normal, -/// dual, and quad SPI configurations. -pub struct Ad9959 { - interface: INTERFACE, - delay: DELAY, - reference_clock_frequency: u32, - system_clock_multiplier: u8, - io_update: UPDATE, -} - -pub trait Interface { - type Error; - - fn configure_mode(&mut self, mode: Mode) -> Result<(), Self::Error>; - - fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Self::Error>; - - fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>; -} - -#[derive(Copy, Clone, PartialEq)] -pub enum Mode { - SingleBitTwoWire = 0b00, - SingleBitThreeWire = 0b01, - TwoBitSerial = 0b10, - FourBitSerial = 0b11, -} - -/// The configuration registers within the AD9959 DDS device. The values of each register are -/// equivalent to the address. -pub enum Register { - CSR = 0x00, - FR1 = 0x01, - FR2 = 0x02, - CFR = 0x03, - CFTW0 = 0x04, - CPOW0 = 0x05, - ACR = 0x06, - LSRR = 0x07, - RDW = 0x08, - FDW = 0x09, - CW1 = 0x0a, - CW2 = 0x0b, - CW3 = 0x0c, - CW4 = 0x0d, - CW5 = 0x0e, - CW6 = 0x0f, - CW7 = 0x10, - CW8 = 0x11, - CW9 = 0x12, - CW10 = 0x13, - CW11 = 0x14, - CW12 = 0x15, - CW13 = 0x16, - CW14 = 0x17, - CW15 = 0x18, -} - -/// Specifies an output channel of the AD9959 DDS chip. -pub enum Channel { - One = 0, - Two = 1, - Three = 2, - Four = 3, -} - -/// Possible errors generated by the AD9959 driver. -#[derive(Debug)] -pub enum Error { - Interface(InterfaceE), - Bounds, - Pin, - Frequency, -} - -impl From for Error { - fn from(interface_error: InterfaceE) -> Self { - Error::Interface(interface_error) - } -} - -impl Ad9959 -where - INTERFACE: Interface, - DELAY: DelayMs, - UPDATE: OutputPin, - -{ - pub fn new(interface: INTERFACE, - reset_pin: &mut RST, - io_update: UPDATE, - delay: DELAY, - desired_mode: Mode, - clock_frequency: u32, - multiplier: u8) -> Result> - where - RST: OutputPin, - { - let mut ad9959 = Ad9959 { - interface: interface, - io_update: io_update, - delay: delay, - reference_clock_frequency: clock_frequency, - system_clock_multiplier: 1, - }; - - ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; - - // Reset the AD9959 - reset_pin.set_high().or_else(|_| Err(Error::Pin))?; - - // Delay for a clock cycle to allow the device to reset. - ad9959.delay.delay_ms((1000.0 / clock_frequency as f32) as u8); - - reset_pin.set_low().or_else(|_| Err(Error::Pin))?; - - ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?; - - // Program the interface configuration in the AD9959. Default to all channels enabled. - let mut csr: [u8; 1] = [0xF0]; - csr[0].set_bits(1..3, desired_mode as u8); - ad9959.interface.write(0, &csr)?; - - // Configure the interface to the desired mode. - ad9959.interface.configure_mode(Mode::FourBitSerial)?; - - // Latch the configuration registers to make them active. - ad9959.latch_configuration()?; - - ad9959.interface.configure_mode(desired_mode)?; - - // Set the clock frequency to configure the device as necessary. - ad9959.configure_system_clock(clock_frequency, multiplier)?; - Ok(ad9959) - } - - fn latch_configuration(&mut self) -> Result<(), Error> { - self.io_update.set_high().or_else(|_| Err(Error::Pin))?; - // The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one - // full SYNC_CLK pulse to register. For safety, we latch for 5 here. - self.delay.delay_ms((5000.0 / self.system_clock_frequency()) as u8); - self.io_update.set_low().or_else(|_| Err(Error::Pin))?; - - Ok(()) - } - - /// Configure the internal system clock of the chip. - /// - /// Arguments: - /// * `reference_clock_frequency` - The reference clock frequency provided to the AD9959 core. - /// * `prescaler` - The frequency prescaler of the system clock. Must be 1 or 4-20. - /// - /// Returns: - /// The actual frequency configured for the internal system clock. - pub fn configure_system_clock(&mut self, - reference_clock_frequency: u32, - prescaler: u8) -> Result> - { - self.reference_clock_frequency = reference_clock_frequency; - - if prescaler != 1 && (prescaler > 20 || prescaler < 4) { - return Err(Error::Bounds); - } - - let frequency = prescaler as f64 * self.reference_clock_frequency as f64; - if frequency > 500_000_000.0f64 { - return Err(Error::Frequency); - } - - // TODO: Update / disable any enabled channels? - let mut fr1: [u8; 3] = [0, 0, 0]; - self.interface.read(Register::FR1 as u8, &mut fr1)?; - fr1[0].set_bits(2..=6, prescaler); - - let vco_range = frequency > 255e6; - fr1[0].set_bit(7, vco_range); - - self.interface.write(Register::FR1 as u8, &fr1)?; - self.system_clock_multiplier = prescaler; - - Ok(self.system_clock_frequency()) - } - - /// Perform a self-test of the communication interface. - /// - /// Note: - /// This modifies the existing channel enables. They are restored upon exit. - /// - /// Returns: - /// True if the self test succeeded. False otherwise. - pub fn self_test(&mut self) -> Result> { - let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; - let old_csr = csr[0]; - - // Enable all channels. - csr[0].set_bits(4..8, 0xF); - self.interface.write(Register::CSR as u8, &csr)?; - - // Read back the enable. - csr[0] = 0; - self.interface.read(Register::CSR as u8, &mut csr)?; - if csr[0].get_bits(4..8) != 0xF { - return Ok(false); - } - - // Clear all channel enables. - csr[0].set_bits(4..8, 0x0); - self.interface.write(Register::CSR as u8, &csr)?; - - // Read back the enable. - csr[0] = 0xFF; - self.interface.read(Register::CSR as u8, &mut csr)?; - if csr[0].get_bits(4..8) != 0 { - return Ok(false); - } - - // Restore the CSR. - csr[0] = old_csr; - self.interface.write(Register::CSR as u8, &csr)?; - - Ok(true) - } - - fn system_clock_frequency(&self) -> f64 { - self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64 - } - - /// Enable an output channel. - pub fn enable_channel(&mut self, channel: Channel) -> Result<(), Error> { - let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; - csr[0].set_bit(channel as usize + 4, true); - self.interface.write(Register::CSR as u8, &csr)?; - - Ok(()) - } - - /// Disable an output channel. - pub fn disable_channel(&mut self, channel: Channel) -> Result<(), Error> { - let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; - csr[0].set_bit(channel as usize + 4, false); - self.interface.write(Register::CSR as u8, &csr)?; - - Ok(()) - } - - fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> { - let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; - - let mut new_csr = csr; - new_csr[0].set_bits(4..8, 0); - new_csr[0].set_bit(4 + channel as usize, true); - - self.interface.write(Register::CSR as u8, &new_csr)?; - - self.interface.write(register as u8, &data)?; - - // Latch the configuration and restore the previous CSR. Note that the re-enable of the - // channel happens immediately, so the CSR update does not need to be latched. - self.latch_configuration()?; - self.interface.write(Register::CSR as u8, &csr)?; - - Ok(()) - } - - /// Configure the phase of a specified channel. - /// - /// Arguments: - /// * `channel` - The channel to configure the frequency of. - /// * `phase_turns` - The desired phase offset in normalized turns. - /// - /// Returns: - /// The actual programmed phase offset of the channel in degrees. - pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result> { - if phase_turns > 1.0 || phase_turns < 0.0 { - return Err(Error::Bounds); - } - - let phase_offset: u16 = (phase_turns * 1u32.wrapping_shl(14) as f32) as u16; - self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?; - Ok((phase_offset as f32 / 1u32.wrapping_shl(14) as f32) * 360.0) - } - - /// Configure the amplitude of a specified channel. - /// - /// Arguments: - /// * `channel` - The channel to configure the frequency of. - /// * `amplitude` - A normalized amplitude setting [0, 1]. - /// - /// Returns: - /// The actual normalized amplitude of the channel relative to full-scale range. - pub fn set_amplitude(&mut self, channel: Channel, amplitude: f32) -> Result> { - if amplitude < 0.0 || amplitude > 1.0 { - return Err(Error::Bounds); - } - - let amplitude_control: u16 = (amplitude / 1u16.wrapping_shl(10) as f32) as u16; - let mut acr: [u8; 3] = [0, amplitude_control.to_be_bytes()[0], amplitude_control.to_be_bytes()[1]]; - - // Enable the amplitude multiplier for the channel if required. The amplitude control has - // full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever - // full-scale is used. - acr[1].set_bit(4, amplitude_control >= 1u16.wrapping_shl(10)); - - self.modify_channel(channel, Register::ACR, &acr)?; - - Ok(amplitude_control as f32 / 1_u16.wrapping_shl(10) as f32) - } - - /// Configure the frequency of a specified channel. - /// - /// Arguments: - /// * `channel` - The channel to configure the frequency of. - /// * `frequency` - The desired output frequency in Hz. - /// - /// Returns: - /// The actual programmed frequency of the channel. - pub fn set_frequency(&mut self, channel: Channel, frequency: f64) -> Result> { - if frequency < 0.0 || frequency > self.system_clock_frequency() { - return Err(Error::Bounds); - } - - // The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the - // frequency tuning word and f_s is the system clock rate. - let tuning_word: u32 = ((frequency as f64 / self.system_clock_frequency()) - * 1u64.wrapping_shl(32) as f64) as u32; - - self.modify_channel(channel, Register::CFTW0, &tuning_word.to_be_bytes())?; - Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency()) - } -} diff --git a/stabilizer/Cargo.toml b/stabilizer/Cargo.toml index ffbd1e4..711476b 100644 --- a/stabilizer/Cargo.toml +++ b/stabilizer/Cargo.toml @@ -49,9 +49,6 @@ version = "0.6" features = ["ethernet", "proto-ipv4", "socket-tcp", "proto-ipv6"] default-features = false -[dependencies.ad9959] -path = "../ad9959" - [dependencies.stm32h7-ethernet] git = "https://github.com/quartiq/stm32h7-ethernet.git" features = ["stm32h743v"] diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 1840360..1b42929 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -1,4 +1,4 @@ -//#![deny(warnings)] +#![deny(warnings)] #![allow(clippy::missing_safety_doc)] #![no_std] #![no_main] @@ -27,10 +27,7 @@ extern crate panic_halt; #[macro_use] extern crate log; -use nb; - // use core::sync::atomic::{AtomicU32, AtomicBool, Ordering}; -use asm_delay; use rtfm::cyccnt::{Instant, U32Ext}; use cortex_m_rt::exception; use cortex_m; @@ -51,7 +48,6 @@ use smoltcp as net; static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); mod eth; -mod pounder; mod server; mod afe; @@ -133,8 +129,6 @@ const APP: () = { _eth_mac: ethernet::EthernetMAC, mac_addr: net::wire::EthernetAddress, - //pounder: pounder::PounderDevices, - #[init([[0.; 5]; 2])] iir_state: [IIRState; 2], #[init([IIR { ba: [1., 0., 0., 0., 0.], y_offset: 0., y_min: -SCALE - 1., y_max: SCALE }; 2])] @@ -167,8 +161,6 @@ const APP: () = { clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); - let mut delay = hal::delay::Delay::new(cp.SYST, clocks.clocks); - let gpioa = dp.GPIOA.split(&mut clocks.ahb4); let gpiob = dp.GPIOB.split(&mut clocks.ahb4); let gpioc = dp.GPIOC.split(&mut clocks.ahb4); @@ -282,88 +274,6 @@ const APP: () = { spi }; - // let pounder_devices = { - // let ad9959 = { - // let qspi_interface = { - // // Instantiate the QUADSPI pins and peripheral interface. - // // TODO: Place these into a pins structure that is provided to the QSPI - // // constructor. - // let _qspi_clk = gpiob.pb2.into_alternate_af9(); - // let _qspi_ncs = gpioc.pc11.into_alternate_af9(); - // let _qspi_io0 = gpioe.pe7.into_alternate_af10(); - // let _qspi_io1 = gpioe.pe8.into_alternate_af10(); - // let _qspi_io2 = gpioe.pe9.into_alternate_af10(); - // let _qspi_io3 = gpioe.pe10.into_alternate_af10(); - - // let qspi = hal::qspi::Qspi::new(dp.QUADSPI, &mut clocks, 10.mhz()).unwrap(); - // pounder::QspiInterface::new(qspi).unwrap() - // }; - - // let mut reset_pin = gpioa.pa0.into_push_pull_output(); - // let io_update = gpiog.pg7.into_push_pull_output(); - - - // let asm_delay = { - // let frequency_hz = clocks.clocks.c_ck().0; - // asm_delay::AsmDelay::new(asm_delay::bitrate::Hertz (frequency_hz)) - // }; - - // ad9959::Ad9959::new(qspi_interface, - // &mut reset_pin, - // io_update, - // asm_delay, - // ad9959::Mode::FourBitSerial, - // 100_000_000, - // 5).unwrap() - // }; - - // let io_expander = { - // let sda = gpiob.pb7.into_alternate_af4().set_open_drain(); - // let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); - // let i2c1 = dp.I2C1.i2c((scl, sda), 100.khz(), &clocks); - // mcp23017::MCP23017::default(i2c1).unwrap() - // }; - - // let spi = { - // let spi_mosi = gpiod.pd7.into_alternate_af5(); - // let spi_miso = gpioa.pa6.into_alternate_af5(); - // let spi_sck = gpiog.pg11.into_alternate_af5(); - - // let config = hal::spi::Config::new(hal::spi::Mode{ - // polarity: hal::spi::Polarity::IdleHigh, - // phase: hal::spi::Phase::CaptureOnSecondTransition, - // }) - // .frame_size(8); - - // dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 25.mhz(), &clocks) - // }; - - // let adc1 = { - // let mut adc = dp.ADC1.adc(&mut delay, &mut clocks); - // adc.calibrate(); - - // adc.enable() - // }; - - // let adc2 = { - // let mut adc = dp.ADC2.adc(&mut delay, &mut clocks); - // adc.calibrate(); - - // adc.enable() - // }; - - // let adc1_in_p = gpiof.pf11.into_analog(); - // let adc2_in_p = gpiof.pf14.into_analog(); - - // pounder::PounderDevices::new(io_expander, - // ad9959, - // spi, - // adc1, - // adc2, - // adc1_in_p, - // adc2_in_p).unwrap() - // }; - let mut fp_led_0 = gpiod.pd5.into_push_pull_output(); let mut fp_led_1 = gpiod.pd6.into_push_pull_output(); let mut fp_led_2 = gpiod.pd12.into_push_pull_output(); @@ -470,7 +380,6 @@ const APP: () = { _afe2: afe2, timer: timer2, - //pounder: pounder_devices, eeprom_i2c: eeprom_i2c, net_interface: network_interface, diff --git a/stabilizer/src/pounder/attenuators.rs b/stabilizer/src/pounder/attenuators.rs deleted file mode 100644 index d9785f8..0000000 --- a/stabilizer/src/pounder/attenuators.rs +++ /dev/null @@ -1,57 +0,0 @@ -use super::error::Error; -use super::DdsChannel; - -pub trait AttenuatorInterface { - fn modify(&mut self, attenuation: f32, channel: DdsChannel) -> Result { - if attenuation > 31.5 || attenuation < 0.0 { - return Err(Error::Bounds); - } - - // Calculate the attenuation code to program into the attenuator. The attenuator uses a - // code where the LSB is 0.5 dB. - let attenuation_code = (attenuation * 2.0) as u8; - - // Read all the channels, modify the channel of interest, and write all the channels back. - // This ensures the staging register and the output register are always in sync. - let mut channels = [0_u8; 4]; - self.read_all(&mut channels)?; - - // The lowest 2 bits of the 8-bit shift register on the attenuator are ignored. Shift the - // attenuator code into the upper 6 bits of the register value. Note that the attenuator - // treats inputs as active-low, so the code is inverted before writing. - channels[channel as usize] = !attenuation_code.wrapping_shl(2); - self.write_all(&channels)?; - - // Finally, latch the output of the updated channel to force it into an active state. - self.latch(channel)?; - - Ok(attenuation_code as f32 / 2.0) - } - - fn read(&mut self, channel: DdsChannel) -> Result { - let mut channels = [0_u8; 4]; - - // Reading the data always shifts data out of the staging registers, so we perform a - // duplicate write-back to ensure the staging register is always equal to the output - // register. - self.read_all(&mut channels)?; - self.write_all(&channels)?; - - // The attenuation code is stored in the upper 6 bits of the register, where each LSB - // represents 0.5 dB. The attenuator stores the code as active-low, so inverting the result - // (before the shift) has the affect of transforming the bits of interest (and the - // dont-care bits) into an active-high state and then masking off the don't care bits. If - // the shift occurs before the inversion, the upper 2 bits (which would then be don't - // care) would contain erroneous data. - let attenuation_code = (!channels[channel as usize]).wrapping_shr(2); - - // Convert the desired channel code into dB of attenuation. - Ok(attenuation_code as f32 / 2.0) - } - - fn reset(&mut self) -> Result<(), Error>; - - fn latch(&mut self, channel: DdsChannel) -> Result<(), Error>; - fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>; - fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error>; -} diff --git a/stabilizer/src/pounder/error.rs b/stabilizer/src/pounder/error.rs deleted file mode 100644 index b3cc596..0000000 --- a/stabilizer/src/pounder/error.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[derive(Debug)] -pub enum Error { - Spi, - I2c, - DDS, - Qspi, - Bounds, - InvalidAddress, - InvalidChannel, - Adc, -} diff --git a/stabilizer/src/pounder/mod.rs b/stabilizer/src/pounder/mod.rs deleted file mode 100644 index d29e3bd..0000000 --- a/stabilizer/src/pounder/mod.rs +++ /dev/null @@ -1,265 +0,0 @@ -use mcp23017; -use ad9959; - -pub mod error; -pub mod attenuators; -mod rf_power; -pub mod types; - -use super::hal; - -use error::Error; -use attenuators::AttenuatorInterface; -use types::{DdsChannel, InputChannel}; -use rf_power::PowerMeasurementInterface; - -use embedded_hal::{ - blocking::spi::Transfer, - adc::OneShot -}; - -#[allow(dead_code)] -const OSC_EN_N_PIN: u8 = 8 + 7; - -const EXT_CLK_SEL_PIN: u8 = 8 + 6; - -const ATT_RST_N_PIN: u8 = 8 + 5; - -const ATT_LE0_PIN: u8 = 8 + 0; -const ATT_LE1_PIN: u8 = 8 + 1; -const ATT_LE2_PIN: u8 = 8 + 2; -const ATT_LE3_PIN: u8 = 8 + 3; - -pub struct QspiInterface { - pub qspi: hal::qspi::Qspi, - mode: ad9959::Mode, -} - -impl QspiInterface { - pub fn new(mut qspi: hal::qspi::Qspi) -> Result { - qspi.configure_mode(hal::qspi::QspiMode::FourBit).map_err(|_| Error::Qspi)?; - Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire }) - } -} - -impl ad9959::Interface for QspiInterface { - type Error = Error; - - fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> { - self.mode = mode; - - Ok(()) - } - - fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> { - if (addr & 0x80) != 0 { - return Err(Error::InvalidAddress); - } - - // The QSPI interface implementation always operates in 4-bit mode because the AD9959 uses - // IO3 as SYNC_IO in some output modes. In order for writes to be successful, SYNC_IO must - // be driven low. However, the QSPI peripheral forces IO3 high when operating in 1 or 2 bit - // modes. As a result, any writes while in single- or dual-bit modes has to instead write - // the data encoded into 4-bit QSPI data so that IO3 can be driven low. - match self.mode { - ad9959::Mode::SingleBitTwoWire => { - // Encode the data into a 4-bit QSPI pattern. - - // In 4-bit mode, we can send 2 bits of address and data per byte transfer. As - // such, we need at least 4x more bytes than the length of data. To avoid dynamic - // allocation, we assume the maximum transaction length for single-bit-two-wire is - // 2 bytes. - let mut encoded_data: [u8; 12] = [0; 12]; - - if (data.len() * 4) > (encoded_data.len() - 4) { - return Err(Error::Bounds); - } - - // Encode the address into the first 4 bytes. - for address_bit in 0..8 { - let offset: u8 = { - if address_bit % 2 == 0 { - 4 - } else { - 0 - } - }; - - if addr & address_bit != 0 { - encoded_data[(address_bit >> 1) as usize] |= 1 << offset; - } - } - - // Encode the data into the remaining bytes. - for byte_index in 0..data.len() { - let byte = data[byte_index]; - for address_bit in 0..8 { - let offset: u8 = { - if address_bit % 2 == 0 { - 4 - } else { - 0 - } - }; - - if byte & address_bit != 0 { - encoded_data[(byte_index + 1) * 4 + (address_bit >> 1) as usize] |= 1 << offset; - } - } - } - - let (encoded_address, encoded_payload) = { - let end_index = (1 + data.len()) * 4; - (encoded_data[0], &encoded_data[1..end_index]) - }; - - self.qspi.write(encoded_address, &encoded_payload).map_err(|_| Error::Qspi) - }, - ad9959::Mode::FourBitSerial => { - self.qspi.write(addr, &data).map_err(|_| Error::Qspi) - }, - _ => { - Err(Error::Qspi) - } - } - } - - fn read(&mut self, addr: u8, mut dest: &mut [u8]) -> Result<(), Error> { - if (addr & 0x80) != 0 { - return Err(Error::InvalidAddress); - } - - // It is not possible to read data from the AD9959 in single bit two wire mode because the - // QSPI interface assumes that data is always received on IO1. - if self.mode == ad9959::Mode::SingleBitTwoWire { - return Err(Error::Qspi); - } - - self.qspi.read(0x80_u8 | addr, &mut dest).map_err(|_| Error::Qspi) - } -} - -pub struct PounderDevices { - pub ad9959: ad9959::Ad9959>>, - mcp23017: mcp23017::MCP23017>, - attenuator_spi: hal::spi::Spi, - adc1: hal::adc::Adc, - adc2: hal::adc::Adc, - adc1_in_p: hal::gpio::gpiof::PF11, - adc2_in_p: hal::gpio::gpiof::PF14, -} - -impl PounderDevices -where - DELAY: embedded_hal::blocking::delay::DelayMs, -{ - pub fn new(mcp23017: mcp23017::MCP23017>, - ad9959: ad9959::Ad9959>>, - attenuator_spi: hal::spi::Spi, - adc1: hal::adc::Adc, - adc2: hal::adc::Adc, - adc1_in_p: hal::gpio::gpiof::PF11, - adc2_in_p: hal::gpio::gpiof::PF14, - ) -> Result { - let mut devices = Self { - mcp23017, - ad9959, - attenuator_spi, - adc1, - adc2, - adc1_in_p, - adc2_in_p, - }; - - // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator - // selected, attenuators out of reset. - devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0xF).map_err(|_| Error::I2c)?; - devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, - 1_u8.wrapping_shl(5)).map_err(|_| Error::I2c)?; - devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?; - - // Select the on-board clock with a 5x prescaler (500MHz). - devices.select_onboard_clock(5u8)?; - - Ok(devices) - } - - pub fn select_external_clock(&mut self, frequency: u32, prescaler: u8) -> Result<(), Error>{ - self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?; - self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::DDS)?; - - Ok(()) - } - - pub fn select_onboard_clock(&mut self, prescaler: u8) -> Result<(), Error> { - self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?; - self.ad9959.configure_system_clock(100_000_000, prescaler).map_err(|_| Error::DDS)?; - - Ok(()) - } -} - -impl AttenuatorInterface for PounderDevices -{ - fn reset(&mut self) -> Result<(), Error> { - self.mcp23017.digital_write(ATT_RST_N_PIN, true).map_err(|_| Error::I2c)?; - // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is - // sufficient. Document the delay here. - self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?; - - Ok(()) - } - - fn latch(&mut self, channel: DdsChannel) -> Result<(), Error> { - let pin = match channel { - DdsChannel::Zero => ATT_LE1_PIN, - DdsChannel::One => ATT_LE0_PIN, - DdsChannel::Two => ATT_LE3_PIN, - DdsChannel::Three => ATT_LE2_PIN, - }; - - self.mcp23017.digital_write(pin, true).map_err(|_| Error::I2c)?; - // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is - // sufficient. Document the delay here. - self.mcp23017.digital_write(pin, false).map_err(|_| Error::I2c)?; - - Ok(()) - } - - fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> { - self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?; - - Ok(()) - } - - fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error> { - let mut result = [0_u8; 4]; - result.clone_from_slice(channels); - self.attenuator_spi.transfer(&mut result).map_err(|_| Error::Spi)?; - - Ok(()) - } -} - -impl PowerMeasurementInterface for PounderDevices { - fn sample_converter(&mut self, channel: InputChannel) -> Result { - let adc_scale = match channel { - InputChannel::Zero => { - let adc_reading: u32 = self.adc1.read(&mut self.adc1_in_p).map_err(|_| Error::Adc)?; - adc_reading as f32 / self.adc1.max_sample() as f32 - }, - InputChannel::One => { - let adc_reading: u32 = self.adc2.read(&mut self.adc2_in_p).map_err(|_| Error::Adc)?; - adc_reading as f32 / self.adc2.max_sample() as f32 - }, - }; - - // Convert analog percentage to voltage. - Ok(adc_scale * 3.3) - } -} diff --git a/stabilizer/src/pounder/rf_power.rs b/stabilizer/src/pounder/rf_power.rs deleted file mode 100644 index dd7a3ec..0000000 --- a/stabilizer/src/pounder/rf_power.rs +++ /dev/null @@ -1,14 +0,0 @@ -use super::Error; -use super::InputChannel; - -pub trait PowerMeasurementInterface { - fn sample_converter(&mut self, channel: InputChannel) -> Result; - - fn measure_power(&mut self, channel: InputChannel) -> Result { - let analog_measurement = self.sample_converter(channel)?; - - // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at - // 100MHz. - Ok(analog_measurement / 0.0517) - } -} diff --git a/stabilizer/src/pounder/types.rs b/stabilizer/src/pounder/types.rs deleted file mode 100644 index 9938bfe..0000000 --- a/stabilizer/src/pounder/types.rs +++ /dev/null @@ -1,16 +0,0 @@ - -#[allow(dead_code)] -#[derive(Debug, Copy, Clone)] -pub enum DdsChannel { - Zero, - One, - Two, - Three, -} - -#[allow(dead_code)] -#[derive(Debug, Copy, Clone)] -pub enum InputChannel { - Zero, - One, -} From 80661a51fbcff7597ebf10f23923fbac258b9d76 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 8 Jun 2020 18:20:10 +0200 Subject: [PATCH 24/40] Reimporting pounder-specific code --- Cargo.lock | 15 ++ Cargo.toml | 1 + ad9959/.gitignore | 2 + ad9959/Cargo.toml | 11 + ad9959/src/lib.rs | 350 ++++++++++++++++++++++++++ stabilizer/Cargo.toml | 3 + stabilizer/src/main.rs | 89 +++++++ stabilizer/src/pounder/attenuators.rs | 57 +++++ stabilizer/src/pounder/error.rs | 11 + stabilizer/src/pounder/mod.rs | 265 +++++++++++++++++++ stabilizer/src/pounder/rf_power.rs | 14 ++ stabilizer/src/pounder/types.rs | 16 ++ 12 files changed, 834 insertions(+) create mode 100644 ad9959/.gitignore create mode 100644 ad9959/Cargo.toml create mode 100644 ad9959/src/lib.rs create mode 100644 stabilizer/src/pounder/attenuators.rs create mode 100644 stabilizer/src/pounder/error.rs create mode 100644 stabilizer/src/pounder/mod.rs create mode 100644 stabilizer/src/pounder/rf_power.rs create mode 100644 stabilizer/src/pounder/types.rs diff --git a/Cargo.lock b/Cargo.lock index 3957ff6..bb74654 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,13 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "ad9959" +version = "0.1.0" +dependencies = [ + "bit_field", + "embedded-hal", +] + [[package]] name = "aligned" version = "0.3.2" @@ -46,6 +54,12 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" + [[package]] name = "bitflags" version = "1.2.1" @@ -427,6 +441,7 @@ dependencies = [ name = "stabilizer" version = "0.3.0" dependencies = [ + "ad9959", "asm-delay", "cortex-m", "cortex-m-log", diff --git a/Cargo.toml b/Cargo.toml index ac00002..05df738 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "stabilizer", "stm32h7xx-hal", + "ad9959", ] [profile.dev] diff --git a/ad9959/.gitignore b/ad9959/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/ad9959/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/ad9959/Cargo.toml b/ad9959/Cargo.toml new file mode 100644 index 0000000..f234470 --- /dev/null +++ b/ad9959/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ad9959" +version = "0.1.0" +authors = ["Ryan Summers "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +embedded-hal = {version = "0.2.3", features = ["unproven"]} +bit_field = "0.10.0" diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs new file mode 100644 index 0000000..5346bdc --- /dev/null +++ b/ad9959/src/lib.rs @@ -0,0 +1,350 @@ +#![no_std] + +use bit_field::BitField; +use embedded_hal::{ + digital::v2::OutputPin, + blocking::delay::DelayMs, +}; + +/// A device driver for the AD9959 direct digital synthesis (DDS) chip. +/// +/// This chip provides four independently controllable digital-to-analog output sinusoids with +/// configurable phase, amplitude, and frequency. All channels are inherently synchronized as they +/// are derived off a common system clock. +/// +/// The chip contains a configurable PLL and supports system clock frequencies up to 500 MHz. +/// +/// The chip supports a number of serial interfaces to improve data throughput, including normal, +/// dual, and quad SPI configurations. +pub struct Ad9959 { + interface: INTERFACE, + delay: DELAY, + reference_clock_frequency: u32, + system_clock_multiplier: u8, + io_update: UPDATE, +} + +pub trait Interface { + type Error; + + fn configure_mode(&mut self, mode: Mode) -> Result<(), Self::Error>; + + fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Self::Error>; + + fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>; +} + +#[derive(Copy, Clone, PartialEq)] +pub enum Mode { + SingleBitTwoWire = 0b00, + SingleBitThreeWire = 0b01, + TwoBitSerial = 0b10, + FourBitSerial = 0b11, +} + +/// The configuration registers within the AD9959 DDS device. The values of each register are +/// equivalent to the address. +pub enum Register { + CSR = 0x00, + FR1 = 0x01, + FR2 = 0x02, + CFR = 0x03, + CFTW0 = 0x04, + CPOW0 = 0x05, + ACR = 0x06, + LSRR = 0x07, + RDW = 0x08, + FDW = 0x09, + CW1 = 0x0a, + CW2 = 0x0b, + CW3 = 0x0c, + CW4 = 0x0d, + CW5 = 0x0e, + CW6 = 0x0f, + CW7 = 0x10, + CW8 = 0x11, + CW9 = 0x12, + CW10 = 0x13, + CW11 = 0x14, + CW12 = 0x15, + CW13 = 0x16, + CW14 = 0x17, + CW15 = 0x18, +} + +/// Specifies an output channel of the AD9959 DDS chip. +pub enum Channel { + One = 0, + Two = 1, + Three = 2, + Four = 3, +} + +/// Possible errors generated by the AD9959 driver. +#[derive(Debug)] +pub enum Error { + Interface(InterfaceE), + Bounds, + Pin, + Frequency, +} + +impl From for Error { + fn from(interface_error: InterfaceE) -> Self { + Error::Interface(interface_error) + } +} + +impl Ad9959 +where + INTERFACE: Interface, + DELAY: DelayMs, + UPDATE: OutputPin, + +{ + pub fn new(interface: INTERFACE, + reset_pin: &mut RST, + io_update: UPDATE, + delay: DELAY, + desired_mode: Mode, + clock_frequency: u32, + multiplier: u8) -> Result> + where + RST: OutputPin, + { + let mut ad9959 = Ad9959 { + interface: interface, + io_update: io_update, + delay: delay, + reference_clock_frequency: clock_frequency, + system_clock_multiplier: 1, + }; + + ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; + + // Reset the AD9959 + reset_pin.set_high().or_else(|_| Err(Error::Pin))?; + + // Delay for a clock cycle to allow the device to reset. + ad9959.delay.delay_ms((1000.0 / clock_frequency as f32) as u8); + + reset_pin.set_low().or_else(|_| Err(Error::Pin))?; + + ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?; + + // Program the interface configuration in the AD9959. Default to all channels enabled. + let mut csr: [u8; 1] = [0xF0]; + csr[0].set_bits(1..3, desired_mode as u8); + ad9959.interface.write(0, &csr)?; + + // Configure the interface to the desired mode. + ad9959.interface.configure_mode(Mode::FourBitSerial)?; + + // Latch the configuration registers to make them active. + ad9959.latch_configuration()?; + + ad9959.interface.configure_mode(desired_mode)?; + + // Set the clock frequency to configure the device as necessary. + ad9959.configure_system_clock(clock_frequency, multiplier)?; + Ok(ad9959) + } + + fn latch_configuration(&mut self) -> Result<(), Error> { + self.io_update.set_high().or_else(|_| Err(Error::Pin))?; + // The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one + // full SYNC_CLK pulse to register. For safety, we latch for 5 here. + self.delay.delay_ms((5000.0 / self.system_clock_frequency()) as u8); + self.io_update.set_low().or_else(|_| Err(Error::Pin))?; + + Ok(()) + } + + /// Configure the internal system clock of the chip. + /// + /// Arguments: + /// * `reference_clock_frequency` - The reference clock frequency provided to the AD9959 core. + /// * `prescaler` - The frequency prescaler of the system clock. Must be 1 or 4-20. + /// + /// Returns: + /// The actual frequency configured for the internal system clock. + pub fn configure_system_clock(&mut self, + reference_clock_frequency: u32, + prescaler: u8) -> Result> + { + self.reference_clock_frequency = reference_clock_frequency; + + if prescaler != 1 && (prescaler > 20 || prescaler < 4) { + return Err(Error::Bounds); + } + + let frequency = prescaler as f64 * self.reference_clock_frequency as f64; + if frequency > 500_000_000.0f64 { + return Err(Error::Frequency); + } + + // TODO: Update / disable any enabled channels? + let mut fr1: [u8; 3] = [0, 0, 0]; + self.interface.read(Register::FR1 as u8, &mut fr1)?; + fr1[0].set_bits(2..=6, prescaler); + + let vco_range = frequency > 255e6; + fr1[0].set_bit(7, vco_range); + + self.interface.write(Register::FR1 as u8, &fr1)?; + self.system_clock_multiplier = prescaler; + + Ok(self.system_clock_frequency()) + } + + /// Perform a self-test of the communication interface. + /// + /// Note: + /// This modifies the existing channel enables. They are restored upon exit. + /// + /// Returns: + /// True if the self test succeeded. False otherwise. + pub fn self_test(&mut self) -> Result> { + let mut csr: [u8; 1] = [0]; + self.interface.read(Register::CSR as u8, &mut csr)?; + let old_csr = csr[0]; + + // Enable all channels. + csr[0].set_bits(4..8, 0xF); + self.interface.write(Register::CSR as u8, &csr)?; + + // Read back the enable. + csr[0] = 0; + self.interface.read(Register::CSR as u8, &mut csr)?; + if csr[0].get_bits(4..8) != 0xF { + return Ok(false); + } + + // Clear all channel enables. + csr[0].set_bits(4..8, 0x0); + self.interface.write(Register::CSR as u8, &csr)?; + + // Read back the enable. + csr[0] = 0xFF; + self.interface.read(Register::CSR as u8, &mut csr)?; + if csr[0].get_bits(4..8) != 0 { + return Ok(false); + } + + // Restore the CSR. + csr[0] = old_csr; + self.interface.write(Register::CSR as u8, &csr)?; + + Ok(true) + } + + fn system_clock_frequency(&self) -> f64 { + self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64 + } + + /// Enable an output channel. + pub fn enable_channel(&mut self, channel: Channel) -> Result<(), Error> { + let mut csr: [u8; 1] = [0]; + self.interface.read(Register::CSR as u8, &mut csr)?; + csr[0].set_bit(channel as usize + 4, true); + self.interface.write(Register::CSR as u8, &csr)?; + + Ok(()) + } + + /// Disable an output channel. + pub fn disable_channel(&mut self, channel: Channel) -> Result<(), Error> { + let mut csr: [u8; 1] = [0]; + self.interface.read(Register::CSR as u8, &mut csr)?; + csr[0].set_bit(channel as usize + 4, false); + self.interface.write(Register::CSR as u8, &csr)?; + + Ok(()) + } + + fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> { + let mut csr: [u8; 1] = [0]; + self.interface.read(Register::CSR as u8, &mut csr)?; + + let mut new_csr = csr; + new_csr[0].set_bits(4..8, 0); + new_csr[0].set_bit(4 + channel as usize, true); + + self.interface.write(Register::CSR as u8, &new_csr)?; + + self.interface.write(register as u8, &data)?; + + // Latch the configuration and restore the previous CSR. Note that the re-enable of the + // channel happens immediately, so the CSR update does not need to be latched. + self.latch_configuration()?; + self.interface.write(Register::CSR as u8, &csr)?; + + Ok(()) + } + + /// Configure the phase of a specified channel. + /// + /// Arguments: + /// * `channel` - The channel to configure the frequency of. + /// * `phase_turns` - The desired phase offset in normalized turns. + /// + /// Returns: + /// The actual programmed phase offset of the channel in degrees. + pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result> { + if phase_turns > 1.0 || phase_turns < 0.0 { + return Err(Error::Bounds); + } + + let phase_offset: u16 = (phase_turns * 1u32.wrapping_shl(14) as f32) as u16; + self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?; + Ok((phase_offset as f32 / 1u32.wrapping_shl(14) as f32) * 360.0) + } + + /// Configure the amplitude of a specified channel. + /// + /// Arguments: + /// * `channel` - The channel to configure the frequency of. + /// * `amplitude` - A normalized amplitude setting [0, 1]. + /// + /// Returns: + /// The actual normalized amplitude of the channel relative to full-scale range. + pub fn set_amplitude(&mut self, channel: Channel, amplitude: f32) -> Result> { + if amplitude < 0.0 || amplitude > 1.0 { + return Err(Error::Bounds); + } + + let amplitude_control: u16 = (amplitude / 1u16.wrapping_shl(10) as f32) as u16; + let mut acr: [u8; 3] = [0, amplitude_control.to_be_bytes()[0], amplitude_control.to_be_bytes()[1]]; + + // Enable the amplitude multiplier for the channel if required. The amplitude control has + // full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever + // full-scale is used. + acr[1].set_bit(4, amplitude_control >= 1u16.wrapping_shl(10)); + + self.modify_channel(channel, Register::ACR, &acr)?; + + Ok(amplitude_control as f32 / 1_u16.wrapping_shl(10) as f32) + } + + /// Configure the frequency of a specified channel. + /// + /// Arguments: + /// * `channel` - The channel to configure the frequency of. + /// * `frequency` - The desired output frequency in Hz. + /// + /// Returns: + /// The actual programmed frequency of the channel. + pub fn set_frequency(&mut self, channel: Channel, frequency: f64) -> Result> { + if frequency < 0.0 || frequency > self.system_clock_frequency() { + return Err(Error::Bounds); + } + + // The function for channel frequency is `f_out = FTW * f_s / 2^32`, where FTW is the + // frequency tuning word and f_s is the system clock rate. + let tuning_word: u32 = ((frequency as f64 / self.system_clock_frequency()) + * 1u64.wrapping_shl(32) as f64) as u32; + + self.modify_channel(channel, Register::CFTW0, &tuning_word.to_be_bytes())?; + Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency()) + } +} diff --git a/stabilizer/Cargo.toml b/stabilizer/Cargo.toml index 711476b..ffbd1e4 100644 --- a/stabilizer/Cargo.toml +++ b/stabilizer/Cargo.toml @@ -49,6 +49,9 @@ version = "0.6" features = ["ethernet", "proto-ipv4", "socket-tcp", "proto-ipv6"] default-features = false +[dependencies.ad9959] +path = "../ad9959" + [dependencies.stm32h7-ethernet] git = "https://github.com/quartiq/stm32h7-ethernet.git" features = ["stm32h743v"] diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 1b42929..afe6f44 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -28,6 +28,7 @@ extern crate panic_halt; extern crate log; // use core::sync::atomic::{AtomicU32, AtomicBool, Ordering}; +use asm_delay; use rtfm::cyccnt::{Instant, U32Ext}; use cortex_m_rt::exception; use cortex_m; @@ -48,6 +49,7 @@ use smoltcp as net; static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); mod eth; +mod pounder; mod server; mod afe; @@ -129,6 +131,8 @@ const APP: () = { _eth_mac: ethernet::EthernetMAC, mac_addr: net::wire::EthernetAddress, + pounder: pounder::PounderDevices, + #[init([[0.; 5]; 2])] iir_state: [IIRState; 2], #[init([IIR { ba: [1., 0., 0., 0., 0.], y_offset: 0., y_min: -SCALE - 1., y_max: SCALE }; 2])] @@ -161,6 +165,8 @@ const APP: () = { clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); + let mut delay = hal::delay::Delay::new(cp.SYST, clocks.clocks); + let gpioa = dp.GPIOA.split(&mut clocks.ahb4); let gpiob = dp.GPIOB.split(&mut clocks.ahb4); let gpioc = dp.GPIOC.split(&mut clocks.ahb4); @@ -274,6 +280,88 @@ const APP: () = { spi }; + let pounder_devices = { + let ad9959 = { + let qspi_interface = { + // Instantiate the QUADSPI pins and peripheral interface. + // TODO: Place these into a pins structure that is provided to the QSPI + // constructor. + let _qspi_clk = gpiob.pb2.into_alternate_af9(); + let _qspi_ncs = gpioc.pc11.into_alternate_af9(); + let _qspi_io0 = gpioe.pe7.into_alternate_af10(); + let _qspi_io1 = gpioe.pe8.into_alternate_af10(); + let _qspi_io2 = gpioe.pe9.into_alternate_af10(); + let _qspi_io3 = gpioe.pe10.into_alternate_af10(); + + let qspi = hal::qspi::Qspi::new(dp.QUADSPI, &mut clocks, 10.mhz()).unwrap(); + pounder::QspiInterface::new(qspi).unwrap() + }; + + let mut reset_pin = gpioa.pa0.into_push_pull_output(); + let io_update = gpiog.pg7.into_push_pull_output(); + + + let asm_delay = { + let frequency_hz = clocks.clocks.c_ck().0; + asm_delay::AsmDelay::new(asm_delay::bitrate::Hertz (frequency_hz)) + }; + + ad9959::Ad9959::new(qspi_interface, + &mut reset_pin, + io_update, + asm_delay, + ad9959::Mode::FourBitSerial, + 100_000_000, + 5).unwrap() + }; + + let io_expander = { + let sda = gpiob.pb7.into_alternate_af4().set_open_drain(); + let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); + let i2c1 = dp.I2C1.i2c((scl, sda), 100.khz(), &clocks); + mcp23017::MCP23017::default(i2c1).unwrap() + }; + + let spi = { + let spi_mosi = gpiod.pd7.into_alternate_af5(); + let spi_miso = gpioa.pa6.into_alternate_af5(); + let spi_sck = gpiog.pg11.into_alternate_af5(); + + let config = hal::spi::Config::new(hal::spi::Mode{ + polarity: hal::spi::Polarity::IdleHigh, + phase: hal::spi::Phase::CaptureOnSecondTransition, + }) + .frame_size(8); + + dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 25.mhz(), &clocks) + }; + + let adc1 = { + let mut adc = dp.ADC1.adc(&mut delay, &mut clocks); + adc.calibrate(); + + adc.enable() + }; + + let adc2 = { + let mut adc = dp.ADC2.adc(&mut delay, &mut clocks); + adc.calibrate(); + + adc.enable() + }; + + let adc1_in_p = gpiof.pf11.into_analog(); + let adc2_in_p = gpiof.pf14.into_analog(); + + pounder::PounderDevices::new(io_expander, + ad9959, + spi, + adc1, + adc2, + adc1_in_p, + adc2_in_p).unwrap() + }; + let mut fp_led_0 = gpiod.pd5.into_push_pull_output(); let mut fp_led_1 = gpiod.pd6.into_push_pull_output(); let mut fp_led_2 = gpiod.pd12.into_push_pull_output(); @@ -380,6 +468,7 @@ const APP: () = { _afe2: afe2, timer: timer2, + pounder: pounder_devices, eeprom_i2c: eeprom_i2c, net_interface: network_interface, diff --git a/stabilizer/src/pounder/attenuators.rs b/stabilizer/src/pounder/attenuators.rs new file mode 100644 index 0000000..d9785f8 --- /dev/null +++ b/stabilizer/src/pounder/attenuators.rs @@ -0,0 +1,57 @@ +use super::error::Error; +use super::DdsChannel; + +pub trait AttenuatorInterface { + fn modify(&mut self, attenuation: f32, channel: DdsChannel) -> Result { + if attenuation > 31.5 || attenuation < 0.0 { + return Err(Error::Bounds); + } + + // Calculate the attenuation code to program into the attenuator. The attenuator uses a + // code where the LSB is 0.5 dB. + let attenuation_code = (attenuation * 2.0) as u8; + + // Read all the channels, modify the channel of interest, and write all the channels back. + // This ensures the staging register and the output register are always in sync. + let mut channels = [0_u8; 4]; + self.read_all(&mut channels)?; + + // The lowest 2 bits of the 8-bit shift register on the attenuator are ignored. Shift the + // attenuator code into the upper 6 bits of the register value. Note that the attenuator + // treats inputs as active-low, so the code is inverted before writing. + channels[channel as usize] = !attenuation_code.wrapping_shl(2); + self.write_all(&channels)?; + + // Finally, latch the output of the updated channel to force it into an active state. + self.latch(channel)?; + + Ok(attenuation_code as f32 / 2.0) + } + + fn read(&mut self, channel: DdsChannel) -> Result { + let mut channels = [0_u8; 4]; + + // Reading the data always shifts data out of the staging registers, so we perform a + // duplicate write-back to ensure the staging register is always equal to the output + // register. + self.read_all(&mut channels)?; + self.write_all(&channels)?; + + // The attenuation code is stored in the upper 6 bits of the register, where each LSB + // represents 0.5 dB. The attenuator stores the code as active-low, so inverting the result + // (before the shift) has the affect of transforming the bits of interest (and the + // dont-care bits) into an active-high state and then masking off the don't care bits. If + // the shift occurs before the inversion, the upper 2 bits (which would then be don't + // care) would contain erroneous data. + let attenuation_code = (!channels[channel as usize]).wrapping_shr(2); + + // Convert the desired channel code into dB of attenuation. + Ok(attenuation_code as f32 / 2.0) + } + + fn reset(&mut self) -> Result<(), Error>; + + fn latch(&mut self, channel: DdsChannel) -> Result<(), Error>; + fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>; + fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error>; +} diff --git a/stabilizer/src/pounder/error.rs b/stabilizer/src/pounder/error.rs new file mode 100644 index 0000000..b3cc596 --- /dev/null +++ b/stabilizer/src/pounder/error.rs @@ -0,0 +1,11 @@ +#[derive(Debug)] +pub enum Error { + Spi, + I2c, + DDS, + Qspi, + Bounds, + InvalidAddress, + InvalidChannel, + Adc, +} diff --git a/stabilizer/src/pounder/mod.rs b/stabilizer/src/pounder/mod.rs new file mode 100644 index 0000000..d29e3bd --- /dev/null +++ b/stabilizer/src/pounder/mod.rs @@ -0,0 +1,265 @@ +use mcp23017; +use ad9959; + +pub mod error; +pub mod attenuators; +mod rf_power; +pub mod types; + +use super::hal; + +use error::Error; +use attenuators::AttenuatorInterface; +use types::{DdsChannel, InputChannel}; +use rf_power::PowerMeasurementInterface; + +use embedded_hal::{ + blocking::spi::Transfer, + adc::OneShot +}; + +#[allow(dead_code)] +const OSC_EN_N_PIN: u8 = 8 + 7; + +const EXT_CLK_SEL_PIN: u8 = 8 + 6; + +const ATT_RST_N_PIN: u8 = 8 + 5; + +const ATT_LE0_PIN: u8 = 8 + 0; +const ATT_LE1_PIN: u8 = 8 + 1; +const ATT_LE2_PIN: u8 = 8 + 2; +const ATT_LE3_PIN: u8 = 8 + 3; + +pub struct QspiInterface { + pub qspi: hal::qspi::Qspi, + mode: ad9959::Mode, +} + +impl QspiInterface { + pub fn new(mut qspi: hal::qspi::Qspi) -> Result { + qspi.configure_mode(hal::qspi::QspiMode::FourBit).map_err(|_| Error::Qspi)?; + Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire }) + } +} + +impl ad9959::Interface for QspiInterface { + type Error = Error; + + fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> { + self.mode = mode; + + Ok(()) + } + + fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> { + if (addr & 0x80) != 0 { + return Err(Error::InvalidAddress); + } + + // The QSPI interface implementation always operates in 4-bit mode because the AD9959 uses + // IO3 as SYNC_IO in some output modes. In order for writes to be successful, SYNC_IO must + // be driven low. However, the QSPI peripheral forces IO3 high when operating in 1 or 2 bit + // modes. As a result, any writes while in single- or dual-bit modes has to instead write + // the data encoded into 4-bit QSPI data so that IO3 can be driven low. + match self.mode { + ad9959::Mode::SingleBitTwoWire => { + // Encode the data into a 4-bit QSPI pattern. + + // In 4-bit mode, we can send 2 bits of address and data per byte transfer. As + // such, we need at least 4x more bytes than the length of data. To avoid dynamic + // allocation, we assume the maximum transaction length for single-bit-two-wire is + // 2 bytes. + let mut encoded_data: [u8; 12] = [0; 12]; + + if (data.len() * 4) > (encoded_data.len() - 4) { + return Err(Error::Bounds); + } + + // Encode the address into the first 4 bytes. + for address_bit in 0..8 { + let offset: u8 = { + if address_bit % 2 == 0 { + 4 + } else { + 0 + } + }; + + if addr & address_bit != 0 { + encoded_data[(address_bit >> 1) as usize] |= 1 << offset; + } + } + + // Encode the data into the remaining bytes. + for byte_index in 0..data.len() { + let byte = data[byte_index]; + for address_bit in 0..8 { + let offset: u8 = { + if address_bit % 2 == 0 { + 4 + } else { + 0 + } + }; + + if byte & address_bit != 0 { + encoded_data[(byte_index + 1) * 4 + (address_bit >> 1) as usize] |= 1 << offset; + } + } + } + + let (encoded_address, encoded_payload) = { + let end_index = (1 + data.len()) * 4; + (encoded_data[0], &encoded_data[1..end_index]) + }; + + self.qspi.write(encoded_address, &encoded_payload).map_err(|_| Error::Qspi) + }, + ad9959::Mode::FourBitSerial => { + self.qspi.write(addr, &data).map_err(|_| Error::Qspi) + }, + _ => { + Err(Error::Qspi) + } + } + } + + fn read(&mut self, addr: u8, mut dest: &mut [u8]) -> Result<(), Error> { + if (addr & 0x80) != 0 { + return Err(Error::InvalidAddress); + } + + // It is not possible to read data from the AD9959 in single bit two wire mode because the + // QSPI interface assumes that data is always received on IO1. + if self.mode == ad9959::Mode::SingleBitTwoWire { + return Err(Error::Qspi); + } + + self.qspi.read(0x80_u8 | addr, &mut dest).map_err(|_| Error::Qspi) + } +} + +pub struct PounderDevices { + pub ad9959: ad9959::Ad9959>>, + mcp23017: mcp23017::MCP23017>, + attenuator_spi: hal::spi::Spi, + adc1: hal::adc::Adc, + adc2: hal::adc::Adc, + adc1_in_p: hal::gpio::gpiof::PF11, + adc2_in_p: hal::gpio::gpiof::PF14, +} + +impl PounderDevices +where + DELAY: embedded_hal::blocking::delay::DelayMs, +{ + pub fn new(mcp23017: mcp23017::MCP23017>, + ad9959: ad9959::Ad9959>>, + attenuator_spi: hal::spi::Spi, + adc1: hal::adc::Adc, + adc2: hal::adc::Adc, + adc1_in_p: hal::gpio::gpiof::PF11, + adc2_in_p: hal::gpio::gpiof::PF14, + ) -> Result { + let mut devices = Self { + mcp23017, + ad9959, + attenuator_spi, + adc1, + adc2, + adc1_in_p, + adc2_in_p, + }; + + // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator + // selected, attenuators out of reset. + devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0xF).map_err(|_| Error::I2c)?; + devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, + 1_u8.wrapping_shl(5)).map_err(|_| Error::I2c)?; + devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?; + + // Select the on-board clock with a 5x prescaler (500MHz). + devices.select_onboard_clock(5u8)?; + + Ok(devices) + } + + pub fn select_external_clock(&mut self, frequency: u32, prescaler: u8) -> Result<(), Error>{ + self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?; + self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::DDS)?; + + Ok(()) + } + + pub fn select_onboard_clock(&mut self, prescaler: u8) -> Result<(), Error> { + self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?; + self.ad9959.configure_system_clock(100_000_000, prescaler).map_err(|_| Error::DDS)?; + + Ok(()) + } +} + +impl AttenuatorInterface for PounderDevices +{ + fn reset(&mut self) -> Result<(), Error> { + self.mcp23017.digital_write(ATT_RST_N_PIN, true).map_err(|_| Error::I2c)?; + // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is + // sufficient. Document the delay here. + self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?; + + Ok(()) + } + + fn latch(&mut self, channel: DdsChannel) -> Result<(), Error> { + let pin = match channel { + DdsChannel::Zero => ATT_LE1_PIN, + DdsChannel::One => ATT_LE0_PIN, + DdsChannel::Two => ATT_LE3_PIN, + DdsChannel::Three => ATT_LE2_PIN, + }; + + self.mcp23017.digital_write(pin, true).map_err(|_| Error::I2c)?; + // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is + // sufficient. Document the delay here. + self.mcp23017.digital_write(pin, false).map_err(|_| Error::I2c)?; + + Ok(()) + } + + fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> { + self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?; + + Ok(()) + } + + fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error> { + let mut result = [0_u8; 4]; + result.clone_from_slice(channels); + self.attenuator_spi.transfer(&mut result).map_err(|_| Error::Spi)?; + + Ok(()) + } +} + +impl PowerMeasurementInterface for PounderDevices { + fn sample_converter(&mut self, channel: InputChannel) -> Result { + let adc_scale = match channel { + InputChannel::Zero => { + let adc_reading: u32 = self.adc1.read(&mut self.adc1_in_p).map_err(|_| Error::Adc)?; + adc_reading as f32 / self.adc1.max_sample() as f32 + }, + InputChannel::One => { + let adc_reading: u32 = self.adc2.read(&mut self.adc2_in_p).map_err(|_| Error::Adc)?; + adc_reading as f32 / self.adc2.max_sample() as f32 + }, + }; + + // Convert analog percentage to voltage. + Ok(adc_scale * 3.3) + } +} diff --git a/stabilizer/src/pounder/rf_power.rs b/stabilizer/src/pounder/rf_power.rs new file mode 100644 index 0000000..dd7a3ec --- /dev/null +++ b/stabilizer/src/pounder/rf_power.rs @@ -0,0 +1,14 @@ +use super::Error; +use super::InputChannel; + +pub trait PowerMeasurementInterface { + fn sample_converter(&mut self, channel: InputChannel) -> Result; + + fn measure_power(&mut self, channel: InputChannel) -> Result { + let analog_measurement = self.sample_converter(channel)?; + + // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at + // 100MHz. + Ok(analog_measurement / 0.0517) + } +} diff --git a/stabilizer/src/pounder/types.rs b/stabilizer/src/pounder/types.rs new file mode 100644 index 0000000..9938bfe --- /dev/null +++ b/stabilizer/src/pounder/types.rs @@ -0,0 +1,16 @@ + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum DdsChannel { + Zero, + One, + Two, + Three, +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum InputChannel { + Zero, + One, +} From 547fe1bd40b010440478eafd0d09f538c9aaaa36 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 8 Jun 2020 18:36:29 +0200 Subject: [PATCH 25/40] Removing ethernet module --- stabilizer/src/eth.rs | 605 ----------------------------------------- stabilizer/src/main.rs | 4 +- 2 files changed, 1 insertion(+), 608 deletions(-) delete mode 100644 stabilizer/src/eth.rs diff --git a/stabilizer/src/eth.rs b/stabilizer/src/eth.rs deleted file mode 100644 index 267a098..0000000 --- a/stabilizer/src/eth.rs +++ /dev/null @@ -1,605 +0,0 @@ -use core::{cmp, slice}; -use smoltcp::phy; -use smoltcp::time::Instant; -use smoltcp::wire::EthernetAddress; -use smoltcp::Result; -use super::{pac}; - -#[allow(dead_code)] -mod phy_consts { - pub const PHY_REG_BCR: u8 = 0x00; - pub const PHY_REG_BSR: u8 = 0x01; - pub const PHY_REG_ID1: u8 = 0x02; - pub const PHY_REG_ID2: u8 = 0x03; - pub const PHY_REG_ANTX: u8 = 0x04; - pub const PHY_REG_ANRX: u8 = 0x05; - pub const PHY_REG_ANEXP: u8 = 0x06; - pub const PHY_REG_ANNPTX: u8 = 0x07; - pub const PHY_REG_ANNPRX: u8 = 0x08; - pub const PHY_REG_SSR: u8 = 0x1F; // Special Status Register - pub const PHY_REG_CTL: u8 = 0x0D; // Ethernet PHY Register Control - pub const PHY_REG_ADDAR: u8 = 0x0E; // Ethernet PHY Address or Data - - pub const PHY_REG_WUCSR: u16 = 0x8010; - - pub const PHY_REG_BCR_COLTEST: u16 = 1 << 7; - pub const PHY_REG_BCR_FD: u16 = 1 << 8; - pub const PHY_REG_BCR_ANRST: u16 = 1 << 9; - pub const PHY_REG_BCR_ISOLATE: u16 = 1 << 10; - pub const PHY_REG_BCR_POWERDN: u16 = 1 << 11; - pub const PHY_REG_BCR_AN: u16 = 1 << 12; - pub const PHY_REG_BCR_100M: u16 = 1 << 13; - pub const PHY_REG_BCR_LOOPBACK: u16 = 1 << 14; - pub const PHY_REG_BCR_RESET: u16 = 1 << 15; - - pub const PHY_REG_BSR_JABBER: u16 = 1 << 1; - pub const PHY_REG_BSR_UP: u16 = 1 << 2; - pub const PHY_REG_BSR_FAULT: u16 = 1 << 4; - pub const PHY_REG_BSR_ANDONE: u16 = 1 << 5; - - pub const PHY_REG_SSR_ANDONE: u16 = 1 << 12; - pub const PHY_REG_SSR_SPEED: u16 = 0b111 << 2; - pub const PHY_REG_SSR_10BASE_HD: u16 = 0b001 << 2; - pub const PHY_REG_SSR_10BASE_FD: u16 = 0b101 << 2; - pub const PHY_REG_SSR_100BASE_HD: u16 = 0b010 << 2; - pub const PHY_REG_SSR_100BASE_FD: u16 = 0b110 << 2; -} -use self::phy_consts::*; - -const EMAC_DES3_OWN: u32 = 0x8000_0000; -const EMAC_DES3_CTXT: u32 = 0x4000_0000; -const EMAC_DES3_FD: u32 = 0x2000_0000; -const EMAC_DES3_LD: u32 = 0x1000_0000; -const EMAC_DES3_ES: u32 = 0x0000_8000; -const EMAC_TDES2_IOC: u32 = 0x8000_0000; -const EMAC_RDES3_IOC: u32 = 0x4000_0000; -const EMAC_RDES3_PL: u32 = 0x0000_7FFF; -const EMAC_RDES3_BUF1V: u32 = 0x0100_0000; -const EMAC_TDES2_B1L: u32 = 0x0000_3FFF; -const EMAC_DES0_BUF1AP: u32 = 0xFFFF_FFFF; - -// 6 DMAC, 6 SMAC, 4 q tag, 2 ethernet type II, 1500 ip MTU, 4 CRC, 2 padding -const ETH_BUFFER_SIZE: usize = 1524; -const ETH_DESC_U32_SIZE: usize = 4; -const ETH_TX_BUFFER_COUNT: usize = 4; -const ETH_RX_BUFFER_COUNT: usize = 4; - -#[allow(dead_code)] -mod cr_consts { - /* For HCLK 60-100 MHz */ - pub const ETH_MACMIIAR_CR_HCLK_DIV_42: u8 = 0; - /* For HCLK 100-150 MHz */ - pub const ETH_MACMIIAR_CR_HCLK_DIV_62: u8 = 1; - /* For HCLK 20-35 MHz */ - pub const ETH_MACMIIAR_CR_HCLK_DIV_16: u8 = 2; - /* For HCLK 35-60 MHz */ - pub const ETH_MACMIIAR_CR_HCLK_DIV_26: u8 = 3; - /* For HCLK 150-250 MHz */ - pub const ETH_MACMIIAR_CR_HCLK_DIV_102: u8 = 4; - /* For HCLK 250-300 MHz */ - pub const ETH_MACMIIAR_CR_HCLK_DIV_124: u8 = 5; -} -use self::cr_consts::*; - -// set clock range in MAC MII address register -// 200 MHz AHB clock = eth_hclk -const CLOCK_RANGE: u8 = ETH_MACMIIAR_CR_HCLK_DIV_102; - -const PHY_ADDR: u8 = 0; - -fn phy_read(reg_addr: u8, mac: &pac::ETHERNET_MAC) -> u16 { - while mac.macmdioar.read().mb().bit_is_set() {} - mac.macmdioar.modify(|_, w| unsafe { - w.pa() - .bits(PHY_ADDR) - .rda() - .bits(reg_addr) - .goc() - .bits(0b11) // read - .cr() - .bits(CLOCK_RANGE) - .mb() - .set_bit() - }); - while mac.macmdioar.read().mb().bit_is_set() {} - mac.macmdiodr.read().md().bits() -} - -fn phy_write(reg_addr: u8, reg_data: u16, mac: &pac::ETHERNET_MAC) { - while mac.macmdioar.read().mb().bit_is_set() {} - mac.macmdiodr.write(|w| unsafe { w.md().bits(reg_data) }); - mac.macmdioar.modify(|_, w| unsafe { - w.pa() - .bits(PHY_ADDR) - .rda() - .bits(reg_addr) - .goc() - .bits(0b01) // write - .cr() - .bits(CLOCK_RANGE) - .mb() - .set_bit() - }); - while mac.macmdioar.read().mb().bit_is_set() {} -} - -// Writes a value to an extended PHY register in MMD address space -fn phy_write_ext(reg_addr: u16, reg_data: u16, mac: &pac::ETHERNET_MAC) { - phy_write(PHY_REG_CTL, 0x0003, mac); // set address - phy_write(PHY_REG_ADDAR, reg_addr, mac); - phy_write(PHY_REG_CTL, 0x4003, mac); // set data - phy_write(PHY_REG_ADDAR, reg_data, mac); -} - -#[repr(align(4))] -struct RxRing { - desc_buf: [[u32; ETH_DESC_U32_SIZE]; ETH_RX_BUFFER_COUNT], - pkt_buf: [[u8; ETH_BUFFER_SIZE]; ETH_RX_BUFFER_COUNT], - cur_desc: usize, -} - -impl RxRing { - const fn new() -> Self { - Self { - desc_buf: [[0; ETH_DESC_U32_SIZE]; ETH_RX_BUFFER_COUNT], - pkt_buf: [[0; ETH_BUFFER_SIZE]; ETH_RX_BUFFER_COUNT], - cur_desc: 0, - } - } - - unsafe fn init(&mut self, dma: &pac::ETHERNET_DMA) { - assert_eq!(self.desc_buf[0].len() % 4, 0); - assert_eq!(self.pkt_buf[0].len() % 4, 0); - - for i in 0..self.desc_buf.len() { - for j in 0..self.desc_buf[0].len() { - self.desc_buf[i][j] = 0; - } - for j in 0..self.pkt_buf[0].len() { - self.pkt_buf[i][j] = 0; - } - } - - let addr = &self.desc_buf as *const _ as u32; - assert_eq!(addr & 0x3, 0); - dma.dmacrx_dlar.write(|w| w.bits(addr)); - dma.dmacrx_rlr - .write(|w| w.rdrl().bits(self.desc_buf.len() as u16 - 1)); - - self.cur_desc = 0; - for _ in 0..self.desc_buf.len() { - self.buf_release() - } - } - - fn next_desc(&self) -> usize { - (self.cur_desc + 1) % self.desc_buf.len() - } - - // not owned by DMA - fn buf_owned(&self) -> bool { - self.desc_buf[self.cur_desc][3] & EMAC_DES3_OWN == 0 - } - - fn buf_valid(&self) -> bool { - self.desc_buf[self.cur_desc][3] - & (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT) - == (EMAC_DES3_FD | EMAC_DES3_LD) - } - - unsafe fn buf_as_slice_mut<'a>(&self) -> &'a mut [u8] { - let len = (self.desc_buf[self.cur_desc][3] & EMAC_RDES3_PL) as usize; - let len = cmp::min(len, ETH_BUFFER_SIZE); - let addr = &self.pkt_buf[self.cur_desc] as *const _ as *mut u8; - slice::from_raw_parts_mut(addr, len) - } - - fn buf_release(&mut self) { - let addr = &self.pkt_buf[self.cur_desc] as *const _; - self.desc_buf[self.cur_desc][0] = addr as u32 & EMAC_DES0_BUF1AP; - self.desc_buf[self.cur_desc][3] = - EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN; - - let addr = &self.desc_buf[self.cur_desc] as *const _ as u32; - assert_eq!(addr & 0x3, 0); - - let dma = unsafe { pac::Peripherals::steal().ETHERNET_DMA }; - - // Ensure changes to the descriptor (in particular, the OWN flag) are - // committed before DMA engine sees tail pointer store. - cortex_m::asm::dsb(); - - dma.dmacrx_dtpr.write(|w| unsafe { w.bits(addr) }); - - self.cur_desc = self.next_desc(); - } -} - -#[repr(align(4))] -struct TxRing { - desc_buf: [[u32; ETH_DESC_U32_SIZE]; ETH_TX_BUFFER_COUNT], - pkt_buf: [[u8; ETH_BUFFER_SIZE]; ETH_TX_BUFFER_COUNT], - cur_desc: usize, -} - -impl TxRing { - const fn new() -> Self { - Self { - desc_buf: [[0; ETH_DESC_U32_SIZE]; ETH_TX_BUFFER_COUNT], - pkt_buf: [[0; ETH_BUFFER_SIZE]; ETH_TX_BUFFER_COUNT], - cur_desc: 0, - } - } - - unsafe fn init(&mut self, dma: &pac::ETHERNET_DMA) { - assert_eq!(self.desc_buf[0].len() % 4, 0); - assert_eq!(self.pkt_buf[0].len() % 4, 0); - - for i in 0..self.desc_buf.len() { - for j in 0..self.desc_buf[0].len() { - self.desc_buf[i][j] = 0; - } - for j in 0..self.pkt_buf[0].len() { - self.pkt_buf[i][j] = 0; - } - } - self.cur_desc = 0; - - let addr = &self.desc_buf as *const _ as u32; - assert_eq!(addr & 0x3, 0); - dma.dmactx_dlar.write(|w| w.bits(addr)); - dma.dmactx_rlr - .write(|w| w.tdrl().bits(self.desc_buf.len() as u16 - 1)); - let addr = &self.desc_buf[0] as *const _ as u32; - assert_eq!(addr & 0x3, 0); - dma.dmactx_dtpr.write(|w| w.bits(addr)); - } - - fn next_desc(&self) -> usize { - (self.cur_desc + 1) % self.desc_buf.len() - } - - // not owned by DMA - fn buf_owned(&self) -> bool { - self.desc_buf[self.cur_desc][3] & EMAC_DES3_OWN == 0 - } - - unsafe fn buf_as_slice_mut<'a>(&mut self, len: usize) -> &'a mut [u8] { - let len = cmp::min(len, ETH_BUFFER_SIZE); - self.desc_buf[self.cur_desc][2] = - EMAC_TDES2_IOC | (len as u32 & EMAC_TDES2_B1L); - let addr = &self.pkt_buf[self.cur_desc] as *const _ as *mut u8; - self.desc_buf[self.cur_desc][0] = addr as u32 & EMAC_DES0_BUF1AP; - slice::from_raw_parts_mut(addr, len) - } - - fn buf_release(&mut self) { - self.desc_buf[self.cur_desc][3] = - EMAC_DES3_OWN | EMAC_DES3_FD | EMAC_DES3_LD; - self.cur_desc = self.next_desc(); - - let addr = &self.desc_buf[self.cur_desc] as *const _ as u32; - assert_eq!(addr & 0x3, 0); - - let dma = unsafe { pac::Peripherals::steal().ETHERNET_DMA }; - - // Ensure packet contents as well as changes to the descriptor have been - // committed before DMA engine sees the tail pointer store. - cortex_m::asm::dsb(); - - dma.dmactx_dtpr.write(|w| unsafe { w.bits(addr) }); - } -} - -pub struct Device { - rx: RxRing, - tx: TxRing, -} - -impl Device { - pub const fn new() -> Self { - Self { - rx: RxRing::new(), - tx: TxRing::new(), - } - } - - // Initialize the ethernet peripherals - // - // # Safety - // - // This iis transitively unsafe since it sets potentially - // unsafe register values. Might ultimately be safe if the values - // are correct. - // - // After `init` is called, `Device` shall not be moved. - pub unsafe fn init( - &mut self, - mac: EthernetAddress, - eth_mac: &pac::ETHERNET_MAC, - eth_dma: &pac::ETHERNET_DMA, - eth_mtl: &pac::ETHERNET_MTL, - ) { - eth_dma.dmamr.modify(|_, w| w.swr().set_bit()); - while eth_dma.dmamr.read().swr().bit_is_set() {} - - // 200 MHz - eth_mac - .mac1ustcr - .modify(|_, w| w.tic_1us_cntr().bits(200 - 1)); - - // Configuration Register - eth_mac.maccr.modify(|_, w| { - w.arpen() - .clear_bit() - .ipc() - .set_bit() - .ipg() - .bits(0b000) // 96 bit - .ecrsfd() - .clear_bit() - .dcrs() - .clear_bit() - .bl() - .bits(0b00) // 19 - .prelen() - .bits(0b00) // 7 - // CRC stripping for Type frames - .cst() - .set_bit() - // Fast Ethernet speed - .fes() - .set_bit() - // Duplex mode - .dm() - .set_bit() - // Automatic pad/CRC stripping - .acs() - .set_bit() - // Retry disable in half-duplex mode - .dr() - .set_bit() - }); - eth_mac.macecr.modify(|_, w| { - w.eipgen() - .clear_bit() - .usp() - .clear_bit() - .spen() - .clear_bit() - .dcrcc() - .clear_bit() - }); - // Set the MAC address - eth_mac.maca0lr.write(|w| { - w.addrlo().bits( - u32::from(mac.0[0]) - | (u32::from(mac.0[1]) << 8) - | (u32::from(mac.0[2]) << 16) - | (u32::from(mac.0[3]) << 24), - ) - }); - eth_mac.maca0hr.write(|w| { - w.addrhi() - .bits(u16::from(mac.0[4]) | (u16::from(mac.0[5]) << 8)) - }); - // frame filter register - eth_mac.macpfr.modify(|_, w| { - w.dntu() - .clear_bit() - .ipfe() - .clear_bit() - .vtfe() - .clear_bit() - .hpf() - .clear_bit() - .saf() - .clear_bit() - .saif() - .clear_bit() - .pcf() - .bits(0b00) - .dbf() - .clear_bit() - .pm() - .clear_bit() - .daif() - .clear_bit() - .hmc() - .clear_bit() - .huc() - .clear_bit() - // Receive All - .ra() - .clear_bit() - // Promiscuous mode - .pr() - .clear_bit() - }); - eth_mac.macwtr.write(|w| w.pwe().clear_bit()); - // Flow Control Register - eth_mac.macqtx_fcr.modify(|_, w| { - // Pause time - w.pt().bits(0x100) - }); - eth_mac.macrx_fcr.modify(|_, w| w); - eth_mtl.mtlrx_qomr.modify(|_, w| { - w - // Receive store and forward - .rsf() - .set_bit() - // Dropping of TCP/IP checksum error frames disable - .dis_tcp_ef() - .clear_bit() - // Forward error frames - .fep() - .clear_bit() - // Forward undersized good packets - .fup() - .clear_bit() - }); - eth_mtl.mtltx_qomr.modify(|_, w| { - w - // Transmit store and forward - .tsf() - .set_bit() - }); - - if (phy_read(PHY_REG_ID1, eth_mac) != 0x0007) - | (phy_read(PHY_REG_ID2, eth_mac) != 0xC131) - { - error!("PHY ID error!"); - } - - phy_write(PHY_REG_BCR, PHY_REG_BCR_RESET, eth_mac); - while phy_read(PHY_REG_BCR, eth_mac) & PHY_REG_BCR_RESET - == PHY_REG_BCR_RESET - {} - phy_write_ext(PHY_REG_WUCSR, 0, eth_mac); - phy_write( - PHY_REG_BCR, - PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, - eth_mac, - ); - /* - while phy_read(PHY_REG_BSR) & PHY_REG_BSR_UP == 0 {}; - while phy_read(PHY_REG_BSR) & PHY_REG_BSR_ANDONE == 0 {}; - while phy_read(PHY_REG_SSR) & (PHY_REG_SSR_ANDONE | PHY_REG_SSR_SPEED) - != PHY_REG_SSR_ANDONE | PHY_REG_SSR_100BASE_FD {}; - */ - - // operation mode register - eth_dma.dmamr.modify(|_, w| { - w.intm() - .bits(0b00) - // Rx Tx priority ratio 1:1 - .pr() - .bits(0b000) - .txpr() - .clear_bit() - .da() - .clear_bit() - }); - // bus mode register - eth_dma.dmasbmr.modify(|_, w| { - // Address-aligned beats - w.aal() - .set_bit() - // Fixed burst - .fb() - .set_bit() - }); - eth_dma - .dmaccr - .modify(|_, w| w.dsl().bits(0).pblx8().clear_bit().mss().bits(536)); - eth_dma.dmactx_cr.modify(|_, w| { - w - // Tx DMA PBL - .txpbl() - .bits(32) - .tse() - .clear_bit() - // Operate on second frame - .osf() - .clear_bit() - }); - - eth_dma.dmacrx_cr.modify(|_, w| { - w - // receive buffer size - .rbsz() - .bits(ETH_BUFFER_SIZE as u16) - // Rx DMA PBL - .rxpbl() - .bits(32) - // Disable flushing of received frames - .rpf() - .clear_bit() - }); - - self.rx.init(eth_dma); - self.tx.init(eth_dma); - - // Manage MAC transmission and reception - eth_mac.maccr.modify(|_, w| { - w.re() - .bit(true) // Receiver Enable - .te() - .bit(true) // Transmiter Enable - }); - eth_mtl.mtltx_qomr.modify(|_, w| w.ftq().set_bit()); - - // Ensure ring buffer descriptors have been set up in memory before - // enabling DMA engine. - cortex_m::asm::dsb(); - - // Manage DMA transmission and reception - eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit()); - eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit()); - - eth_dma - .dmacsr - .modify(|_, w| w.tps().set_bit().rps().set_bit()); - } -} - -impl<'a, 'b> phy::Device<'a> for &'b mut Device { - type RxToken = RxToken<'a>; - type TxToken = TxToken<'a>; - - fn capabilities(&self) -> phy::DeviceCapabilities { - let mut capabilities = phy::DeviceCapabilities::default(); - // ethernet frame type II (6 smac, 6 dmac, 2 ethertype), - // sans CRC (4), 1500 IP MTU - capabilities.max_transmission_unit = 1514; - capabilities.max_burst_size = Some(self.tx.desc_buf.len()); - capabilities - } - - fn receive(&mut self) -> Option<(RxToken, TxToken)> { - // Skip all queued packets with errors. - while self.rx.buf_owned() && !self.rx.buf_valid() { - self.rx.buf_release() - } - - if !(self.rx.buf_owned() && self.tx.buf_owned()) { - return None; - } - - Some((RxToken(&mut self.rx), TxToken(&mut self.tx))) - } - - fn transmit(&mut self) -> Option { - if !self.tx.buf_owned() { - return None; - } - - Some(TxToken(&mut self.tx)) - } -} - -pub struct RxToken<'a>(&'a mut RxRing); - -impl<'a> phy::RxToken for RxToken<'a> { - fn consume(self, _timestamp: Instant, f: F) -> Result - where - F: FnOnce(&mut [u8]) -> Result, - { - let result = f(unsafe { self.0.buf_as_slice_mut() }); - self.0.buf_release(); - result - } -} - -pub struct TxToken<'a>(&'a mut TxRing); - -impl<'a> phy::TxToken for TxToken<'a> { - fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result - where - F: FnOnce(&mut [u8]) -> Result, - { - let result = f(unsafe { self.0.buf_as_slice_mut(len) }); - self.0.buf_release(); - result - } -} diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 1b42929..5aef04b 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -11,7 +11,7 @@ #[panic_handler] #[cfg(all(feature = "nightly", not(feature = "semihosting")))] fn panic(_info: &core::panic::PanicInfo) -> ! { - let gpiod = unsafe { &*pac::GPIOD::ptr() }; + let gpiod = unsafe { &*hal::stm32::GPIOD::ptr() }; gpiod.odr.modify(|_, w| w.odr6().high().odr12().high()); // FP_LED_1, FP_LED_3 unsafe { core::intrinsics::abort(); @@ -34,7 +34,6 @@ use cortex_m; use stm32h7xx_hal as hal; use stm32h7xx_hal::{ prelude::*, - stm32 as pac, }; use embedded_hal::{ @@ -47,7 +46,6 @@ use smoltcp as net; #[link_section = ".sram3.eth"] static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); -mod eth; mod server; mod afe; From 7fefb4c61261aacfd4794cb2140843b98571e929 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Mon, 8 Jun 2020 18:53:07 +0200 Subject: [PATCH 26/40] Simplifying GPIO API --- stabilizer/src/main.rs | 14 +++++++------- stm32h7xx-hal | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 5aef04b..4a7b69e 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -159,13 +159,13 @@ const APP: () = { clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); - let gpioa = dp.GPIOA.split(&mut clocks.ahb4); - let gpiob = dp.GPIOB.split(&mut clocks.ahb4); - let gpioc = dp.GPIOC.split(&mut clocks.ahb4); - let gpiod = dp.GPIOD.split(&mut clocks.ahb4); - let gpioe = dp.GPIOE.split(&mut clocks.ahb4); - let gpiof = dp.GPIOF.split(&mut clocks.ahb4); - let gpiog = dp.GPIOG.split(&mut clocks.ahb4); + let gpioa = dp.GPIOA.split(&mut clocks); + let gpiob = dp.GPIOB.split(&mut clocks); + let gpioc = dp.GPIOC.split(&mut clocks); + let gpiod = dp.GPIOD.split(&mut clocks); + let gpioe = dp.GPIOE.split(&mut clocks); + let gpiof = dp.GPIOF.split(&mut clocks); + let gpiog = dp.GPIOG.split(&mut clocks); let afe1 = { let a0_pin = gpiof.pf2.into_push_pull_output(); diff --git a/stm32h7xx-hal b/stm32h7xx-hal index 2236b57..d79cb00 160000 --- a/stm32h7xx-hal +++ b/stm32h7xx-hal @@ -1 +1 @@ -Subproject commit 2236b578b48aa195679dd65515f595f491f88513 +Subproject commit d79cb0015a6f0cbb819907efe3a817f7dce14bab From 7494ece6591d5388842190a2446d39158eea2027 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 9 Jun 2020 14:16:01 +0200 Subject: [PATCH 27/40] Refactoring ethernet interface --- .cargo/config | 2 +- stabilizer/src/main.rs | 73 +++++++++++++++++++++++----------------- stabilizer/src/server.rs | 13 +++++-- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/.cargo/config b/.cargo/config index a765d53..382c36e 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "gdb-multiarch -q -x bmp.gdb" +runner = "gdb-multiarch -q -x openocd.gdb" rustflags = ["-C", "link-arg=-Tlink.x"] [build] diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index a5eee0b..1cf6e4b 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -113,47 +113,59 @@ macro_rules! route_request { ($request:ident, readable_attributes: [$(($read_attribute:tt, $getter:tt)),*], modifiable_attributes: [$(($write_attribute:tt, $TYPE:ty, $setter:tt)),*]) => { - match $request { - server::Request::Read{attribute} => { - match attribute { + match $request.req { + server::AccessRequest::Read => { + match $request.attribute { $( - &$read_attribute => { + $read_attribute => { let value = match $getter() { Ok(data) => data, - Err(_) => return server::Response::error(attribute, + Err(_) => return server::Response::error($request.attribute, "Failed to set attribute"), }; let encoded_data: String = match serde_json_core::to_string(&value) { Ok(data) => data, - Err(_) => return server::Response::error(attribute, + Err(_) => return server::Response::error($request.attribute, "Failed to encode attribute value"), }; - server::Response::success(attribute, &encoded_data) + // Encoding data into a string surrounds it with qutotations. Because this + // value is then serialzed into another string, we remove the double + // quotations because they cannot be properly escaped. + server::Response::success($request.attribute, + &encoded_data[1..encoded_data.len()-1]) }, )* - _ => server::Response::error(attribute, "Unknown attribute") + _ => server::Response::error($request.attribute, "Unknown attribute") } }, - server::Request::Write{attribute, value} => { - match attribute { + server::AccessRequest::Write => { + match $request.attribute { $( - &$write_attribute => { - let new_value = match serde_json_core::from_str::<$TYPE>(value) { + $write_attribute => { + // To avoid sending double quotations in the request, they are eliminated on + // the sender side. However, to properly deserialize the data, quotes need + // to be added back. + let mut value: String = String::new(); + value.push('"').unwrap(); + value.push_str($request.value).unwrap(); + value.push('"').unwrap(); + + let new_value = match serde_json_core::from_str::<$TYPE>(value.as_str()) { Ok(data) => data, - Err(_) => return server::Response::error(attribute, + Err(_) => return server::Response::error($request.attribute, "Failed to decode value"), }; match $setter(new_value) { - Ok(_) => server::Response::success(attribute, value), - Err(_) => server::Response::error(attribute, + Ok(_) => server::Response::success($request.attribute, $request.value), + Err(_) => server::Response::error($request.attribute, "Failed to set attribute"), } } )* - _ => server::Response::error(attribute, "Unknown attribute") + _ => server::Response::error($request.attribute, "Unknown attribute") } } } @@ -176,7 +188,7 @@ const APP: () = { timer: hal::timer::Timer, net_interface: net::iface::EthernetInterface<'static, 'static, 'static, ethernet::EthernetDMA<'static>>, - _eth_mac: ethernet::EthernetMAC, + eth_mac: ethernet::EthernetMAC, mac_addr: net::wire::EthernetAddress, #[init([[0.; 5]; 2])] @@ -211,6 +223,8 @@ const APP: () = { clocks.rb.d2ccip1r.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q()); + let mut delay = hal::delay::Delay::new(cp.SYST, clocks.clocks); + let gpioa = dp.GPIOA.split(&mut clocks); let gpiob = dp.GPIOB.split(&mut clocks); let gpioc = dp.GPIOC.split(&mut clocks); @@ -326,8 +340,8 @@ const APP: () = { let mut fp_led_0 = gpiod.pd5.into_push_pull_output(); let mut fp_led_1 = gpiod.pd6.into_push_pull_output(); - let mut fp_led_2 = gpiod.pd12.into_push_pull_output(); - let mut fp_led_3 = gpiog.pg4.into_push_pull_output(); + let mut fp_led_2 = gpiog.pg4.into_push_pull_output(); + let mut fp_led_3 = gpiod.pd12.into_push_pull_output(); fp_led_0.set_low().unwrap(); fp_led_1.set_low().unwrap(); @@ -344,8 +358,8 @@ const APP: () = { { // Reset the PHY before configuring pins. let mut eth_phy_nrst = gpioe.pe3.into_push_pull_output(); - eth_phy_nrst.set_high().unwrap(); eth_phy_nrst.set_low().unwrap(); + delay.delay_us(200u8); eth_phy_nrst.set_high().unwrap(); let _rmii_ref_clk = gpioa.pa1.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); let _rmii_mdio = gpioa.pa2.into_alternate_af11().set_speed(hal::gpio::Speed::VeryHigh); @@ -377,6 +391,8 @@ const APP: () = { mac_addr.clone()) }; + unsafe { ethernet::enable_interrupt() }; + let store = unsafe { &mut NET_STORE }; store.ip_addrs[0] = net::wire::IpCidr::new(net::wire::IpAddress::v4(10, 0, 16, 99), 24); @@ -394,7 +410,6 @@ const APP: () = { cp.SCB.enable_icache(); - init_log(); // info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap()); // info!("Built on {}", build_info::BUILT_TIME_UTC); // info!("{} {}", build_info::RUSTC_VERSION, build_info::TARGET); @@ -433,7 +448,7 @@ const APP: () = { eeprom_i2c: eeprom_i2c, net_interface: network_interface, - _eth_mac: eth_mac, + eth_mac: eth_mac, mac_addr: mac_addr, } } @@ -468,7 +483,7 @@ const APP: () = { c.resources.dac1.send(output).unwrap(); } - #[idle(resources=[net_interface, mac_addr, iir_state, iir_ch, afe1, afe2])] + #[idle(resources=[net_interface, mac_addr, eth_mac, iir_state, iir_ch, afe1, afe2])] fn idle(mut c: idle::Context) -> ! { let mut socket_set_entries: [_; 8] = Default::default(); @@ -566,7 +581,7 @@ const APP: () = { let sleep = match c.resources.net_interface.poll(&mut sockets, net::time::Instant::from_millis(time as i64)) { - Ok(changed) => changed, + Ok(changed) => changed == false, Err(net::Error::Unrecognized) => true, Err(e) => { info!("iface poll error: {:?}", e); @@ -580,14 +595,10 @@ const APP: () = { } } - /* - #[task(binds = ETH, resources = [net_interface], priority = 1)] - fn eth(c: eth::Context) { - let dma = &c.resources.net_interface.device(); - ETHERNET_PENDING.store(true, Ordering::Relaxed); - dma.interrupt_handler() + #[task(binds = ETH, priority = 1)] + fn eth(_: eth::Context) { + unsafe { ethernet::interrupt_handler() } } - */ extern "C" { // hw interrupt handlers for RTFM to use for scheduling tasks diff --git a/stabilizer/src/server.rs b/stabilizer/src/server.rs index 68edafe..adf2fdc 100644 --- a/stabilizer/src/server.rs +++ b/stabilizer/src/server.rs @@ -21,9 +21,16 @@ use super::net; use super::iir; #[derive(Deserialize, Serialize, Debug)] -pub enum Request<'a, 'b> { - Read{attribute: &'a str}, - Write{attribute: &'a str, value: &'b str}, +pub enum AccessRequest { + Read, + Write, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct Request<'a, 'b> { + pub req: AccessRequest, + pub attribute: &'a str, + pub value: &'b str, } #[derive(Serialize, Deserialize)] From 9ee39aa8a0ca91feb877be69c5160b12c97c04f4 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Tue, 9 Jun 2020 14:16:49 +0200 Subject: [PATCH 28/40] Adding log initialization --- stabilizer/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 1cf6e4b..a815769 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -216,6 +216,8 @@ const APP: () = { .pll2_q_ck(100.mhz()) .freeze(vos, &dp.SYSCFG); + init_log(); + // Enable SRAM3 for the ethernet descriptor ring. clocks.rb.ahb2enr.modify(|_, w| w.sram3en().set_bit()); From 519fa860587639159e481069214c1e5ba12d2577 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Wed, 10 Jun 2020 12:40:44 +0200 Subject: [PATCH 29/40] Adding work after functional DDS interface --- Cargo.toml | 2 +- ad9959/src/lib.rs | 131 ++++++++++++--- stabilizer/src/main.rs | 113 ++++++++----- stabilizer/src/pounder/attenuators.rs | 29 ++-- stabilizer/src/pounder/error.rs | 11 -- stabilizer/src/pounder/mod.rs | 228 +++++++++++++++++++++----- stabilizer/src/pounder/rf_power.rs | 7 +- stabilizer/src/pounder/types.rs | 16 -- stabilizer/src/server.rs | 69 +++++++- 9 files changed, 439 insertions(+), 167 deletions(-) delete mode 100644 stabilizer/src/pounder/error.rs delete mode 100644 stabilizer/src/pounder/types.rs diff --git a/Cargo.toml b/Cargo.toml index 05df738..ec83f63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ [profile.dev] codegen-units = 1 incremental = false -opt-level = 3 +opt-level = 1 [profile.release] debug = true diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 5346bdc..75277e6 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -19,7 +19,7 @@ use embedded_hal::{ pub struct Ad9959 { interface: INTERFACE, delay: DELAY, - reference_clock_frequency: u32, + reference_clock_frequency: f32, system_clock_multiplier: u8, io_update: UPDATE, } @@ -84,6 +84,7 @@ pub enum Channel { #[derive(Debug)] pub enum Error { Interface(InterfaceE), + Check, Bounds, Pin, Frequency, @@ -107,7 +108,7 @@ where io_update: UPDATE, delay: DELAY, desired_mode: Mode, - clock_frequency: u32, + clock_frequency: f32, multiplier: u8) -> Result> where RST: OutputPin, @@ -120,7 +121,7 @@ where system_clock_multiplier: 1, }; - ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; + ad9959.io_update.set_low().or_else(|_| Err(Error::Pin))?; // Reset the AD9959 reset_pin.set_high().or_else(|_| Err(Error::Pin))?; @@ -130,12 +131,12 @@ where reset_pin.set_low().or_else(|_| Err(Error::Pin))?; - ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?; + ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?; // Program the interface configuration in the AD9959. Default to all channels enabled. let mut csr: [u8; 1] = [0xF0]; - csr[0].set_bits(1..3, desired_mode as u8); - ad9959.interface.write(0, &csr)?; + csr[0].set_bits(1..3, desired_mode as u8); + ad9959.interface.write(Register::CSR as u8, &csr)?; // Configure the interface to the desired mode. ad9959.interface.configure_mode(Mode::FourBitSerial)?; @@ -145,6 +146,13 @@ where ad9959.interface.configure_mode(desired_mode)?; + // Read back the CSR to ensure it specifies the mode correctly. + let mut updated_csr: [u8; 1] = [0]; + ad9959.interface.read(Register::CSR as u8, &mut updated_csr)?; + if updated_csr[0] != csr[0] { + return Err(Error::Check); + } + // Set the clock frequency to configure the device as necessary. ad9959.configure_system_clock(clock_frequency, multiplier)?; Ok(ad9959) @@ -164,21 +172,21 @@ where /// /// Arguments: /// * `reference_clock_frequency` - The reference clock frequency provided to the AD9959 core. - /// * `prescaler` - The frequency prescaler of the system clock. Must be 1 or 4-20. + /// * `multiplier` - The frequency multiplier of the system clock. Must be 1 or 4-20. /// /// Returns: /// The actual frequency configured for the internal system clock. pub fn configure_system_clock(&mut self, - reference_clock_frequency: u32, - prescaler: u8) -> Result> + reference_clock_frequency: f32, + multiplier: u8) -> Result> { self.reference_clock_frequency = reference_clock_frequency; - if prescaler != 1 && (prescaler > 20 || prescaler < 4) { + if multiplier != 1 && (multiplier > 20 || multiplier < 4) { return Err(Error::Bounds); } - let frequency = prescaler as f64 * self.reference_clock_frequency as f64; + let frequency = multiplier as f64 * self.reference_clock_frequency as f64; if frequency > 500_000_000.0f64 { return Err(Error::Frequency); } @@ -186,17 +194,28 @@ where // TODO: Update / disable any enabled channels? let mut fr1: [u8; 3] = [0, 0, 0]; self.interface.read(Register::FR1 as u8, &mut fr1)?; - fr1[0].set_bits(2..=6, prescaler); + fr1[0].set_bits(2..=6, multiplier); let vco_range = frequency > 255e6; fr1[0].set_bit(7, vco_range); self.interface.write(Register::FR1 as u8, &fr1)?; - self.system_clock_multiplier = prescaler; + self.system_clock_multiplier = multiplier; Ok(self.system_clock_frequency()) } + pub fn get_reference_clock_frequency(&self) -> f32 { + self.reference_clock_frequency + } + + pub fn get_reference_clock_multiplier(&mut self) -> Result> { + let mut fr1: [u8; 3] = [0, 0, 0]; + self.interface.read(Register::FR1 as u8, &mut fr1)?; + + Ok(fr1[0].get_bits(2..=6) as u8) + } + /// Perform a self-test of the communication interface. /// /// Note: @@ -262,6 +281,13 @@ where Ok(()) } + pub fn is_enabled(&mut self, channel: Channel) -> Result> { + let mut csr: [u8; 1] = [0; 1]; + self.interface.read(Register::CSR as u8, &mut csr)?; + + Ok(csr[0].get_bit(channel as usize + 4)) + } + fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> { let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; @@ -282,22 +308,48 @@ where Ok(()) } + fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error> { + let mut csr: [u8; 1] = [0]; + self.interface.read(Register::CSR as u8, &mut csr)?; + + let mut new_csr = csr; + new_csr[0].set_bits(4..8, 0); + new_csr[0].set_bit(4 + channel as usize, true); + + self.interface.write(Register::CSR as u8, &new_csr)?; + + self.interface.read(register as u8, &mut data)?; + + // Restore the previous CSR. Note that the re-enable of the channel happens immediately, so + // the CSR update does not need to be latched. + self.interface.write(Register::CSR as u8, &csr)?; + + Ok(()) + } + /// Configure the phase of a specified channel. /// /// Arguments: /// * `channel` - The channel to configure the frequency of. - /// * `phase_turns` - The desired phase offset in normalized turns. + /// * `phase_turns` - The desired phase offset in turns. /// /// Returns: - /// The actual programmed phase offset of the channel in degrees. + /// The actual programmed phase offset of the channel in turns. pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result> { - if phase_turns > 1.0 || phase_turns < 0.0 { - return Err(Error::Bounds); - } + let phase_offset: u16 = (phase_turns * (1 << 14) as f32) as u16 & 0x3FFFu16; - let phase_offset: u16 = (phase_turns * 1u32.wrapping_shl(14) as f32) as u16; self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?; - Ok((phase_offset as f32 / 1u32.wrapping_shl(14) as f32) * 360.0) + + Ok((phase_offset as f32) / ((1 << 14) as f32)) + } + + pub fn get_phase(&mut self, channel: Channel) -> Result> { + let mut phase_offset: [u8; 2] = [0; 2]; + self.read_channel(channel, Register::CPOW0, &mut phase_offset)?; + + let phase_offset = u16::from_be_bytes(phase_offset) & 0x3FFFu16; + + Ok((phase_offset as f32) / ((1 << 14) as f32)) } /// Configure the amplitude of a specified channel. @@ -313,17 +365,38 @@ where return Err(Error::Bounds); } - let amplitude_control: u16 = (amplitude / 1u16.wrapping_shl(10) as f32) as u16; - let mut acr: [u8; 3] = [0, amplitude_control.to_be_bytes()[0], amplitude_control.to_be_bytes()[1]]; + let amplitude_control: u16 = (amplitude * (1 << 10) as f32) as u16; + + let mut acr: [u8; 3] = [0; 3]; // Enable the amplitude multiplier for the channel if required. The amplitude control has // full-scale at 0x3FF (amplitude of 1), so the multiplier should be disabled whenever // full-scale is used. - acr[1].set_bit(4, amplitude_control >= 1u16.wrapping_shl(10)); + if amplitude_control < (1 << 10) { + + let masked_control = amplitude_control & 0x3FF; + acr[1] = masked_control.to_be_bytes()[0]; + acr[2] = masked_control.to_be_bytes()[1]; + + // Enable the amplitude multiplier + acr[1].set_bit(4, true); + } self.modify_channel(channel, Register::ACR, &acr)?; - Ok(amplitude_control as f32 / 1_u16.wrapping_shl(10) as f32) + Ok(amplitude_control as f32 / (1 << 10) as f32) + } + + pub fn get_amplitude(&mut self, channel: Channel) -> Result> { + let mut acr: [u8; 3] = [0; 3]; + self.read_channel(channel, Register::ACR, &mut acr)?; + + if acr[1].get_bit(4) { + let amplitude_control: u16 = (((acr[1] as u16) << 8) | (acr[2] as u16)) & 0x3FF; + Ok(amplitude_control as f32 / (1 << 10) as f32) + } else { + Ok(1.0) + } } /// Configure the frequency of a specified channel. @@ -347,4 +420,14 @@ where self.modify_channel(channel, Register::CFTW0, &tuning_word.to_be_bytes())?; Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency()) } + + pub fn get_frequency(&mut self, channel: Channel) -> Result> { + // Read the frequency tuning word for the channel. + let mut tuning_word: [u8; 4] = [0; 4]; + self.read_channel(channel, Register::CFTW0, &mut tuning_word)?; + let tuning_word = u32::from_be_bytes(tuning_word); + + // Convert the tuning word into a frequency. + Ok(tuning_word as f64 * self.system_clock_frequency() / ((1u64 << 32)) as f64) + } } diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 8e0b07c..a514c05 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -113,8 +113,8 @@ type AFE2 = afe::ProgrammableGainAmplifier< macro_rules! route_request { ($request:ident, - readable_attributes: [$(($read_attribute:tt, $getter:tt)),*], - modifiable_attributes: [$(($write_attribute:tt, $TYPE:ty, $setter:tt)),*]) => { + readable_attributes: [$($read_attribute:tt: $getter:tt),*], + modifiable_attributes: [$($write_attribute:tt: $TYPE:ty, $setter:tt),*]) => { match $request.req { server::AccessRequest::Read => { match $request.attribute { @@ -126,17 +126,13 @@ macro_rules! route_request { "Failed to set attribute"), }; - let encoded_data: String = match serde_json_core::to_string(&value) { + let encoded_data: String = match serde_json_core::to_string(&value) { Ok(data) => data, Err(_) => return server::Response::error($request.attribute, "Failed to encode attribute value"), }; - // Encoding data into a string surrounds it with qutotations. Because this - // value is then serialzed into another string, we remove the double - // quotations because they cannot be properly escaped. - server::Response::success($request.attribute, - &encoded_data[1..encoded_data.len()-1]) + server::Response::success($request.attribute, &encoded_data) }, )* _ => server::Response::error($request.attribute, "Unknown attribute") @@ -146,22 +142,14 @@ macro_rules! route_request { match $request.attribute { $( $write_attribute => { - // To avoid sending double quotations in the request, they are eliminated on - // the sender side. However, to properly deserialize the data, quotes need - // to be added back. - let mut value: String = String::new(); - value.push('"').unwrap(); - value.push_str($request.value).unwrap(); - value.push('"').unwrap(); - - let new_value = match serde_json_core::from_str::<$TYPE>(value.as_str()) { + let new_value = match serde_json_core::from_str::<$TYPE>(&$request.value) { Ok(data) => data, Err(_) => return server::Response::error($request.attribute, "Failed to decode value"), }; match $setter(new_value) { - Ok(_) => server::Response::success($request.attribute, $request.value), + Ok(_) => server::Response::success($request.attribute, &$request.value), Err(_) => server::Response::error($request.attribute, "Failed to set attribute"), } @@ -344,6 +332,17 @@ const APP: () = { spi }; + let mut fp_led_0 = gpiod.pd5.into_push_pull_output(); + let mut fp_led_1 = gpiod.pd6.into_push_pull_output(); + let mut fp_led_2 = gpiog.pg4.into_push_pull_output(); + let mut fp_led_3 = gpiod.pd12.into_push_pull_output(); + + fp_led_0.set_low().unwrap(); + fp_led_1.set_low().unwrap(); + fp_led_2.set_low().unwrap(); + fp_led_3.set_low().unwrap(); + + let pounder_devices = { let ad9959 = { let qspi_interface = { @@ -375,7 +374,7 @@ const APP: () = { io_update, asm_delay, ad9959::Mode::FourBitSerial, - 100_000_000, + 100_000_000f32, 5).unwrap() }; @@ -426,16 +425,6 @@ const APP: () = { adc2_in_p).unwrap() }; - let mut fp_led_0 = gpiod.pd5.into_push_pull_output(); - let mut fp_led_1 = gpiod.pd6.into_push_pull_output(); - let mut fp_led_2 = gpiog.pg4.into_push_pull_output(); - let mut fp_led_3 = gpiod.pd12.into_push_pull_output(); - - fp_led_0.set_low().unwrap(); - fp_led_1.set_low().unwrap(); - fp_led_2.set_low().unwrap(); - fp_led_3.set_low().unwrap(); - let mut eeprom_i2c = { let sda = gpiof.pf0.into_alternate_af4().set_open_drain(); let scl = gpiof.pf1.into_alternate_af4().set_open_drain(); @@ -517,7 +506,7 @@ const APP: () = { hal::dma::DMAREQ_ID::TIM2_CH2); // Configure timer 2 to trigger conversions for the ADC - let mut timer2 = dp.TIM2.timer(500.khz(), &mut clocks); + let mut timer2 = dp.TIM2.timer(50.khz(), &mut clocks); timer2.configure_channel(hal::timer::Channel::One, 0.25); timer2.configure_channel(hal::timer::Channel::Two, 0.75); @@ -572,7 +561,7 @@ const APP: () = { c.resources.dac1.send(output).unwrap(); } - #[idle(resources=[net_interface, mac_addr, eth_mac, iir_state, iir_ch, afe1, afe2])] + #[idle(resources=[net_interface, pounder, mac_addr, eth_mac, iir_state, iir_ch, afe1, afe2])] fn idle(mut c: idle::Context) -> ! { let mut socket_set_entries: [_; 8] = Default::default(); @@ -617,7 +606,7 @@ const APP: () = { info!("Got request: {:?}", req); route_request!(req, readable_attributes: [ - ("stabilizer/iir/state", (|| { + "stabilizer/iir/state": (|| { let state = c.resources.iir_state.lock(|iir_state| server::Status { t: time, @@ -628,13 +617,32 @@ const APP: () = { }); Ok::(state) - })), - ("stabilizer/afe1/gain", (|| c.resources.afe1.get_gain())), - ("stabilizer/afe2/gain", (|| c.resources.afe2.get_gain())) + }), + "stabilizer/afe1/gain": (|| c.resources.afe1.get_gain()), + "stabilizer/afe2/gain": (|| c.resources.afe2.get_gain()), + "pounder/in0": (|| { + c.resources.pounder.get_input_channel_state( + pounder::Channel::In0) + }), + "pounder/in1": (|| { + c.resources.pounder.get_input_channel_state( + pounder::Channel::In1) + }), + "pounder/out0": (|| { + c.resources.pounder.get_output_channel_state( + pounder::Channel::Out0) + }), + "pounder/out1": (|| { + c.resources.pounder.get_output_channel_state( + pounder::Channel::Out1) + }), + "pounder/dds/clock": (|| { + c.resources.pounder.get_dds_clock_config() + }) ], modifiable_attributes: [ - ("stabilizer/iir1/state", server::IirRequest, (|req: server::IirRequest| { + "stabilizer/iir1/state": server::IirRequest, (|req: server::IirRequest| { c.resources.iir_ch.lock(|iir_ch| { if req.channel > 1 { return Err(()); @@ -644,8 +652,8 @@ const APP: () = { Ok::(req) }) - })), - ("stabilizer/iir2/state", server::IirRequest, (|req: server::IirRequest| { + }), + "stabilizer/iir2/state": server::IirRequest, (|req: server::IirRequest| { c.resources.iir_ch.lock(|iir_ch| { if req.channel > 1 { return Err(()); @@ -655,13 +663,32 @@ const APP: () = { Ok::(req) }) - })), - ("stabilizer/afe1/gain", afe::Gain, (|gain| { + }), + "pounder/in0": pounder::ChannelState, (|state| { + c.resources.pounder.set_channel_state(pounder::Channel::In0, + state) + }), + "pounder/in1": pounder::ChannelState, (|state| { + c.resources.pounder.set_channel_state(pounder::Channel::In1, + state) + }), + "pounder/out0": pounder::ChannelState, (|state| { + c.resources.pounder.set_channel_state(pounder::Channel::Out0, + state) + }), + "pounder/out1": pounder::ChannelState, (|state| { + c.resources.pounder.set_channel_state(pounder::Channel::Out1, + state) + }), + "pounder/dds/clock": pounder::DdsClockConfig, (|config| { + c.resources.pounder.configure_dds_clock(config) + }), + "stabilizer/afe1/gain": afe::Gain, (|gain| { Ok::<(), ()>(c.resources.afe1.set_gain(gain)) - })), - ("stabilizer/afe2/gain", afe::Gain, (|gain| { + }), + "stabilizer/afe2/gain": afe::Gain, (|gain| { Ok::<(), ()>(c.resources.afe2.set_gain(gain)) - })) + }) ] ) }); diff --git a/stabilizer/src/pounder/attenuators.rs b/stabilizer/src/pounder/attenuators.rs index d9785f8..5e1dc8f 100644 --- a/stabilizer/src/pounder/attenuators.rs +++ b/stabilizer/src/pounder/attenuators.rs @@ -1,8 +1,7 @@ -use super::error::Error; -use super::DdsChannel; +use super::{Channel, Error}; pub trait AttenuatorInterface { - fn modify(&mut self, attenuation: f32, channel: DdsChannel) -> Result { + fn set_attenuation(&mut self, channel: Channel, attenuation: f32) -> Result { if attenuation > 31.5 || attenuation < 0.0 { return Err(Error::Bounds); } @@ -14,28 +13,28 @@ pub trait AttenuatorInterface { // Read all the channels, modify the channel of interest, and write all the channels back. // This ensures the staging register and the output register are always in sync. let mut channels = [0_u8; 4]; - self.read_all(&mut channels)?; + self.read_all_attenuators(&mut channels)?; // The lowest 2 bits of the 8-bit shift register on the attenuator are ignored. Shift the // attenuator code into the upper 6 bits of the register value. Note that the attenuator // treats inputs as active-low, so the code is inverted before writing. - channels[channel as usize] = !attenuation_code.wrapping_shl(2); - self.write_all(&channels)?; + channels[channel as usize] = (!attenuation_code) << 2; + self.write_all_attenuators(&channels)?; // Finally, latch the output of the updated channel to force it into an active state. - self.latch(channel)?; + self.latch_attenuators(channel)?; Ok(attenuation_code as f32 / 2.0) } - fn read(&mut self, channel: DdsChannel) -> Result { + fn get_attenuation(&mut self, channel: Channel) -> Result { let mut channels = [0_u8; 4]; // Reading the data always shifts data out of the staging registers, so we perform a // duplicate write-back to ensure the staging register is always equal to the output // register. - self.read_all(&mut channels)?; - self.write_all(&channels)?; + self.read_all_attenuators(&mut channels)?; + self.write_all_attenuators(&channels)?; // The attenuation code is stored in the upper 6 bits of the register, where each LSB // represents 0.5 dB. The attenuator stores the code as active-low, so inverting the result @@ -43,15 +42,15 @@ pub trait AttenuatorInterface { // dont-care bits) into an active-high state and then masking off the don't care bits. If // the shift occurs before the inversion, the upper 2 bits (which would then be don't // care) would contain erroneous data. - let attenuation_code = (!channels[channel as usize]).wrapping_shr(2); + let attenuation_code = (!channels[channel as usize]) >> 2; // Convert the desired channel code into dB of attenuation. Ok(attenuation_code as f32 / 2.0) } - fn reset(&mut self) -> Result<(), Error>; + fn reset_attenuators(&mut self) -> Result<(), Error>; - fn latch(&mut self, channel: DdsChannel) -> Result<(), Error>; - fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>; - fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error>; + fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error>; + fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error>; + fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error>; } diff --git a/stabilizer/src/pounder/error.rs b/stabilizer/src/pounder/error.rs deleted file mode 100644 index b3cc596..0000000 --- a/stabilizer/src/pounder/error.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[derive(Debug)] -pub enum Error { - Spi, - I2c, - DDS, - Qspi, - Bounds, - InvalidAddress, - InvalidChannel, - Adc, -} diff --git a/stabilizer/src/pounder/mod.rs b/stabilizer/src/pounder/mod.rs index d29e3bd..cef9f81 100644 --- a/stabilizer/src/pounder/mod.rs +++ b/stabilizer/src/pounder/mod.rs @@ -1,16 +1,13 @@ use mcp23017; use ad9959; -pub mod error; -pub mod attenuators; +use serde::{Serialize, Deserialize}; +mod attenuators; mod rf_power; -pub mod types; use super::hal; -use error::Error; use attenuators::AttenuatorInterface; -use types::{DdsChannel, InputChannel}; use rf_power::PowerMeasurementInterface; use embedded_hal::{ @@ -18,17 +15,79 @@ use embedded_hal::{ adc::OneShot }; +const EXT_CLK_SEL_PIN: u8 = 8 + 7; #[allow(dead_code)] -const OSC_EN_N_PIN: u8 = 8 + 7; - -const EXT_CLK_SEL_PIN: u8 = 8 + 6; - +const OSC_EN_N_PIN: u8 = 8 + 6; const ATT_RST_N_PIN: u8 = 8 + 5; - -const ATT_LE0_PIN: u8 = 8 + 0; -const ATT_LE1_PIN: u8 = 8 + 1; -const ATT_LE2_PIN: u8 = 8 + 2; const ATT_LE3_PIN: u8 = 8 + 3; +const ATT_LE2_PIN: u8 = 8 + 2; +const ATT_LE1_PIN: u8 = 8 + 1; +const ATT_LE0_PIN: u8 = 8 + 0; + +#[derive(Debug, Copy, Clone)] +pub enum Error { + Spi, + I2c, + Dds, + Qspi, + Bounds, + InvalidAddress, + InvalidChannel, + Adc, +} + +#[derive(Debug, Copy, Clone)] +pub enum Channel { + In0, + In1, + Out0, + Out1, +} + +#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +pub struct DdsChannelState { + pub phase_offset: f32, + pub frequency: f64, + pub amplitude: f32, + pub enabled: bool, +} + +#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +pub struct ChannelState { + pub parameters: DdsChannelState, + pub attenuation: f32, +} + +#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +pub struct InputChannelState { + pub attenuation: f32, + pub power: f32, + pub mixer: DdsChannelState, +} + +#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +pub struct OutputChannelState { + pub attenuation: f32, + pub channel: DdsChannelState, +} + +#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +pub struct DdsClockConfig { + pub multiplier: u8, + pub reference_clock: f32, + pub external_clock: bool, +} + +impl Into for Channel { + fn into(self) -> ad9959::Channel { + match self { + Channel::In0 => ad9959::Channel::Two, + Channel::In1 => ad9959::Channel::Four, + Channel::Out0 => ad9959::Channel::One, + Channel::Out1 => ad9959::Channel::Three, + } + } +} pub struct QspiInterface { pub qspi: hal::qspi::Qspi, @@ -78,32 +137,40 @@ impl ad9959::Interface for QspiInterface { // Encode the address into the first 4 bytes. for address_bit in 0..8 { let offset: u8 = { - if address_bit % 2 == 0 { + if address_bit % 2 != 0 { 4 } else { 0 } }; - if addr & address_bit != 0 { - encoded_data[(address_bit >> 1) as usize] |= 1 << offset; + // Encode MSB first. Least significant bits are placed at the most significant + // byte. + let byte_position = 3 - (address_bit >> 1) as usize; + + if addr & (1 << address_bit) != 0 { + encoded_data[byte_position] |= 1 << offset; } } // Encode the data into the remaining bytes. for byte_index in 0..data.len() { let byte = data[byte_index]; - for address_bit in 0..8 { + for bit in 0..8 { let offset: u8 = { - if address_bit % 2 == 0 { + if bit % 2 != 0 { 4 } else { 0 } }; - if byte & address_bit != 0 { - encoded_data[(byte_index + 1) * 4 + (address_bit >> 1) as usize] |= 1 << offset; + // Encode MSB first. Least significant bits are placed at the most + // significant byte. + let byte_position = 3 - (bit >> 1) as usize; + + if byte & (1 << bit) != 0 { + encoded_data[(byte_index + 1) * 4 + byte_position] |= 1 << offset; } } } @@ -129,9 +196,8 @@ impl ad9959::Interface for QspiInterface { return Err(Error::InvalidAddress); } - // It is not possible to read data from the AD9959 in single bit two wire mode because the - // QSPI interface assumes that data is always received on IO1. - if self.mode == ad9959::Mode::SingleBitTwoWire { + // This implementation only supports operation (read) in four-bit-serial mode. + if self.mode != ad9959::Mode::FourBitSerial { return Err(Error::Qspi); } @@ -178,27 +244,100 @@ where // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator // selected, attenuators out of reset. - devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0xF).map_err(|_| Error::I2c)?; - devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, - 1_u8.wrapping_shl(5)).map_err(|_| Error::I2c)?; + devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0x3F).map_err(|_| Error::I2c)?; + devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, 1 << 5).map_err(|_| Error::I2c)?; devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?; // Select the on-board clock with a 5x prescaler (500MHz). - devices.select_onboard_clock(5u8)?; + devices.select_onboard_clock(4u8)?; Ok(devices) } - pub fn select_external_clock(&mut self, frequency: u32, prescaler: u8) -> Result<(), Error>{ + fn select_external_clock(&mut self, frequency: f32, prescaler: u8) -> Result<(), Error>{ self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?; - self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::DDS)?; + self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::Dds)?; Ok(()) } - pub fn select_onboard_clock(&mut self, prescaler: u8) -> Result<(), Error> { + fn select_onboard_clock(&mut self, prescaler: u8) -> Result<(), Error> { self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?; - self.ad9959.configure_system_clock(100_000_000, prescaler).map_err(|_| Error::DDS)?; + self.ad9959.configure_system_clock(100_000_000f32, prescaler).map_err(|_| Error::Dds)?; + + Ok(()) + } + + pub fn configure_dds_clock(&mut self, config: DdsClockConfig) -> Result<(), Error> { + if config.external_clock { + self.select_external_clock(config.reference_clock, config.multiplier) + } else { + self.select_onboard_clock(config.multiplier) + } + } + + pub fn get_dds_clock_config(&mut self) -> Result { + let external_clock = self.mcp23017.digital_read(EXT_CLK_SEL_PIN).map_err(|_| Error::I2c)?; + let multiplier = self.ad9959.get_reference_clock_multiplier().map_err(|_| Error::Dds)?; + let reference_clock = self.ad9959.get_reference_clock_frequency(); + + Ok(DdsClockConfig{multiplier, reference_clock, external_clock}) + } + + pub fn get_input_channel_state(&mut self, channel: Channel) -> Result { + match channel { + Channel::In0 | Channel::In1 => { + let channel_state = self.get_dds_channel_state(channel)?; + + let attenuation = self.get_attenuation(channel)?; + let power = self.measure_power(channel)?; + + Ok(InputChannelState { + attenuation: attenuation, + power: power, + mixer: channel_state + }) + } + _ => Err(Error::InvalidChannel), + } + } + + fn get_dds_channel_state(&mut self, channel: Channel) -> Result { + let frequency = self.ad9959.get_frequency(channel.into()).map_err(|_| Error::Dds)?; + let phase_offset = self.ad9959.get_phase(channel.into()).map_err(|_| Error::Dds)?; + let amplitude = self.ad9959.get_amplitude(channel.into()).map_err(|_| Error::Dds)?; + let enabled = self.ad9959.is_enabled(channel.into()).map_err(|_| Error::Dds)?; + + Ok(DdsChannelState {phase_offset, frequency, amplitude, enabled}) + } + + pub fn get_output_channel_state(&mut self, channel: Channel) -> Result { + match channel { + Channel::Out0 | Channel::Out1 => { + let channel_state = self.get_dds_channel_state(channel)?; + let attenuation = self.get_attenuation(channel)?; + + Ok(OutputChannelState { + attenuation: attenuation, + channel: channel_state, + }) + } + _ => Err(Error::InvalidChannel), + } + } + + pub fn set_channel_state(&mut self, channel: Channel, state: ChannelState) -> Result<(), Error> { + self.ad9959.set_frequency(channel.into(), state.parameters.frequency).map_err(|_| Error::Dds)?; + self.ad9959.set_phase(channel.into(), state.parameters.phase_offset).map_err(|_| Error::Dds)?; + self.ad9959.set_amplitude(channel.into(), state.parameters.amplitude).map_err(|_| Error::Dds)?; + + if state.parameters.enabled { + self.ad9959.enable_channel(channel.into()).map_err(|_| Error::Dds)?; + } else { + self.ad9959.disable_channel(channel.into()).map_err(|_| Error::Dds)?; + } + + self.set_attenuation(channel, state.attenuation)?; Ok(()) } @@ -206,21 +345,21 @@ where impl AttenuatorInterface for PounderDevices { - fn reset(&mut self) -> Result<(), Error> { - self.mcp23017.digital_write(ATT_RST_N_PIN, true).map_err(|_| Error::I2c)?; + fn reset_attenuators(&mut self) -> Result<(), Error> { + self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?; // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is // sufficient. Document the delay here. - self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?; + self.mcp23017.digital_write(ATT_RST_N_PIN, true).map_err(|_| Error::I2c)?; Ok(()) } - fn latch(&mut self, channel: DdsChannel) -> Result<(), Error> { + fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> { let pin = match channel { - DdsChannel::Zero => ATT_LE1_PIN, - DdsChannel::One => ATT_LE0_PIN, - DdsChannel::Two => ATT_LE3_PIN, - DdsChannel::Three => ATT_LE2_PIN, + Channel::In0 => ATT_LE0_PIN, + Channel::In1 => ATT_LE2_PIN, + Channel::Out0 => ATT_LE1_PIN, + Channel::Out1 => ATT_LE3_PIN, }; self.mcp23017.digital_write(pin, true).map_err(|_| Error::I2c)?; @@ -231,13 +370,13 @@ impl AttenuatorInterface for PounderDevices Ok(()) } - fn read_all(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> { + fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> { self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?; Ok(()) } - fn write_all(&mut self, channels: &[u8; 4]) -> Result<(), Error> { + fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error> { let mut result = [0_u8; 4]; result.clone_from_slice(channels); self.attenuator_spi.transfer(&mut result).map_err(|_| Error::Spi)?; @@ -247,16 +386,17 @@ impl AttenuatorInterface for PounderDevices } impl PowerMeasurementInterface for PounderDevices { - fn sample_converter(&mut self, channel: InputChannel) -> Result { + fn sample_converter(&mut self, channel: Channel) -> Result { let adc_scale = match channel { - InputChannel::Zero => { + Channel::In0 => { let adc_reading: u32 = self.adc1.read(&mut self.adc1_in_p).map_err(|_| Error::Adc)?; adc_reading as f32 / self.adc1.max_sample() as f32 }, - InputChannel::One => { + Channel::In1 => { let adc_reading: u32 = self.adc2.read(&mut self.adc2_in_p).map_err(|_| Error::Adc)?; adc_reading as f32 / self.adc2.max_sample() as f32 }, + _ => return Err(Error::InvalidChannel), }; // Convert analog percentage to voltage. diff --git a/stabilizer/src/pounder/rf_power.rs b/stabilizer/src/pounder/rf_power.rs index dd7a3ec..31e6bd4 100644 --- a/stabilizer/src/pounder/rf_power.rs +++ b/stabilizer/src/pounder/rf_power.rs @@ -1,10 +1,9 @@ -use super::Error; -use super::InputChannel; +use super::{Error, Channel}; pub trait PowerMeasurementInterface { - fn sample_converter(&mut self, channel: InputChannel) -> Result; + fn sample_converter(&mut self, channel: Channel) -> Result; - fn measure_power(&mut self, channel: InputChannel) -> Result { + fn measure_power(&mut self, channel: Channel) -> Result { let analog_measurement = self.sample_converter(channel)?; // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at diff --git a/stabilizer/src/pounder/types.rs b/stabilizer/src/pounder/types.rs deleted file mode 100644 index 9938bfe..0000000 --- a/stabilizer/src/pounder/types.rs +++ /dev/null @@ -1,16 +0,0 @@ - -#[allow(dead_code)] -#[derive(Debug, Copy, Clone)] -pub enum DdsChannel { - Zero, - One, - Two, - Three, -} - -#[allow(dead_code)] -#[derive(Debug, Copy, Clone)] -pub enum InputChannel { - Zero, - One, -} diff --git a/stabilizer/src/server.rs b/stabilizer/src/server.rs index adf2fdc..1a21dfb 100644 --- a/stabilizer/src/server.rs +++ b/stabilizer/src/server.rs @@ -27,10 +27,10 @@ pub enum AccessRequest { } #[derive(Deserialize, Serialize, Debug)] -pub struct Request<'a, 'b> { +pub struct Request<'a> { pub req: AccessRequest, pub attribute: &'a str, - pub value: &'b str, + pub value: String, } #[derive(Serialize, Deserialize)] @@ -42,24 +42,74 @@ pub struct IirRequest { #[derive(Serialize)] pub struct Response { code: i32, - attribute: String, - value: String, + attribute: String, + value: String, +} + +impl<'a> Request<'a> { + pub fn restore_value(&mut self) { + let mut new_value: String = String::new(); + for byte in self.value.as_str().chars() { + if byte == '\'' { + new_value.push('"').unwrap(); + } else { + new_value.push(byte).unwrap(); + } + } + + self.value = new_value; + } } impl Response { + + fn sanitize_value(&mut self) { + let mut new_value: String = String::new(); + for byte in self.value.as_str().chars() { + if byte == '"' { + new_value.push('\'').unwrap(); + } else { + new_value.push(byte).unwrap(); + } + } + + self.value = new_value; + } + + fn wrap_and_sanitize_value(&mut self) { + let mut new_value: String = String::new(); + new_value.push('\'').unwrap(); + for byte in self.value.as_str().chars() { + if byte == '"' { + new_value.push('\'').unwrap(); + } else { + new_value.push(byte).unwrap(); + } + } + new_value.push('\'').unwrap(); + + self.value = new_value; + } + pub fn success<'a, 'b>(attribute: &'a str, value: &'b str) -> Self { - Self { code: 200, attribute: String::from(attribute), value: String::from(value)} + let mut res = Self { code: 200, attribute: String::from(attribute), value: String::from(value)}; + res.sanitize_value(); + res } pub fn error<'a, 'b>(attribute: &'a str, message: &'b str) -> Self { - Self { code: 400, attribute: String::from(attribute), value: String::from(message)} + let mut res = Self { code: 400, attribute: String::from(attribute), value: String::from(message)}; + res.wrap_and_sanitize_value(); + res } pub fn custom<'a>(code: i32, message : &'a str) -> Self { - Self { code: code, attribute: String::from(""), value: String::from(message)} + let mut res = Self { code: code, attribute: String::from(""), value: String::from(message)}; + res.wrap_and_sanitize_value(); + res } } @@ -73,7 +123,7 @@ pub struct Status { } pub fn json_reply(socket: &mut net::socket::TcpSocket, msg: &T) { - let mut u: String = to_string(msg).unwrap(); + let mut u: String = to_string(msg).unwrap(); u.push('\n').unwrap(); socket.write_str(&u).unwrap(); } @@ -122,7 +172,8 @@ impl Server { } else { let r = from_slice::(&self.data[..self.data.len() - 1]); match r { - Ok(res) => { + Ok(mut res) => { + res.restore_value(); let response = f(&res); json_reply(socket, &response); }, From 06e82ddd4a5065794a7d272cbbb58e5214482954 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 10:37:50 +0200 Subject: [PATCH 30/40] Fixing attenuator configuration --- stabilizer/src/main.rs | 6 +++--- stabilizer/src/pounder/mod.rs | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index a514c05..2e76070 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -386,9 +386,9 @@ const APP: () = { }; let spi = { - let spi_mosi = gpiod.pd7.into_alternate_af5(); - let spi_miso = gpioa.pa6.into_alternate_af5(); - let spi_sck = gpiog.pg11.into_alternate_af5(); + let spi_mosi = gpiod.pd7.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); + let spi_miso = gpioa.pa6.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); + let spi_sck = gpiog.pg11.into_alternate_af5().set_speed(hal::gpio::Speed::VeryHigh); let config = hal::spi::Config::new(hal::spi::Mode{ polarity: hal::spi::Polarity::IdleHigh, diff --git a/stabilizer/src/pounder/mod.rs b/stabilizer/src/pounder/mod.rs index cef9f81..5b9822f 100644 --- a/stabilizer/src/pounder/mod.rs +++ b/stabilizer/src/pounder/mod.rs @@ -2,6 +2,7 @@ use mcp23017; use ad9959; use serde::{Serialize, Deserialize}; + mod attenuators; mod rf_power; @@ -12,7 +13,7 @@ use rf_power::PowerMeasurementInterface; use embedded_hal::{ blocking::spi::Transfer, - adc::OneShot + adc::OneShot, }; const EXT_CLK_SEL_PIN: u8 = 8 + 7; @@ -243,12 +244,13 @@ where }; // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator - // selected, attenuators out of reset. + // selected, attenuators out of reset. Note that testing indicates the output state needs to + // be set first to properly update the output registers. + devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?; devices.mcp23017.write_gpio(mcp23017::Port::GPIOA, 0x3F).map_err(|_| Error::I2c)?; devices.mcp23017.write_gpio(mcp23017::Port::GPIOB, 1 << 5).map_err(|_| Error::I2c)?; - devices.mcp23017.all_pin_mode(mcp23017::PinMode::OUTPUT).map_err(|_| Error::I2c)?; - // Select the on-board clock with a 5x prescaler (500MHz). + // Select the on-board clock with a 4x prescaler (400MHz). devices.select_onboard_clock(4u8)?; Ok(devices) From 2d20063848c452bfda0f3ca4dd6432efe530a24d Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 10:50:47 +0200 Subject: [PATCH 31/40] Adding python test script --- test.py | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 test.py diff --git a/test.py b/test.py new file mode 100644 index 0000000..a50856a --- /dev/null +++ b/test.py @@ -0,0 +1,111 @@ +#!/usr/bin/python3 +""" +Description: Test Stabilizer communication and DDS configuration. + +Author: Ryan Summers +""" + +import socket +import json + +HOST = '10.0.16.99' +PORT = 1235 + +def do_request(s, request): + """ Perform a request with the Stabilizer. + + Args: + s: The socket to the stabilizer. + request: The request to transmit. + + Returns: + The received response object. + """ + # Transform the value field. + request['value'] = json.dumps(request['value'], separators=[',', ':']).replace('"', "'") + data = (json.dumps(request, separators=[',', ':']) + '\n').encode('ascii') + s.send(data) + + response = b'' + while not response.endswith(b'\n'): + response += s.recv(1024) + + # Decode the value array + response = json.loads(response.decode('ascii')) + response['value'] = response['value'].replace("'", '"') + response['value'] = json.loads(response['value']) + + return response + + +def read_attribute(s, attribute_name): + """ Read an attribute on the Stabilizer device. + + Args: + s: The socket to the stabilizer. + attribute_name: The name of the endpoint to write to (the attribute name). + + Returns: + The value of the attribute. May be a string or a dictionary. + """ + request = { + "req": "Read", + "attribute": attribute_name, + "value": "", + } + + response = do_request(s, request) + + if 'code' not in response or response['code'] != 200: + raise Exception(f'Failed to read {attribute_name}: {response}') + + return response['value'] + + +def write_attribute(s, attribute_name, attribute_value): + """ Write an attribute on the Stabilizer device. + + Args: + s: The socket to the stabilizer. + attribute_name: The name of the endpoint to write to (the attribute name). + attribute_value: The value to write to the attribute. May be a string or a dictionary. + """ + request = { + "req": "Write", + "attribute": attribute_name, + "value": attribute_value, + } + + response = do_request(s, request) + + if 'code' not in response or response['code'] != 200: + raise Exception(f'Failed to write {attribute_name}: {response}') + + +def main(): + """ Main program entry point. """ + with socket.socket() as s: + + # Connect to the stabilizer. + s.connect((HOST, PORT)) + + # A sample configuration for an output channel. + channel_config = { + 'attenuation': 31.5, + 'parameters': { + 'phase_offset': 0.5, + 'frequency': 100.0e6, + 'amplitude': 0.2, + 'enabled': True, + } + } + + # Configure OUT0 and read it back. + write_attribute(s, "pounder/out0", channel_config) + print('Pounder OUT0: ', read_attribute(s, "pounder/out0")) + + print('Pounder IN1: ', read_attribute(s, "pounder/in1")) + print('Pounder OUT1: ', read_attribute(s, "pounder/out1")) + +if __name__ == '__main__': + main() From 818047775b3c4cfca596b34d322ac9942f07f3d6 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 11:17:12 +0200 Subject: [PATCH 32/40] Correcting ADC full-scale voltage --- stabilizer/src/pounder/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stabilizer/src/pounder/mod.rs b/stabilizer/src/pounder/mod.rs index 5b9822f..093cfd2 100644 --- a/stabilizer/src/pounder/mod.rs +++ b/stabilizer/src/pounder/mod.rs @@ -401,7 +401,8 @@ impl PowerMeasurementInterface for PounderDevices { _ => return Err(Error::InvalidChannel), }; - // Convert analog percentage to voltage. - Ok(adc_scale * 3.3) + // Convert analog percentage to voltage. Note that the ADC uses an external 2.048V analog + // reference. + Ok(adc_scale * 2.048) } } From c4979f08f6c7794a8e300b1e53f7b719f6d7e51d Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 11:51:52 +0200 Subject: [PATCH 33/40] Updating code comments --- ad9959/src/lib.rs | 56 ++++++++++++++++ stabilizer/src/afe.rs | 8 +++ stabilizer/src/pounder/attenuators.rs | 21 ++++++ stabilizer/src/pounder/mod.rs | 95 ++++++++++++++++++++++++++- stabilizer/src/pounder/rf_power.rs | 7 ++ stabilizer/src/server.rs | 34 ++++++++++ 6 files changed, 219 insertions(+), 2 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 75277e6..14f8ddd 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -24,6 +24,7 @@ pub struct Ad9959 { io_update: UPDATE, } +/// A trait that allows a HAL to provide a means of communicating with the AD9959. pub trait Interface { type Error; @@ -34,6 +35,8 @@ pub trait Interface { fn read(&mut self, addr: u8, dest: &mut [u8]) -> Result<(), Self::Error>; } +/// Indicates various communication modes of the DDS. The value of this enumeration is equivalent to +/// the configuration bits of the DDS CSR register. #[derive(Copy, Clone, PartialEq)] pub enum Mode { SingleBitTwoWire = 0b00, @@ -103,6 +106,17 @@ where UPDATE: OutputPin, { + /// Construct and initialize the DDS. + /// + /// Args: + /// * `interface` - An interface to the DDS. + /// * `reset_pin` - A pin connected to the DDS reset input. + /// * `io_update` - A pin connected to the DDS io_update input. + /// * `delay` - A delay implementation for blocking operation for specific amounts of time. + /// * `desired_mode` - The desired communication mode of the interface to the DDS. + /// * `clock_frequency` - The clock frequency of the reference clock input. + /// * `multiplier` - The desired clock multiplier for the system clock. This multiplies + /// `clock_frequency` to generate the system clock. pub fn new(interface: INTERFACE, reset_pin: &mut RST, io_update: UPDATE, @@ -158,6 +172,7 @@ where Ok(ad9959) } + /// Latch the DDS configuration to ensure it is active on the output channels. fn latch_configuration(&mut self) -> Result<(), Error> { self.io_update.set_high().or_else(|_| Err(Error::Pin))?; // The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one @@ -205,10 +220,12 @@ where Ok(self.system_clock_frequency()) } + /// Get the current reference clock frequency in Hz. pub fn get_reference_clock_frequency(&self) -> f32 { self.reference_clock_frequency } + /// Get the current reference clock multiplier. pub fn get_reference_clock_multiplier(&mut self) -> Result> { let mut fr1: [u8; 3] = [0, 0, 0]; self.interface.read(Register::FR1 as u8, &mut fr1)?; @@ -257,6 +274,7 @@ where Ok(true) } + /// Get the current system clock frequency in Hz. fn system_clock_frequency(&self) -> f64 { self.system_clock_multiplier as f64 * self.reference_clock_frequency as f64 } @@ -281,6 +299,7 @@ where Ok(()) } + /// Determine if an output channel is enabled. pub fn is_enabled(&mut self, channel: Channel) -> Result> { let mut csr: [u8; 1] = [0; 1]; self.interface.read(Register::CSR as u8, &mut csr)?; @@ -288,7 +307,15 @@ where Ok(csr[0].get_bit(channel as usize + 4)) } + /// Update an output channel configuration register. + /// + /// Args: + /// * `channel` - The channel to configure. + /// * `register` - The register to update. + /// * `data` - The contents to write to the provided register. fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> { + // Disable all other outputs so that we can update the configuration register of only the + // specified channel. let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; @@ -308,7 +335,15 @@ where Ok(()) } + /// Read a configuration register of a specific channel. + /// + /// Args: + /// * `channel` - The channel to read. + /// * `register` - The register to read. + /// * `data` - A location to store the read register contents. fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error> { + // Disable all other channels in the CSR so that we can read the configuration register of + // only the desired channel. let mut csr: [u8; 1] = [0]; self.interface.read(Register::CSR as u8, &mut csr)?; @@ -343,6 +378,13 @@ where Ok((phase_offset as f32) / ((1 << 14) as f32)) } + /// Get the current phase of a specified channel. + /// + /// Args: + /// * `channel` - The channel to get the phase of. + /// + /// Returns: + /// The phase of the channel in turns. pub fn get_phase(&mut self, channel: Channel) -> Result> { let mut phase_offset: [u8; 2] = [0; 2]; self.read_channel(channel, Register::CPOW0, &mut phase_offset)?; @@ -387,6 +429,13 @@ where Ok(amplitude_control as f32 / (1 << 10) as f32) } + /// Get the configured amplitude of a channel. + /// + /// Args: + /// * `channel` - The channel to get the amplitude of. + /// + /// Returns: + /// The normalized amplitude of the channel. pub fn get_amplitude(&mut self, channel: Channel) -> Result> { let mut acr: [u8; 3] = [0; 3]; self.read_channel(channel, Register::ACR, &mut acr)?; @@ -421,6 +470,13 @@ where Ok((tuning_word as f64 / 1u64.wrapping_shl(32) as f64) * self.system_clock_frequency()) } + /// Get the frequency of a channel. + /// + /// Arguments: + /// * `channel` - The channel to get the frequency of. + /// + /// Returns: + /// The frequency of the channel in Hz. pub fn get_frequency(&mut self, channel: Channel) -> Result> { // Read the frequency tuning word for the channel. let mut tuning_word: [u8; 4] = [0; 4]; diff --git a/stabilizer/src/afe.rs b/stabilizer/src/afe.rs index 868c475..62d9cb4 100644 --- a/stabilizer/src/afe.rs +++ b/stabilizer/src/afe.rs @@ -12,6 +12,7 @@ pub enum Gain { G10 = 0b11 } +/// A programmable gain amplifier that allows for setting the gain via GPIO. pub struct ProgrammableGainAmplifier { a0: A0, a1: A1 @@ -38,6 +39,11 @@ where A1: embedded_hal::digital::v2::StatefulOutputPin, A1::Error: core::fmt::Debug, { + /// Construct a new programmable gain driver. + /// + /// Args: + /// * `a0` - An output connected to the A0 input of the amplifier. + /// * `a1` - An output connected to the A1 input of the amplifier. pub fn new(a0: A0, a1: A1) -> Self { let mut afe = Self { a0: a0, a1: a1}; @@ -47,6 +53,7 @@ where afe } + /// Set the gain of the front-end. pub fn set_gain(&mut self, gain: Gain) { if (gain as u8 & 0b01) != 0 { self.a0.set_high().unwrap(); @@ -61,6 +68,7 @@ where } } + /// Get the programmed gain of the analog front-end. pub fn get_gain(&self) -> Result { let mut code: u8 = 0; if self.a0.is_set_high().unwrap() { diff --git a/stabilizer/src/pounder/attenuators.rs b/stabilizer/src/pounder/attenuators.rs index 5e1dc8f..2042f47 100644 --- a/stabilizer/src/pounder/attenuators.rs +++ b/stabilizer/src/pounder/attenuators.rs @@ -1,6 +1,20 @@ use super::{Channel, Error}; +/// Provide an interface for managing digital attenuators on Pounder hardware. +/// +/// Note: The digital attenuators do not allow read-back of attenuation. To circumvent this, this +/// driver maintains the attenuation code in both the shift register as well as the latched output +/// register of the attenuators. This allows the "active" attenuation code to be read back by +/// reading the shfit register. The downside of this approach is that any read is destructive, so a +/// read-writeback approach is employed. pub trait AttenuatorInterface { + + /// Set the attenuation of a single channel. + /// + /// Args: + /// * `channel` - The pounder channel to configure the attenuation of. + /// * `attenuation` - The desired attenuation of the channel in dB. This has a resolution of + /// 0.5dB. fn set_attenuation(&mut self, channel: Channel, attenuation: f32) -> Result { if attenuation > 31.5 || attenuation < 0.0 { return Err(Error::Bounds); @@ -27,6 +41,13 @@ pub trait AttenuatorInterface { Ok(attenuation_code as f32 / 2.0) } + /// Get the attenuation of a channel. + /// + /// Args: + /// * `channel` - The channel to get the attenuation of. + /// + /// Returns: + /// The programmed attenuation of the channel in dB. fn get_attenuation(&mut self, channel: Channel) -> Result { let mut channels = [0_u8; 4]; diff --git a/stabilizer/src/pounder/mod.rs b/stabilizer/src/pounder/mod.rs index 093cfd2..7be3304 100644 --- a/stabilizer/src/pounder/mod.rs +++ b/stabilizer/src/pounder/mod.rs @@ -80,6 +80,7 @@ pub struct DdsClockConfig { } impl Into for Channel { + /// Translate pounder channels to DDS output channels. fn into(self) -> ad9959::Channel { match self { Channel::In0 => ad9959::Channel::Two, @@ -90,13 +91,21 @@ impl Into for Channel { } } +/// A structure for the QSPI interface for the DDS. pub struct QspiInterface { pub qspi: hal::qspi::Qspi, mode: ad9959::Mode, } impl QspiInterface { + /// Initialize the QSPI interface. + /// + /// Args: + /// * `qspi` - The QSPI peripheral driver. pub fn new(mut qspi: hal::qspi::Qspi) -> Result { + // This driver only supports operation in 4-bit mode due to bus inconsistencies between the + // QSPI peripheral and the DDS. Instead, we will bit-bang communications in + // single-bit-two-wire to the DDS to configure it to 4-bit operation. qspi.configure_mode(hal::qspi::QspiMode::FourBit).map_err(|_| Error::Qspi)?; Ok(Self { qspi: qspi, mode: ad9959::Mode::SingleBitTwoWire }) } @@ -105,12 +114,21 @@ impl QspiInterface { impl ad9959::Interface for QspiInterface { type Error = Error; + /// Configure the operations mode of the interface. + /// + /// Args: + /// * `mode` - The newly desired operational mode. fn configure_mode(&mut self, mode: ad9959::Mode) -> Result<(), Error> { self.mode = mode; Ok(()) } + /// Write data over QSPI to the DDS. + /// + /// Args: + /// * `addr` - The address to write over QSPI to the DDS. + /// * `data` - The data to write. fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), Error> { if (addr & 0x80) != 0 { return Err(Error::InvalidAddress); @@ -206,6 +224,7 @@ impl ad9959::Interface for QspiInterface { } } +/// A structure containing implementation for Pounder hardware. pub struct PounderDevices { pub ad9959: ad9959::Ad9959 PounderDevices where DELAY: embedded_hal::blocking::delay::DelayMs, { + /// Construct and initialize pounder-specific hardware. + /// + /// Args: + /// * `ad9959` - The DDS driver for the pounder hardware. + /// * `attenuator_spi` - A SPI interface to control digital attenuators. + /// * `adc1` - The ADC1 peripheral for measuring power. + /// * `adc2` - The ADC2 peripheral for measuring power. + /// * `adc1_in_p` - The input channel for the RF power measurement on IN0. + /// * `adc2_in_p` - The input channel for the RF power measurement on IN1. pub fn new(mcp23017: mcp23017::MCP23017>, ad9959: ad9959::Ad9959 Result<(), Error>{ self.mcp23017.digital_write(EXT_CLK_SEL_PIN, true).map_err(|_| Error::I2c)?; self.ad9959.configure_system_clock(frequency, prescaler).map_err(|_| Error::Dds)?; @@ -263,13 +296,21 @@ where Ok(()) } - fn select_onboard_clock(&mut self, prescaler: u8) -> Result<(), Error> { + /// Select the onboard oscillator for the DDS reference clock source. + /// + /// Args: + /// * `multiplier` - The multiplier of the reference clock to use in the DDS. + fn select_onboard_clock(&mut self, multiplier: u8) -> Result<(), Error> { self.mcp23017.digital_write(EXT_CLK_SEL_PIN, false).map_err(|_| Error::I2c)?; - self.ad9959.configure_system_clock(100_000_000f32, prescaler).map_err(|_| Error::Dds)?; + self.ad9959.configure_system_clock(100_000_000f32, multiplier).map_err(|_| Error::Dds)?; Ok(()) } + /// Configure the Pounder DDS clock. + /// + /// Args: + /// * `config` - The configuration of the DDS clock desired. pub fn configure_dds_clock(&mut self, config: DdsClockConfig) -> Result<(), Error> { if config.external_clock { self.select_external_clock(config.reference_clock, config.multiplier) @@ -278,6 +319,10 @@ where } } + /// Get the pounder DDS clock configuration + /// + /// Returns: + /// The current pounder DDS clock configuration. pub fn get_dds_clock_config(&mut self) -> Result { let external_clock = self.mcp23017.digital_read(EXT_CLK_SEL_PIN).map_err(|_| Error::I2c)?; let multiplier = self.ad9959.get_reference_clock_multiplier().map_err(|_| Error::Dds)?; @@ -286,6 +331,13 @@ where Ok(DdsClockConfig{multiplier, reference_clock, external_clock}) } + /// Get the state of a Pounder input channel. + /// + /// Args: + /// * `channel` - The pounder channel to get the state of. Must be an input channel + /// + /// Returns: + /// The read-back channel input state. pub fn get_input_channel_state(&mut self, channel: Channel) -> Result { match channel { Channel::In0 | Channel::In1 => { @@ -304,6 +356,13 @@ where } } + /// Get the state of a DDS channel. + /// + /// Args: + /// * `channel` - The pounder channel to get the state of. + /// + /// Returns: + /// The read-back channel state. fn get_dds_channel_state(&mut self, channel: Channel) -> Result { let frequency = self.ad9959.get_frequency(channel.into()).map_err(|_| Error::Dds)?; let phase_offset = self.ad9959.get_phase(channel.into()).map_err(|_| Error::Dds)?; @@ -313,6 +372,13 @@ where Ok(DdsChannelState {phase_offset, frequency, amplitude, enabled}) } + /// Get the state of a DDS output channel. + /// + /// Args: + /// * `channel` - The pounder channel to get the output state of. Must be an output channel. + /// + /// Returns: + /// The read-back output channel state. pub fn get_output_channel_state(&mut self, channel: Channel) -> Result { match channel { Channel::Out0 | Channel::Out1 => { @@ -328,6 +394,11 @@ where } } + /// Configure a DDS channel. + /// + /// Args: + /// * `channel` - The pounder channel to configure. + /// * `state` - The state to configure the channel for. pub fn set_channel_state(&mut self, channel: Channel, state: ChannelState) -> Result<(), Error> { self.ad9959.set_frequency(channel.into(), state.parameters.frequency).map_err(|_| Error::Dds)?; self.ad9959.set_phase(channel.into(), state.parameters.phase_offset).map_err(|_| Error::Dds)?; @@ -347,6 +418,7 @@ where impl AttenuatorInterface for PounderDevices { + /// Reset all of the attenuators to a power-on default state. fn reset_attenuators(&mut self) -> Result<(), Error> { self.mcp23017.digital_write(ATT_RST_N_PIN, false).map_err(|_| Error::I2c)?; // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is @@ -356,6 +428,10 @@ impl AttenuatorInterface for PounderDevices Ok(()) } + /// Latch a configuration into a digital attenuator. + /// + /// Args: + /// * `channel` - The attenuator channel to latch. fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> { let pin = match channel { Channel::In0 => ATT_LE0_PIN, @@ -372,12 +448,20 @@ impl AttenuatorInterface for PounderDevices Ok(()) } + /// Read the raw attenuation codes stored in the attenuator shift registers. + /// + /// Args: + /// * `channels` - A slice to store the channel readings into. fn read_all_attenuators(&mut self, channels: &mut [u8; 4]) -> Result<(), Error> { self.attenuator_spi.transfer(channels).map_err(|_| Error::Spi)?; Ok(()) } + /// Write the attenuator shift registers. + /// + /// Args: + /// * `channels` - The data to write into the attenuators. fn write_all_attenuators(&mut self, channels: &[u8; 4]) -> Result<(), Error> { let mut result = [0_u8; 4]; result.clone_from_slice(channels); @@ -388,6 +472,13 @@ impl AttenuatorInterface for PounderDevices } impl PowerMeasurementInterface for PounderDevices { + /// Sample an ADC channel. + /// + /// Args: + /// * `channel` - The channel to sample. + /// + /// Returns: + /// The sampled voltage of the specified channel. fn sample_converter(&mut self, channel: Channel) -> Result { let adc_scale = match channel { Channel::In0 => { diff --git a/stabilizer/src/pounder/rf_power.rs b/stabilizer/src/pounder/rf_power.rs index 31e6bd4..ffe30f1 100644 --- a/stabilizer/src/pounder/rf_power.rs +++ b/stabilizer/src/pounder/rf_power.rs @@ -1,8 +1,15 @@ use super::{Error, Channel}; +/// Provide an interface to measure RF input power in dB. pub trait PowerMeasurementInterface { fn sample_converter(&mut self, channel: Channel) -> Result; + /// Measure the power of an inpu channel in dB. + /// + /// Note: This function assumes the input channel is connected to an AD8363 output. + /// + /// Args: + /// * `channel` - The pounder channel to measure the power of. fn measure_power(&mut self, channel: Channel) -> Result { let analog_measurement = self.sample_converter(channel)?; diff --git a/stabilizer/src/server.rs b/stabilizer/src/server.rs index 1a21dfb..7361592 100644 --- a/stabilizer/src/server.rs +++ b/stabilizer/src/server.rs @@ -63,6 +63,7 @@ impl<'a> Request<'a> { impl Response { + /// Remove all double quotation marks from the `value` field of a response. fn sanitize_value(&mut self) { let mut new_value: String = String::new(); for byte in self.value.as_str().chars() { @@ -76,6 +77,8 @@ impl Response { self.value = new_value; } + /// Remove all double quotation marks from the `value` field of a response and wrap it in single + /// quotes. fn wrap_and_sanitize_value(&mut self) { let mut new_value: String = String::new(); new_value.push('\'').unwrap(); @@ -91,6 +94,13 @@ impl Response { self.value = new_value; } + /// Construct a successful reply. + /// + /// Note: `value` will be sanitized to convert all single quotes to double quotes. + /// + /// Args: + /// * `attrbute` - The attribute of the success. + /// * `value` - The value of the attribute. pub fn success<'a, 'b>(attribute: &'a str, value: &'b str) -> Self { let mut res = Self { code: 200, attribute: String::from(attribute), value: String::from(value)}; @@ -98,6 +108,13 @@ impl Response { res } + /// Construct an error reply. + /// + /// Note: `message` will be sanitized to convert all single quotes to double quotes. + /// + /// Args: + /// * `attrbute` - The attribute of the success. + /// * `message` - The message denoting the error. pub fn error<'a, 'b>(attribute: &'a str, message: &'b str) -> Self { let mut res = Self { code: 400, attribute: String::from(attribute), value: String::from(message)}; @@ -105,6 +122,13 @@ impl Response { res } + /// Construct a custom reply. + /// + /// Note: `message` will be sanitized to convert all single quotes to double quotes. + /// + /// Args: + /// * `attrbute` - The attribute of the success. + /// * `message` - The message denoting the status. pub fn custom<'a>(code: i32, message : &'a str) -> Self { let mut res = Self { code: code, attribute: String::from(""), value: String::from(message)}; @@ -134,6 +158,7 @@ pub struct Server { } impl Server { + /// Construct a new server object for managing requests. pub fn new() -> Self { Self { data: Vec::new(), @@ -141,6 +166,11 @@ impl Server { } } + /// Poll the server for potential data updates. + /// + /// Args: + /// * `socket` - The socket to check contents from. + /// * `f` - A closure that can be called if a request has been received on the server. pub fn poll( &mut self, socket: &mut net::socket::TcpSocket, @@ -173,6 +203,10 @@ impl Server { let r = from_slice::(&self.data[..self.data.len() - 1]); match r { Ok(mut res) => { + // Note that serde_json_core doesn't escape quotations within a string. + // To account for this, we manually translate all single quotes to + // double quotes. This occurs because we doubly-serialize this field in + // some cases. res.restore_value(); let response = f(&res); json_reply(socket, &response); From 8283c94381c4f74b97c723fe827e88e93835bdf4 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 13:39:12 +0200 Subject: [PATCH 34/40] Correcting attenuators and RF power measurement: --- stabilizer/src/main.rs | 4 +++- stabilizer/src/pounder/rf_power.rs | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 2e76070..452e2da 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -396,7 +396,9 @@ const APP: () = { }) .frame_size(8); - dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 25.mhz(), &clocks) + // The maximum frequency of this SPI must be limited due to capacitance on the MISO + // line causing a long RC decay. + dp.SPI1.spi((spi_sck, spi_miso, spi_mosi), config, 5.mhz(), &clocks) }; let adc1 = { diff --git a/stabilizer/src/pounder/rf_power.rs b/stabilizer/src/pounder/rf_power.rs index ffe30f1..57a7f7d 100644 --- a/stabilizer/src/pounder/rf_power.rs +++ b/stabilizer/src/pounder/rf_power.rs @@ -4,17 +4,17 @@ use super::{Error, Channel}; pub trait PowerMeasurementInterface { fn sample_converter(&mut self, channel: Channel) -> Result; - /// Measure the power of an inpu channel in dB. + /// Measure the power of an input channel in dBm. /// /// Note: This function assumes the input channel is connected to an AD8363 output. /// /// Args: - /// * `channel` - The pounder channel to measure the power of. + /// * `channel` - The pounder channel to measure the power of in dBm. fn measure_power(&mut self, channel: Channel) -> Result { let analog_measurement = self.sample_converter(channel)?; // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at - // 100MHz. - Ok(analog_measurement / 0.0517) + // 100MHz. It also indicates a y-intercept of -58dBm. + Ok(analog_measurement / 0.0517 - 58.0) } } From 67b7dbcb70dde500432a8e9c8b4a7fc1f3f111e4 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 15:03:08 +0200 Subject: [PATCH 35/40] Updating back to 500khz sampling --- stabilizer/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 452e2da..510c2a9 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -508,7 +508,7 @@ const APP: () = { hal::dma::DMAREQ_ID::TIM2_CH2); // Configure timer 2 to trigger conversions for the ADC - let mut timer2 = dp.TIM2.timer(50.khz(), &mut clocks); + let mut timer2 = dp.TIM2.timer(500.khz(), &mut clocks); timer2.configure_channel(hal::timer::Channel::One, 0.25); timer2.configure_channel(hal::timer::Channel::Two, 0.75); From 58bd61afd5c1b99a3f61994dbf99f982f02c52ad Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 18:02:01 +0200 Subject: [PATCH 36/40] Refactoring AD9959 errors --- ad9959/src/lib.rs | 101 +++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 55 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 14f8ddd..558772d 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -85,23 +85,17 @@ pub enum Channel { /// Possible errors generated by the AD9959 driver. #[derive(Debug)] -pub enum Error { - Interface(InterfaceE), +pub enum Error { + Interface, Check, Bounds, Pin, Frequency, } -impl From for Error { - fn from(interface_error: InterfaceE) -> Self { - Error::Interface(interface_error) - } -} - -impl Ad9959 +impl Ad9959 where - INTERFACE: Interface, + INTERFACE: Interface, DELAY: DelayMs, UPDATE: OutputPin, @@ -123,7 +117,7 @@ where delay: DELAY, desired_mode: Mode, clock_frequency: f32, - multiplier: u8) -> Result> + multiplier: u8) -> Result where RST: OutputPin, { @@ -145,24 +139,21 @@ where reset_pin.set_low().or_else(|_| Err(Error::Pin))?; - ad9959.interface.configure_mode(Mode::SingleBitTwoWire)?; + ad9959.interface.configure_mode(Mode::SingleBitTwoWire).map_err(|_| Error::Interface)?; // Program the interface configuration in the AD9959. Default to all channels enabled. let mut csr: [u8; 1] = [0xF0]; csr[0].set_bits(1..3, desired_mode as u8); - ad9959.interface.write(Register::CSR as u8, &csr)?; - - // Configure the interface to the desired mode. - ad9959.interface.configure_mode(Mode::FourBitSerial)?; + ad9959.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?; // Latch the configuration registers to make them active. ad9959.latch_configuration()?; - ad9959.interface.configure_mode(desired_mode)?; + ad9959.interface.configure_mode(desired_mode).map_err(|_| Error::Interface)?; // Read back the CSR to ensure it specifies the mode correctly. let mut updated_csr: [u8; 1] = [0]; - ad9959.interface.read(Register::CSR as u8, &mut updated_csr)?; + ad9959.interface.read(Register::CSR as u8, &mut updated_csr).map_err(|_| Error::Interface)?; if updated_csr[0] != csr[0] { return Err(Error::Check); } @@ -173,7 +164,7 @@ where } /// Latch the DDS configuration to ensure it is active on the output channels. - fn latch_configuration(&mut self) -> Result<(), Error> { + fn latch_configuration(&mut self) -> Result<(), Error> { self.io_update.set_high().or_else(|_| Err(Error::Pin))?; // The SYNC_CLK is 1/4 the system clock frequency. The IO_UPDATE pin must be latched for one // full SYNC_CLK pulse to register. For safety, we latch for 5 here. @@ -193,7 +184,7 @@ where /// The actual frequency configured for the internal system clock. pub fn configure_system_clock(&mut self, reference_clock_frequency: f32, - multiplier: u8) -> Result> + multiplier: u8) -> Result { self.reference_clock_frequency = reference_clock_frequency; @@ -208,13 +199,13 @@ where // TODO: Update / disable any enabled channels? let mut fr1: [u8; 3] = [0, 0, 0]; - self.interface.read(Register::FR1 as u8, &mut fr1)?; + self.interface.read(Register::FR1 as u8, &mut fr1).map_err(|_| Error::Interface)?; fr1[0].set_bits(2..=6, multiplier); let vco_range = frequency > 255e6; fr1[0].set_bit(7, vco_range); - self.interface.write(Register::FR1 as u8, &fr1)?; + self.interface.write(Register::FR1 as u8, &fr1).map_err(|_| Error::Interface)?; self.system_clock_multiplier = multiplier; Ok(self.system_clock_frequency()) @@ -226,9 +217,9 @@ where } /// Get the current reference clock multiplier. - pub fn get_reference_clock_multiplier(&mut self) -> Result> { + pub fn get_reference_clock_multiplier(&mut self) -> Result { let mut fr1: [u8; 3] = [0, 0, 0]; - self.interface.read(Register::FR1 as u8, &mut fr1)?; + self.interface.read(Register::FR1 as u8, &mut fr1).map_err(|_| Error::Interface)?; Ok(fr1[0].get_bits(2..=6) as u8) } @@ -240,36 +231,36 @@ where /// /// Returns: /// True if the self test succeeded. False otherwise. - pub fn self_test(&mut self) -> Result> { + pub fn self_test(&mut self) -> Result { let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; + self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?; let old_csr = csr[0]; // Enable all channels. csr[0].set_bits(4..8, 0xF); - self.interface.write(Register::CSR as u8, &csr)?; + self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?; // Read back the enable. csr[0] = 0; - self.interface.read(Register::CSR as u8, &mut csr)?; + self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?; if csr[0].get_bits(4..8) != 0xF { return Ok(false); } // Clear all channel enables. csr[0].set_bits(4..8, 0x0); - self.interface.write(Register::CSR as u8, &csr)?; + self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?; // Read back the enable. csr[0] = 0xFF; - self.interface.read(Register::CSR as u8, &mut csr)?; + self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?; if csr[0].get_bits(4..8) != 0 { return Ok(false); } // Restore the CSR. csr[0] = old_csr; - self.interface.write(Register::CSR as u8, &csr)?; + self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?; Ok(true) } @@ -280,29 +271,29 @@ where } /// Enable an output channel. - pub fn enable_channel(&mut self, channel: Channel) -> Result<(), Error> { + pub fn enable_channel(&mut self, channel: Channel) -> Result<(), Error> { let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; + self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?; csr[0].set_bit(channel as usize + 4, true); - self.interface.write(Register::CSR as u8, &csr)?; + self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?; Ok(()) } /// Disable an output channel. - pub fn disable_channel(&mut self, channel: Channel) -> Result<(), Error> { + pub fn disable_channel(&mut self, channel: Channel) -> Result<(), Error> { let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; + self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?; csr[0].set_bit(channel as usize + 4, false); - self.interface.write(Register::CSR as u8, &csr)?; + self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?; Ok(()) } /// Determine if an output channel is enabled. - pub fn is_enabled(&mut self, channel: Channel) -> Result> { + pub fn is_enabled(&mut self, channel: Channel) -> Result { let mut csr: [u8; 1] = [0; 1]; - self.interface.read(Register::CSR as u8, &mut csr)?; + self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?; Ok(csr[0].get_bit(channel as usize + 4)) } @@ -313,24 +304,24 @@ where /// * `channel` - The channel to configure. /// * `register` - The register to update. /// * `data` - The contents to write to the provided register. - fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> { + fn modify_channel(&mut self, channel: Channel, register: Register, data: &[u8]) -> Result<(), Error> { // Disable all other outputs so that we can update the configuration register of only the // specified channel. let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; + self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?; let mut new_csr = csr; new_csr[0].set_bits(4..8, 0); new_csr[0].set_bit(4 + channel as usize, true); - self.interface.write(Register::CSR as u8, &new_csr)?; + self.interface.write(Register::CSR as u8, &new_csr).map_err(|_| Error::Interface)?; - self.interface.write(register as u8, &data)?; + self.interface.write(register as u8, &data).map_err(|_| Error::Interface)?; // Latch the configuration and restore the previous CSR. Note that the re-enable of the // channel happens immediately, so the CSR update does not need to be latched. self.latch_configuration()?; - self.interface.write(Register::CSR as u8, &csr)?; + self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?; Ok(()) } @@ -341,23 +332,23 @@ where /// * `channel` - The channel to read. /// * `register` - The register to read. /// * `data` - A location to store the read register contents. - fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error> { + fn read_channel(&mut self, channel: Channel, register: Register, mut data: &mut [u8]) -> Result<(), Error> { // Disable all other channels in the CSR so that we can read the configuration register of // only the desired channel. let mut csr: [u8; 1] = [0]; - self.interface.read(Register::CSR as u8, &mut csr)?; + self.interface.read(Register::CSR as u8, &mut csr).map_err(|_| Error::Interface)?; let mut new_csr = csr; new_csr[0].set_bits(4..8, 0); new_csr[0].set_bit(4 + channel as usize, true); - self.interface.write(Register::CSR as u8, &new_csr)?; + self.interface.write(Register::CSR as u8, &new_csr).map_err(|_| Error::Interface)?; - self.interface.read(register as u8, &mut data)?; + self.interface.read(register as u8, &mut data).map_err(|_| Error::Interface)?; // Restore the previous CSR. Note that the re-enable of the channel happens immediately, so // the CSR update does not need to be latched. - self.interface.write(Register::CSR as u8, &csr)?; + self.interface.write(Register::CSR as u8, &csr).map_err(|_| Error::Interface)?; Ok(()) } @@ -370,7 +361,7 @@ where /// /// Returns: /// The actual programmed phase offset of the channel in turns. - pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result> { + pub fn set_phase(&mut self, channel: Channel, phase_turns: f32) -> Result { let phase_offset: u16 = (phase_turns * (1 << 14) as f32) as u16 & 0x3FFFu16; self.modify_channel(channel, Register::CPOW0, &phase_offset.to_be_bytes())?; @@ -385,7 +376,7 @@ where /// /// Returns: /// The phase of the channel in turns. - pub fn get_phase(&mut self, channel: Channel) -> Result> { + pub fn get_phase(&mut self, channel: Channel) -> Result { let mut phase_offset: [u8; 2] = [0; 2]; self.read_channel(channel, Register::CPOW0, &mut phase_offset)?; @@ -402,7 +393,7 @@ where /// /// Returns: /// The actual normalized amplitude of the channel relative to full-scale range. - pub fn set_amplitude(&mut self, channel: Channel, amplitude: f32) -> Result> { + pub fn set_amplitude(&mut self, channel: Channel, amplitude: f32) -> Result { if amplitude < 0.0 || amplitude > 1.0 { return Err(Error::Bounds); } @@ -436,7 +427,7 @@ where /// /// Returns: /// The normalized amplitude of the channel. - pub fn get_amplitude(&mut self, channel: Channel) -> Result> { + pub fn get_amplitude(&mut self, channel: Channel) -> Result { let mut acr: [u8; 3] = [0; 3]; self.read_channel(channel, Register::ACR, &mut acr)?; @@ -456,7 +447,7 @@ where /// /// Returns: /// The actual programmed frequency of the channel. - pub fn set_frequency(&mut self, channel: Channel, frequency: f64) -> Result> { + pub fn set_frequency(&mut self, channel: Channel, frequency: f64) -> Result { if frequency < 0.0 || frequency > self.system_clock_frequency() { return Err(Error::Bounds); } @@ -477,7 +468,7 @@ where /// /// Returns: /// The frequency of the channel in Hz. - pub fn get_frequency(&mut self, channel: Channel) -> Result> { + pub fn get_frequency(&mut self, channel: Channel) -> Result { // Read the frequency tuning word for the channel. let mut tuning_word: [u8; 4] = [0; 4]; self.read_channel(channel, Register::CFTW0, &mut tuning_word)?; From 48bd35e1d818507a868eeacbff96e36686afe5ac Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 18:09:01 +0200 Subject: [PATCH 37/40] Updating error messages --- ad9959/src/lib.rs | 11 ++++++----- stabilizer/src/main.rs | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 558772d..13ea7cf 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -152,11 +152,12 @@ where ad9959.interface.configure_mode(desired_mode).map_err(|_| Error::Interface)?; // Read back the CSR to ensure it specifies the mode correctly. - let mut updated_csr: [u8; 1] = [0]; - ad9959.interface.read(Register::CSR as u8, &mut updated_csr).map_err(|_| Error::Interface)?; - if updated_csr[0] != csr[0] { - return Err(Error::Check); - } + // TODO: This is disabled until QSPI readback issues have been resolved. + //let mut updated_csr: [u8; 1] = [0]; + //ad9959.interface.read(Register::CSR as u8, &mut updated_csr).map_err(|_| Error::Interface)?; + //if updated_csr[0] != csr[0] { + // return Err(Error::Check); + //} // Set the clock frequency to configure the device as necessary. ad9959.configure_system_clock(clock_frequency, multiplier)?; diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 510c2a9..33fd489 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -123,7 +123,7 @@ macro_rules! route_request { let value = match $getter() { Ok(data) => data, Err(_) => return server::Response::error($request.attribute, - "Failed to set attribute"), + "Failed to read attribute"), }; let encoded_data: String = match serde_json_core::to_string(&value) { From b7100f2b146059d0184ae184eff6a9ab68baca69 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 19:00:37 +0200 Subject: [PATCH 38/40] Correcting issues at high optimization levels --- Cargo.toml | 3 ++- ad9959/src/lib.rs | 11 +++++------ stm32h7xx-hal | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ec83f63..0f43a74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,10 @@ members = [ [profile.dev] codegen-units = 1 incremental = false -opt-level = 1 +opt-level = 3 [profile.release] +opt-level = 3 debug = true lto = true codegen-units = 1 diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 13ea7cf..558772d 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -152,12 +152,11 @@ where ad9959.interface.configure_mode(desired_mode).map_err(|_| Error::Interface)?; // Read back the CSR to ensure it specifies the mode correctly. - // TODO: This is disabled until QSPI readback issues have been resolved. - //let mut updated_csr: [u8; 1] = [0]; - //ad9959.interface.read(Register::CSR as u8, &mut updated_csr).map_err(|_| Error::Interface)?; - //if updated_csr[0] != csr[0] { - // return Err(Error::Check); - //} + let mut updated_csr: [u8; 1] = [0]; + ad9959.interface.read(Register::CSR as u8, &mut updated_csr).map_err(|_| Error::Interface)?; + if updated_csr[0] != csr[0] { + return Err(Error::Check); + } // Set the clock frequency to configure the device as necessary. ad9959.configure_system_clock(clock_frequency, multiplier)?; diff --git a/stm32h7xx-hal b/stm32h7xx-hal index d79cb00..40ad468 160000 --- a/stm32h7xx-hal +++ b/stm32h7xx-hal @@ -1 +1 @@ -Subproject commit d79cb0015a6f0cbb819907efe3a817f7dce14bab +Subproject commit 40ad4687e8473e329aea4b61551d107f3dfca567 From d5145924c477492844c81091813cc7b7cbdfd336 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 19:06:24 +0200 Subject: [PATCH 39/40] Updating QSPI pin speed --- stabilizer/src/main.rs | 12 ++++++------ stm32h7xx-hal | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stabilizer/src/main.rs b/stabilizer/src/main.rs index 33fd489..a903717 100644 --- a/stabilizer/src/main.rs +++ b/stabilizer/src/main.rs @@ -349,12 +349,12 @@ const APP: () = { // Instantiate the QUADSPI pins and peripheral interface. // TODO: Place these into a pins structure that is provided to the QSPI // constructor. - let _qspi_clk = gpiob.pb2.into_alternate_af9(); - let _qspi_ncs = gpioc.pc11.into_alternate_af9(); - let _qspi_io0 = gpioe.pe7.into_alternate_af10(); - let _qspi_io1 = gpioe.pe8.into_alternate_af10(); - let _qspi_io2 = gpioe.pe9.into_alternate_af10(); - let _qspi_io3 = gpioe.pe10.into_alternate_af10(); + let _qspi_clk = gpiob.pb2.into_alternate_af9().set_speed(hal::gpio::Speed::VeryHigh); + let _qspi_ncs = gpioc.pc11.into_alternate_af9().set_speed(hal::gpio::Speed::VeryHigh); + let _qspi_io0 = gpioe.pe7.into_alternate_af10().set_speed(hal::gpio::Speed::VeryHigh); + let _qspi_io1 = gpioe.pe8.into_alternate_af10().set_speed(hal::gpio::Speed::VeryHigh); + let _qspi_io2 = gpioe.pe9.into_alternate_af10().set_speed(hal::gpio::Speed::VeryHigh); + let _qspi_io3 = gpioe.pe10.into_alternate_af10().set_speed(hal::gpio::Speed::VeryHigh); let qspi = hal::qspi::Qspi::new(dp.QUADSPI, &mut clocks, 10.mhz()).unwrap(); pounder::QspiInterface::new(qspi).unwrap() diff --git a/stm32h7xx-hal b/stm32h7xx-hal index 40ad468..ff00e93 160000 --- a/stm32h7xx-hal +++ b/stm32h7xx-hal @@ -1 +1 @@ -Subproject commit 40ad4687e8473e329aea4b61551d107f3dfca567 +Subproject commit ff00e938f2b226211c178f26c092f36462c44404 From 0521ee8c5adf3ea48f48fab29aaec218fdfe59e7 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 11 Jun 2020 19:10:02 +0200 Subject: [PATCH 40/40] Renaming pounder test script --- test.py => pounder_test.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test.py => pounder_test.py (100%) diff --git a/test.py b/pounder_test.py similarity index 100% rename from test.py rename to pounder_test.py