Initial commit.
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.pio
|
||||
.omo
|
||||
.junie
|
||||
.idea
|
||||
104
AGENTS.md
Normal file
104
AGENTS.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# AGENTS.md
|
||||
|
||||
PlatformIO / Arduino-framework firmware for a NUCLEO-F042K6 reading two MS5611
|
||||
barometers (one I2C, one SPI) and emitting validated CSV over the ST-LINK VCP.
|
||||
See `README.md` for wiring tables, CSV format and status codes.
|
||||
|
||||
## Commands
|
||||
|
||||
```sh
|
||||
pio run # build (the only real verification gate)
|
||||
pio run -t upload # flash over ST-LINK
|
||||
pio device monitor # 115200 baud
|
||||
pio run -t clean
|
||||
```
|
||||
|
||||
There is one env, `nucleo_f042k6`. No lint, format, or typecheck step exists.
|
||||
|
||||
**Always read the size report at the end of `pio run`.** It is the acceptance
|
||||
criterion for any change (see below). Current: flash 88.8% (29108 / 32768),
|
||||
RAM 32.6% (2004 / 6144).
|
||||
|
||||
## Flash budget is the dominant constraint
|
||||
|
||||
3.6 KB of flash headroom. This shapes almost every design decision here, and it
|
||||
is the single easiest thing to break without noticing.
|
||||
|
||||
`-flto` and `-fsingle-precision-constant` in `platformio.ini` are load-bearing.
|
||||
Verified: building without them overflows `FLASH` by **7252 bytes** and fails to
|
||||
link. Both MS5611 driver libraries write their compensation maths with unsuffixed
|
||||
double literals, which otherwise links the double soft-float helpers.
|
||||
|
||||
Consequences for new code:
|
||||
|
||||
- Never introduce `double`, unsuffixed floating literals, or `<math.h>` calls on
|
||||
doubles. Use `float` and `f`-suffixed constants.
|
||||
- Never call `Serial.print(someFloat, digits)` — it resolves to
|
||||
`Print::print(double, int)` and pulls in soft-float. `main.cpp::printFixed2()`
|
||||
exists solely to format floats via scaled integers; use it.
|
||||
- Wrap string literals in `F()` so they stay in flash.
|
||||
- No `String`, no `new`/`delete`. `~BaroChannel()` is deliberately non-virtual
|
||||
and `protected` to keep a destructor slot out of the vtable and avoid linking
|
||||
`operator delete`. Do not "fix" this into a virtual destructor.
|
||||
- `lib_deps` pin exact driver tags (`MS5611#0.5.2`, `MS5611_SPI#0.4.3`). Bumping
|
||||
them can blow the budget; rebuild and check size if you do.
|
||||
|
||||
## Architecture
|
||||
|
||||
- `include/config.h` — every pin, address, rate, threshold and validation limit.
|
||||
Change hardware behaviour **here**, not in the `.cpp` files.
|
||||
- `include/baro_channel.h` / `src/baro_channel.cpp` — `BaroChannel`, the shared
|
||||
state machine: init + PROM/CRC handshake, per-poll validation, error streaks,
|
||||
re-init, and the 1 s error LED pulse. Five pure-virtual `driver*()` hooks are
|
||||
the only per-bus surface.
|
||||
- `src/baro_i2c.cpp` / `src/baro_spi.cpp` — one concrete subclass each, plus a
|
||||
file-static instance exposed through `baroI2cChannel()` / `baroSpiChannel()`.
|
||||
- `src/ms5611_crc.cpp` — standalone PROM CRC-4 (AN520).
|
||||
- `src/main.cpp` — `setup()`/`loop()`, CSV formatting, 10 Hz report scheduling.
|
||||
|
||||
**The two-file split is mandatory, not stylistic.** `MS5611.h` and
|
||||
`MS5611_SPI.h` each define their own `enum osr_t` and `MS5611_READ_OK`, so they
|
||||
cannot be included in the same translation unit. `baro_channel.h` therefore
|
||||
includes neither driver header, and the channels are handed out via accessor
|
||||
functions. Do not merge these TUs or hoist a driver include into the header.
|
||||
|
||||
## Conventions that differ from defaults
|
||||
|
||||
- **millis() rollover safety**: deadline comparisons use a signed difference —
|
||||
`if ((int32_t)(now - deadline) >= 0)`. Never write `now >= deadline`.
|
||||
- **`poll()` blocks** for ~2 ADC conversions (~5 ms/sensor at OSR 1024).
|
||||
`main.cpp` re-reads `millis()` after polling before evaluating LED and report
|
||||
deadlines. Preserve that if you touch the loop.
|
||||
- **Validation belongs in `BaroChannel`, not the drivers.** Neither library
|
||||
checks the PROM CRC, and `MS5611::read()` returns `MS5611_READ_OK` for an ADC
|
||||
read taken before the conversion completed (which yields 0). Do not simplify
|
||||
the range / stale / all-zero-PROM checks away by trusting driver return codes.
|
||||
- Style is Allman braces, 2-space indent, `_camelCase` private members, `// `
|
||||
(two spaces) block comments. There is **no `.clang-format`** in the repo —
|
||||
running `clang-format` would reformat everything into LLVM style. Don't.
|
||||
- `SPI` and `I2C` appear in `lib_deps`, but the code uses `Wire` directly and
|
||||
nothing includes the third-party `I2C` library.
|
||||
|
||||
## Testing
|
||||
|
||||
There is no automated test suite. `test/` holds only the stock PlatformIO
|
||||
README, and `pio test` collects 0 cases. Do not claim tests pass.
|
||||
|
||||
`ms5611_crc.{h,cpp}` is intentionally free of Arduino headers so it can be
|
||||
exercised on the host:
|
||||
|
||||
```sh
|
||||
clang++ -std=c++17 -Wall -Wextra -c src/ms5611_crc.cpp -Iinclude -o /tmp/crc.o
|
||||
```
|
||||
|
||||
The differential check against the NuttX reference described in `README.md` was
|
||||
a host-side exercise and is not committed.
|
||||
|
||||
Anything beyond the CRC needs real hardware: build, flash, and watch the CSV
|
||||
stream. Unplugging a sensor is the quick way to exercise the error paths
|
||||
(`ERR_INIT` / `ERR_CRC`, LED pulse, auto re-init after 10 consecutive failures).
|
||||
|
||||
## Notes
|
||||
|
||||
`.pio/`, `.omo/`, `.junie/` and `.idea/` are gitignored tooling state, not
|
||||
project sources.
|
||||
146
README.md
Normal file
146
README.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Barometric pressure test
|
||||
|
||||
## Goals
|
||||
|
||||
1. Read barometric pressure from two sensors. One connected to the MCU with SPI and the other connected with I2C bus.
|
||||
2. The reading must be done in real time and data should be checked for validity.
|
||||
3. The output should be sent to a serial port.
|
||||
4. In case of reading error the LED should be turned on for 1 second. Each sensor has its own LED.
|
||||
|
||||
## Hardware
|
||||
- MCU - STM32F042K6T6 (Nucleo-32, board MB1180) - 32 KB flash, 6 KB RAM
|
||||
- Barometric pressure sensor - 2x GY-63 (MS5611)
|
||||
|
||||
## Wiring
|
||||
|
||||
Pin assignments follow ST UM1956 Table 10 and the MB1180 C.2 schematic, and match
|
||||
the STM32duino defaults for this variant, so `Wire.begin()` and `SPI.begin()` need
|
||||
no explicit pin overrides.
|
||||
|
||||

|
||||
|
||||
Vector source: [`docs/wiring.svg`](docs/wiring.svg). Pins there are grouped by
|
||||
function, not by physical header order.
|
||||
|
||||
The same wiring drawn physically, with the real header order from UM1956
|
||||
Table 10, so it is clear which hole each jumper goes into:
|
||||
|
||||

