28: Read MAC address from EEPROM r=jordens a=cjbe

Try and read the unique MAC address from EEPROM.
If the EEPROM can not be read (e.g. on the unpatched hardware version 1.0 where the EEPROM is unpowered) a hardcoded default of 10:E2:D5:00:03:00 is used.

The MAC address found / used is logged. At the moment the only way of accessing this log is via semihosting.

Closes #23 

30: build(deps): bump serde from 1.0.99 to 1.0.100 r=jordens a=dependabot-preview[bot]

Bumps [serde](https://github.com/serde-rs/serde) from 1.0.99 to 1.0.100.
<details>
<summary>Release notes</summary>

*Sourced from [serde's releases](https://github.com/serde-rs/serde/releases).*

> ## v1.0.100
> - Provide `serde::ser::StdError` and `serde:🇩🇪:StdError` which are either a re-export of `std::error::Error` (if Serde's "std" feature is enabled) or a new identical trait (otherwise).
> 
>     ```rust
>     #[cfg(feature = "std")]
>     pub use std::error::Error as StdError;
> 
>     #[cfg(not(feature = "std"))]
>     pub trait StdError: Debug + Display {
>         fn source(&self) -> Option<&(StdError + 'static)> { None }
>     }
>     ```
> 
>     Serde's error traits `serde::ser::Error` and `serde:🇩🇪:Error` require `std::error::Error` as a supertrait, but only when Serde is built with "std" enabled. Data formats that don't care about no\_std support should generally provide their error types with a `std::error::Error` impl directly:
> 
>     ```rust
>     #[derive(Debug)]
>     struct MySerError {...}
> 
>     impl serde::ser::Error for MySerError {...}
> 
>     impl std::fmt::Display for MySerError {...}
> 
>     // We don't support no_std!
>     impl std::error::Error for MySerError {}
>     ```
> 
>     Data formats that *do* support no\_std may either have a "std" feature of their own as has been required in the past:
> 
>     ```toml
>     [features]
>     std = ["serde/std"]
>     ```
> 
>     ```rust
>     #[cfg(feature = "std")]
>     impl std::error::Error for MySerError {}
>     ```
> 
>     ... or else now may provide the std Error impl unconditionally via Serde's re-export:
> 
>     ```rust
>     impl serde::ser::StdError for MySerError {}
>     ```
</details>
<details>
<summary>Commits</summary>

- [`b6a77c4`](b6a77c4413) Release 1.0.100
- [`3343885`](33438850a6) Merge pull request [#1620](https://github-redirect.dependabot.com/serde-rs/serde/issues/1620) from dtolnay/error
- [`c083cfd`](c083cfd65e) Export std error type so downstream doesn't need "std" feature
- [`4cea81f`](4cea81f93f) Merge pull request [#1615](https://github-redirect.dependabot.com/serde-rs/serde/issues/1615) from jamesmunns/patch-1
- [`2d36be7`](2d36be753a) Add Postcard to the list of Serde Data Formats
- [`738d29e`](738d29eaa9) Update serde_derive_internals to syn 1.0
- [`b536fb6`](b536fb67a4) Merge pull request [#1604](https://github-redirect.dependabot.com/serde-rs/serde/issues/1604) from UnHumbleBen/patch-1
- [`b10c23a`](b10c23a950) Fixed a typo
- [`85a5cf7`](85a5cf7cb1) Document serde_derive minimum rustc
- See full diff in [compare view](https://github.com/serde-rs/serde/compare/v1.0.99...v1.0.100)
</details>
<br />

[![Dependabot compatibility score](https://api.dependabot.com/badges/compatibility_score?dependency-name=serde&package-manager=cargo&previous-version=1.0.99&new-version=1.0.100)](https://dependabot.com/compatibility-score.html?dependency-name=serde&package-manager=cargo&previous-version=1.0.99&new-version=1.0.100)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language
- `@dependabot badge me` will comment on this PR with code to add a "Dependabot enabled" badge to your readme

Additionally, you can set the following in your Dependabot [dashboard](https://app.dependabot.com):
- Update frequency (including time of day and day of week)
- Automerge options (never/patch/minor, and dev/runtime dependencies)
- Pull request limits (per update run and/or open at any time)
- Out-of-range updates (receive only lockfile updates, if desired)
- Security updates (receive only security updates, if desired)

Finally, you can contact us by mentioning @dependabot.

</details>

Co-authored-by: Chris Ballance <chris.ballance@physics.ox.ac.uk>
Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
master
bors[bot] 2019-09-11 14:18:18 +00:00 committed by GitHub
commit 7f295285da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 241 additions and 6 deletions

8
Cargo.lock generated
View File

@ -268,7 +268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.99"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
@ -280,7 +280,7 @@ version = "0.0.1"
source = "git+https://github.com/quartiq/serde-json-core.git?rev=fc764de#fc764deb8dfb82e5cfcc6c5059d8d5c3031e0591"
dependencies = [
"heapless 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -314,7 +314,7 @@ dependencies = [
"heapless 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"panic-semihosting 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)",
"serde-json-core 0.0.1 (git+https://github.com/quartiq/serde-json-core.git?rev=fc764de)",
"smoltcp 0.5.0 (git+https://github.com/m-labs/smoltcp.git?rev=1ada3da)",
"stm32h7 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -419,7 +419,7 @@ dependencies = [
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f"
"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a"
"checksum serde-json-core 0.0.1 (git+https://github.com/quartiq/serde-json-core.git?rev=fc764de)" = "<none>"
"checksum serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "01e69e1b8a631f245467ee275b8c757b818653c6d704cdbcaeb56b56767b529c"
"checksum smoltcp 0.5.0 (git+https://github.com/m-labs/smoltcp.git?rev=1ada3da)" = "<none>"

0
Cargo.toml Normal file → Executable file
View File

23
src/eeprom.rs Executable file
View File

@ -0,0 +1,23 @@
use stm32h7::stm32h743 as pac;
use super::i2c;
const I2C_ADDR: u8 = 0xa0;
pub fn read_eui48<'a>(i2c: &pac::I2C2) -> Result<[u8; 6], i2c::Error> {
let mut buffer = [0u8; 6];
i2c::write_read(i2c, I2C_ADDR, &[0xFAu8], &mut buffer)?;
Ok(buffer)
}
pub fn read(i2c: &pac::I2C2, addr: u8, mut buffer: &mut [u8]) -> Result<(), i2c::Error> {
i2c::write_read(i2c, I2C_ADDR, &[addr], &mut buffer)?;
Ok(())
}
pub fn write(i2c: &pac::I2C2, addr: u8, buffer: &[u8]) -> Result<(), i2c::Error> {
for (i, &byte) in buffer.iter().enumerate() {
i2c::write(i2c, I2C_ADDR, &[addr+i as u8, byte])?;
}
Ok(())
}

182
src/i2c.rs Executable file
View File

@ -0,0 +1,182 @@
use stm32h7::stm32h743 as pac;
// Adapted from stm32h7xx embedded-hal
// I2C error
#[derive(Debug)]
pub enum Error {
// Bus error
Bus,
// Arbitration loss
Arbitration,
// Address not ACKd within a reasonable time (no device present?)
Timeout,
// Unexpected NACK during transfer
NAck
}
// Maximum number of times to retry NACKed address phase before timing out
// Note that many devices indicate a busy condition by NACKing (e.g. 24xx
// EEPROMs during write)
const N_RETRY: usize = 100; // ~ 10ms @ 100 kHz bus clock
pub fn setup(rcc: &pac::RCC, i2c: &pac::I2C2) {
rcc.apb1lenr.modify(|_,w|
w.i2c2en().set_bit()
);
// Disable the peripheral before setting timings
i2c.cr1.modify(|_, w| w.pe().clear_bit());
// Values from STM32MXCube for 100 kHz I2C clock with 100 MHz peripheral clock
i2c.timingr.modify( |_,w|
w.presc().bits(1)
.scldel().bits(0x12)
.sdadel().bits(0)
.sclh().bits(0xec)
.scll().bits(0xff)
);
// Enable the peripheral
i2c.cr1.write(|w| w.pe().set_bit());
}
// Busy-wait for a flag to be asserted, erroring out on unrecoverable problems
macro_rules! busy_wait_errors {
($i2c:expr, $flag:ident) => {
loop {
let isr = $i2c.isr.read();
if isr.berr().bit_is_set() {
return Err(Error::Bus);
} else if isr.arlo().bit_is_set() {
return Err(Error::Arbitration);
} else if isr.nackf().bit_is_set() {
return Err(Error::NAck);
} else if isr.$flag().bit_is_set() {
break;
}
}
};
}
fn poll_for_start_ack(
i2c: &pac::I2C2,
addr: u8,
r_wn: bool,
data_len: usize,
autoend: bool,
start: bool
) -> Result<(), Error>
{
for _i in 0..N_RETRY {
// START and prepare to send `data_len`
i2c.cr2.write(|w| {
w.start().bit(start)
.sadd().bits(addr as u16)
.add10().clear_bit()
.rd_wrn().bit(r_wn)
.nbytes().bits( data_len as u8 )
.autoend().bit(autoend)
});
loop {
let isr = i2c.isr.read();
if isr.berr().bit_is_set() {
return Err(Error::Bus);
} else if isr.arlo().bit_is_set() {
return Err(Error::Arbitration);
} else if isr.nackf().bit_is_set() {
i2c.icr.write(|w| { w.nackcf().set_bit() });
// Wait to finish handling NACK-STOP
loop {
if i2c.isr.read().busy().bit_is_clear() {
break;
}
}
break;
} else if isr.txis().bit_is_set() || isr.rxne().bit_is_set() {
return Ok(())
}
}
}
return Err(Error::Timeout);
}
pub fn write(
i2c: &pac::I2C2,
addr: u8,
bytes: &[u8]
) -> Result<(), Error> {
assert!(bytes.len() < 256 && bytes.len() > 0);
poll_for_start_ack(i2c, addr|0, false, bytes.len(), true, true)?;
for byte in bytes {
busy_wait_errors!(i2c, txis);
i2c.txdr.write(|w| w.txdata().bits(*byte));
}
// automatic STOP
Ok(())
}
pub fn write_read(
i2c: &pac::I2C2,
addr: u8,
bytes: &[u8],
buffer: &mut [u8],
) -> Result<(), Error> {
assert!(bytes.len() < 256 && bytes.len() > 0);
assert!(buffer.len() < 256 && buffer.len() > 0);
poll_for_start_ack(i2c, addr|0, false, bytes.len(), false, true)?;
for byte in bytes {
// Wait until we are allowed to send data (START has been ACKed or last
// byte when through)
busy_wait_errors!(i2c, txis);
i2c.txdr.write(|w| w.txdata().bits(*byte));
}
// Wait until the last transmission is finished
busy_wait_errors!(i2c, tc);
poll_for_start_ack(i2c, addr|1, true, buffer.len(), true, true)?;
for byte in buffer {
// Wait until we have received something
busy_wait_errors!(i2c, rxne);
*byte = i2c.rxdr.read().rxdata().bits();
}
// automatic STOP
Ok(())
}
pub fn read(
i2c: &pac::I2C2,
addr: u8,
buffer: &mut [u8],
) -> Result<(), Error> {
assert!(buffer.len() < 256 && buffer.len() > 0);
poll_for_start_ack(i2c, addr|0, true, buffer.len(), true, true)?;
for byte in buffer {
// Wait until we have received something
busy_wait_errors!(i2c, rxne);
*byte = i2c.rxdr.read().rxdata().bits();
}
// automatic STOP
Ok(())
}

34
src/main.rs Normal file → Executable file
View File

@ -38,6 +38,9 @@ mod eth;
mod iir;
use iir::*;
pub mod i2c;
mod eeprom;
#[cfg(not(feature = "semihosting"))]
fn init_log() {}
@ -291,6 +294,20 @@ fn gpio_setup(gpioa: &pac::GPIOA, gpiob: &pac::GPIOB, gpiod: &pac::GPIOD,
.odr15().low()
);
// I2C2: SDA,SCL: PF0,PF1
gpiof.moder.modify(|_, w|
w.moder0().alternate()
.moder1().alternate()
);
gpiof.afrl.modify(|_, w|
w.afr0().af4()
.afr1().af4()
);
gpiof.otyper.modify(|_, w|
w.ot0().open_drain()
.ot1().open_drain()
);
// ADC1
// SCK: PF6
gpiof.moder.modify(|_, w| w.moder7().alternate());
@ -524,6 +541,7 @@ macro_rules! create_socket {
const APP: () = {
struct Resources {
spi: (pac::SPI1, pac::SPI2, pac::SPI4, pac::SPI5),
i2c: pac::I2C2,
ethernet_periph: (pac::ETHERNET_MAC, pac::ETHERNET_DMA, pac::ETHERNET_MTL),
#[init([[0.; 5]; 2])]
iir_state: [IIRState; 2],
@ -611,6 +629,9 @@ const APP: () = {
tim2_setup(&dp.TIM2);
let i2c2 = dp.I2C2;
i2c::setup(&rcc, &i2c2);
eth::setup(&rcc, &dp.SYSCFG);
eth::setup_pins(&dp.GPIOA, &dp.GPIOB, &dp.GPIOC, &dp.GPIOG);
@ -618,15 +639,24 @@ const APP: () = {
init::LateResources {
spi: (spi1, spi2, spi4, spi5),
i2c: i2c2,
ethernet_periph: (dp.ETHERNET_MAC, dp.ETHERNET_DMA, dp.ETHERNET_MTL),
}
}
#[idle(resources = [ethernet, ethernet_periph, iir_state, iir_ch])]
#[idle(resources = [ethernet, ethernet_periph, iir_state, iir_ch, i2c])]
fn idle(c: idle::Context) -> ! {
let (MAC, DMA, MTL) = c.resources.ethernet_periph;
let hardware_addr = net::wire::EthernetAddress([0x10, 0xE2, 0xD5, 0x00, 0x03, 0x00]);
let hardware_addr = match eeprom::read_eui48(c.resources.i2c) {
Err(_) => {
info!("Could not read EEPROM, using default MAC address");
net::wire::EthernetAddress([0x10, 0xE2, 0xD5, 0x00, 0x03, 0x00])
},
Ok(raw_mac) => net::wire::EthernetAddress(raw_mac)
};
info!("MAC: {}", hardware_addr);
unsafe { c.resources.ethernet.init(hardware_addr, MAC, DMA, MTL) };
let mut neighbor_cache_storage = [None; 8];
let neighbor_cache = net::iface::NeighborCache::new(&mut neighbor_cache_storage[..]);