├── openocd.cfg ├── memory.x ├── .gitignore ├── .cargo └── config ├── README.md ├── Cargo.toml ├── src ├── rcc.rs ├── ts.rs ├── clock.rs └── main.rs ├── openocd.gdb ├── .github └── workflows │ └── ci.yml └── Cargo.lock /openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/stlink.cfg] 2 | source [find target/stm32f1x.cfg] 3 | -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH : ORIGIN = 0x08000000, LENGTH = 128K 4 | RAM : ORIGIN = 0x20000000, LENGTH = 20K 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # These are backup files generated by rustfmt 6 | **/*.rs.bk 7 | -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | [target.thumbv7m-none-eabi] 2 | runner = "arm-none-eabi-gdb -x openocd.gdb -q" # is appended 3 | 4 | rustflags = ["-C", "link-arg=-Tlink.x"] 5 | 6 | [build] 7 | target = "thumbv7m-none-eabi" 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # the-elements 2 | temperature monitoring 3 | 4 | ## Build 5 | 6 | `cargo build --release` 7 | 8 | `cargo size --bin the-elements --release` 9 | 10 | `cargo objdump --bin the-elements --release -- -disassemble -no-show-raw-insn -print-imm-hex` 11 | 12 | ## Run 13 | 14 | `openocd &` 15 | 16 | `cargo run --release` 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "the-elements" 3 | version = "0.0.0" 4 | authors = [] 5 | 6 | [dependencies] 7 | cortex-m = "0.5" 8 | cortex-m-rt = "0.6" 9 | panic-halt = "0.2" 10 | stm32f103xx = "0.10" 11 | 12 | [[bin]] 13 | name = "the-elements" 14 | test = false 15 | bench = false 16 | 17 | [profile.release] 18 | lto = true 19 | codegen-units = 1 20 | debug = true 21 | -------------------------------------------------------------------------------- /src/rcc.rs: -------------------------------------------------------------------------------- 1 | use stm32f103xx::RCC; 2 | 3 | use clock::AsDivisor; 4 | 5 | pub fn adc_prescaler(rcc: &RCC) -> u32 { 6 | // sysclk 7 | let ahb = rcc.cfgr.read().hpre().as_divisor(); // -> through AHB prescaler 8 | let abp2 = rcc.cfgr.read().ppre2().as_divisor(); // -> through APB2 prescaler 9 | let adc = rcc.cfgr.read().adcpre().as_divisor(); // -> through ADC prescaler 10 | u32::from(ahb) * u32::from(abp2) * u32::from(adc) 11 | } 12 | -------------------------------------------------------------------------------- /src/ts.rs: -------------------------------------------------------------------------------- 1 | pub fn to_tenths_celsius(sense: u16) -> i32 { 2 | const MAX_SENSE: u64 = 1 << 12; // LSB 3 | const FULL_SCALE: u64 = 3_200_000; // uV 4 | const AVG_SLOPE: i32 = 4_300 / 10; // uV / (1/10)C 5 | const V_25: i32 = 1_430_000; // uV 6 | const OFFSET_25: i32 = 250; // (1/10)C 7 | 8 | let sense = u64::from(sense); 9 | let v_sense = ((sense * FULL_SCALE) / MAX_SENSE) as i32; // uV 10 | ((V_25 - v_sense) / AVG_SLOPE) + OFFSET_25 // (1/10)C 11 | } 12 | -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | target remote :3333 2 | 3 | set print asm-demangle on 4 | 5 | break DefaultHandler 6 | break UserHardFault 7 | break rust_begin_unwind 8 | 9 | # *try* to stop at the user entry point (it might be gone due to inlining) 10 | break main 11 | 12 | monitor arm semihosting enable 13 | 14 | # # send captured ITM to the file itm.fifo 15 | # # (the microcontroller SWO pin must be connected to the programmer SWO pin) 16 | # # 8000000 must match the core clock frequency 17 | # monitor tpiu config internal itm.txt uart off 8000000 18 | 19 | # # OR: make the microcontroller SWO pin output compatible with UART (8N1) 20 | # # 8000000 must match the core clock frequency 21 | # # 2000000 is the frequency of the SWO pin 22 | # monitor tpiu config external uart off 8000000 2000000 23 | 24 | # # enable ITM port 0 25 | # monitor itm port 0 on 26 | 27 | load 28 | 29 | # start the process but immediately halt the processor 30 | stepi 31 | -------------------------------------------------------------------------------- /src/clock.rs: -------------------------------------------------------------------------------- 1 | use stm32f103xx::rcc::cfgr::{ADCPRER, HPRER, PPRE2R}; 2 | 3 | pub trait AsDivisor { 4 | fn as_divisor(&self) -> u16; 5 | } 6 | 7 | impl AsDivisor for HPRER { 8 | fn as_divisor(&self) -> u16 { 9 | match self { 10 | HPRER::NODIV => 1, 11 | HPRER::DIV2 => 2, 12 | HPRER::DIV4 => 4, 13 | HPRER::DIV8 => 8, 14 | HPRER::DIV16 => 16, 15 | HPRER::DIV64 => 64, 16 | HPRER::DIV128 => 128, 17 | HPRER::DIV256 => 256, 18 | HPRER::DIV512 => 512, 19 | HPRER::_Reserved(_) => unreachable!(), 20 | } 21 | } 22 | } 23 | 24 | impl AsDivisor for PPRE2R { 25 | fn as_divisor(&self) -> u16 { 26 | match self { 27 | PPRE2R::NODIV => 1, 28 | PPRE2R::DIV2 => 2, 29 | PPRE2R::DIV4 => 4, 30 | PPRE2R::DIV8 => 8, 31 | PPRE2R::DIV16 => 16, 32 | PPRE2R::_Reserved(_) => unreachable!(), 33 | } 34 | } 35 | } 36 | 37 | impl AsDivisor for ADCPRER { 38 | fn as_divisor(&self) -> u16 { 39 | match self { 40 | ADCPRER::DIV2 => 2, 41 | ADCPRER::DIV4 => 4, 42 | ADCPRER::DIV6 => 6, 43 | ADCPRER::DIV8 => 8, 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - v*.*.* 9 | pull_request: 10 | 11 | jobs: 12 | fmt: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v1 16 | - uses: actions-rs/toolchain@v1 17 | with: 18 | toolchain: stable 19 | - run: rustup component add rustfmt 20 | - run: cargo fmt --all -- --check 21 | 22 | clippy: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v1 26 | - uses: actions-rs/toolchain@v1 27 | with: 28 | toolchain: stable 29 | target: thumbv7m-none-eabi 30 | - run: rustup component add clippy 31 | - run: RUSTFLAGS="-D warnings" cargo clippy 32 | 33 | test: 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v1 37 | - uses: actions-rs/toolchain@v1 38 | with: 39 | toolchain: stable 40 | target: thumbv7m-none-eabi 41 | - run: cargo test 42 | 43 | build: 44 | runs-on: ubuntu-latest 45 | steps: 46 | - uses: actions/checkout@v1 47 | - uses: actions-rs/toolchain@v1 48 | with: 49 | toolchain: stable 50 | target: thumbv7m-none-eabi 51 | - run: cargo build --release 52 | - run: strip target/release/the-elements 53 | - run: ls -lh target/release/the-elements 54 | - uses: softprops/action-gh-release@v1 55 | if: startsWith(github.ref, 'refs/tags/') 56 | with: 57 | files: target/release/the-elements 58 | env: 59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 60 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate cortex_m; 5 | extern crate cortex_m_rt; 6 | extern crate stm32f103xx; 7 | 8 | extern crate panic_halt; 9 | 10 | mod clock; 11 | mod rcc; 12 | mod ts; 13 | 14 | use cortex_m::asm::{bkpt, nop, wfi}; 15 | use cortex_m::interrupt; 16 | use cortex_m::peripheral::syst::SystClkSource; 17 | use cortex_m_rt::{entry, exception}; 18 | use stm32f103xx::ADC1; 19 | 20 | use rcc::adc_prescaler; 21 | 22 | const SYSCLK_FREQ: u32 = 8_000_000; 23 | 24 | #[entry] 25 | fn main() -> ! { 26 | let cp = cortex_m::Peripherals::take().unwrap(); 27 | let dp = stm32f103xx::Peripherals::take().unwrap(); 28 | 29 | // prepare adc channel 16 (temperature sensor) 30 | dp.RCC.apb2enr.modify(|_, w| w.adc1en().enabled()); 31 | dp.ADC1 32 | .cr2 33 | .modify(|_, w| w.adon().set_bit().tsvrefe().set_bit()); 34 | dp.ADC1.sqr3.modify(|_, w| unsafe { w.sq1().bits(16) }); 35 | dp.ADC1.smpr1.modify(|_, w| unsafe { 36 | // recommended sample time 17.1us ~ 138 sysclk cycles @ 8MHz 37 | // default overall adc prescaler /2 => ~69 adc cycles 38 | // closest is 0b110 = 71.5 adc cycles 39 | // but use the max 0b111 = 239.5 adc cycles 40 | // for extra stability, and because we don't need the performance 41 | w.smp16().bits(0b111) 42 | }); 43 | // calibration: "must have been in power-on state (ADON bit = ‘1’) for at least two ADC clock cycles" 44 | // also, must wait t_STAB = 1us for adc to stabilize in general; 1us ~ 8 sysclk cycles @ 8MHz 45 | for _ in 0..(2 * adc_prescaler(&dp.RCC)).max(8) { 46 | nop(); 47 | } 48 | // start calibration and wait for it to be done 49 | dp.ADC1.cr2.modify(|_, w| w.cal().set_bit()); 50 | while dp.ADC1.cr2.read().cal().bit_is_set() {} 51 | 52 | // run ~1Hz timer 53 | let mut syst = cp.SYST; 54 | syst.set_clock_source(SystClkSource::Core); 55 | syst.set_reload(SYSCLK_FREQ); 56 | syst.enable_counter(); 57 | syst.enable_interrupt(); 58 | 59 | interrupt::free(|_| unsafe { 60 | ADC1 = Some(dp.ADC1); 61 | }); 62 | 63 | loop { 64 | wfi(); 65 | } 66 | } 67 | 68 | static mut ADC1: Option = None; 69 | 70 | #[exception] 71 | fn SysTick() { 72 | if let Some(adc1) = unsafe { &ADC1 } { 73 | // start conversion and wait for it to be done 74 | adc1.cr2.modify(|_, w| w.adon().set_bit()); 75 | while adc1.sr.read().eoc().bit_is_clear() {} 76 | let data = adc1.dr.read().data().bits(); 77 | let _temp = ts::to_tenths_celsius(data); 78 | bkpt(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "aligned" 3 | version = "0.2.0" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "bare-metal" 8 | version = "0.2.3" 9 | source = "registry+https://github.com/rust-lang/crates.io-index" 10 | 11 | [[package]] 12 | name = "cortex-m" 13 | version = "0.5.8" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [[package]] 22 | name = "cortex-m-rt" 23 | version = "0.6.5" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | dependencies = [ 26 | "cortex-m-rt-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 27 | "r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 28 | ] 29 | 30 | [[package]] 31 | name = "cortex-m-rt-macros" 32 | version = "0.1.3" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | dependencies = [ 35 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", 38 | "syn 0.15.15 (registry+https://github.com/rust-lang/crates.io-index)", 39 | ] 40 | 41 | [[package]] 42 | name = "panic-halt" 43 | version = "0.2.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | 46 | [[package]] 47 | name = "proc-macro2" 48 | version = "0.4.20" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | dependencies = [ 51 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 52 | ] 53 | 54 | [[package]] 55 | name = "quote" 56 | version = "0.6.8" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | dependencies = [ 59 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 60 | ] 61 | 62 | [[package]] 63 | name = "r0" 64 | version = "0.2.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | 67 | [[package]] 68 | name = "rand" 69 | version = "0.5.5" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | dependencies = [ 72 | "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 73 | ] 74 | 75 | [[package]] 76 | name = "rand_core" 77 | version = "0.2.2" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | dependencies = [ 80 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 81 | ] 82 | 83 | [[package]] 84 | name = "rand_core" 85 | version = "0.3.0" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | 88 | [[package]] 89 | name = "stm32f103xx" 90 | version = "0.10.0" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | dependencies = [ 93 | "bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 94 | "cortex-m 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", 95 | "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 96 | ] 97 | 98 | [[package]] 99 | name = "syn" 100 | version = "0.15.15" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | dependencies = [ 103 | "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 106 | ] 107 | 108 | [[package]] 109 | name = "the-elements" 110 | version = "0.0.0" 111 | dependencies = [ 112 | "cortex-m 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "cortex-m-rt 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 114 | "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 115 | "stm32f103xx 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 116 | ] 117 | 118 | [[package]] 119 | name = "unicode-xid" 120 | version = "0.1.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | 123 | [[package]] 124 | name = "vcell" 125 | version = "0.1.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | 128 | [[package]] 129 | name = "volatile-register" 130 | version = "0.2.0" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | dependencies = [ 133 | "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 134 | ] 135 | 136 | [metadata] 137 | "checksum aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" 138 | "checksum bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1bdcf9294ed648c7cd29b11db06ea244005aeef50ae8f605b1a3af2940bf8f92" 139 | "checksum cortex-m 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dab2164a0fc216781a47fc343347365112ae6917421d3fa4bac6faf0fbaaaec7" 140 | "checksum cortex-m-rt 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d121b6d9989813d5e7716584b1e0644d863fd0e87f47dbb5f654444c9aa3abd1" 141 | "checksum cortex-m-rt-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1474b168c0d92d716f5252afe162a4d893bae79a7121ded1b214be0c4498b829" 142 | "checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" 143 | "checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" 144 | "checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" 145 | "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" 146 | "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" 147 | "checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" 148 | "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" 149 | "checksum stm32f103xx 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0984862c7c29bd701be77ae20b77ad0bbaa7978ca48877d058b18cd3ec5342e" 150 | "checksum syn 0.15.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0a9c2bf1e53c21704a7cce1b2a42768f1ae32a6777108a0d7f1faa4bfe7f7c04" 151 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 152 | "checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" 153 | "checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" 154 | --------------------------------------------------------------------------------