|
||||
|
||||
Vector source: [`docs/wiring-physical.svg`](docs/wiring-physical.svg). Note that
|
||||
SPI is split across both connectors: MOSI, MISO and CS are on CN3, while SCK is
|
||||
CN4 pin 15.
|
||||
|
||||
### Baro0 - I2C (GY-63 <=> STM32F042)
|
||||
| GY-63 | Nucleo | STM32 | Note |
|
||||
|-------|--------|-------|------|
|
||||
| VCC | 3V3 | - | |
|
||||
| GND | GND | - | |
|
||||
| PS | 3.3V | - | high selects I2C |
|
||||
| SDA | A4 | PB7 | `PIN_WIRE_SDA`, reaches the A4 pad through SB18 |
|
||||
| SCL | A5 | PB6 | `PIN_WIRE_SCL`, reaches the A5 pad through SB16 |
|
||||
| CSB | GND | - | sets address `0x77`; tie to VCC for `0x76` |
|
||||
|
||||
The MS5611 address is `1110 11Cx`, where C is the **complement** of CSB
|
||||
(datasheet p.12). `CSB` must not be left floating. If you strap it high, change
|
||||
`BARO_I2C_ADDRESS` in [`include/config.h`](include/config.h).
|
||||
|
||||
### Baro1 - SPI (GY-63 <=> STM32F042)
|
||||
| GY-63 | Nucleo | STM32 | Note |
|
||||
|-------|--------|-------|------|
|
||||
| VCC | 3V3 | - | |
|
||||
| GND | GND | - | |
|
||||
| PS | GND | - | low selects SPI |
|
||||
| SCLK | D13 | PB3 | SPI1_SCK |
|
||||
| SDI | D11 | PB5 | SPI1_MOSI |
|
||||
| SDO | D12 | PB4 | SPI1_MISO |
|
||||
| CSB | D10 | PA11 | chip select, `BARO_SPI_CS_PIN` |
|
||||
|
||||
SPI1 has to stay on this pin group. The alternative group (PA5/PA6/PA7) is
|
||||
unusable while I2C is active: with the factory-default solder bridges SB16 and
|
||||
SB18 closed, PA6 shares a net with PB6 (SCL) and PA5 shares a net with PB7 (SDA).
|
||||
|
||||
### Error LEDs
|
||||
|
||||
Two external LEDs, one per sensor, each in series with roughly 510 R to GND.
|
||||
|
||||
| Signal | Nucleo | STM32 |
|
||||
|--------|--------|-------|
|
||||
| I2C sensor error | D3 | PB0 |
|
||||
| SPI sensor error | D6 | PB1 |
|
||||
|
||||
Both pins are set in [`include/config.h`](include/config.h).
|
||||
|
||||
## Build and run
|
||||
|
||||
```sh
|
||||
pio run # build
|
||||
pio run -t upload # flash over ST-LINK
|
||||
pio device monitor # 115200 baud, ST-LINK Virtual COM Port
|
||||
```
|
||||
|
||||
`Serial` is USART2 on PA2/PA15, wired to the ST-LINK VCP, so no USB-serial
|
||||
adapter is needed.
|
||||
|
||||
The image is close to the flash ceiling (about 89% of 32 KB). `-flto` and
|
||||
`-fsingle-precision-constant` in [`platformio.ini`](platformio.ini) are required,
|
||||
not cosmetic: both MS5611 drivers write their compensation maths with unsuffixed
|
||||
double literals, which otherwise links roughly 5 KB of double soft-float helpers
|
||||
and overflows flash.
|
||||
|
||||
## Output
|
||||
|
||||
CSV at 10 Hz on the serial port, with a header line printed once at startup:
|
||||
|
||||
```
|
||||
ms,i2c_status,i2c_p_mbar,i2c_t_c,spi_status,spi_p_mbar,spi_t_c
|
||||
1043,OK,1013.24,24.31,OK,1013.19,24.44
|
||||
1143,OK,1013.25,24.31,ERR_READ,1013.19,24.44
|
||||
```
|
||||
|
||||
Pressure is in mbar, temperature in degrees C, both to two decimals. The status
|
||||
column is authoritative: when it is not `OK`, the two values next to it are the
|
||||
last ones successfully computed, not fresh readings.
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| `INIT` | not initialised yet |
|
||||
| `OK` | reading passed every check |
|
||||
| `ERR_INIT` | reset / PROM handshake did not complete |
|
||||
| `ERR_CRC` | factory calibration failed CRC-4, or is all-zero / all-ones |
|
||||
| `ERR_READ` | driver reported a bus or ADC failure |
|
||||
| `ERR_RANGE` | value outside the MS5611 operating envelope |
|
||||
| `ERR_STALE` | sensor still answers but stopped producing new samples |
|
||||
|
||||
Sensors are polled continuously, as fast as the ADC conversions allow (roughly
|
||||
90 Hz per sensor at OSR 1024); the serial report is throttled to 10 Hz.
|
||||
|
||||
## Validation
|
||||
|
||||
Neither driver library detects every failure on its own, so the checks live in
|
||||
[`BaroChannel`](src/baro_channel.cpp):
|
||||
|
||||
- **PROM CRC-4** at init, per datasheet p.13 / AN520. Neither library verifies it.
|
||||
- **All-zero / all-ones PROM screening**, because an absent part reads back as
|
||||
one of those, `0xFFFF` is accepted by both libraries' `reset()`, and an
|
||||
all-zero PROM satisfies the CRC.
|
||||
- **Driver return code** on every read.
|
||||
- **Range check** against 10..1200 mbar and -40..+85 C. This is the one that
|
||||
catches the failure the I2C driver misses entirely: an ADC read taken before
|
||||
the conversion completes returns 0 (datasheet p.11), and `MS5611::read()`
|
||||
still reports `MS5611_READ_OK` for it.
|
||||
- **Stale detection** - bit-identical pressure *and* temperature for 32
|
||||
consecutive polls means the part stopped converting while still acknowledging.
|
||||
|
||||
Any failure lights that sensor's LED for 1 second (non-blocking, extended if
|
||||
further errors follow). After 10 consecutive failures the channel is taken back
|
||||
through the full reset and PROM handshake, so a sensor that was unplugged and
|
||||
reconnected recovers on its own.
|
||||
|
||||
The CRC-4 implementation was verified differentially on the host against the
|
||||
independent NuttX reference over 200000 randomised PROM images.
|
||||
|
||||
## References
|
||||
- [Barometric Pressure Sensor MS5611](docs/datasheets/ENG_DS_MS5611-01BA03_B3.pdf)
|
||||
- [STM32F042K6T6 Nucleo](docs/datasheets/DS_stm32f042k6.pdf)
|
||||
- [STM32 Nucleo-32 boards (MB1180)](https://www.st.com/resource/en/user_manual/um1956-stm32-nucleo32-boards-mb1180-stmicroelectronics.pdf) - UM1956, LED and connector tables
|
||||
BIN
docs/datasheets/DB_nucleo-f042k6.pdf
Normal file
BIN
docs/datasheets/DB_nucleo-f042k6.pdf
Normal file
Binary file not shown.
BIN
docs/datasheets/DS_stm32f042k6.pdf
Normal file
BIN
docs/datasheets/DS_stm32f042k6.pdf
Normal file
Binary file not shown.
BIN
docs/datasheets/ENG_DS_MS5611-01BA03_B3.pdf
Normal file
BIN
docs/datasheets/ENG_DS_MS5611-01BA03_B3.pdf
Normal file
Binary file not shown.
BIN
docs/wiring-physical.png
Normal file
BIN
docs/wiring-physical.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 637 KiB |
330
docs/wiring-physical.svg
Normal file
330
docs/wiring-physical.svg
Normal file
@@ -0,0 +1,330 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1120" width="1600" height="1120">
|
||||
<defs>
|
||||
<linearGradient id="sheen" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.10"/>
|
||||
<stop offset="55%" stop-color="#ffffff" stop-opacity="0.02"/>
|
||||
<stop offset="100%" stop-color="#000000" stop-opacity="0.10"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="usb" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#e9ecef"/>
|
||||
<stop offset="50%" stop-color="#c7ccd1"/>
|
||||
<stop offset="100%" stop-color="#9ba2a9"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<style>
|
||||
text { font-family: "Helvetica Neue", Arial, sans-serif; fill: #1b2733; }
|
||||
.mono { font-family: "SF Mono", "DejaVu Sans Mono", Menlo, Consolas, monospace; }
|
||||
.title{ font-size: 25px; font-weight: 700; }
|
||||
.sub { font-size: 13.5px; fill: #5a6b7d; }
|
||||
.hdr { font-size: 16px; font-weight: 700; }
|
||||
.spec { font-size: 12.5px; fill: #5a6b7d; }
|
||||
.note { font-size: 12.5px; }
|
||||
.silk { fill: #ffffff; font-size: 11px; font-weight: 600; }
|
||||
.silkb{ fill: #ffffff; font-size: 15px; font-weight: 700; letter-spacing: 1px; }
|
||||
.bsilk{ font-size: 10px; fill: #2f4f7f; font-weight: 600; }
|
||||
.panel{ fill: #f4f6f8; stroke: #b6c1cc; stroke-width: 1.5; }
|
||||
|
||||
.jw { fill: none; stroke-width: 4.6; stroke-linecap: round; }
|
||||
.c-v33 { stroke: #d62828; }
|
||||
.c-gnd { stroke: #22252a; }
|
||||
.c-sda { stroke: #1f6feb; }
|
||||
.c-scl { stroke: #00a0b0; }
|
||||
.c-sck { stroke: #e07b00; }
|
||||
.c-mosi{ stroke: #b5179e; }
|
||||
.c-miso{ stroke: #7209b7; }
|
||||
.c-cs { stroke: #c99700; }
|
||||
.c-led { stroke: #2a9d4a; }
|
||||
|
||||
.rail-g { fill: none; stroke: #22252a; stroke-width: 7; stroke-linecap: round; }
|
||||
.rail-v { fill: none; stroke: #d62828; stroke-width: 7; stroke-linecap: round; }
|
||||
</style>
|
||||
|
||||
<rect x="0" y="0" width="1600" height="1120" fill="#ffffff"/>
|
||||
|
||||
<text class="title" x="800" y="38" text-anchor="middle">Physical wiring - NUCLEO-F042K6 + 2 x GY-63 (MS5611)</text>
|
||||
<text class="sub" x="800" y="62" text-anchor="middle">Board seen from the component side, USB at the top. Header order follows ST UM1956 Table 10. Highlighted pads are the ones this firmware uses.</text>
|
||||
|
||||
<!-- =================================================================== -->
|
||||
<!-- NUCLEO-F042K6 -->
|
||||
<!-- =================================================================== -->
|
||||
<g id="nucleo">
|
||||
<!-- USB Micro-B, CN1 -->
|
||||
<rect x="762" y="122" width="66" height="34" rx="3" fill="url(#usb)" stroke="#7b8288" stroke-width="1.2"/>
|
||||
<rect x="770" y="128" width="50" height="14" rx="2" fill="#6f767c"/>
|
||||
|
||||
<!-- PCB -->
|
||||
<rect x="700" y="150" width="190" height="530" rx="8" fill="#f7f8f9" stroke="#c3c9ce" stroke-width="1.6"/>
|
||||
<rect x="706" y="156" width="178" height="518" rx="5" fill="none" stroke="#3a7ac0" stroke-width="1"/>
|
||||
|
||||
<!-- ST-LINK section -->
|
||||
<rect x="718" y="163" width="24" height="20" rx="3" fill="#2f9e44" stroke="#1e6f2f" stroke-width="1"/>
|
||||
<text class="bsilk" x="730" y="196" text-anchor="middle">LD1</text>
|
||||
<rect x="762" y="248" width="66" height="40" rx="2" fill="#1b1b1b"/>
|
||||
<text x="795" y="272" text-anchor="middle" font-size="9" fill="#8a8a8a" class="mono">ST-LINK</text>
|
||||
<text x="795" y="232" text-anchor="middle" font-size="19" font-weight="700" fill="#03234b">ST</text>
|
||||
|
||||
<!-- target MCU, LQFP32 -->
|
||||
<rect x="757" y="424" width="76" height="76" rx="3" fill="#1b1b1b"/>
|
||||
<circle cx="766" cy="433" r="3.4" fill="#4a4a4a"/>
|
||||
<text x="795" y="466" text-anchor="middle" font-size="8.5" fill="#8a8a8a" class="mono">STM32F042</text>
|
||||
<g fill="#5b6167">
|
||||
<rect x="749" y="432" width="8" height="3"/><rect x="749" y="443" width="8" height="3"/>
|
||||
<rect x="749" y="454" width="8" height="3"/><rect x="749" y="465" width="8" height="3"/>
|
||||
<rect x="749" y="476" width="8" height="3"/><rect x="749" y="487" width="8" height="3"/>
|
||||
<rect x="833" y="432" width="8" height="3"/><rect x="833" y="443" width="8" height="3"/>
|
||||
<rect x="833" y="454" width="8" height="3"/><rect x="833" y="465" width="8" height="3"/>
|
||||
<rect x="833" y="476" width="8" height="3"/><rect x="833" y="487" width="8" height="3"/>
|
||||
</g>
|
||||
|
||||
<!-- bottom features -->
|
||||
<rect x="716" y="640" width="20" height="16" rx="2" fill="#2f9e44" stroke="#1e6f2f" stroke-width="1"/>
|
||||
<text class="bsilk" x="726" y="668" text-anchor="middle">LD2</text>
|
||||
<rect x="854" y="640" width="20" height="16" rx="2" fill="#2f9e44" stroke="#1e6f2f" stroke-width="1"/>
|
||||
<text class="bsilk" x="864" y="668" text-anchor="middle">LD3</text>
|
||||
<rect x="778" y="638" width="34" height="20" rx="3" fill="#d9dde1" stroke="#9aa1a8" stroke-width="1"/>
|
||||
<text class="bsilk" x="795" y="668" text-anchor="middle">B1</text>
|
||||
|
||||
<text class="bsilk" x="714" y="215" text-anchor="middle" font-size="9">CN3</text>
|
||||
<text class="bsilk" x="876" y="215" text-anchor="middle" font-size="9">CN4</text>
|
||||
</g>
|
||||
|
||||
<!-- header pads: generated positions, CN3 x=714 / CN4 x=876, pin i y = 240+(i-1)*28 -->
|
||||
<g id="pads">
|
||||
<!-- CN3 gold rings -->
|
||||
<g fill="#d9b64a">
|
||||
<circle cx="714" cy="240" r="7"/><circle cx="714" cy="268" r="7"/><circle cx="714" cy="296" r="7"/>
|
||||
<circle cx="714" cy="324" r="7"/><circle cx="714" cy="352" r="7"/><circle cx="714" cy="380" r="7"/>
|
||||
<circle cx="714" cy="408" r="7"/><circle cx="714" cy="436" r="7"/><circle cx="714" cy="464" r="7"/>
|
||||
<circle cx="714" cy="492" r="7"/><circle cx="714" cy="520" r="7"/><circle cx="714" cy="548" r="7"/>
|
||||
<circle cx="714" cy="576" r="7"/><circle cx="714" cy="604" r="7"/><circle cx="714" cy="632" r="7"/>
|
||||
<circle cx="876" cy="240" r="7"/><circle cx="876" cy="268" r="7"/><circle cx="876" cy="296" r="7"/>
|
||||
<circle cx="876" cy="324" r="7"/><circle cx="876" cy="352" r="7"/><circle cx="876" cy="380" r="7"/>
|
||||
<circle cx="876" cy="408" r="7"/><circle cx="876" cy="436" r="7"/><circle cx="876" cy="464" r="7"/>
|
||||
<circle cx="876" cy="492" r="7"/><circle cx="876" cy="520" r="7"/><circle cx="876" cy="548" r="7"/>
|
||||
<circle cx="876" cy="576" r="7"/><circle cx="876" cy="604" r="7"/><circle cx="876" cy="632" r="7"/>
|
||||
</g>
|
||||
<g fill="#ffffff">
|
||||
<circle cx="714" cy="240" r="3"/><circle cx="714" cy="268" r="3"/><circle cx="714" cy="296" r="3"/>
|
||||
<circle cx="714" cy="324" r="3"/><circle cx="714" cy="352" r="3"/><circle cx="714" cy="380" r="3"/>
|
||||
<circle cx="714" cy="408" r="3"/><circle cx="714" cy="436" r="3"/><circle cx="714" cy="464" r="3"/>
|
||||
<circle cx="714" cy="492" r="3"/><circle cx="714" cy="520" r="3"/><circle cx="714" cy="548" r="3"/>
|
||||
<circle cx="714" cy="576" r="3"/><circle cx="714" cy="604" r="3"/><circle cx="714" cy="632" r="3"/>
|
||||
<circle cx="876" cy="240" r="3"/><circle cx="876" cy="268" r="3"/><circle cx="876" cy="296" r="3"/>
|
||||
<circle cx="876" cy="324" r="3"/><circle cx="876" cy="352" r="3"/><circle cx="876" cy="380" r="3"/>
|
||||
<circle cx="876" cy="408" r="3"/><circle cx="876" cy="436" r="3"/><circle cx="876" cy="464" r="3"/>
|
||||
<circle cx="876" cy="492" r="3"/><circle cx="876" cy="520" r="3"/><circle cx="876" cy="548" r="3"/>
|
||||
<circle cx="876" cy="576" r="3"/><circle cx="876" cy="604" r="3"/><circle cx="876" cy="632" r="3"/>
|
||||
</g>
|
||||
<!-- used pads get a coloured ring -->
|
||||
<g fill="none" stroke-width="2.6">
|
||||
<circle cx="714" cy="380" r="10.5" stroke="#2a9d4a"/>
|
||||
<circle cx="714" cy="464" r="10.5" stroke="#2a9d4a"/>
|
||||
<circle cx="714" cy="576" r="10.5" stroke="#c99700"/>
|
||||
<circle cx="714" cy="604" r="10.5" stroke="#b5179e"/>
|
||||
<circle cx="714" cy="632" r="10.5" stroke="#7209b7"/>
|
||||
<circle cx="876" cy="268" r="10.5" stroke="#22252a"/>
|
||||
<circle cx="876" cy="408" r="10.5" stroke="#00a0b0"/>
|
||||
<circle cx="876" cy="436" r="10.5" stroke="#1f6feb"/>
|
||||
<circle cx="876" cy="604" r="10.5" stroke="#d62828"/>
|
||||
<circle cx="876" cy="632" r="10.5" stroke="#e07b00"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- header silkscreen -->
|
||||
<g class="bsilk">
|
||||
<text x="729" y="244">D1</text> <text x="729" y="272">D0</text> <text x="729" y="300">RST</text>
|
||||
<text x="729" y="328">GND</text> <text x="729" y="356">D2</text> <text x="729" y="384">D3</text>
|
||||
<text x="729" y="412">D4</text> <text x="729" y="440">D5</text> <text x="729" y="468">D6</text>
|
||||
<text x="729" y="496">D7</text> <text x="729" y="524">D8</text> <text x="729" y="552">D9</text>
|
||||
<text x="729" y="580">D10</text> <text x="729" y="608">D11</text> <text x="729" y="636">D12</text>
|
||||
<text x="861" y="244" text-anchor="end">VIN</text> <text x="861" y="272" text-anchor="end">GND</text>
|
||||
<text x="861" y="300" text-anchor="end">RST</text> <text x="861" y="328" text-anchor="end">5V</text>
|
||||
<text x="861" y="356" text-anchor="end">A7</text> <text x="861" y="384" text-anchor="end">A6</text>
|
||||
<text x="861" y="412" text-anchor="end">A5</text> <text x="861" y="440" text-anchor="end">A4</text>
|
||||
<text x="861" y="468" text-anchor="end">A3</text> <text x="861" y="496" text-anchor="end">A2</text>
|
||||
<text x="861" y="524" text-anchor="end">A1</text> <text x="861" y="552" text-anchor="end">A0</text>
|
||||
<text x="861" y="580" text-anchor="end">REF</text> <text x="861" y="608" text-anchor="end">3V3</text>
|
||||
<text x="861" y="636" text-anchor="end">D13</text>
|
||||
</g>
|
||||
|
||||
<!-- =================================================================== -->
|
||||
<!-- GY-63 #2 - SPI, left -->
|
||||
<!-- =================================================================== -->
|
||||
<g id="gy63-spi" transform="translate(300,470)">
|
||||
<rect x="0" y="0" width="150" height="190" rx="5" fill="#174a91" stroke="#0e2f5e" stroke-width="1.5"/>
|
||||
<rect x="0" y="0" width="150" height="190" rx="5" fill="url(#sheen)"/>
|
||||
<circle cx="26" cy="27" r="14" fill="#c9a227"/><circle cx="26" cy="27" r="10" fill="#ffffff"/>
|
||||
<g fill="#d9b64a">
|
||||
<circle cx="133" cy="24" r="8"/><circle cx="133" cy="47" r="8"/><circle cx="133" cy="70" r="8"/>
|
||||
<circle cx="133" cy="93" r="8"/><circle cx="133" cy="116" r="8"/><circle cx="133" cy="139" r="8"/>
|
||||
<circle cx="133" cy="162" r="8"/>
|
||||
</g>
|
||||
<g fill="#10305f">
|
||||
<circle cx="133" cy="24" r="3.6"/><circle cx="133" cy="47" r="3.6"/><circle cx="133" cy="70" r="3.6"/>
|
||||
<circle cx="133" cy="93" r="3.6"/><circle cx="133" cy="116" r="3.6"/><circle cx="133" cy="139" r="3.6"/>
|
||||
<circle cx="133" cy="162" r="3.6"/>
|
||||
</g>
|
||||
<text class="silk" x="120" y="28" text-anchor="end">PS</text>
|
||||
<text class="silk" x="120" y="51" text-anchor="end">SDO</text>
|
||||
<text class="silk" x="120" y="74" text-anchor="end">CSB</text>
|
||||
<text class="silk" x="120" y="97" text-anchor="end">SDA</text>
|
||||
<text class="silk" x="120" y="120" text-anchor="end">SCL</text>
|
||||
<text class="silk" x="120" y="143" text-anchor="end">GND</text>
|
||||
<text class="silk" x="120" y="166" text-anchor="end">VCC</text>
|
||||
<rect x="36" y="88" width="52" height="42" rx="3" fill="#b9bfc6" stroke="#7e878f" stroke-width="1.2"/>
|
||||
<rect x="40" y="92" width="44" height="34" rx="2" fill="#a8aeb5"/>
|
||||
<circle cx="62" cy="109" r="7" fill="#5c646c"/><circle cx="62" cy="109" r="4" fill="#3a4148"/>
|
||||
<rect x="38" y="156" width="20" height="11" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="66" y="156" width="20" height="11" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="72" y="140" width="14" height="9" rx="1" fill="#d8801f"/>
|
||||
<rect x="36" y="70" width="18" height="10" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="68" y="70" width="18" height="10" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="56" y="52" width="12" height="8" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="74" y="52" width="12" height="8" rx="1" fill="#1b1b1b"/>
|
||||
<text class="silkb" x="14" y="150" transform="rotate(-90,14,150)">MS5611</text>
|
||||
</g>
|
||||
<text class="hdr" x="375" y="452" text-anchor="middle">GY-63 #2 - SPI</text>
|
||||
|
||||
<!-- =================================================================== -->
|
||||
<!-- GY-63 #1 - I2C, right -->
|
||||
<!-- =================================================================== -->
|
||||
<g id="gy63-i2c" transform="translate(1150,330)">
|
||||
<rect x="0" y="0" width="150" height="190" rx="5" fill="#174a91" stroke="#0e2f5e" stroke-width="1.5"/>
|
||||
<rect x="0" y="0" width="150" height="190" rx="5" fill="url(#sheen)"/>
|
||||
<circle cx="124" cy="163" r="14" fill="#c9a227"/><circle cx="124" cy="163" r="10" fill="#ffffff"/>
|
||||
<g fill="#d9b64a">
|
||||
<circle cx="17" cy="24" r="8"/><circle cx="17" cy="47" r="8"/><circle cx="17" cy="70" r="8"/>
|
||||
<circle cx="17" cy="93" r="8"/><circle cx="17" cy="116" r="8"/><circle cx="17" cy="139" r="8"/>
|
||||
<circle cx="17" cy="162" r="8"/>
|
||||
</g>
|
||||
<g fill="#10305f">
|
||||
<circle cx="17" cy="24" r="3.6"/><circle cx="17" cy="47" r="3.6"/><circle cx="17" cy="70" r="3.6"/>
|
||||
<circle cx="17" cy="93" r="3.6"/><circle cx="17" cy="116" r="3.6"/><circle cx="17" cy="139" r="3.6"/>
|
||||
<circle cx="17" cy="162" r="3.6"/>
|
||||
</g>
|
||||
<text class="silk" x="30" y="28">VCC</text><text class="silk" x="30" y="51">GND</text>
|
||||
<text class="silk" x="30" y="74">SCL</text><text class="silk" x="30" y="97">SDA</text>
|
||||
<text class="silk" x="30" y="120">CSB</text><text class="silk" x="30" y="143">SDO</text>
|
||||
<text class="silk" x="30" y="166">PS</text>
|
||||
<rect x="62" y="58" width="52" height="42" rx="3" fill="#b9bfc6" stroke="#7e878f" stroke-width="1.2"/>
|
||||
<rect x="66" y="62" width="44" height="34" rx="2" fill="#a8aeb5"/>
|
||||
<circle cx="88" cy="79" r="7" fill="#5c646c"/><circle cx="88" cy="79" r="4" fill="#3a4148"/>
|
||||
<rect x="64" y="22" width="20" height="11" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="92" y="22" width="20" height="11" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="64" y="40" width="14" height="9" rx="1" fill="#d8801f"/>
|
||||
<rect x="64" y="110" width="18" height="10" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="96" y="110" width="18" height="10" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="64" y="128" width="12" height="8" rx="1" fill="#1b1b1b"/>
|
||||
<rect x="82" y="128" width="12" height="8" rx="1" fill="#1b1b1b"/>
|
||||
<text class="silkb" x="140" y="38" transform="rotate(90,140,38)">MS5611</text>
|
||||
</g>
|
||||
<text class="hdr" x="1225" y="312" text-anchor="middle">GY-63 #1 - I2C</text>
|
||||
|
||||
<!-- =================================================================== -->
|
||||
<!-- POWER RAILS -->
|
||||
<!-- =================================================================== -->
|
||||
<path class="rail-g" d="M 300,940 L 1290,940"/>
|
||||
<path class="rail-v" d="M 240,988 L 1400,988"/>
|
||||
<text class="mono" x="1302" y="945" font-size="13" fill="#22252a">GND rail</text>
|
||||
<text class="mono" x="1412" y="993" font-size="13" fill="#d62828">3V3 rail</text>
|
||||
|
||||
<!-- =================================================================== -->
|
||||
<!-- JUMPER WIRES -->
|
||||
<!-- =================================================================== -->
|
||||
<!-- I2C signals: module #1 -> CN4 A5 / A4 -->
|
||||
<path class="jw c-scl" d="M 1167,400 C 1080,398 970,406 883,408"/>
|
||||
<path class="jw c-sda" d="M 1167,423 C 1080,422 970,432 883,436"/>
|
||||
|
||||
<!-- SPI signals: module #2 -> CN3 D10 / D11 / D12 -->
|
||||
<path class="jw c-cs" d="M 433,540 C 540,545 610,570 707,576"/>
|
||||
<path class="jw c-mosi" d="M 433,563 C 540,570 610,598 707,604"/>
|
||||
<path class="jw c-miso" d="M 433,517 C 560,520 620,614 707,632"/>
|
||||
<!-- SCK has to travel to the far side: CN4 pin 15 -->
|
||||
<path class="jw c-sck" d="M 433,586 C 560,600 520,732 660,742 C 820,752 962,730 962,670 C 962,642 918,632 883,632"/>
|
||||
|
||||
<!-- power taps from the board -->
|
||||
<path class="jw c-v33" d="M 883,604 C 940,616 946,860 1006,988"/>
|
||||
<path class="jw c-gnd" d="M 883,268 C 1006,300 1028,760 1046,940"/>
|
||||
|
||||
<!-- module #1 power and straps -->
|
||||
<path class="jw c-v33" d="M 1167,354 C 1062,404 1010,790 1120,988"/>
|
||||
<path class="jw c-gnd" d="M 1167,377 C 1090,448 1064,824 1176,940"/>
|
||||
<path class="jw c-gnd" d="M 1167,446 C 1122,600 1152,852 1244,940"/>
|
||||
<path class="jw c-v33" d="M 1167,492 C 1206,650 1258,880 1334,988"/>
|
||||
|
||||
<!-- module #2 power and strap -->
|
||||
<path class="jw c-v33" d="M 433,632 C 540,730 520,880 500,988"/>
|
||||
<path class="jw c-gnd" d="M 433,609 C 510,700 410,860 380,940"/>
|
||||
<path class="jw c-gnd" d="M 433,494 C 540,506 560,812 440,940"/>
|
||||
|
||||
<!-- LED drives -->
|
||||
<path class="jw c-led" d="M 707,380 C 600,420 500,652 560,772"/>
|
||||
<path class="jw c-led" d="M 707,464 C 648,512 662,664 700,772"/>
|
||||
|
||||
<!-- =================================================================== -->
|
||||
<!-- LEDS -->
|
||||
<!-- =================================================================== -->
|
||||
<g id="led1">
|
||||
<rect x="545" y="772" width="30" height="48" rx="2" fill="#ffffff" stroke="#22252a" stroke-width="2"/>
|
||||
<line x1="560" y1="820" x2="560" y2="842" stroke="#2a9d4a" stroke-width="4.6" stroke-linecap="round"/>
|
||||
<polygon points="542,842 578,842 560,872" fill="#ffffff" stroke="#22252a" stroke-width="2"/>
|
||||
<line x1="540" y1="872" x2="580" y2="872" stroke="#22252a" stroke-width="2.6" stroke-linecap="round"/>
|
||||
<path class="jw c-gnd" d="M 560,872 L 560,940"/>
|
||||
<text class="mono" x="590" y="800" font-size="12">510 R</text>
|
||||
<text x="590" y="862" font-size="12">I2C error</text>
|
||||
</g>
|
||||
<g id="led2">
|
||||
<rect x="685" y="772" width="30" height="48" rx="2" fill="#ffffff" stroke="#22252a" stroke-width="2"/>
|
||||
<line x1="700" y1="820" x2="700" y2="842" stroke="#2a9d4a" stroke-width="4.6" stroke-linecap="round"/>
|
||||
<polygon points="682,842 718,842 700,872" fill="#ffffff" stroke="#22252a" stroke-width="2"/>
|
||||
<line x1="680" y1="872" x2="720" y2="872" stroke="#22252a" stroke-width="2.6" stroke-linecap="round"/>
|
||||
<path class="jw c-gnd" d="M 700,872 L 700,940"/>
|
||||
<text class="mono" x="734" y="800" font-size="12">510 R</text>
|
||||
<text x="734" y="862" font-size="12">SPI error</text>
|
||||
</g>
|
||||
|
||||
<!-- junction dots where a wire really meets a rail -->
|
||||
<g fill="#22252a">
|
||||
<circle cx="1046" cy="940" r="5"/><circle cx="1176" cy="940" r="5"/><circle cx="1244" cy="940" r="5"/>
|
||||
<circle cx="380" cy="940" r="5"/><circle cx="440" cy="940" r="5"/>
|
||||
<circle cx="560" cy="940" r="5"/><circle cx="700" cy="940" r="5"/>
|
||||
</g>
|
||||
<g fill="#d62828">
|
||||
<circle cx="1006" cy="988" r="5"/><circle cx="1120" cy="988" r="5"/>
|
||||
<circle cx="1334" cy="988" r="5"/><circle cx="500" cy="988" r="5"/>
|
||||
</g>
|
||||
|
||||
<!-- =================================================================== -->
|
||||
<!-- LEGEND + NOTES -->
|
||||
<!-- =================================================================== -->
|
||||
<rect class="panel" x="60" y="742" width="300" height="176" rx="6"/>
|
||||
<text class="hdr" x="76" y="768">WIRE COLOURS</text>
|
||||
<g stroke-width="4.6" stroke-linecap="round">
|
||||
<line x1="80" y1="790" x2="118" y2="790" stroke="#d62828"/><text x="128" y="795" font-size="12.5">3V3</text>
|
||||
<line x1="80" y1="812" x2="118" y2="812" stroke="#22252a"/><text x="128" y="817" font-size="12.5">GND</text>
|
||||
<line x1="80" y1="834" x2="118" y2="834" stroke="#00a0b0"/><text x="128" y="839" font-size="12.5">SCL</text>
|
||||
<line x1="80" y1="856" x2="118" y2="856" stroke="#1f6feb"/><text x="128" y="861" font-size="12.5">SDA</text>
|
||||
<line x1="80" y1="878" x2="118" y2="878" stroke="#2a9d4a"/><text x="128" y="883" font-size="12.5">LED drive</text>
|
||||
<line x1="212" y1="790" x2="250" y2="790" stroke="#e07b00"/><text x="260" y="795" font-size="12.5">SCK</text>
|
||||
<line x1="212" y1="812" x2="250" y2="812" stroke="#b5179e"/><text x="260" y="817" font-size="12.5">MOSI</text>
|
||||
<line x1="212" y1="834" x2="250" y2="834" stroke="#7209b7"/><text x="260" y="839" font-size="12.5">MISO</text>
|
||||
<line x1="212" y1="856" x2="250" y2="856" stroke="#c99700"/><text x="260" y="861" font-size="12.5">CS</text>
|
||||
</g>
|
||||
|
||||
<rect class="panel" x="60" y="92" width="490" height="336" rx="6"/>
|
||||
<text class="hdr" x="78" y="118">NOTES</text>
|
||||
<text class="note" x="78" y="144">SPI is split across both headers: MOSI / MISO / CS sit on CN3</text>
|
||||
<text class="note" x="78" y="162">(D11 / D12 / D10) but SCK is CN4 pin 15 (D13). One jumper has</text>
|
||||
<text class="note" x="78" y="180">to cross to the far side of the board - that is normal here.</text>
|
||||
<text class="note" x="78" y="206">I2C is wired to A5 / A4 on CN4. Solder bridges SB16 / SB18 are</text>
|
||||
<text class="note" x="78" y="224">closed from the factory, which puts PB6 / PB7 on those pads.</text>
|
||||
<text class="note" x="78" y="242">D5 / D4 on CN3 are the same two nets - either pair works.</text>
|
||||
<text class="note" x="78" y="268">Do not power the sensors from 5V. MS5611 runs on 1.8 - 3.6 V.</text>
|
||||
<text class="note" x="78" y="294">Both LEDs are external and active high: the GPIO drives the</text>
|
||||
<text class="note" x="78" y="312">anode through 510 R. LD3 on the board cannot be used - it</text>
|
||||
<text class="note" x="78" y="330">shares PB3 with SPI1 SCK.</text>
|
||||
<text class="note" x="78" y="356">A dot means the wire connects to the rail. Wires that merely</text>
|
||||
<text class="note" x="78" y="374">cross without a dot are not connected.</text>
|
||||
<text class="note" x="78" y="400">Sensor #2 is drawn turned 180 deg so its pad row faces the board -</text>
|
||||
<text class="note" x="78" y="418">that is why its pads read PS first and VCC last.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/wiring.png
Normal file
BIN
docs/wiring.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 392 KiB |
226
docs/wiring.svg
Normal file
226
docs/wiring.svg
Normal file
@@ -0,0 +1,226 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1500 1010" width="1500" height="1010">
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
text { font-family: "Helvetica Neue", Arial, sans-serif; fill: #1b2733; }
|
||||
.mono { font-family: "SF Mono", "DejaVu Sans Mono", Menlo, Consolas, monospace; }
|
||||
.title { font-size: 25px; font-weight: 700; }
|
||||
.sub { font-size: 13.5px; fill: #5a6b7d; }
|
||||
.bname { font-size: 19px; font-weight: 700; }
|
||||
.mname { font-size: 16px; font-weight: 700; }
|
||||
.spec { font-size: 12.5px; fill: #5a6b7d; }
|
||||
.pin { font-size: 13px; }
|
||||
.net { font-size: 12px; }
|
||||
.note { font-size: 12.5px; }
|
||||
.lgd { font-size: 13px; }
|
||||
|
||||
.board { fill: #e8eef6; stroke: #2f4f7f; stroke-width: 2; }
|
||||
.modu { fill: #faf3e6; stroke: #96703a; stroke-width: 2; }
|
||||
.panel { fill: #f4f6f8; stroke: #b6c1cc; stroke-width: 1.5; }
|
||||
|
||||
.w { fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.w-v33 { stroke: #cc2b2b; }
|
||||
.w-gnd { stroke: #2b2f36; }
|
||||
.w-i2c { stroke: #1668c4; }
|
||||
.w-spi { stroke: #d98207; }
|
||||
.w-led { stroke: #1f9350; }
|
||||
.rail { fill: none; stroke: #cc2b2b; stroke-width: 3.4; stroke-linecap: round; }
|
||||
|
||||
.stub { stroke: #2f4f7f; stroke-width: 2; }
|
||||
.pad { fill: #96703a; }
|
||||
.jn { fill: #cc2b2b; }
|
||||
.part { fill: #ffffff; stroke: #2b2f36; stroke-width: 2; }
|
||||
.gsym { fill: none; stroke: #2b2f36; stroke-width: 2; stroke-linecap: round; }
|
||||
</style>
|
||||
|
||||
<rect class="bg" x="0" y="0" width="1500" height="1010"/>
|
||||
|
||||
<text class="title" x="750" y="38" text-anchor="middle">NUCLEO-F042K6 + 2 x GY-63 (MS5611) - wiring</text>
|
||||
<text class="sub" x="750" y="60" text-anchor="middle">Logical net diagram. Pins are grouped by function, not by physical header order - see UM1956 Table 10 for the real connector layout.</text>
|
||||
|
||||
<!-- ============================ POWER SOURCE ============================ -->
|
||||
<rect class="panel" x="90" y="90" width="300" height="86" rx="6"/>
|
||||
<text class="mname" x="106" y="114">POWER SOURCE</text>
|
||||
<text class="spec" x="106" y="136">USB Micro-B to CN1, 5 V from the host PC</text>
|
||||
<text class="spec" x="106" y="154">ST-LINK V2-1 + on-board 3.3 V LDO</text>
|
||||
<text class="spec" x="106" y="170">same cable carries the serial VCP</text>
|
||||
|
||||
<!-- USB feed into the board, hopping over the 3V3 rail -->
|
||||
<path class="w w-gnd" d="M 390,133 L 630,133 L 630,196 A 9,9 0 0,0 630,214 L 630,250"/>
|
||||
<text class="net mono" x="452" y="126">5 V + serial</text>
|
||||
|
||||
<!-- ============================ 3V3 RAIL ============================ -->
|
||||
<path class="rail" d="M 490,205 L 1350,205"/>
|
||||
<text class="pin mono" x="1362" y="210" fill="#cc2b2b">+3V3</text>
|
||||
<circle class="jn" cx="490" cy="205" r="4.5"/>
|
||||
<circle class="jn" cx="720" cy="205" r="4.5"/>
|
||||
<circle class="jn" cx="1050" cy="205" r="4.5"/>
|
||||
|
||||
<!-- ============================ NUCLEO BOARD ============================ -->
|
||||
<rect class="board" x="600" y="250" width="300" height="540" rx="10"/>
|
||||
<text class="bname" x="750" y="332" text-anchor="middle">NUCLEO-F042K6</text>
|
||||
<text class="spec" x="750" y="354" text-anchor="middle">STM32F042K6T6 - board MB1180</text>
|
||||
<text class="spec" x="750" y="372" text-anchor="middle">32 KB flash / 6 KB RAM</text>
|
||||
|
||||
<!-- top edge pins -->
|
||||
<line class="stub" x1="630" y1="250" x2="630" y2="240"/>
|
||||
<line class="stub" x1="720" y1="250" x2="720" y2="240"/>
|
||||
<line class="stub" x1="790" y1="250" x2="790" y2="240"/>
|
||||
<text class="pin mono" x="630" y="272" text-anchor="middle">CN1</text>
|
||||
<text class="pin mono" x="720" y="272" text-anchor="middle">3V3</text>
|
||||
<text class="pin mono" x="790" y="272" text-anchor="middle">GND</text>
|
||||
|
||||
<!-- left edge pins: I2C -->
|
||||
<line class="stub" x1="600" y1="440" x2="590" y2="440"/>
|
||||
<line class="stub" x1="600" y1="475" x2="590" y2="475"/>
|
||||
<text class="pin mono" x="616" y="444">PB6 / D5 <tspan class="spec">(pad A5)</tspan></text>
|
||||
<text class="pin mono" x="616" y="479">PB7 / D4 <tspan class="spec">(pad A4)</tspan></text>
|
||||
|
||||
<!-- right edge pins: SPI -->
|
||||
<line class="stub" x1="900" y1="440" x2="910" y2="440"/>
|
||||
<line class="stub" x1="900" y1="475" x2="910" y2="475"/>
|
||||
<line class="stub" x1="900" y1="510" x2="910" y2="510"/>
|
||||
<line class="stub" x1="900" y1="545" x2="910" y2="545"/>
|
||||
<text class="pin mono" x="884" y="444" text-anchor="end">PB3 / D13</text>
|
||||
<text class="pin mono" x="884" y="479" text-anchor="end">PB5 / D11</text>
|
||||
<text class="pin mono" x="884" y="514" text-anchor="end">PA11 / D10</text>
|
||||
<text class="pin mono" x="884" y="549" text-anchor="end">PB4 / D12</text>
|
||||
|
||||
<!-- bottom edge pins: LEDs -->
|
||||
<line class="stub" x1="660" y1="790" x2="660" y2="800"/>
|
||||
<line class="stub" x1="850" y1="790" x2="850" y2="800"/>
|
||||
<text class="pin mono" x="660" y="778" text-anchor="middle">PB0 / D3</text>
|
||||
<text class="pin mono" x="850" y="778" text-anchor="middle">PB1 / D6</text>
|
||||
|
||||
<text class="spec" x="750" y="702" text-anchor="middle">USART2 = PA2 / PA15 to ST-LINK VCP</text>
|
||||
<text class="spec" x="750" y="720" text-anchor="middle">115200 8N1 - no USB-serial adapter needed</text>
|
||||
|
||||
<!-- ============================ GY-63 #1 : I2C ============================ -->
|
||||
<rect class="modu" x="110" y="280" width="280" height="340" rx="8"/>
|
||||
<text class="mname" x="250" y="312" text-anchor="middle">GY-63 #1</text>
|
||||
<text class="spec" x="250" y="334" text-anchor="middle">MS5611-01BA03 - I2C mode</text>
|
||||
|
||||
<circle class="pad" cx="390" cy="370" r="4"/>
|
||||
<circle class="pad" cx="390" cy="405" r="4"/>
|
||||
<circle class="pad" cx="390" cy="440" r="4"/>
|
||||
<circle class="pad" cx="390" cy="475" r="4"/>
|
||||
<circle class="pad" cx="390" cy="510" r="4"/>
|
||||
<circle class="pad" cx="390" cy="545" r="4"/>
|
||||
<circle class="pad" cx="390" cy="580" r="4"/>
|
||||
<text class="pin mono" x="376" y="374" text-anchor="end">VCC</text>
|
||||
<text class="pin mono" x="376" y="409" text-anchor="end">GND</text>
|
||||
<text class="pin mono" x="376" y="444" text-anchor="end">SCL</text>
|
||||
<text class="pin mono" x="376" y="479" text-anchor="end">SDA</text>
|
||||
<text class="pin mono" x="376" y="514" text-anchor="end">CSB</text>
|
||||
<text class="pin mono" x="376" y="549" text-anchor="end">SDO</text>
|
||||
<text class="pin mono" x="376" y="584" text-anchor="end">PS</text>
|
||||
<text class="spec" x="284" y="549" text-anchor="end">not connected</text>
|
||||
|
||||
<!-- ============================ GY-63 #2 : SPI ============================ -->
|
||||
<rect class="modu" x="1110" y="280" width="280" height="340" rx="8"/>
|
||||
<text class="mname" x="1250" y="312" text-anchor="middle">GY-63 #2</text>
|
||||
<text class="spec" x="1250" y="334" text-anchor="middle">MS5611-01BA03 - SPI mode</text>
|
||||
|
||||
<circle class="pad" cx="1110" cy="370" r="4"/>
|
||||
<circle class="pad" cx="1110" cy="405" r="4"/>
|
||||
<circle class="pad" cx="1110" cy="440" r="4"/>
|
||||
<circle class="pad" cx="1110" cy="475" r="4"/>
|
||||
<circle class="pad" cx="1110" cy="510" r="4"/>
|
||||
<circle class="pad" cx="1110" cy="545" r="4"/>
|
||||
<circle class="pad" cx="1110" cy="580" r="4"/>
|
||||
<text class="pin mono" x="1124" y="374">VCC</text>
|
||||
<text class="pin mono" x="1124" y="409">GND</text>
|
||||
<text class="pin mono" x="1124" y="444">SCL <tspan class="spec">= SCLK</tspan></text>
|
||||
<text class="pin mono" x="1124" y="479">SDA <tspan class="spec">= SDI / MOSI</tspan></text>
|
||||
<text class="pin mono" x="1124" y="514">CSB <tspan class="spec">= CS</tspan></text>
|
||||
<text class="pin mono" x="1124" y="549">SDO <tspan class="spec">= MISO</tspan></text>
|
||||
<text class="pin mono" x="1124" y="584">PS</text>
|
||||
|
||||
<!-- ============================ POWER WIRING ============================ -->
|
||||
<path class="w w-v33" d="M 390,370 L 490,370 L 490,205"/>
|
||||
<path class="w w-v33" d="M 1110,370 L 1050,370 L 1050,205"/>
|
||||
<path class="w w-v33" d="M 720,250 L 720,205"/>
|
||||
|
||||
<path class="w w-gnd" d="M 390,405 L 530,405 L 530,232"/>
|
||||
<path class="w w-gnd" d="M 1110,405 L 1010,405 L 1010,232"/>
|
||||
<path class="w w-gnd" d="M 790,250 L 790,232"/>
|
||||
<g class="gsym"><line x1="516" y1="232" x2="544" y2="232"/><line x1="521" y1="238" x2="539" y2="238"/><line x1="526" y1="244" x2="534" y2="244"/></g>
|
||||
<g class="gsym"><line x1="996" y1="232" x2="1024" y2="232"/><line x1="1001" y1="238" x2="1019" y2="238"/><line x1="1006" y1="244" x2="1014" y2="244"/></g>
|
||||
<g class="gsym"><line x1="776" y1="232" x2="804" y2="232"/><line x1="781" y1="238" x2="799" y2="238"/><line x1="786" y1="244" x2="794" y2="244"/></g>
|
||||
|
||||
<!-- ============================ I2C SIGNALS ============================ -->
|
||||
<path class="w w-i2c" d="M 390,440 L 600,440"/>
|
||||
<path class="w w-i2c" d="M 390,475 L 600,475"/>
|
||||
<text class="net mono" x="495" y="432" text-anchor="middle" fill="#1668c4">SCL</text>
|
||||
<text class="net mono" x="495" y="467" text-anchor="middle" fill="#1668c4">SDA</text>
|
||||
|
||||
<!-- CSB -> GND (address 0x77) -->
|
||||
<path class="w w-gnd" d="M 390,510 L 460,510 L 460,548"/>
|
||||
<g class="gsym"><line x1="446" y1="548" x2="474" y2="548"/><line x1="451" y1="554" x2="469" y2="554"/><line x1="456" y1="560" x2="464" y2="560"/></g>
|
||||
<text class="net" x="484" y="524" fill="#5a6b7d">0x77</text>
|
||||
|
||||
<!-- PS -> 3V3 (selects I2C) -->
|
||||
<path class="w w-v33" d="M 390,580 L 545,580 L 545,556"/>
|
||||
<line class="w w-v33" x1="531" y1="556" x2="559" y2="556"/>
|
||||
<text class="net mono" x="545" y="546" text-anchor="middle" fill="#cc2b2b">3V3</text>
|
||||
|
||||
<!-- ============================ SPI SIGNALS ============================ -->
|
||||
<path class="w w-spi" d="M 900,440 L 1110,440"/>
|
||||
<path class="w w-spi" d="M 900,475 L 1110,475"/>
|
||||
<path class="w w-spi" d="M 900,510 L 1110,510"/>
|
||||
<path class="w w-spi" d="M 900,545 L 1110,545"/>
|
||||
<text class="net mono" x="1005" y="432" text-anchor="middle" fill="#d98207">SCK</text>
|
||||
<text class="net mono" x="1005" y="467" text-anchor="middle" fill="#d98207">MOSI</text>
|
||||
<text class="net mono" x="1005" y="502" text-anchor="middle" fill="#d98207">CS</text>
|
||||
<text class="net mono" x="1005" y="537" text-anchor="middle" fill="#d98207">MISO</text>
|
||||
|
||||
<!-- PS -> GND (selects SPI) -->
|
||||
<path class="w w-gnd" d="M 1110,580 L 1040,580 L 1040,614"/>
|
||||
<g class="gsym"><line x1="1026" y1="614" x2="1054" y2="614"/><line x1="1031" y1="620" x2="1049" y2="620"/><line x1="1036" y1="626" x2="1044" y2="626"/></g>
|
||||
|
||||
<!-- ============================ ERROR LEDS ============================ -->
|
||||
<!-- branch 1 : PB0 -->
|
||||
<path class="w w-led" d="M 660,800 L 660,832"/>
|
||||
<rect class="part" x="645" y="832" width="30" height="50" rx="2"/>
|
||||
<path class="w w-led" d="M 660,882 L 660,906"/>
|
||||
<polygon class="part" points="642,906 678,906 660,936"/>
|
||||
<line class="w w-led" x1="640" y1="936" x2="680" y2="936"/>
|
||||
<path class="w w-led" d="M 660,936 L 660,964"/>
|
||||
<g class="gsym"><line x1="646" y1="964" x2="674" y2="964"/><line x1="651" y1="970" x2="669" y2="970"/><line x1="656" y1="976" x2="664" y2="976"/></g>
|
||||
<text class="pin mono" x="690" y="862">510 R</text>
|
||||
<text class="pin" x="690" y="926">LED - I2C sensor error</text>
|
||||
|
||||
<!-- branch 2 : PB1 -->
|
||||
<path class="w w-led" d="M 850,800 L 850,832"/>
|
||||
<rect class="part" x="835" y="832" width="30" height="50" rx="2"/>
|
||||
<path class="w w-led" d="M 850,882 L 850,906"/>
|
||||
<polygon class="part" points="832,906 868,906 850,936"/>
|
||||
<line class="w w-led" x1="830" y1="936" x2="870" y2="936"/>
|
||||
<path class="w w-led" d="M 850,936 L 850,964"/>
|
||||
<g class="gsym"><line x1="836" y1="964" x2="864" y2="964"/><line x1="841" y1="970" x2="859" y2="970"/><line x1="846" y1="976" x2="854" y2="976"/></g>
|
||||
<text class="pin mono" x="880" y="862">510 R</text>
|
||||
<text class="pin" x="880" y="926">LED - SPI sensor error</text>
|
||||
|
||||
<!-- ============================ LEGEND ============================ -->
|
||||
<rect class="panel" x="90" y="660" width="290" height="196" rx="6"/>
|
||||
<text class="mname" x="106" y="686">LEGEND</text>
|
||||
<line class="w w-v33" x1="110" y1="712" x2="150" y2="712"/><text class="lgd" x="162" y="717">3.3 V</text>
|
||||
<line class="w w-gnd" x1="110" y1="742" x2="150" y2="742"/><text class="lgd" x="162" y="747">GND</text>
|
||||
<line class="w w-i2c" x1="110" y1="772" x2="150" y2="772"/><text class="lgd" x="162" y="777">I2C bus</text>
|
||||
<line class="w w-spi" x1="110" y1="802" x2="150" y2="802"/><text class="lgd" x="162" y="807">SPI bus</text>
|
||||
<line class="w w-led" x1="110" y1="832" x2="150" y2="832"/><text class="lgd" x="162" y="837">LED drive (active high)</text>
|
||||
|
||||
<!-- ============================ NOTES ============================ -->
|
||||
<rect class="panel" x="1080" y="660" width="330" height="284" rx="6"/>
|
||||
<text class="mname" x="1096" y="686">NOTES</text>
|
||||
<text class="note" x="1096" y="712">MS5611 is 3.3 V only (1.8 - 3.6 V).</text>
|
||||
<text class="note" x="1096" y="730">Never feed it 5 V.</text>
|
||||
<text class="note" x="1096" y="756">GY-63 boards normally carry SDA / SCL</text>
|
||||
<text class="note" x="1096" y="774">pull-ups - check yours before adding any.</text>
|
||||
<text class="note" x="1096" y="800">PS high selects I2C, PS low selects SPI.</text>
|
||||
<text class="note" x="1096" y="826">CSB low on module #1 sets address 0x77</text>
|
||||
<text class="note" x="1096" y="844">(BARO_I2C_ADDRESS in include/config.h).</text>
|
||||
<text class="note" x="1096" y="870">SPI1 must stay on PB3 / PB4 / PB5: bridges</text>
|
||||
<text class="note" x="1096" y="888">SB16 / SB18 tie PA5 / PA6 to the I2C net.</text>
|
||||
<text class="note" x="1096" y="914">The Nucleo-32 has a single 3V3 pin -</text>
|
||||
<text class="note" x="1096" y="932">distribute it over a breadboard rail.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
39
include/README
Normal file
39
include/README
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
100
include/baro_channel.h
Normal file
100
include/baro_channel.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "ms5611_crc.h"
|
||||
|
||||
//
|
||||
// One validated MS5611 measurement channel.
|
||||
//
|
||||
// The class owns everything that is identical for both sensors - reset and
|
||||
// PROM/CRC handshake, per-poll validation, error accounting and the 1 second
|
||||
// error LED pulse. Only the five driver calls at the bottom are virtual, and
|
||||
// they are implemented once for the I2C part and once for the SPI part.
|
||||
//
|
||||
|
||||
enum class BaroStatus : uint8_t
|
||||
{
|
||||
NotInitialised = 0,
|
||||
Ok,
|
||||
InitFailed, // reset/handshake did not complete
|
||||
PromCrcError, // factory calibration failed its CRC-4 or is implausible
|
||||
ReadError, // the driver reported a bus/ADC failure
|
||||
OutOfRange, // values outside the MS5611 operating envelope
|
||||
Stale, // sensor answers but has stopped producing new samples
|
||||
};
|
||||
|
||||
// Short, log-friendly name for a status - never returns NULL.
|
||||
const char *baroStatusName(BaroStatus status);
|
||||
|
||||
|
||||
class BaroChannel
|
||||
{
|
||||
public:
|
||||
BaroChannel(const char *name, uint8_t ledPin)
|
||||
: _name(name), _ledPin(ledPin) {}
|
||||
|
||||
// Configures the LED pin and makes the first initialisation attempt.
|
||||
void begin(uint32_t now);
|
||||
|
||||
// One acquisition cycle: read, validate, update status and LED.
|
||||
// Blocks for roughly 2x the ADC conversion time of the configured OSR.
|
||||
void poll(uint32_t now);
|
||||
|
||||
// Releases the error LED once its hold time has elapsed. Cheap, call often.
|
||||
void updateLed(uint32_t now);
|
||||
|
||||
const char *name() const { return _name; }
|
||||
BaroStatus status() const { return _status; }
|
||||
bool isOk() const { return _status == BaroStatus::Ok; }
|
||||
float pressure() const { return _pressure; } // mbar
|
||||
float temperature() const { return _temperature; } // degrees C
|
||||
uint32_t errorCount() const { return _errorCount; }
|
||||
|
||||
protected:
|
||||
// Not deleted through this type - keeps the vtable free of a destructor
|
||||
// slot and avoids dragging in operator delete.
|
||||
~BaroChannel() = default;
|
||||
|
||||
// Reset the part and apply the configured oversampling.
|
||||
virtual bool driverBegin() = 0;
|
||||
// Fill prom[0..7] with the factory calibration words.
|
||||
virtual void driverReadProm(uint16_t *prom) = 0;
|
||||
// True when the driver reported MS5611_READ_OK.
|
||||
virtual bool driverRead() = 0;
|
||||
virtual float driverPressure() = 0;
|
||||
virtual float driverTemperature() = 0;
|
||||
|
||||
private:
|
||||
bool tryInit(uint32_t now);
|
||||
void fail(BaroStatus status, uint32_t now);
|
||||
|
||||
const char *_name;
|
||||
uint8_t _ledPin;
|
||||
|
||||
BaroStatus _status = BaroStatus::NotInitialised;
|
||||
bool _initialised = false;
|
||||
uint32_t _lastInitAttempt = 0;
|
||||
|
||||
float _pressure = NAN;
|
||||
float _temperature = NAN;
|
||||
|
||||
uint8_t _repeatCount = 0;
|
||||
uint8_t _errorStreak = 0;
|
||||
uint32_t _errorCount = 0;
|
||||
|
||||
bool _ledOn = false;
|
||||
uint32_t _ledOffAt = 0;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// The two concrete channels are built in their own translation units on
|
||||
// purpose: MS5611.h and MS5611_SPI.h each define `enum osr_t` and their own
|
||||
// MS5611_READ_OK, so including both in one file does not compile. These
|
||||
// accessors hand out the instances without leaking either driver header.
|
||||
//
|
||||
BaroChannel &baroI2cChannel();
|
||||
BaroChannel &baroSpiChannel();
|
||||
120
include/config.h
Normal file
120
include/config.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
//
|
||||
// Project-wide hardware and behaviour configuration.
|
||||
//
|
||||
// Board : NUCLEO-F042K6 (STM32F042K6T6, LQFP32, board MB1180)
|
||||
// 32 KB flash / 6 KB RAM
|
||||
// Sensor: 2x GY-63 breakout carrying a MS5611-01BA03
|
||||
// one on I2C1, one on SPI1
|
||||
//
|
||||
// Pin facts below are taken from ST UM1956 "STM32 Nucleo-32 boards (MB1180)"
|
||||
// Table 10 + the MB1180 C.2 schematic, cross-checked against the STM32duino
|
||||
// variant files for NUCLEO_F042K6.
|
||||
//
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// I2C sensor ("baro0")
|
||||
// ---------------------------------------------------------------------------
|
||||
// STM32duino default Wire pins for this variant:
|
||||
// PIN_WIRE_SDA = PB7 (Arduino D4, routed to the A4 header pad via SB18)
|
||||
// PIN_WIRE_SCL = PB6 (Arduino D5, routed to the A5 header pad via SB16)
|
||||
// Both are the STM32duino defaults, so plain Wire.begin() selects them and no
|
||||
// explicit setSDA()/setSCL() call is required.
|
||||
//
|
||||
// MS5611 I2C address is 1110 11Cx where C is the COMPLEMENT of the CSB pin
|
||||
// (datasheet p.12), therefore:
|
||||
// CSB tied to GND -> 0x77
|
||||
// CSB tied to VCC -> 0x76
|
||||
// Change this if the GY-63 CSB pad is strapped high.
|
||||
#define BARO_I2C_ADDRESS 0x77
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SPI sensor ("baro1")
|
||||
// ---------------------------------------------------------------------------
|
||||
// SPI1 MUST stay on the PB3/PB4/PB5 group on this board.
|
||||
//
|
||||
// The alternative SPI1 group (PA5 SCK / PA6 MISO / PA7 MOSI) is NOT usable
|
||||
// here: with the factory-default solder bridges SB16 and SB18 closed, PA6 is
|
||||
// tied to the same net as PB6 (I2C SCL) and PA5 is tied to the same net as
|
||||
// PB7 (I2C SDA). Driving SPI on PA5/PA6 while I2C runs on PB6/PB7 would put
|
||||
// two peripherals on one net. See UM1956 Table 8 (SB16/SB18).
|
||||
//
|
||||
// SCK = PB3 (Arduino D13) <- also the on-board user LED LD3, see below
|
||||
// MISO = PB4 (Arduino D12) <- connect to sensor SDO
|
||||
// MOSI = PB5 (Arduino D11) <- connect to sensor SDI
|
||||
// These three are the STM32duino defaults (core falls back to Arduino pin
|
||||
// numbers 13/12/11), so plain SPI.begin() selects them.
|
||||
#define BARO_SPI_CS_PIN PA11 // Arduino D10, core default PIN_SPI_SS
|
||||
|
||||
// MS5611 accepts SPI mode 0 and mode 3, up to 20 MHz (datasheet p.5/p.6).
|
||||
// The library hardcodes mode 0; 1 MHz is its default and is plenty here.
|
||||
#define BARO_SPI_CLOCK_HZ 1000000UL
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Error indicator LEDs - one per sensor
|
||||
// ---------------------------------------------------------------------------
|
||||
// IMPORTANT: the NUCLEO-F042K6 has exactly ONE user-controllable LED, and it
|
||||
// is unusable for this project:
|
||||
//
|
||||
// LD1 (COM, tricolor) - driven by the ST-LINK MCU, not by the target
|
||||
// LD2 (PWR, red) - hardwired to the power rail, not by the target
|
||||
// LD3 (user, green) - on PB3 via SB15 + R23, and PB3 is our SPI1 SCK
|
||||
//
|
||||
// So both indicators are external LEDs (LED + ~510R to GND) on free GPIOs.
|
||||
// PB0/PB1 are plain GPIO on this board and collide with nothing we use.
|
||||
#define LED_BARO_I2C_PIN PB0 // Arduino D3 - error LED for the I2C sensor
|
||||
#define LED_BARO_SPI_PIN PB1 // Arduino D6 - error LED for the SPI sensor
|
||||
|
||||
// How long an LED stays lit after an error is detected.
|
||||
#define LED_ERROR_HOLD_MS 1000UL
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// UART
|
||||
// ---------------------------------------------------------------------------
|
||||
// `Serial` on this variant is USART2 (SERIAL_UART_INSTANCE 2) on PA2/PA15,
|
||||
// which is wired to the ST-LINK Virtual COM Port. No extra wiring needed.
|
||||
#define UART_BAUD 115200UL
|
||||
|
||||
// Measurement report rate: 10 Hz.
|
||||
#define REPORT_PERIOD_MS 100UL
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Acquisition
|
||||
// ---------------------------------------------------------------------------
|
||||
// The driver's read() busy-waits through two conversions, so a poll of both
|
||||
// sensors is the loop period, and the report deadline can only be evaluated
|
||||
// on that grid. Keeping the cycle short keeps the 10 Hz output jitter small:
|
||||
//
|
||||
// OSR_STANDARD (1024) 2.28 ms/conv -> ~5 ms/sensor -> ~11 ms cycle
|
||||
// OSR_ULTRA_HIGH (4096) 9.04 ms/conv -> ~19 ms/sensor -> ~37 ms cycle
|
||||
//
|
||||
// Conversion times are the datasheet p.3 maxima. OSR 1024 already resolves
|
||||
// well under a mbar, so it is the better trade here; raise it if resolution
|
||||
// matters more than tight report timing.
|
||||
#define BARO_OVERSAMPLING OSR_STANDARD
|
||||
|
||||
// Retry interval for a sensor that failed to initialise.
|
||||
#define BARO_INIT_RETRY_MS 2000UL
|
||||
|
||||
// Consecutive failed polls after which the channel is torn down and taken
|
||||
// back through the full reset + PROM/CRC handshake. Covers a sensor that was
|
||||
// unplugged, browned out, or otherwise lost its calibration constants.
|
||||
#define BARO_REINIT_AFTER_ERRORS 10
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Validation limits
|
||||
// ---------------------------------------------------------------------------
|
||||
// MS5611-01BA03 operating ranges (datasheet p.2/p.4).
|
||||
#define BARO_PRESSURE_MIN_MBAR 10.0f
|
||||
#define BARO_PRESSURE_MAX_MBAR 1200.0f
|
||||
#define BARO_TEMP_MIN_C (-40.0f)
|
||||
#define BARO_TEMP_MAX_C 85.0f
|
||||
|
||||
// A healthy MS5611 dithers by well under a mbar but never repeats a 24-bit
|
||||
// reading bit-for-bit many times running. Identical pressure AND temperature
|
||||
// this many polls in a row means the sensor stopped converting while still
|
||||
// answering on the bus.
|
||||
#define BARO_STALE_LIMIT 32
|
||||
11
include/ms5611_crc.h
Normal file
11
include/ms5611_crc.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// MS5611 factory PROM CRC-4 (datasheet p.13, algorithm specified in AN520).
|
||||
// `prom` holds the 8 PROM words as read from the part; word 7 carries the
|
||||
// stored CRC in its low nibble. The array is restored before returning.
|
||||
//
|
||||
// Deliberately free of Arduino headers so it can be built and tested on the
|
||||
// host against the AN520 reference vector.
|
||||
bool ms5611PromCrcOk(uint16_t prom[8]);
|
||||
46
lib/README
Normal file
46
lib/README
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
33
platformio.ini
Normal file
33
platformio.ini
Normal file
@@ -0,0 +1,33 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[common]
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
SPI
|
||||
I2C
|
||||
https://github.com/RobTillaart/MS5611#0.5.2
|
||||
https://github.com/RobTillaart/MS5611_SPI#0.4.3
|
||||
|
||||
|
||||
[env:nucleo_f042k6]
|
||||
extends = common
|
||||
platform = ststm32
|
||||
board = nucleo_f042k6
|
||||
monitor_speed = 115200
|
||||
; -flto and -fsingle-precision-constant are load-bearing, not tuning: the
|
||||
; STM32F042K6 has only 32 KB of flash and the build overflows without them.
|
||||
; Both MS5611 drivers write their compensation maths with unsuffixed double
|
||||
; literals, which would otherwise link ~5 KB of double soft-float helpers.
|
||||
build_flags =
|
||||
-Wall
|
||||
-Wextra
|
||||
-flto
|
||||
-fsingle-precision-constant
|
||||
151
src/baro_channel.cpp
Normal file
151
src/baro_channel.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "baro_channel.h"
|
||||
|
||||
const char *baroStatusName(BaroStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case BaroStatus::NotInitialised: return "INIT";
|
||||
case BaroStatus::Ok: return "OK";
|
||||
case BaroStatus::InitFailed: return "ERR_INIT";
|
||||
case BaroStatus::PromCrcError: return "ERR_CRC";
|
||||
case BaroStatus::ReadError: return "ERR_READ";
|
||||
case BaroStatus::OutOfRange: return "ERR_RANGE";
|
||||
case BaroStatus::Stale: return "ERR_STALE";
|
||||
}
|
||||
return "ERR_UNKNOWN";
|
||||
}
|
||||
|
||||
|
||||
void BaroChannel::begin(uint32_t now)
|
||||
{
|
||||
pinMode(_ledPin, OUTPUT);
|
||||
digitalWrite(_ledPin, LOW);
|
||||
_ledOn = false;
|
||||
|
||||
tryInit(now);
|
||||
}
|
||||
|
||||
|
||||
bool BaroChannel::tryInit(uint32_t now)
|
||||
{
|
||||
_lastInitAttempt = now;
|
||||
_initialised = false;
|
||||
_repeatCount = 0;
|
||||
|
||||
if (!driverBegin())
|
||||
{
|
||||
fail(BaroStatus::InitFailed, now);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t prom[8] = { 0 };
|
||||
driverReadProm(prom);
|
||||
|
||||
// An absent part reads back as all-zero (I2C NACK) or all-ones (floating
|
||||
// MISO). Neither driver rejects 0xFFFF, and an all-zero PROM would satisfy
|
||||
// the CRC, so the calibration words are screened before the checksum runs.
|
||||
for (uint8_t i = 1; i <= 6; i++)
|
||||
{
|
||||
if (prom[i] == 0x0000 || prom[i] == 0xFFFF)
|
||||
{
|
||||
fail(BaroStatus::PromCrcError, now);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ms5611PromCrcOk(prom))
|
||||
{
|
||||
fail(BaroStatus::PromCrcError, now);
|
||||
return false;
|
||||
}
|
||||
|
||||
_initialised = true;
|
||||
_errorStreak = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void BaroChannel::poll(uint32_t now)
|
||||
{
|
||||
if (!_initialised)
|
||||
{
|
||||
if ((uint32_t)(now - _lastInitAttempt) < BARO_INIT_RETRY_MS) return;
|
||||
if (!tryInit(now)) return;
|
||||
}
|
||||
|
||||
if (!driverRead())
|
||||
{
|
||||
fail(BaroStatus::ReadError, now);
|
||||
return;
|
||||
}
|
||||
|
||||
const float pressure = driverPressure();
|
||||
const float temperature = driverTemperature();
|
||||
|
||||
// Catches the failure the drivers miss: an ADC read taken before the
|
||||
// conversion finished returns 0 (datasheet p.11), which the I2C driver
|
||||
// still reports as MS5611_READ_OK but which lands far outside this envelope.
|
||||
if (!isfinite(pressure) || !isfinite(temperature) ||
|
||||
pressure < BARO_PRESSURE_MIN_MBAR || pressure > BARO_PRESSURE_MAX_MBAR ||
|
||||
temperature < BARO_TEMP_MIN_C || temperature > BARO_TEMP_MAX_C)
|
||||
{
|
||||
_pressure = pressure;
|
||||
_temperature = temperature;
|
||||
fail(BaroStatus::OutOfRange, now);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool repeated = (pressure == _pressure) && (temperature == _temperature);
|
||||
|
||||
_pressure = pressure;
|
||||
_temperature = temperature;
|
||||
|
||||
if (repeated)
|
||||
{
|
||||
if (_repeatCount < BARO_STALE_LIMIT) _repeatCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_repeatCount = 0;
|
||||
}
|
||||
|
||||
if (_repeatCount >= BARO_STALE_LIMIT)
|
||||
{
|
||||
fail(BaroStatus::Stale, now);
|
||||
return;
|
||||
}
|
||||
|
||||
_status = BaroStatus::Ok;
|
||||
_errorStreak = 0;
|
||||
}
|
||||
|
||||
|
||||
void BaroChannel::fail(BaroStatus status, uint32_t now)
|
||||
{
|
||||
_status = status;
|
||||
_errorCount++;
|
||||
|
||||
_ledOn = true;
|
||||
_ledOffAt = now + LED_ERROR_HOLD_MS;
|
||||
digitalWrite(_ledPin, HIGH);
|
||||
|
||||
if (_errorStreak < BARO_REINIT_AFTER_ERRORS) _errorStreak++;
|
||||
|
||||
if (_errorStreak >= BARO_REINIT_AFTER_ERRORS)
|
||||
{
|
||||
_initialised = false;
|
||||
_errorStreak = 0;
|
||||
_lastInitAttempt = now;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BaroChannel::updateLed(uint32_t now)
|
||||
{
|
||||
// Signed difference so the comparison survives the millis() rollover.
|
||||
if (_ledOn && (int32_t)(now - _ledOffAt) >= 0)
|
||||
{
|
||||
_ledOn = false;
|
||||
digitalWrite(_ledPin, LOW);
|
||||
}
|
||||
}
|
||||
46
src/baro_i2c.cpp
Normal file
46
src/baro_i2c.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <Wire.h>
|
||||
#include <MS5611.h>
|
||||
|
||||
#include "baro_channel.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class BaroI2cChannel : public BaroChannel
|
||||
{
|
||||
public:
|
||||
BaroI2cChannel()
|
||||
: BaroChannel("i2c", LED_BARO_I2C_PIN), _driver(BARO_I2C_ADDRESS, &Wire) {}
|
||||
|
||||
protected:
|
||||
bool driverBegin() override
|
||||
{
|
||||
if (!_busStarted)
|
||||
{
|
||||
Wire.begin();
|
||||
_busStarted = true;
|
||||
}
|
||||
|
||||
_driver.setOversampling(BARO_OVERSAMPLING);
|
||||
|
||||
return _driver.begin();
|
||||
}
|
||||
|
||||
void driverReadProm(uint16_t *prom) override
|
||||
{
|
||||
for (uint8_t i = 0; i < 8; i++) prom[i] = _driver.getProm(i);
|
||||
}
|
||||
|
||||
bool driverRead() override { return _driver.read() == MS5611_READ_OK; }
|
||||
float driverPressure() override { return _driver.getPressure(); }
|
||||
float driverTemperature() override { return _driver.getTemperature(); }
|
||||
|
||||
private:
|
||||
MS5611 _driver;
|
||||
bool _busStarted = false;
|
||||
};
|
||||
|
||||
BaroI2cChannel instance;
|
||||
|
||||
} // namespace
|
||||
|
||||
BaroChannel &baroI2cChannel() { return instance; }
|
||||
49
src/baro_spi.cpp
Normal file
49
src/baro_spi.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <SPI.h>
|
||||
#include <MS5611_SPI.h>
|
||||
|
||||
#include "baro_channel.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class BaroSpiChannel : public BaroChannel
|
||||
{
|
||||
public:
|
||||
BaroSpiChannel()
|
||||
: BaroChannel("spi", LED_BARO_SPI_PIN), _driver(BARO_SPI_CS_PIN, &SPI) {}
|
||||
|
||||
protected:
|
||||
bool driverBegin() override
|
||||
{
|
||||
if (!_busStarted)
|
||||
{
|
||||
SPI.begin();
|
||||
_busStarted = true;
|
||||
}
|
||||
|
||||
// Both must precede begin(), which re-applies the stored SPI speed and
|
||||
// then talks to the part.
|
||||
_driver.setSPIspeed(BARO_SPI_CLOCK_HZ);
|
||||
_driver.setOversampling(BARO_OVERSAMPLING);
|
||||
|
||||
return _driver.begin();
|
||||
}
|
||||
|
||||
void driverReadProm(uint16_t *prom) override
|
||||
{
|
||||
for (uint8_t i = 0; i < 8; i++) prom[i] = _driver.getProm(i);
|
||||
}
|
||||
|
||||
bool driverRead() override { return _driver.read() == MS5611_READ_OK; }
|
||||
float driverPressure() override { return _driver.getPressure(); }
|
||||
float driverTemperature() override { return _driver.getTemperature(); }
|
||||
|
||||
private:
|
||||
MS5611_SPI _driver;
|
||||
bool _busStarted = false;
|
||||
};
|
||||
|
||||
BaroSpiChannel instance;
|
||||
|
||||
} // namespace
|
||||
|
||||
BaroChannel &baroSpiChannel() { return instance; }
|
||||
93
src/main.cpp
Normal file
93
src/main.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "baro_channel.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace {
|
||||
|
||||
uint32_t nextReportAt = 0;
|
||||
|
||||
// Serial.print(float, digits) resolves to Print::print(double, int), which
|
||||
// would pull the double-precision soft-float routines into a 32 KB image.
|
||||
// Formatting from a scaled integer keeps the whole build single-precision.
|
||||
void printFixed2(float value)
|
||||
{
|
||||
if (!isfinite(value))
|
||||
{
|
||||
Serial.print(F("nan"));
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t hundredths = (int32_t)(value * 100.0f + (value < 0.0f ? -0.5f : 0.5f));
|
||||
|
||||
if (hundredths < 0)
|
||||
{
|
||||
Serial.print('-');
|
||||
hundredths = -hundredths;
|
||||
}
|
||||
|
||||
const int32_t fraction = hundredths % 100;
|
||||
|
||||
Serial.print(hundredths / 100);
|
||||
Serial.print('.');
|
||||
if (fraction < 10) Serial.print('0');
|
||||
Serial.print(fraction);
|
||||
}
|
||||
|
||||
void printChannel(BaroChannel &channel)
|
||||
{
|
||||
Serial.print(',');
|
||||
Serial.print(baroStatusName(channel.status()));
|
||||
Serial.print(',');
|
||||
printFixed2(channel.pressure());
|
||||
Serial.print(',');
|
||||
printFixed2(channel.temperature());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(UART_BAUD);
|
||||
|
||||
const uint32_t now = millis();
|
||||
baroI2cChannel().begin(now);
|
||||
baroSpiChannel().begin(now);
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("ms,i2c_status,i2c_p_mbar,i2c_t_c,spi_status,spi_p_mbar,spi_t_c"));
|
||||
|
||||
nextReportAt = millis() + REPORT_PERIOD_MS;
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
uint32_t now = millis();
|
||||
|
||||
baroI2cChannel().poll(now);
|
||||
baroSpiChannel().poll(now);
|
||||
|
||||
// poll() blocks for the ADC conversions, so re-read the clock before the
|
||||
// LED and report deadlines are evaluated.
|
||||
now = millis();
|
||||
|
||||
baroI2cChannel().updateLed(now);
|
||||
baroSpiChannel().updateLed(now);
|
||||
|
||||
if ((int32_t)(now - nextReportAt) < 0) return;
|
||||
|
||||
// Advancing by a fixed period keeps the long-run average at exactly 10 Hz
|
||||
// instead of drifting by one loop iteration per report.
|
||||
nextReportAt += REPORT_PERIOD_MS;
|
||||
|
||||
// Unless a stall put us more than a full period behind, in which case the
|
||||
// grid is re-based rather than emitting a burst of catch-up lines.
|
||||
if ((int32_t)(now - nextReportAt) >= 0) nextReportAt = now + REPORT_PERIOD_MS;
|
||||
|
||||
Serial.print(now);
|
||||
printChannel(baroI2cChannel());
|
||||
printChannel(baroSpiChannel());
|
||||
Serial.println();
|
||||
}
|
||||
27
src/ms5611_crc.cpp
Normal file
27
src/ms5611_crc.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "ms5611_crc.h"
|
||||
|
||||
bool ms5611PromCrcOk(uint16_t prom[8])
|
||||
{
|
||||
const uint16_t crcRead = prom[7];
|
||||
uint16_t remainder = 0;
|
||||
|
||||
// The 4 CRC bits live in the low nibble of word 7 and must read as zero
|
||||
// while the remainder is computed, so the whole low byte is masked off.
|
||||
prom[7] &= 0xFF00;
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
{
|
||||
if (i & 1) remainder ^= (uint16_t)(prom[i >> 1] & 0x00FF);
|
||||
else remainder ^= (uint16_t)(prom[i >> 1] >> 8);
|
||||
|
||||
for (uint8_t bit = 8; bit > 0; bit--)
|
||||
{
|
||||
remainder = (remainder & 0x8000) ? (uint16_t)((remainder << 1) ^ 0x3000)
|
||||
: (uint16_t)(remainder << 1);
|
||||
}
|
||||
}
|
||||
|
||||
prom[7] = crcRead;
|
||||
|
||||
return (uint16_t)((remainder >> 12) & 0x000F) == (uint16_t)(crcRead & 0x000F);
|
||||
}
|
||||
11
test/README
Normal file
11
test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||
Reference in New Issue
Block a user