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>
This commit is contained in:
commit
7f295285da
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -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
0
Cargo.toml
Normal file → Executable file
23
src/eeprom.rs
Executable file
23
src/eeprom.rs
Executable 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
182
src/i2c.rs
Executable 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
34
src/main.rs
Normal file → Executable 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[..]);
|
||||
|
Loading…
Reference in New Issue
Block a user