rustfmt: run
This commit is contained in:
parent
501b3c11d3
commit
b34fdf7c48
540
src/board.rs
540
src/board.rs
|
@ -1,14 +1,11 @@
|
||||||
use super::pac;
|
|
||||||
use super::i2c;
|
|
||||||
use super::eth;
|
use super::eth;
|
||||||
|
use super::i2c;
|
||||||
|
use super::pac;
|
||||||
|
|
||||||
fn pwr_setup(pwr: &pac::PWR) {
|
fn pwr_setup(pwr: &pac::PWR) {
|
||||||
// go to VOS1 voltage scale for high perf
|
// go to VOS1 voltage scale for high perf
|
||||||
pwr.cr3.write(|w|
|
pwr.cr3
|
||||||
w.scuen().set_bit()
|
.write(|w| w.scuen().set_bit().ldoen().set_bit().bypass().clear_bit());
|
||||||
.ldoen().set_bit()
|
|
||||||
.bypass().clear_bit()
|
|
||||||
);
|
|
||||||
while pwr.csr1.read().actvosrdy().bit_is_clear() {}
|
while pwr.csr1.read().actvosrdy().bit_is_clear() {}
|
||||||
pwr.d3cr.write(|w| unsafe { w.vos().bits(0b11) }); // vos1
|
pwr.d3cr.write(|w| unsafe { w.vos().bits(0b11) }); // vos1
|
||||||
while pwr.d3cr.read().vosrdy().bit_is_clear() {}
|
while pwr.d3cr.read().vosrdy().bit_is_clear() {}
|
||||||
|
@ -50,64 +47,89 @@ fn rcc_pll_setup(rcc: &pac::RCC, flash: &pac::FLASH) {
|
||||||
rcc.cfgr.reset();
|
rcc.cfgr.reset();
|
||||||
|
|
||||||
// Ensure HSE is on and stable
|
// Ensure HSE is on and stable
|
||||||
rcc.cr.modify(|_, w|
|
rcc.cr.modify(|_, w| w.hseon().on().hsebyp().not_bypassed());
|
||||||
w.hseon().on()
|
|
||||||
.hsebyp().not_bypassed());
|
|
||||||
while !rcc.cr.read().hserdy().is_ready() {}
|
while !rcc.cr.read().hserdy().is_ready() {}
|
||||||
|
|
||||||
rcc.pllckselr.modify(|_, w|
|
rcc.pllckselr.modify(
|
||||||
w.pllsrc().hse()
|
|_, w| {
|
||||||
.divm1().bits(1) // ref prescaler
|
w.pllsrc()
|
||||||
.divm2().bits(1) // ref prescaler
|
.hse()
|
||||||
|
.divm1()
|
||||||
|
.bits(1) // ref prescaler
|
||||||
|
.divm2()
|
||||||
|
.bits(1)
|
||||||
|
}, // ref prescaler
|
||||||
);
|
);
|
||||||
// Configure PLL1: 8MHz /1 *100 /2 = 400 MHz
|
// Configure PLL1: 8MHz /1 *100 /2 = 400 MHz
|
||||||
rcc.pllcfgr.modify(|_, w|
|
rcc.pllcfgr.modify(|_, w| {
|
||||||
w.pll1vcosel().wide_vco() // 192-836 MHz VCO
|
w.pll1vcosel()
|
||||||
.pll1rge().range8() // 8-16 MHz PFD
|
.wide_vco() // 192-836 MHz VCO
|
||||||
.pll1fracen().reset()
|
.pll1rge()
|
||||||
.divp1en().enabled()
|
.range8() // 8-16 MHz PFD
|
||||||
.pll2vcosel().medium_vco() // 150-420 MHz VCO
|
.pll1fracen()
|
||||||
.pll2rge().range8() // 8-16 MHz PFD
|
.reset()
|
||||||
.pll2fracen().reset()
|
.divp1en()
|
||||||
.divp2en().enabled()
|
.enabled()
|
||||||
.divq2en().enabled()
|
.pll2vcosel()
|
||||||
);
|
.medium_vco() // 150-420 MHz VCO
|
||||||
|
.pll2rge()
|
||||||
|
.range8() // 8-16 MHz PFD
|
||||||
|
.pll2fracen()
|
||||||
|
.reset()
|
||||||
|
.divp2en()
|
||||||
|
.enabled()
|
||||||
|
.divq2en()
|
||||||
|
.enabled()
|
||||||
|
});
|
||||||
rcc.pll1divr.write(|w| unsafe {
|
rcc.pll1divr.write(|w| unsafe {
|
||||||
w.divn1().bits(100 - 1) // feebdack divider
|
w.divn1()
|
||||||
.divp1().div2() // p output divider
|
.bits(100 - 1) // feebdack divider
|
||||||
|
.divp1()
|
||||||
|
.div2() // p output divider
|
||||||
});
|
});
|
||||||
rcc.cr.modify(|_, w| w.pll1on().on());
|
rcc.cr.modify(|_, w| w.pll1on().on());
|
||||||
while !rcc.cr.read().pll1rdy().is_ready() {}
|
while !rcc.cr.read().pll1rdy().is_ready() {}
|
||||||
|
|
||||||
// Configure PLL2: 8MHz /1 *25 / 2 = 100 MHz
|
// Configure PLL2: 8MHz /1 *25 / 2 = 100 MHz
|
||||||
rcc.pll2divr.write(|w| unsafe {
|
rcc.pll2divr.write(|w| unsafe {
|
||||||
w.divn2().bits(25 - 1) // feebdack divider
|
w.divn2()
|
||||||
.divp2().bits(2 - 1) // p output divider
|
.bits(25 - 1) // feebdack divider
|
||||||
.divq2().bits(2 - 1) // q output divider
|
.divp2()
|
||||||
|
.bits(2 - 1) // p output divider
|
||||||
|
.divq2()
|
||||||
|
.bits(2 - 1) // q output divider
|
||||||
});
|
});
|
||||||
rcc.cr.modify(|_, w| w.pll2on().on());
|
rcc.cr.modify(|_, w| w.pll2on().on());
|
||||||
while !rcc.cr.read().pll2rdy().is_ready() {}
|
while !rcc.cr.read().pll2rdy().is_ready() {}
|
||||||
|
|
||||||
// hclk 200 MHz, pclk 100 MHz
|
// hclk 200 MHz, pclk 100 MHz
|
||||||
rcc.d1cfgr.write(|w|
|
rcc.d1cfgr.write(
|
||||||
w.d1cpre().div1() // sys_ck not divided
|
|w| {
|
||||||
.hpre().div2() // rcc_hclk3 = sys_d1cpre_ck / 2
|
w.d1cpre()
|
||||||
.d1ppre().div2() // rcc_pclk3 = rcc_hclk3 / 2
|
.div1() // sys_ck not divided
|
||||||
|
.hpre()
|
||||||
|
.div2() // rcc_hclk3 = sys_d1cpre_ck / 2
|
||||||
|
.d1ppre()
|
||||||
|
.div2()
|
||||||
|
}, // rcc_pclk3 = rcc_hclk3 / 2
|
||||||
);
|
);
|
||||||
rcc.d2cfgr.write(|w|
|
rcc.d2cfgr.write(
|
||||||
w.d2ppre1().div2() // rcc_pclk1 = rcc_hclk3 / 2
|
|w| {
|
||||||
.d2ppre2().div2() // rcc_pclk2 = rcc_hclk3 / 2
|
w.d2ppre1()
|
||||||
|
.div2() // rcc_pclk1 = rcc_hclk3 / 2
|
||||||
|
.d2ppre2()
|
||||||
|
.div2()
|
||||||
|
}, // rcc_pclk2 = rcc_hclk3 / 2
|
||||||
);
|
);
|
||||||
rcc.d3cfgr.write(|w|
|
rcc.d3cfgr.write(
|
||||||
w.d3ppre().div2() // rcc_pclk4 = rcc_hclk3 / 2
|
|w| w.d3ppre().div2(), // rcc_pclk4 = rcc_hclk3 / 2
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2 wait states, 0b10 programming delay
|
// 2 wait states, 0b10 programming delay
|
||||||
// 185-210 MHz
|
// 185-210 MHz
|
||||||
flash.acr.write(|w| unsafe {
|
flash
|
||||||
w.wrhighfreq().bits(2)
|
.acr
|
||||||
.latency().bits(2)
|
.write(|w| unsafe { w.wrhighfreq().bits(2).latency().bits(2) });
|
||||||
});
|
|
||||||
while flash.acr.read().latency().bits() != 2 {}
|
while flash.acr.read().latency().bits() != 2 {}
|
||||||
|
|
||||||
// CSI for I/O compensationc ell
|
// CSI for I/O compensationc ell
|
||||||
|
@ -119,24 +141,26 @@ fn rcc_pll_setup(rcc: &pac::RCC, flash: &pac::FLASH) {
|
||||||
while !rcc.cfgr.read().sws().is_pll1() {}
|
while !rcc.cfgr.read().sws().is_pll1() {}
|
||||||
|
|
||||||
rcc.d1ccipr.write(|w| w.ckpersel().hse());
|
rcc.d1ccipr.write(|w| w.ckpersel().hse());
|
||||||
rcc.d2ccip1r.modify(|_, w|
|
rcc.d2ccip1r
|
||||||
w.spi123sel().pll2_p()
|
.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q());
|
||||||
.spi45sel().pll2_q()
|
|
||||||
);
|
|
||||||
rcc.d3ccipr.modify(|_, w| w.spi6sel().pll2_q());
|
rcc.d3ccipr.modify(|_, w| w.spi6sel().pll2_q());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn io_compensation_setup(syscfg: &pac::SYSCFG) {
|
fn io_compensation_setup(syscfg: &pac::SYSCFG) {
|
||||||
syscfg.cccsr.modify(|_, w|
|
syscfg
|
||||||
w.en().set_bit()
|
.cccsr
|
||||||
.cs().clear_bit()
|
.modify(|_, w| w.en().set_bit().cs().clear_bit().hslv().clear_bit());
|
||||||
.hslv().clear_bit()
|
|
||||||
);
|
|
||||||
while syscfg.cccsr.read().ready().bit_is_clear() {}
|
while syscfg.cccsr.read().ready().bit_is_clear() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gpio_setup(gpioa: &pac::GPIOA, gpiob: &pac::GPIOB, gpiod: &pac::GPIOD,
|
fn gpio_setup(
|
||||||
gpioe: &pac::GPIOE, gpiof: &pac::GPIOF, gpiog: &pac::GPIOG) {
|
gpioa: &pac::GPIOA,
|
||||||
|
gpiob: &pac::GPIOB,
|
||||||
|
gpiod: &pac::GPIOD,
|
||||||
|
gpioe: &pac::GPIOE,
|
||||||
|
gpiof: &pac::GPIOF,
|
||||||
|
gpiog: &pac::GPIOG,
|
||||||
|
) {
|
||||||
// FP_LED0
|
// FP_LED0
|
||||||
gpiod.otyper.modify(|_, w| w.ot5().push_pull());
|
gpiod.otyper.modify(|_, w| w.ot5().push_pull());
|
||||||
gpiod.moder.modify(|_, w| w.moder5().output());
|
gpiod.moder.modify(|_, w| w.moder5().output());
|
||||||
|
@ -158,18 +182,13 @@ fn gpio_setup(gpioa: &pac::GPIOA, gpiob: &pac::GPIOB, gpiod: &pac::GPIOD,
|
||||||
gpiod.odr.modify(|_, w| w.odr12().low());
|
gpiod.odr.modify(|_, w| w.odr12().low());
|
||||||
|
|
||||||
// AFE0_A0,1: PG2,PG3
|
// AFE0_A0,1: PG2,PG3
|
||||||
gpiog.otyper.modify(|_, w|
|
gpiog
|
||||||
w.ot2().push_pull()
|
.otyper
|
||||||
.ot3().push_pull()
|
.modify(|_, w| w.ot2().push_pull().ot3().push_pull());
|
||||||
);
|
gpiog
|
||||||
gpiog.moder.modify(|_, w|
|
.moder
|
||||||
w.moder2().output()
|
.modify(|_, w| w.moder2().output().moder3().output());
|
||||||
.moder3().output()
|
gpiog.odr.modify(|_, w| w.odr2().low().odr3().low());
|
||||||
);
|
|
||||||
gpiog.odr.modify(|_, w|
|
|
||||||
w.odr2().low()
|
|
||||||
.odr3().low()
|
|
||||||
);
|
|
||||||
|
|
||||||
// ADC0
|
// ADC0
|
||||||
// SCK: PG11
|
// SCK: PG11
|
||||||
|
@ -216,32 +235,22 @@ fn gpio_setup(gpioa: &pac::GPIOA, gpiob: &pac::GPIOB, gpiod: &pac::GPIOD,
|
||||||
gpioe.odr.modify(|_, w| w.odr12().high());
|
gpioe.odr.modify(|_, w| w.odr12().high());
|
||||||
|
|
||||||
// AFE1_A0,1: PD14,PD15
|
// AFE1_A0,1: PD14,PD15
|
||||||
gpiod.otyper.modify(|_, w|
|
gpiod
|
||||||
w.ot14().push_pull()
|
.otyper
|
||||||
.ot15().push_pull()
|
.modify(|_, w| w.ot14().push_pull().ot15().push_pull());
|
||||||
);
|
gpiod
|
||||||
gpiod.moder.modify(|_, w|
|
.moder
|
||||||
w.moder14().output()
|
.modify(|_, w| w.moder14().output().moder15().output());
|
||||||
.moder15().output()
|
gpiod.odr.modify(|_, w| w.odr14().low().odr15().low());
|
||||||
);
|
|
||||||
gpiod.odr.modify(|_, w|
|
|
||||||
w.odr14().low()
|
|
||||||
.odr15().low()
|
|
||||||
);
|
|
||||||
|
|
||||||
// I2C2: SDA,SCL: PF0,PF1
|
// I2C2: SDA,SCL: PF0,PF1
|
||||||
gpiof.moder.modify(|_, w|
|
gpiof
|
||||||
w.moder0().alternate()
|
.moder
|
||||||
.moder1().alternate()
|
.modify(|_, w| w.moder0().alternate().moder1().alternate());
|
||||||
);
|
gpiof.afrl.modify(|_, w| w.afr0().af4().afr1().af4());
|
||||||
gpiof.afrl.modify(|_, w|
|
gpiof
|
||||||
w.afr0().af4()
|
.otyper
|
||||||
.afr1().af4()
|
.modify(|_, w| w.ot0().open_drain().ot1().open_drain());
|
||||||
);
|
|
||||||
gpiof.otyper.modify(|_, w|
|
|
||||||
w.ot0().open_drain()
|
|
||||||
.ot1().open_drain()
|
|
||||||
);
|
|
||||||
|
|
||||||
// ADC1
|
// ADC1
|
||||||
// SCK: PF6
|
// SCK: PF6
|
||||||
|
@ -285,81 +294,114 @@ fn gpio_setup(gpioa: &pac::GPIOA, gpiob: &pac::GPIOB, gpiod: &pac::GPIOD,
|
||||||
|
|
||||||
// ADC0
|
// ADC0
|
||||||
fn spi1_setup(spi1: &pac::SPI1) {
|
fn spi1_setup(spi1: &pac::SPI1) {
|
||||||
spi1.cfg1.modify(|_, w|
|
spi1.cfg1
|
||||||
w.mbr().div4()
|
.modify(|_, w| w.mbr().div4().dsize().bits(16 - 1).fthlv().one_frame());
|
||||||
.dsize().bits(16 - 1)
|
spi1.cfg2.modify(|_, w| {
|
||||||
.fthlv().one_frame()
|
w.afcntr()
|
||||||
);
|
.controlled()
|
||||||
spi1.cfg2.modify(|_, w|
|
.ssom()
|
||||||
w.afcntr().controlled()
|
.not_asserted()
|
||||||
.ssom().not_asserted()
|
.ssoe()
|
||||||
.ssoe().enabled()
|
.enabled()
|
||||||
.ssiop().active_low()
|
.ssiop()
|
||||||
.ssm().disabled()
|
.active_low()
|
||||||
.cpol().idle_high()
|
.ssm()
|
||||||
.cpha().second_edge()
|
.disabled()
|
||||||
.lsbfrst().msbfirst()
|
.cpol()
|
||||||
.master().master()
|
.idle_high()
|
||||||
.sp().motorola()
|
.cpha()
|
||||||
.comm().receiver()
|
.second_edge()
|
||||||
.ioswp().disabled()
|
.lsbfrst()
|
||||||
.midi().bits(0)
|
.msbfirst()
|
||||||
.mssi().bits(6)
|
.master()
|
||||||
);
|
.master()
|
||||||
|
.sp()
|
||||||
|
.motorola()
|
||||||
|
.comm()
|
||||||
|
.receiver()
|
||||||
|
.ioswp()
|
||||||
|
.disabled()
|
||||||
|
.midi()
|
||||||
|
.bits(0)
|
||||||
|
.mssi()
|
||||||
|
.bits(6)
|
||||||
|
});
|
||||||
spi1.cr2.modify(|_, w| w.tsize().bits(1));
|
spi1.cr2.modify(|_, w| w.tsize().bits(1));
|
||||||
spi1.cr1.write(|w| w.spe().enabled());
|
spi1.cr1.write(|w| w.spe().enabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ADC1
|
// ADC1
|
||||||
fn spi5_setup(spi5: &pac::SPI5) {
|
fn spi5_setup(spi5: &pac::SPI5) {
|
||||||
spi5.cfg1.modify(|_, w|
|
spi5.cfg1
|
||||||
w.mbr().div4()
|
.modify(|_, w| w.mbr().div4().dsize().bits(16 - 1).fthlv().one_frame());
|
||||||
.dsize().bits(16 - 1)
|
spi5.cfg2.modify(|_, w| {
|
||||||
.fthlv().one_frame()
|
w.afcntr()
|
||||||
);
|
.controlled()
|
||||||
spi5.cfg2.modify(|_, w|
|
.ssom()
|
||||||
w.afcntr().controlled()
|
.not_asserted()
|
||||||
.ssom().not_asserted()
|
.ssoe()
|
||||||
.ssoe().enabled()
|
.enabled()
|
||||||
.ssiop().active_low()
|
.ssiop()
|
||||||
.ssm().disabled()
|
.active_low()
|
||||||
.cpol().idle_high()
|
.ssm()
|
||||||
.cpha().second_edge()
|
.disabled()
|
||||||
.lsbfrst().msbfirst()
|
.cpol()
|
||||||
.master().master()
|
.idle_high()
|
||||||
.sp().motorola()
|
.cpha()
|
||||||
.comm().receiver()
|
.second_edge()
|
||||||
.ioswp().disabled()
|
.lsbfrst()
|
||||||
.midi().bits(0)
|
.msbfirst()
|
||||||
.mssi().bits(6)
|
.master()
|
||||||
);
|
.master()
|
||||||
|
.sp()
|
||||||
|
.motorola()
|
||||||
|
.comm()
|
||||||
|
.receiver()
|
||||||
|
.ioswp()
|
||||||
|
.disabled()
|
||||||
|
.midi()
|
||||||
|
.bits(0)
|
||||||
|
.mssi()
|
||||||
|
.bits(6)
|
||||||
|
});
|
||||||
spi5.cr2.modify(|_, w| w.tsize().bits(1));
|
spi5.cr2.modify(|_, w| w.tsize().bits(1));
|
||||||
spi5.cr1.write(|w| w.spe().enabled());
|
spi5.cr1.write(|w| w.spe().enabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
// DAC0
|
// DAC0
|
||||||
fn spi2_setup(spi2: &pac::SPI2) {
|
fn spi2_setup(spi2: &pac::SPI2) {
|
||||||
spi2.cfg1.modify(|_, w|
|
spi2.cfg1
|
||||||
w.mbr().div2()
|
.modify(|_, w| w.mbr().div2().dsize().bits(16 - 1).fthlv().one_frame());
|
||||||
.dsize().bits(16 - 1)
|
spi2.cfg2.modify(|_, w| {
|
||||||
.fthlv().one_frame()
|
w.afcntr()
|
||||||
);
|
.controlled()
|
||||||
spi2.cfg2.modify(|_, w|
|
.ssom()
|
||||||
w.afcntr().controlled()
|
.not_asserted()
|
||||||
.ssom().not_asserted()
|
.ssoe()
|
||||||
.ssoe().enabled()
|
.enabled()
|
||||||
.ssiop().active_low()
|
.ssiop()
|
||||||
.ssm().disabled()
|
.active_low()
|
||||||
.cpol().idle_low()
|
.ssm()
|
||||||
.cpha().first_edge()
|
.disabled()
|
||||||
.lsbfrst().msbfirst()
|
.cpol()
|
||||||
.master().master()
|
.idle_low()
|
||||||
.sp().motorola()
|
.cpha()
|
||||||
.comm().transmitter()
|
.first_edge()
|
||||||
.ioswp().disabled()
|
.lsbfrst()
|
||||||
.midi().bits(0)
|
.msbfirst()
|
||||||
.mssi().bits(0)
|
.master()
|
||||||
);
|
.master()
|
||||||
|
.sp()
|
||||||
|
.motorola()
|
||||||
|
.comm()
|
||||||
|
.transmitter()
|
||||||
|
.ioswp()
|
||||||
|
.disabled()
|
||||||
|
.midi()
|
||||||
|
.bits(0)
|
||||||
|
.mssi()
|
||||||
|
.bits(0)
|
||||||
|
});
|
||||||
spi2.cr2.modify(|_, w| w.tsize().bits(0));
|
spi2.cr2.modify(|_, w| w.tsize().bits(0));
|
||||||
spi2.cr1.write(|w| w.spe().enabled());
|
spi2.cr1.write(|w| w.spe().enabled());
|
||||||
spi2.cr1.modify(|_, w| w.cstart().started());
|
spi2.cr1.modify(|_, w| w.cstart().started());
|
||||||
|
@ -367,27 +409,38 @@ fn spi2_setup(spi2: &pac::SPI2) {
|
||||||
|
|
||||||
// DAC1
|
// DAC1
|
||||||
fn spi4_setup(spi4: &pac::SPI4) {
|
fn spi4_setup(spi4: &pac::SPI4) {
|
||||||
spi4.cfg1.modify(|_, w|
|
spi4.cfg1
|
||||||
w.mbr().div2()
|
.modify(|_, w| w.mbr().div2().dsize().bits(16 - 1).fthlv().one_frame());
|
||||||
.dsize().bits(16 - 1)
|
spi4.cfg2.modify(|_, w| {
|
||||||
.fthlv().one_frame()
|
w.afcntr()
|
||||||
);
|
.controlled()
|
||||||
spi4.cfg2.modify(|_, w|
|
.ssom()
|
||||||
w.afcntr().controlled()
|
.not_asserted()
|
||||||
.ssom().not_asserted()
|
.ssoe()
|
||||||
.ssoe().enabled()
|
.enabled()
|
||||||
.ssiop().active_low()
|
.ssiop()
|
||||||
.ssm().disabled()
|
.active_low()
|
||||||
.cpol().idle_low()
|
.ssm()
|
||||||
.cpha().first_edge()
|
.disabled()
|
||||||
.lsbfrst().msbfirst()
|
.cpol()
|
||||||
.master().master()
|
.idle_low()
|
||||||
.sp().motorola()
|
.cpha()
|
||||||
.comm().transmitter()
|
.first_edge()
|
||||||
.ioswp().disabled()
|
.lsbfrst()
|
||||||
.midi().bits(0)
|
.msbfirst()
|
||||||
.mssi().bits(0)
|
.master()
|
||||||
);
|
.master()
|
||||||
|
.sp()
|
||||||
|
.motorola()
|
||||||
|
.comm()
|
||||||
|
.transmitter()
|
||||||
|
.ioswp()
|
||||||
|
.disabled()
|
||||||
|
.midi()
|
||||||
|
.bits(0)
|
||||||
|
.mssi()
|
||||||
|
.bits(0)
|
||||||
|
});
|
||||||
spi4.cr2.modify(|_, w| w.tsize().bits(0));
|
spi4.cr2.modify(|_, w| w.tsize().bits(0));
|
||||||
spi4.cr1.write(|w| w.spe().enabled());
|
spi4.cr1.write(|w| w.spe().enabled());
|
||||||
spi4.cr1.modify(|_, w| w.cstart().started());
|
spi4.cr1.modify(|_, w| w.cstart().started());
|
||||||
|
@ -398,11 +451,16 @@ fn tim2_setup(tim2: &pac::TIM2) {
|
||||||
tim2.arr.write(|w| unsafe { w.bits(2 - 1) }); // s
|
tim2.arr.write(|w| unsafe { w.bits(2 - 1) }); // s
|
||||||
tim2.dier.write(|w| w.ude().set_bit());
|
tim2.dier.write(|w| w.ude().set_bit());
|
||||||
tim2.egr.write(|w| w.ug().set_bit());
|
tim2.egr.write(|w| w.ug().set_bit());
|
||||||
tim2.cr1.modify(|_, w|
|
tim2.cr1.modify(|_, w| w.dir().clear_bit()); // up
|
||||||
w.dir().clear_bit()); // up
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dma1_setup(dma1: &pac::DMA1, dmamux1: &pac::DMAMUX1, ma: usize, pa0: usize, pa1: usize) {
|
fn dma1_setup(
|
||||||
|
dma1: &pac::DMA1,
|
||||||
|
dmamux1: &pac::DMAMUX1,
|
||||||
|
ma: usize,
|
||||||
|
pa0: usize,
|
||||||
|
pa1: usize,
|
||||||
|
) {
|
||||||
dma1.st[0].cr.modify(|_, w| w.en().clear_bit());
|
dma1.st[0].cr.modify(|_, w| w.en().clear_bit());
|
||||||
while dma1.st[0].cr.read().en().bit_is_set() {}
|
while dma1.st[0].cr.read().en().bit_is_set() {}
|
||||||
|
|
||||||
|
@ -411,17 +469,28 @@ fn dma1_setup(dma1: &pac::DMA1, dmamux1: &pac::DMAMUX1, ma: usize, pa0: usize, p
|
||||||
dma1.st[0].ndtr.write(|w| unsafe { w.ndt().bits(1) });
|
dma1.st[0].ndtr.write(|w| unsafe { w.ndt().bits(1) });
|
||||||
dmamux1.ccr[0].modify(|_, w| w.dmareq_id().tim2_up());
|
dmamux1.ccr[0].modify(|_, w| w.dmareq_id().tim2_up());
|
||||||
dma1.st[0].cr.modify(|_, w| unsafe {
|
dma1.st[0].cr.modify(|_, w| unsafe {
|
||||||
w.pl().bits(0b01) // medium
|
w.pl()
|
||||||
.circ().set_bit() // reload ndtr
|
.bits(0b01) // medium
|
||||||
.msize().bits(0b10) // 32
|
.circ()
|
||||||
.minc().clear_bit()
|
.set_bit() // reload ndtr
|
||||||
.mburst().bits(0b00)
|
.msize()
|
||||||
.psize().bits(0b10) // 32
|
.bits(0b10) // 32
|
||||||
.pinc().clear_bit()
|
.minc()
|
||||||
.pburst().bits(0b00)
|
.clear_bit()
|
||||||
.dbm().clear_bit()
|
.mburst()
|
||||||
.dir().bits(0b01) // memory_to_peripheral
|
.bits(0b00)
|
||||||
.pfctrl().clear_bit() // dma is FC
|
.psize()
|
||||||
|
.bits(0b10) // 32
|
||||||
|
.pinc()
|
||||||
|
.clear_bit()
|
||||||
|
.pburst()
|
||||||
|
.bits(0b00)
|
||||||
|
.dbm()
|
||||||
|
.clear_bit()
|
||||||
|
.dir()
|
||||||
|
.bits(0b01) // memory_to_peripheral
|
||||||
|
.pfctrl()
|
||||||
|
.clear_bit() // dma is FC
|
||||||
});
|
});
|
||||||
dma1.st[0].fcr.modify(|_, w| w.dmdis().clear_bit());
|
dma1.st[0].fcr.modify(|_, w| w.dmdis().clear_bit());
|
||||||
dma1.st[0].cr.modify(|_, w| w.en().set_bit());
|
dma1.st[0].cr.modify(|_, w| w.en().set_bit());
|
||||||
|
@ -434,17 +503,28 @@ fn dma1_setup(dma1: &pac::DMA1, dmamux1: &pac::DMAMUX1, ma: usize, pa0: usize, p
|
||||||
dma1.st[1].ndtr.write(|w| unsafe { w.ndt().bits(1) });
|
dma1.st[1].ndtr.write(|w| unsafe { w.ndt().bits(1) });
|
||||||
dmamux1.ccr[1].modify(|_, w| w.dmareq_id().tim2_up());
|
dmamux1.ccr[1].modify(|_, w| w.dmareq_id().tim2_up());
|
||||||
dma1.st[1].cr.modify(|_, w| unsafe {
|
dma1.st[1].cr.modify(|_, w| unsafe {
|
||||||
w.pl().bits(0b01) // medium
|
w.pl()
|
||||||
.circ().set_bit() // reload ndtr
|
.bits(0b01) // medium
|
||||||
.msize().bits(0b10) // 32
|
.circ()
|
||||||
.minc().clear_bit()
|
.set_bit() // reload ndtr
|
||||||
.mburst().bits(0b00)
|
.msize()
|
||||||
.psize().bits(0b10) // 32
|
.bits(0b10) // 32
|
||||||
.pinc().clear_bit()
|
.minc()
|
||||||
.pburst().bits(0b00)
|
.clear_bit()
|
||||||
.dbm().clear_bit()
|
.mburst()
|
||||||
.dir().bits(0b01) // memory_to_peripheral
|
.bits(0b00)
|
||||||
.pfctrl().clear_bit() // dma is FC
|
.psize()
|
||||||
|
.bits(0b10) // 32
|
||||||
|
.pinc()
|
||||||
|
.clear_bit()
|
||||||
|
.pburst()
|
||||||
|
.bits(0b00)
|
||||||
|
.dbm()
|
||||||
|
.clear_bit()
|
||||||
|
.dir()
|
||||||
|
.bits(0b01) // memory_to_peripheral
|
||||||
|
.pfctrl()
|
||||||
|
.clear_bit() // dma is FC
|
||||||
});
|
});
|
||||||
dma1.st[1].fcr.modify(|_, w| w.dmdis().clear_bit());
|
dma1.st[1].fcr.modify(|_, w| w.dmdis().clear_bit());
|
||||||
dma1.st[1].cr.modify(|_, w| w.en().set_bit());
|
dma1.st[1].cr.modify(|_, w| w.en().set_bit());
|
||||||
|
@ -469,16 +549,25 @@ pub fn init() {
|
||||||
// cp.SCB.enable_dcache(&mut cp.CPUID);
|
// cp.SCB.enable_dcache(&mut cp.CPUID);
|
||||||
cp.DWT.enable_cycle_counter(); // japaric/cortex-m-rtfm#184
|
cp.DWT.enable_cycle_counter(); // japaric/cortex-m-rtfm#184
|
||||||
|
|
||||||
rcc.ahb4enr.modify(|_, w|
|
rcc.ahb4enr.modify(|_, w| {
|
||||||
w.gpioaen().set_bit()
|
w.gpioaen()
|
||||||
.gpioben().set_bit()
|
.set_bit()
|
||||||
.gpiocen().set_bit()
|
.gpioben()
|
||||||
.gpioden().set_bit()
|
.set_bit()
|
||||||
.gpioeen().set_bit()
|
.gpiocen()
|
||||||
.gpiofen().set_bit()
|
.set_bit()
|
||||||
.gpiogen().set_bit()
|
.gpioden()
|
||||||
|
.set_bit()
|
||||||
|
.gpioeen()
|
||||||
|
.set_bit()
|
||||||
|
.gpiofen()
|
||||||
|
.set_bit()
|
||||||
|
.gpiogen()
|
||||||
|
.set_bit()
|
||||||
|
});
|
||||||
|
gpio_setup(
|
||||||
|
&dp.GPIOA, &dp.GPIOB, &dp.GPIOD, &dp.GPIOE, &dp.GPIOF, &dp.GPIOG,
|
||||||
);
|
);
|
||||||
gpio_setup(&dp.GPIOA, &dp.GPIOB, &dp.GPIOD, &dp.GPIOE, &dp.GPIOF, &dp.GPIOG);
|
|
||||||
|
|
||||||
rcc.apb1lenr.modify(|_, w| w.spi2en().set_bit());
|
rcc.apb1lenr.modify(|_, w| w.spi2en().set_bit());
|
||||||
let spi2 = dp.SPI2;
|
let spi2 = dp.SPI2;
|
||||||
|
@ -498,12 +587,14 @@ pub fn init() {
|
||||||
spi5_setup(&spi5);
|
spi5_setup(&spi5);
|
||||||
// spi5.ier.write(|w| w.eotie().set_bit());
|
// spi5.ier.write(|w| w.eotie().set_bit());
|
||||||
|
|
||||||
rcc.ahb2enr.modify(|_, w|
|
rcc.ahb2enr.modify(|_, w| {
|
||||||
w
|
w.sram1en()
|
||||||
.sram1en().set_bit()
|
.set_bit()
|
||||||
.sram2en().set_bit()
|
.sram2en()
|
||||||
.sram3en().set_bit()
|
.set_bit()
|
||||||
);
|
.sram3en()
|
||||||
|
.set_bit()
|
||||||
|
});
|
||||||
rcc.ahb1enr.modify(|_, w| w.dma1en().set_bit());
|
rcc.ahb1enr.modify(|_, w| w.dma1en().set_bit());
|
||||||
// init SRAM1 rodata can't load with sram1 disabled
|
// init SRAM1 rodata can't load with sram1 disabled
|
||||||
unsafe { DAT = 0x201 }; // EN | CSTART
|
unsafe { DAT = 0x201 }; // EN | CSTART
|
||||||
|
@ -511,9 +602,13 @@ pub fn init() {
|
||||||
let dat_addr = unsafe { &DAT as *const _ } as usize;
|
let dat_addr = unsafe { &DAT as *const _ } as usize;
|
||||||
cp.SCB.clean_dcache_by_address(dat_addr, 4);
|
cp.SCB.clean_dcache_by_address(dat_addr, 4);
|
||||||
|
|
||||||
dma1_setup(&dp.DMA1, &dp.DMAMUX1, dat_addr,
|
dma1_setup(
|
||||||
|
&dp.DMA1,
|
||||||
|
&dp.DMAMUX1,
|
||||||
|
dat_addr,
|
||||||
&spi1.cr1 as *const _ as usize,
|
&spi1.cr1 as *const _ as usize,
|
||||||
&spi5.cr1 as *const _ as usize);
|
&spi5.cr1 as *const _ as usize,
|
||||||
|
);
|
||||||
|
|
||||||
rcc.apb1lenr.modify(|_, w| w.tim2en().set_bit());
|
rcc.apb1lenr.modify(|_, w| w.tim2en().set_bit());
|
||||||
|
|
||||||
|
@ -528,11 +623,16 @@ pub fn init() {
|
||||||
|
|
||||||
rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit());
|
rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit());
|
||||||
rcc.ahb1enr.modify(|_, w| {
|
rcc.ahb1enr.modify(|_, w| {
|
||||||
w.eth1macen().set_bit()
|
w.eth1macen()
|
||||||
.eth1txen().set_bit()
|
.set_bit()
|
||||||
.eth1rxen().set_bit()
|
.eth1txen()
|
||||||
|
.set_bit()
|
||||||
|
.eth1rxen()
|
||||||
|
.set_bit()
|
||||||
});
|
});
|
||||||
dp.SYSCFG.pmcr.modify(|_, w| unsafe { w.epis().bits(0b100) }); // RMII
|
dp.SYSCFG
|
||||||
|
.pmcr
|
||||||
|
.modify(|_, w| unsafe { w.epis().bits(0b100) }); // RMII
|
||||||
eth::setup_pins(&dp.GPIOA, &dp.GPIOB, &dp.GPIOC, &dp.GPIOG);
|
eth::setup_pins(&dp.GPIOA, &dp.GPIOB, &dp.GPIOC, &dp.GPIOG);
|
||||||
|
|
||||||
// enable TIM2 this must be late to be able to handle the first ADC SPI
|
// enable TIM2 this must be late to be able to handle the first ADC SPI
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use stm32h7::stm32h743 as pac;
|
|
||||||
use super::i2c;
|
use super::i2c;
|
||||||
|
use stm32h7::stm32h743 as pac;
|
||||||
|
|
||||||
const I2C_ADDR: u8 = 0xa0;
|
const I2C_ADDR: u8 = 0xa0;
|
||||||
|
|
||||||
|
|
301
src/eth.rs
301
src/eth.rs
|
@ -1,9 +1,9 @@
|
||||||
use core::{slice, cmp};
|
use core::{cmp, slice};
|
||||||
use stm32h7::stm32h743 as pac;
|
use smoltcp::phy;
|
||||||
use smoltcp::Result;
|
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use smoltcp::wire::EthernetAddress;
|
use smoltcp::wire::EthernetAddress;
|
||||||
use smoltcp::phy;
|
use smoltcp::Result;
|
||||||
|
use stm32h7::stm32h743 as pac;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod phy_consts {
|
mod phy_consts {
|
||||||
|
@ -85,8 +85,12 @@ use self::cr_consts::*;
|
||||||
// 200 MHz AHB clock = eth_hclk
|
// 200 MHz AHB clock = eth_hclk
|
||||||
const CLOCK_RANGE: u8 = ETH_MACMIIAR_CR_HCLK_DIV_102;
|
const CLOCK_RANGE: u8 = ETH_MACMIIAR_CR_HCLK_DIV_102;
|
||||||
|
|
||||||
pub fn setup_pins(gpioa: &pac::GPIOA, gpiob: &pac::GPIOB,
|
pub fn setup_pins(
|
||||||
gpioc: &pac::GPIOC, gpiog: &pac::GPIOG) {
|
gpioa: &pac::GPIOA,
|
||||||
|
gpiob: &pac::GPIOB,
|
||||||
|
gpioc: &pac::GPIOC,
|
||||||
|
gpiog: &pac::GPIOG,
|
||||||
|
) {
|
||||||
// PA1 RMII_REF_CLK
|
// PA1 RMII_REF_CLK
|
||||||
gpioa.moder.modify(|_, w| w.moder1().alternate());
|
gpioa.moder.modify(|_, w| w.moder1().alternate());
|
||||||
gpioa.afrl.modify(|_, w| w.afr1().af11());
|
gpioa.afrl.modify(|_, w| w.afr1().af11());
|
||||||
|
@ -130,12 +134,16 @@ const PHY_ADDR: u8 = 0;
|
||||||
fn phy_read(reg_addr: u8, mac: &pac::ETHERNET_MAC) -> u16 {
|
fn phy_read(reg_addr: u8, mac: &pac::ETHERNET_MAC) -> u16 {
|
||||||
while mac.macmdioar.read().mb().bit_is_set() {}
|
while mac.macmdioar.read().mb().bit_is_set() {}
|
||||||
mac.macmdioar.modify(|_, w| unsafe {
|
mac.macmdioar.modify(|_, w| unsafe {
|
||||||
w
|
w.pa()
|
||||||
.pa().bits(PHY_ADDR)
|
.bits(PHY_ADDR)
|
||||||
.rda().bits(reg_addr)
|
.rda()
|
||||||
.goc().bits(0b11) // read
|
.bits(reg_addr)
|
||||||
.cr().bits(CLOCK_RANGE)
|
.goc()
|
||||||
.mb().set_bit()
|
.bits(0b11) // read
|
||||||
|
.cr()
|
||||||
|
.bits(CLOCK_RANGE)
|
||||||
|
.mb()
|
||||||
|
.set_bit()
|
||||||
});
|
});
|
||||||
while mac.macmdioar.read().mb().bit_is_set() {}
|
while mac.macmdioar.read().mb().bit_is_set() {}
|
||||||
mac.macmdiodr.read().md().bits()
|
mac.macmdiodr.read().md().bits()
|
||||||
|
@ -145,12 +153,16 @@ fn phy_write(reg_addr: u8, reg_data: u16, mac: &pac::ETHERNET_MAC) {
|
||||||
while mac.macmdioar.read().mb().bit_is_set() {}
|
while mac.macmdioar.read().mb().bit_is_set() {}
|
||||||
mac.macmdiodr.write(|w| unsafe { w.md().bits(reg_data) });
|
mac.macmdiodr.write(|w| unsafe { w.md().bits(reg_data) });
|
||||||
mac.macmdioar.modify(|_, w| unsafe {
|
mac.macmdioar.modify(|_, w| unsafe {
|
||||||
w
|
w.pa()
|
||||||
.pa().bits(PHY_ADDR)
|
.bits(PHY_ADDR)
|
||||||
.rda().bits(reg_addr)
|
.rda()
|
||||||
.goc().bits(0b01) // write
|
.bits(reg_addr)
|
||||||
.cr().bits(CLOCK_RANGE)
|
.goc()
|
||||||
.mb().set_bit()
|
.bits(0b01) // write
|
||||||
|
.cr()
|
||||||
|
.bits(CLOCK_RANGE)
|
||||||
|
.mb()
|
||||||
|
.set_bit()
|
||||||
});
|
});
|
||||||
while mac.macmdioar.read().mb().bit_is_set() {}
|
while mac.macmdioar.read().mb().bit_is_set() {}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +207,8 @@ impl RxRing {
|
||||||
let addr = &self.desc_buf as *const _ as u32;
|
let addr = &self.desc_buf as *const _ as u32;
|
||||||
assert_eq!(addr & 0x3, 0);
|
assert_eq!(addr & 0x3, 0);
|
||||||
dma.dmacrx_dlar.write(|w| w.bits(addr));
|
dma.dmacrx_dlar.write(|w| w.bits(addr));
|
||||||
dma.dmacrx_rlr.write(|w| w.rdrl().bits(self.desc_buf.len() as u16 - 1));
|
dma.dmacrx_rlr
|
||||||
|
.write(|w| w.rdrl().bits(self.desc_buf.len() as u16 - 1));
|
||||||
|
|
||||||
self.cur_desc = 0;
|
self.cur_desc = 0;
|
||||||
for _ in 0..self.desc_buf.len() {
|
for _ in 0..self.desc_buf.len() {
|
||||||
|
@ -213,9 +226,9 @@ impl RxRing {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buf_valid(&self) -> bool {
|
fn buf_valid(&self) -> bool {
|
||||||
self.desc_buf[self.cur_desc][3] &
|
self.desc_buf[self.cur_desc][3]
|
||||||
(EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT) ==
|
& (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT)
|
||||||
(EMAC_DES3_FD | EMAC_DES3_LD)
|
== (EMAC_DES3_FD | EMAC_DES3_LD)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn buf_as_slice_mut<'a>(&self) -> &'a mut [u8] {
|
unsafe fn buf_as_slice_mut<'a>(&self) -> &'a mut [u8] {
|
||||||
|
@ -228,7 +241,8 @@ impl RxRing {
|
||||||
fn buf_release(&mut self) {
|
fn buf_release(&mut self) {
|
||||||
let addr = &self.pkt_buf[self.cur_desc] as *const _;
|
let addr = &self.pkt_buf[self.cur_desc] as *const _;
|
||||||
self.desc_buf[self.cur_desc][0] = addr as u32 & EMAC_DES0_BUF1AP;
|
self.desc_buf[self.cur_desc][0] = addr as u32 & EMAC_DES0_BUF1AP;
|
||||||
self.desc_buf[self.cur_desc][3] = EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN;
|
self.desc_buf[self.cur_desc][3] =
|
||||||
|
EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN;
|
||||||
|
|
||||||
let addr = &self.desc_buf[self.cur_desc] as *const _ as u32;
|
let addr = &self.desc_buf[self.cur_desc] as *const _ as u32;
|
||||||
assert_eq!(addr & 0x3, 0);
|
assert_eq!(addr & 0x3, 0);
|
||||||
|
@ -278,7 +292,8 @@ impl TxRing {
|
||||||
let addr = &self.desc_buf as *const _ as u32;
|
let addr = &self.desc_buf as *const _ as u32;
|
||||||
assert_eq!(addr & 0x3, 0);
|
assert_eq!(addr & 0x3, 0);
|
||||||
dma.dmactx_dlar.write(|w| w.bits(addr));
|
dma.dmactx_dlar.write(|w| w.bits(addr));
|
||||||
dma.dmactx_rlr.write(|w| w.tdrl().bits(self.desc_buf.len() as u16 - 1));
|
dma.dmactx_rlr
|
||||||
|
.write(|w| w.tdrl().bits(self.desc_buf.len() as u16 - 1));
|
||||||
let addr = &self.desc_buf[0] as *const _ as u32;
|
let addr = &self.desc_buf[0] as *const _ as u32;
|
||||||
assert_eq!(addr & 0x3, 0);
|
assert_eq!(addr & 0x3, 0);
|
||||||
dma.dmactx_dtpr.write(|w| w.bits(addr));
|
dma.dmactx_dtpr.write(|w| w.bits(addr));
|
||||||
|
@ -295,14 +310,16 @@ impl TxRing {
|
||||||
|
|
||||||
unsafe fn buf_as_slice_mut<'a>(&mut self, len: usize) -> &'a mut [u8] {
|
unsafe fn buf_as_slice_mut<'a>(&mut self, len: usize) -> &'a mut [u8] {
|
||||||
let len = cmp::min(len, ETH_BUFFER_SIZE);
|
let len = cmp::min(len, ETH_BUFFER_SIZE);
|
||||||
self.desc_buf[self.cur_desc][2] = EMAC_TDES2_IOC | (len as u32 & EMAC_TDES2_B1L);
|
self.desc_buf[self.cur_desc][2] =
|
||||||
|
EMAC_TDES2_IOC | (len as u32 & EMAC_TDES2_B1L);
|
||||||
let addr = &self.pkt_buf[self.cur_desc] as *const _ as *mut u8;
|
let addr = &self.pkt_buf[self.cur_desc] as *const _ as *mut u8;
|
||||||
self.desc_buf[self.cur_desc][0] = addr as u32 & EMAC_DES0_BUF1AP;
|
self.desc_buf[self.cur_desc][0] = addr as u32 & EMAC_DES0_BUF1AP;
|
||||||
slice::from_raw_parts_mut(addr, len)
|
slice::from_raw_parts_mut(addr, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buf_release(&mut self) {
|
fn buf_release(&mut self) {
|
||||||
self.desc_buf[self.cur_desc][3] = EMAC_DES3_OWN | EMAC_DES3_FD | EMAC_DES3_LD;
|
self.desc_buf[self.cur_desc][3] =
|
||||||
|
EMAC_DES3_OWN | EMAC_DES3_FD | EMAC_DES3_LD;
|
||||||
self.cur_desc = self.next_desc();
|
self.cur_desc = self.next_desc();
|
||||||
|
|
||||||
let addr = &self.desc_buf[self.cur_desc] as *const _ as u32;
|
let addr = &self.desc_buf[self.cur_desc] as *const _ as u32;
|
||||||
|
@ -325,7 +342,10 @@ pub struct Device {
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self{ rx: RxRing::new(), tx: TxRing::new() }
|
Self {
|
||||||
|
rx: RxRing::new(),
|
||||||
|
tx: TxRing::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the ethernet peripherals
|
// Initialize the ethernet peripherals
|
||||||
|
@ -337,7 +357,9 @@ impl Device {
|
||||||
// are correct.
|
// are correct.
|
||||||
//
|
//
|
||||||
// After `init` is called, `Device` shall not be moved.
|
// After `init` is called, `Device` shall not be moved.
|
||||||
pub unsafe fn init(&mut self, mac: EthernetAddress,
|
pub unsafe fn init(
|
||||||
|
&mut self,
|
||||||
|
mac: EthernetAddress,
|
||||||
eth_mac: &pac::ETHERNET_MAC,
|
eth_mac: &pac::ETHERNET_MAC,
|
||||||
eth_dma: &pac::ETHERNET_DMA,
|
eth_dma: &pac::ETHERNET_DMA,
|
||||||
eth_mtl: &pac::ETHERNET_MTL,
|
eth_mtl: &pac::ETHERNET_MTL,
|
||||||
|
@ -346,66 +368,97 @@ impl Device {
|
||||||
while eth_dma.dmamr.read().swr().bit_is_set() {}
|
while eth_dma.dmamr.read().swr().bit_is_set() {}
|
||||||
|
|
||||||
// 200 MHz
|
// 200 MHz
|
||||||
eth_mac.mac1ustcr.modify(|_, w| w.tic_1us_cntr().bits(200 - 1));
|
eth_mac
|
||||||
|
.mac1ustcr
|
||||||
|
.modify(|_, w| w.tic_1us_cntr().bits(200 - 1));
|
||||||
|
|
||||||
// Configuration Register
|
// Configuration Register
|
||||||
eth_mac.maccr.modify(|_, w| {
|
eth_mac.maccr.modify(|_, w| {
|
||||||
w
|
w.arpen()
|
||||||
.arpen().clear_bit()
|
.clear_bit()
|
||||||
.ipc().set_bit()
|
.ipc()
|
||||||
.ipg().bits(0b000) // 96 bit
|
.set_bit()
|
||||||
.ecrsfd().clear_bit()
|
.ipg()
|
||||||
.dcrs().clear_bit()
|
.bits(0b000) // 96 bit
|
||||||
.bl().bits(0b00) // 19
|
.ecrsfd()
|
||||||
.prelen().bits(0b00) // 7
|
.clear_bit()
|
||||||
|
.dcrs()
|
||||||
|
.clear_bit()
|
||||||
|
.bl()
|
||||||
|
.bits(0b00) // 19
|
||||||
|
.prelen()
|
||||||
|
.bits(0b00) // 7
|
||||||
// CRC stripping for Type frames
|
// CRC stripping for Type frames
|
||||||
.cst().set_bit()
|
.cst()
|
||||||
|
.set_bit()
|
||||||
// Fast Ethernet speed
|
// Fast Ethernet speed
|
||||||
.fes().set_bit()
|
.fes()
|
||||||
|
.set_bit()
|
||||||
// Duplex mode
|
// Duplex mode
|
||||||
.dm().set_bit()
|
.dm()
|
||||||
|
.set_bit()
|
||||||
// Automatic pad/CRC stripping
|
// Automatic pad/CRC stripping
|
||||||
.acs().set_bit()
|
.acs()
|
||||||
|
.set_bit()
|
||||||
// Retry disable in half-duplex mode
|
// Retry disable in half-duplex mode
|
||||||
.dr().set_bit()
|
.dr()
|
||||||
|
.set_bit()
|
||||||
});
|
});
|
||||||
eth_mac.macecr.modify(|_, w| {
|
eth_mac.macecr.modify(|_, w| {
|
||||||
w
|
w.eipgen()
|
||||||
.eipgen().clear_bit()
|
.clear_bit()
|
||||||
.usp().clear_bit()
|
.usp()
|
||||||
.spen().clear_bit()
|
.clear_bit()
|
||||||
.dcrcc().clear_bit()
|
.spen()
|
||||||
|
.clear_bit()
|
||||||
|
.dcrcc()
|
||||||
|
.clear_bit()
|
||||||
});
|
});
|
||||||
// Set the MAC address
|
// Set the MAC address
|
||||||
eth_mac.maca0lr.write(|w|
|
eth_mac.maca0lr.write(|w| {
|
||||||
w.addrlo().bits( u32::from(mac.0[0]) |
|
w.addrlo().bits(
|
||||||
(u32::from(mac.0[1]) << 8) |
|
u32::from(mac.0[0])
|
||||||
(u32::from(mac.0[2]) << 16) |
|
| (u32::from(mac.0[1]) << 8)
|
||||||
(u32::from(mac.0[3]) << 24))
|
| (u32::from(mac.0[2]) << 16)
|
||||||
);
|
| (u32::from(mac.0[3]) << 24),
|
||||||
eth_mac.maca0hr.write(|w|
|
)
|
||||||
w.addrhi().bits( u16::from(mac.0[4]) |
|
});
|
||||||
(u16::from(mac.0[5]) << 8))
|
eth_mac.maca0hr.write(|w| {
|
||||||
);
|
w.addrhi()
|
||||||
|
.bits(u16::from(mac.0[4]) | (u16::from(mac.0[5]) << 8))
|
||||||
|
});
|
||||||
// frame filter register
|
// frame filter register
|
||||||
eth_mac.macpfr.modify(|_, w| {
|
eth_mac.macpfr.modify(|_, w| {
|
||||||
w
|
w.dntu()
|
||||||
.dntu().clear_bit()
|
.clear_bit()
|
||||||
.ipfe().clear_bit()
|
.ipfe()
|
||||||
.vtfe().clear_bit()
|
.clear_bit()
|
||||||
.hpf().clear_bit()
|
.vtfe()
|
||||||
.saf().clear_bit()
|
.clear_bit()
|
||||||
.saif().clear_bit()
|
.hpf()
|
||||||
.pcf().bits(0b00)
|
.clear_bit()
|
||||||
.dbf().clear_bit()
|
.saf()
|
||||||
.pm().clear_bit()
|
.clear_bit()
|
||||||
.daif().clear_bit()
|
.saif()
|
||||||
.hmc().clear_bit()
|
.clear_bit()
|
||||||
.huc().clear_bit()
|
.pcf()
|
||||||
|
.bits(0b00)
|
||||||
|
.dbf()
|
||||||
|
.clear_bit()
|
||||||
|
.pm()
|
||||||
|
.clear_bit()
|
||||||
|
.daif()
|
||||||
|
.clear_bit()
|
||||||
|
.hmc()
|
||||||
|
.clear_bit()
|
||||||
|
.huc()
|
||||||
|
.clear_bit()
|
||||||
// Receive All
|
// Receive All
|
||||||
.ra().clear_bit()
|
.ra()
|
||||||
|
.clear_bit()
|
||||||
// Promiscuous mode
|
// Promiscuous mode
|
||||||
.pr().clear_bit()
|
.pr()
|
||||||
|
.clear_bit()
|
||||||
});
|
});
|
||||||
eth_mac.macwtr.write(|w| w.pwe().clear_bit());
|
eth_mac.macwtr.write(|w| w.pwe().clear_bit());
|
||||||
// Flow Control Register
|
// Flow Control Register
|
||||||
|
@ -414,31 +467,44 @@ impl Device {
|
||||||
w.pt().bits(0x100)
|
w.pt().bits(0x100)
|
||||||
});
|
});
|
||||||
eth_mac.macrx_fcr.modify(|_, w| w);
|
eth_mac.macrx_fcr.modify(|_, w| w);
|
||||||
eth_mtl.mtlrx_qomr.modify(|_, w|
|
eth_mtl.mtlrx_qomr.modify(|_, w| {
|
||||||
w
|
w
|
||||||
// Receive store and forward
|
// Receive store and forward
|
||||||
.rsf().set_bit()
|
.rsf()
|
||||||
|
.set_bit()
|
||||||
// Dropping of TCP/IP checksum error frames disable
|
// Dropping of TCP/IP checksum error frames disable
|
||||||
.dis_tcp_ef().clear_bit()
|
.dis_tcp_ef()
|
||||||
|
.clear_bit()
|
||||||
// Forward error frames
|
// Forward error frames
|
||||||
.fep().clear_bit()
|
.fep()
|
||||||
|
.clear_bit()
|
||||||
// Forward undersized good packets
|
// Forward undersized good packets
|
||||||
.fup().clear_bit()
|
.fup()
|
||||||
);
|
.clear_bit()
|
||||||
|
});
|
||||||
eth_mtl.mtltx_qomr.modify(|_, w| {
|
eth_mtl.mtltx_qomr.modify(|_, w| {
|
||||||
w
|
w
|
||||||
// Transmit store and forward
|
// Transmit store and forward
|
||||||
.tsf().set_bit()
|
.tsf()
|
||||||
|
.set_bit()
|
||||||
});
|
});
|
||||||
|
|
||||||
if (phy_read(PHY_REG_ID1, eth_mac) != 0x0007) | (phy_read(PHY_REG_ID2, eth_mac) != 0xC131) {
|
if (phy_read(PHY_REG_ID1, eth_mac) != 0x0007)
|
||||||
|
| (phy_read(PHY_REG_ID2, eth_mac) != 0xC131)
|
||||||
|
{
|
||||||
error!("PHY ID error!");
|
error!("PHY ID error!");
|
||||||
}
|
}
|
||||||
|
|
||||||
phy_write(PHY_REG_BCR, PHY_REG_BCR_RESET, eth_mac);
|
phy_write(PHY_REG_BCR, PHY_REG_BCR_RESET, eth_mac);
|
||||||
while phy_read(PHY_REG_BCR, eth_mac) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {};
|
while phy_read(PHY_REG_BCR, eth_mac) & PHY_REG_BCR_RESET
|
||||||
|
== PHY_REG_BCR_RESET
|
||||||
|
{}
|
||||||
phy_write_ext(PHY_REG_WUCSR, 0, eth_mac);
|
phy_write_ext(PHY_REG_WUCSR, 0, eth_mac);
|
||||||
phy_write(PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, eth_mac);
|
phy_write(
|
||||||
|
PHY_REG_BCR,
|
||||||
|
PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M,
|
||||||
|
eth_mac,
|
||||||
|
);
|
||||||
/*
|
/*
|
||||||
while phy_read(PHY_REG_BSR) & PHY_REG_BSR_UP == 0 {};
|
while phy_read(PHY_REG_BSR) & PHY_REG_BSR_UP == 0 {};
|
||||||
while phy_read(PHY_REG_BSR) & PHY_REG_BSR_ANDONE == 0 {};
|
while phy_read(PHY_REG_BSR) & PHY_REG_BSR_ANDONE == 0 {};
|
||||||
|
@ -448,43 +514,51 @@ impl Device {
|
||||||
|
|
||||||
// operation mode register
|
// operation mode register
|
||||||
eth_dma.dmamr.modify(|_, w| {
|
eth_dma.dmamr.modify(|_, w| {
|
||||||
w
|
w.intm()
|
||||||
.intm().bits(0b00)
|
.bits(0b00)
|
||||||
// Rx Tx priority ratio 1:1
|
// Rx Tx priority ratio 1:1
|
||||||
.pr().bits(0b000)
|
.pr()
|
||||||
.txpr().clear_bit()
|
.bits(0b000)
|
||||||
.da().clear_bit()
|
.txpr()
|
||||||
|
.clear_bit()
|
||||||
|
.da()
|
||||||
|
.clear_bit()
|
||||||
});
|
});
|
||||||
// bus mode register
|
// bus mode register
|
||||||
eth_dma.dmasbmr.modify(|_, w| {
|
eth_dma.dmasbmr.modify(|_, w| {
|
||||||
// Address-aligned beats
|
// Address-aligned beats
|
||||||
w.aal().set_bit()
|
w.aal()
|
||||||
|
.set_bit()
|
||||||
// Fixed burst
|
// Fixed burst
|
||||||
.fb().set_bit()
|
.fb()
|
||||||
});
|
.set_bit()
|
||||||
eth_dma.dmaccr.modify(|_, w| {
|
|
||||||
w
|
|
||||||
.dsl().bits(0)
|
|
||||||
.pblx8().clear_bit()
|
|
||||||
.mss().bits(536)
|
|
||||||
});
|
});
|
||||||
|
eth_dma
|
||||||
|
.dmaccr
|
||||||
|
.modify(|_, w| w.dsl().bits(0).pblx8().clear_bit().mss().bits(536));
|
||||||
eth_dma.dmactx_cr.modify(|_, w| {
|
eth_dma.dmactx_cr.modify(|_, w| {
|
||||||
w
|
w
|
||||||
// Tx DMA PBL
|
// Tx DMA PBL
|
||||||
.txpbl().bits(32)
|
.txpbl()
|
||||||
.tse().clear_bit()
|
.bits(32)
|
||||||
|
.tse()
|
||||||
|
.clear_bit()
|
||||||
// Operate on second frame
|
// Operate on second frame
|
||||||
.osf().clear_bit()
|
.osf()
|
||||||
|
.clear_bit()
|
||||||
});
|
});
|
||||||
|
|
||||||
eth_dma.dmacrx_cr.modify(|_, w| {
|
eth_dma.dmacrx_cr.modify(|_, w| {
|
||||||
w
|
w
|
||||||
// receive buffer size
|
// receive buffer size
|
||||||
.rbsz().bits(ETH_BUFFER_SIZE as u16)
|
.rbsz()
|
||||||
|
.bits(ETH_BUFFER_SIZE as u16)
|
||||||
// Rx DMA PBL
|
// Rx DMA PBL
|
||||||
.rxpbl().bits(32)
|
.rxpbl()
|
||||||
|
.bits(32)
|
||||||
// Disable flushing of received frames
|
// Disable flushing of received frames
|
||||||
.rpf().clear_bit()
|
.rpf()
|
||||||
|
.clear_bit()
|
||||||
});
|
});
|
||||||
|
|
||||||
self.rx.init(eth_dma);
|
self.rx.init(eth_dma);
|
||||||
|
@ -492,8 +566,10 @@ impl Device {
|
||||||
|
|
||||||
// Manage MAC transmission and reception
|
// Manage MAC transmission and reception
|
||||||
eth_mac.maccr.modify(|_, w| {
|
eth_mac.maccr.modify(|_, w| {
|
||||||
w.re().bit(true) // Receiver Enable
|
w.re()
|
||||||
.te().bit(true) // Transmiter Enable
|
.bit(true) // Receiver Enable
|
||||||
|
.te()
|
||||||
|
.bit(true) // Transmiter Enable
|
||||||
});
|
});
|
||||||
eth_mtl.mtltx_qomr.modify(|_, w| w.ftq().set_bit());
|
eth_mtl.mtltx_qomr.modify(|_, w| w.ftq().set_bit());
|
||||||
|
|
||||||
|
@ -505,10 +581,9 @@ impl Device {
|
||||||
eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit());
|
eth_dma.dmactx_cr.modify(|_, w| w.st().set_bit());
|
||||||
eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit());
|
eth_dma.dmacrx_cr.modify(|_, w| w.sr().set_bit());
|
||||||
|
|
||||||
eth_dma.dmacsr.modify(|_, w|
|
eth_dma
|
||||||
w.tps().set_bit()
|
.dmacsr
|
||||||
.rps().set_bit()
|
.modify(|_, w| w.tps().set_bit().rps().set_bit());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,7 +607,7 @@ impl<'a, 'b> phy::Device<'a> for &'b mut Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(self.rx.buf_owned() && self.tx.buf_owned()) {
|
if !(self.rx.buf_owned() && self.tx.buf_owned()) {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((RxToken(&mut self.rx), TxToken(&mut self.tx)))
|
Some((RxToken(&mut self.rx), TxToken(&mut self.tx)))
|
||||||
|
@ -540,7 +615,7 @@ impl<'a, 'b> phy::Device<'a> for &'b mut Device {
|
||||||
|
|
||||||
fn transmit(&mut self) -> Option<TxToken> {
|
fn transmit(&mut self) -> Option<TxToken> {
|
||||||
if !self.tx.buf_owned() {
|
if !self.tx.buf_owned() {
|
||||||
return None
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(TxToken(&mut self.tx))
|
Some(TxToken(&mut self.tx))
|
||||||
|
@ -551,7 +626,9 @@ pub struct RxToken<'a>(&'a mut RxRing);
|
||||||
|
|
||||||
impl<'a> phy::RxToken for RxToken<'a> {
|
impl<'a> phy::RxToken for RxToken<'a> {
|
||||||
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
|
fn consume<R, F>(self, _timestamp: Instant, f: F) -> Result<R>
|
||||||
where F: FnOnce(&mut [u8]) -> Result<R> {
|
where
|
||||||
|
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||||
|
{
|
||||||
let result = f(unsafe { self.0.buf_as_slice_mut() });
|
let result = f(unsafe { self.0.buf_as_slice_mut() });
|
||||||
self.0.buf_release();
|
self.0.buf_release();
|
||||||
result
|
result
|
||||||
|
@ -562,7 +639,9 @@ pub struct TxToken<'a>(&'a mut TxRing);
|
||||||
|
|
||||||
impl<'a> phy::TxToken for TxToken<'a> {
|
impl<'a> phy::TxToken for TxToken<'a> {
|
||||||
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
|
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||||
where F: FnOnce(&mut [u8]) -> Result<R> {
|
where
|
||||||
|
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||||
|
{
|
||||||
let result = f(unsafe { self.0.buf_as_slice_mut(len) });
|
let result = f(unsafe { self.0.buf_as_slice_mut(len) });
|
||||||
self.0.buf_release();
|
self.0.buf_release();
|
||||||
result
|
result
|
||||||
|
|
52
src/i2c.rs
52
src/i2c.rs
|
@ -12,7 +12,7 @@ pub enum Error {
|
||||||
// Address not ACKd within a reasonable time (no device present?)
|
// Address not ACKd within a reasonable time (no device present?)
|
||||||
Timeout,
|
Timeout,
|
||||||
// Unexpected NACK during transfer
|
// Unexpected NACK during transfer
|
||||||
NAck
|
NAck,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maximum number of times to retry NACKed address phase before timing out
|
// Maximum number of times to retry NACKed address phase before timing out
|
||||||
|
@ -20,25 +20,28 @@ pub enum Error {
|
||||||
// EEPROMs during write)
|
// EEPROMs during write)
|
||||||
const N_RETRY: usize = 100; // ~ 10ms @ 100 kHz bus clock
|
const N_RETRY: usize = 100; // ~ 10ms @ 100 kHz bus clock
|
||||||
|
|
||||||
|
|
||||||
pub fn setup(i2c: &pac::I2C2) {
|
pub fn setup(i2c: &pac::I2C2) {
|
||||||
// Disable the peripheral before setting timings
|
// Disable the peripheral before setting timings
|
||||||
i2c.cr1.modify(|_, w| w.pe().clear_bit());
|
i2c.cr1.modify(|_, w| w.pe().clear_bit());
|
||||||
|
|
||||||
// Values from STM32MXCube for 100 kHz I2C clock with 100 MHz peripheral clock
|
// Values from STM32MXCube for 100 kHz I2C clock with 100 MHz peripheral clock
|
||||||
i2c.timingr.modify( |_,w|
|
i2c.timingr.modify(|_, w| {
|
||||||
w.presc().bits(1)
|
w.presc()
|
||||||
.scldel().bits(0x12)
|
.bits(1)
|
||||||
.sdadel().bits(0)
|
.scldel()
|
||||||
.sclh().bits(0xec)
|
.bits(0x12)
|
||||||
.scll().bits(0xff)
|
.sdadel()
|
||||||
);
|
.bits(0)
|
||||||
|
.sclh()
|
||||||
|
.bits(0xec)
|
||||||
|
.scll()
|
||||||
|
.bits(0xff)
|
||||||
|
});
|
||||||
|
|
||||||
// Enable the peripheral
|
// Enable the peripheral
|
||||||
i2c.cr1.write(|w| w.pe().set_bit());
|
i2c.cr1.write(|w| w.pe().set_bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Busy-wait for a flag to be asserted, erroring out on unrecoverable problems
|
// Busy-wait for a flag to be asserted, erroring out on unrecoverable problems
|
||||||
macro_rules! busy_wait_errors {
|
macro_rules! busy_wait_errors {
|
||||||
($i2c:expr, $flag:ident) => {
|
($i2c:expr, $flag:ident) => {
|
||||||
|
@ -58,25 +61,29 @@ macro_rules! busy_wait_errors {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn poll_for_start_ack(
|
fn poll_for_start_ack(
|
||||||
i2c: &pac::I2C2,
|
i2c: &pac::I2C2,
|
||||||
addr: u8,
|
addr: u8,
|
||||||
r_wn: bool,
|
r_wn: bool,
|
||||||
data_len: usize,
|
data_len: usize,
|
||||||
autoend: bool,
|
autoend: bool,
|
||||||
start: bool
|
start: bool,
|
||||||
) -> Result<(), Error>
|
) -> Result<(), Error> {
|
||||||
{
|
|
||||||
for _i in 0..N_RETRY {
|
for _i in 0..N_RETRY {
|
||||||
// START and prepare to send `data_len`
|
// START and prepare to send `data_len`
|
||||||
i2c.cr2.write(|w| {
|
i2c.cr2.write(|w| {
|
||||||
w.start().bit(start)
|
w.start()
|
||||||
.sadd().bits(addr as u16)
|
.bit(start)
|
||||||
.add10().clear_bit()
|
.sadd()
|
||||||
.rd_wrn().bit(r_wn)
|
.bits(addr as u16)
|
||||||
.nbytes().bits( data_len as u8 )
|
.add10()
|
||||||
.autoend().bit(autoend)
|
.clear_bit()
|
||||||
|
.rd_wrn()
|
||||||
|
.bit(r_wn)
|
||||||
|
.nbytes()
|
||||||
|
.bits(data_len as u8)
|
||||||
|
.autoend()
|
||||||
|
.bit(autoend)
|
||||||
});
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -87,7 +94,7 @@ fn poll_for_start_ack(
|
||||||
} else if isr.arlo().bit_is_set() {
|
} else if isr.arlo().bit_is_set() {
|
||||||
return Err(Error::Arbitration);
|
return Err(Error::Arbitration);
|
||||||
} else if isr.nackf().bit_is_set() {
|
} else if isr.nackf().bit_is_set() {
|
||||||
i2c.icr.write(|w| { w.nackcf().set_bit() });
|
i2c.icr.write(|w| w.nackcf().set_bit());
|
||||||
// Wait to finish handling NACK-STOP
|
// Wait to finish handling NACK-STOP
|
||||||
loop {
|
loop {
|
||||||
if i2c.isr.read().busy().bit_is_clear() {
|
if i2c.isr.read().busy().bit_is_clear() {
|
||||||
|
@ -96,7 +103,7 @@ fn poll_for_start_ack(
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else if isr.txis().bit_is_set() || isr.rxne().bit_is_set() {
|
} else if isr.txis().bit_is_set() || isr.rxne().bit_is_set() {
|
||||||
return Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +111,6 @@ fn poll_for_start_ack(
|
||||||
Err(Error::Timeout)
|
Err(Error::Timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn write_read(
|
pub fn write_read(
|
||||||
i2c: &pac::I2C2,
|
i2c: &pac::I2C2,
|
||||||
addr: u8,
|
addr: u8,
|
||||||
|
|
40
src/iir.rs
40
src/iir.rs
|
@ -1,5 +1,5 @@
|
||||||
use core::ops::{Add, Mul};
|
use core::ops::{Add, Mul};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use core::f32;
|
use core::f32;
|
||||||
|
|
||||||
|
@ -14,7 +14,11 @@ pub struct IIR {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn abs(x: f32) -> f32 {
|
fn abs(x: f32) -> f32 {
|
||||||
if x >= 0. { x } else { -x }
|
if x >= 0. {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
-x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copysign(x: f32, y: f32) -> f32 {
|
fn copysign(x: f32, y: f32) -> f32 {
|
||||||
|
@ -26,34 +30,48 @@ fn copysign(x: f32, y: f32) -> f32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max(x: f32, y: f32) -> f32 {
|
fn max(x: f32, y: f32) -> f32 {
|
||||||
if x > y { x } else { y }
|
if x > y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn min(x: f32, y: f32) -> f32 {
|
fn min(x: f32, y: f32) -> f32 {
|
||||||
if x < y { x } else { y }
|
if x < y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn macc<T>(y0: T, x: &[T], a: &[T]) -> T
|
fn macc<T>(y0: T, x: &[T], a: &[T]) -> T
|
||||||
where T: Add<Output=T> + Mul<Output=T> + Copy
|
where
|
||||||
|
T: Add<Output = T> + Mul<Output = T> + Copy,
|
||||||
{
|
{
|
||||||
x.iter().zip(a.iter()).map(|(&i, &j)| i * j).fold(y0, |y, xa| y + xa)
|
x.iter()
|
||||||
|
.zip(a.iter())
|
||||||
|
.map(|(&i, &j)| i * j)
|
||||||
|
.fold(y0, |y, xa| y + xa)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IIR {
|
impl IIR {
|
||||||
pub fn set_pi(&mut self, kp: f32, ki: f32, g: f32) -> Result<(), &str> {
|
pub fn set_pi(&mut self, kp: f32, ki: f32, g: f32) -> Result<(), &str> {
|
||||||
let ki = copysign(ki, kp);
|
let ki = copysign(ki, kp);
|
||||||
let g = copysign(g, kp);
|
let g = copysign(g, kp);
|
||||||
let (a1, b0, b1) =
|
let (a1, b0, b1) = if abs(ki) < f32::EPSILON {
|
||||||
if abs(ki) < f32::EPSILON {
|
|
||||||
(0., kp, 0.)
|
(0., kp, 0.)
|
||||||
} else {
|
} else {
|
||||||
let c = if abs(g) < f32::EPSILON { 1. }
|
let c = if abs(g) < f32::EPSILON {
|
||||||
else { 1./(1. + ki/g) };
|
1.
|
||||||
|
} else {
|
||||||
|
1. / (1. + ki / g)
|
||||||
|
};
|
||||||
let a1 = 2. * c - 1.;
|
let a1 = 2. * c - 1.;
|
||||||
let b0 = ki * c + kp;
|
let b0 = ki * c + kp;
|
||||||
let b1 = ki * c - a1 * kp;
|
let b1 = ki * c - a1 * kp;
|
||||||
if abs(b0 + b1) < f32::EPSILON {
|
if abs(b0 + b1) < f32::EPSILON {
|
||||||
return Err("low integrator gain and/or gain limit")
|
return Err("low integrator gain and/or gain limit");
|
||||||
}
|
}
|
||||||
(a1, b0, b1)
|
(a1, b0, b1)
|
||||||
};
|
};
|
||||||
|
|
139
src/main.rs
139
src/main.rs
|
@ -1,6 +1,5 @@
|
||||||
#![deny(warnings)]
|
#![deny(warnings)]
|
||||||
#![allow(clippy::missing_safety_doc)]
|
#![allow(clippy::missing_safety_doc)]
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![cfg_attr(feature = "nightly", feature(asm))]
|
#![cfg_attr(feature = "nightly", feature(asm))]
|
||||||
|
@ -14,7 +13,9 @@
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
let gpiod = unsafe { &*pac::GPIOD::ptr() };
|
let gpiod = unsafe { &*pac::GPIOD::ptr() };
|
||||||
gpiod.odr.modify(|_, w| w.odr6().high().odr12().high()); // FP_LED_1, FP_LED_3
|
gpiod.odr.modify(|_, w| w.odr6().high().odr12().high()); // FP_LED_1, FP_LED_3
|
||||||
unsafe { core::intrinsics::abort(); }
|
unsafe {
|
||||||
|
core::intrinsics::abort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "semihosting")]
|
#[cfg(feature = "semihosting")]
|
||||||
|
@ -30,40 +31,38 @@ use core::ptr;
|
||||||
// use core::sync::atomic::{AtomicU32, AtomicBool, Ordering};
|
// use core::sync::atomic::{AtomicU32, AtomicBool, Ordering};
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use cortex_m_rt::exception;
|
use cortex_m_rt::exception;
|
||||||
use stm32h7::stm32h743 as pac;
|
use heapless::{consts::*, String, Vec};
|
||||||
use heapless::{String, Vec, consts::*};
|
|
||||||
use rtfm::cyccnt::{Instant, U32Ext as _};
|
use rtfm::cyccnt::{Instant, U32Ext as _};
|
||||||
|
use stm32h7::stm32h743 as pac;
|
||||||
|
|
||||||
use smoltcp as net;
|
use smoltcp as net;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize, de::DeserializeOwned};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use serde_json_core::{ser::to_string, de::from_slice};
|
use serde_json_core::{de::from_slice, ser::to_string};
|
||||||
|
|
||||||
mod eth;
|
mod eth;
|
||||||
|
|
||||||
mod iir;
|
mod iir;
|
||||||
use iir::*;
|
use iir::*;
|
||||||
|
|
||||||
mod i2c;
|
|
||||||
mod eeprom;
|
|
||||||
mod board;
|
mod board;
|
||||||
|
mod eeprom;
|
||||||
|
mod i2c;
|
||||||
|
|
||||||
#[cfg(not(feature = "semihosting"))]
|
#[cfg(not(feature = "semihosting"))]
|
||||||
fn init_log() {}
|
fn init_log() {}
|
||||||
|
|
||||||
#[cfg(feature = "semihosting")]
|
#[cfg(feature = "semihosting")]
|
||||||
fn init_log() {
|
fn init_log() {
|
||||||
|
use cortex_m_log::log::{init as init_log, Logger};
|
||||||
|
use cortex_m_log::printer::semihosting::{hio::HStdout, InterruptOk};
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use cortex_m_log::log::{Logger, init as init_log};
|
|
||||||
use cortex_m_log::printer::semihosting::{InterruptOk, hio::HStdout};
|
|
||||||
static mut LOGGER: Option<Logger<InterruptOk<HStdout>>> = None;
|
static mut LOGGER: Option<Logger<InterruptOk<HStdout>>> = None;
|
||||||
let logger = Logger {
|
let logger = Logger {
|
||||||
inner: InterruptOk::<_>::stdout().unwrap(),
|
inner: InterruptOk::<_>::stdout().unwrap(),
|
||||||
level: LevelFilter::Info,
|
level: LevelFilter::Info,
|
||||||
};
|
};
|
||||||
let logger = unsafe {
|
let logger = unsafe { LOGGER.get_or_insert(logger) };
|
||||||
LOGGER.get_or_insert(logger)
|
|
||||||
};
|
|
||||||
|
|
||||||
init_log(logger).unwrap();
|
init_log(logger).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +73,6 @@ mod build_info {
|
||||||
// include!(concat!(env!("OUT_DIR"), "/built.rs"));
|
// include!(concat!(env!("OUT_DIR"), "/built.rs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const SCALE: f32 = ((1 << 15) - 1) as f32;
|
const SCALE: f32 = ((1 << 15) - 1) as f32;
|
||||||
|
|
||||||
// static ETHERNET_PENDING: AtomicBool = AtomicBool::new(true);
|
// static ETHERNET_PENDING: AtomicBool = AtomicBool::new(true);
|
||||||
|
@ -83,14 +81,17 @@ const TCP_RX_BUFFER_SIZE: usize = 8192;
|
||||||
const TCP_TX_BUFFER_SIZE: usize = 8192;
|
const TCP_TX_BUFFER_SIZE: usize = 8192;
|
||||||
|
|
||||||
macro_rules! create_socket {
|
macro_rules! create_socket {
|
||||||
($set:ident, $rx_storage:ident, $tx_storage:ident, $target:ident) => (
|
($set:ident, $rx_storage:ident, $tx_storage:ident, $target:ident) => {
|
||||||
let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE];
|
let mut $rx_storage = [0; TCP_RX_BUFFER_SIZE];
|
||||||
let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE];
|
let mut $tx_storage = [0; TCP_TX_BUFFER_SIZE];
|
||||||
let tcp_rx_buffer = net::socket::TcpSocketBuffer::new(&mut $rx_storage[..]);
|
let tcp_rx_buffer =
|
||||||
let tcp_tx_buffer = net::socket::TcpSocketBuffer::new(&mut $tx_storage[..]);
|
net::socket::TcpSocketBuffer::new(&mut $rx_storage[..]);
|
||||||
let tcp_socket = net::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
let tcp_tx_buffer =
|
||||||
|
net::socket::TcpSocketBuffer::new(&mut $tx_storage[..]);
|
||||||
|
let tcp_socket =
|
||||||
|
net::socket::TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||||
let $target = $set.add(tcp_socket);
|
let $target = $set.add(tcp_socket);
|
||||||
)
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rtfm::app(device = stm32h7::stm32h743, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)]
|
#[rtfm::app(device = stm32h7::stm32h743, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)]
|
||||||
|
@ -98,7 +99,8 @@ const APP: () = {
|
||||||
struct Resources {
|
struct Resources {
|
||||||
spi: (pac::SPI1, pac::SPI2, pac::SPI4, pac::SPI5),
|
spi: (pac::SPI1, pac::SPI2, pac::SPI4, pac::SPI5),
|
||||||
i2c: pac::I2C2,
|
i2c: pac::I2C2,
|
||||||
ethernet_periph: (pac::ETHERNET_MAC, pac::ETHERNET_DMA, pac::ETHERNET_MTL),
|
ethernet_periph:
|
||||||
|
(pac::ETHERNET_MAC, pac::ETHERNET_DMA, pac::ETHERNET_MTL),
|
||||||
#[init([[0.; 5]; 2])]
|
#[init([[0.; 5]; 2])]
|
||||||
iir_state: [IIRState; 2],
|
iir_state: [IIRState; 2],
|
||||||
#[init([IIR { ba: [1., 0., 0., 0., 0.], y_offset: 0., y_min: -SCALE - 1., y_max: SCALE }; 2])]
|
#[init([IIR { ba: [1., 0., 0., 0., 0.], y_offset: 0., y_min: -SCALE - 1., y_max: SCALE }; 2])]
|
||||||
|
@ -123,7 +125,11 @@ const APP: () = {
|
||||||
init::LateResources {
|
init::LateResources {
|
||||||
spi: (dp.SPI1, dp.SPI2, dp.SPI4, dp.SPI5),
|
spi: (dp.SPI1, dp.SPI2, dp.SPI4, dp.SPI5),
|
||||||
i2c: dp.I2C2,
|
i2c: dp.I2C2,
|
||||||
ethernet_periph: (dp.ETHERNET_MAC, dp.ETHERNET_DMA, dp.ETHERNET_MTL),
|
ethernet_periph: (
|
||||||
|
dp.ETHERNET_MAC,
|
||||||
|
dp.ETHERNET_DMA,
|
||||||
|
dp.ETHERNET_MTL,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,23 +141,26 @@ const APP: () = {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
info!("Could not read EEPROM, using default MAC address");
|
info!("Could not read EEPROM, using default MAC address");
|
||||||
net::wire::EthernetAddress([0x10, 0xE2, 0xD5, 0x00, 0x03, 0x00])
|
net::wire::EthernetAddress([0x10, 0xE2, 0xD5, 0x00, 0x03, 0x00])
|
||||||
},
|
}
|
||||||
Ok(raw_mac) => net::wire::EthernetAddress(raw_mac)
|
Ok(raw_mac) => net::wire::EthernetAddress(raw_mac),
|
||||||
};
|
};
|
||||||
info!("MAC: {}", hardware_addr);
|
info!("MAC: {}", hardware_addr);
|
||||||
|
|
||||||
unsafe { c.resources.ethernet.init(hardware_addr, MAC, DMA, MTL) };
|
unsafe { c.resources.ethernet.init(hardware_addr, MAC, DMA, MTL) };
|
||||||
let mut neighbor_cache_storage = [None; 8];
|
let mut neighbor_cache_storage = [None; 8];
|
||||||
let neighbor_cache = net::iface::NeighborCache::new(&mut neighbor_cache_storage[..]);
|
let neighbor_cache =
|
||||||
|
net::iface::NeighborCache::new(&mut neighbor_cache_storage[..]);
|
||||||
let local_addr = net::wire::IpAddress::v4(10, 0, 16, 99);
|
let local_addr = net::wire::IpAddress::v4(10, 0, 16, 99);
|
||||||
let mut ip_addrs = [net::wire::IpCidr::new(local_addr, 24)];
|
let mut ip_addrs = [net::wire::IpCidr::new(local_addr, 24)];
|
||||||
let mut iface = net::iface::EthernetInterfaceBuilder::new(c.resources.ethernet)
|
let mut iface =
|
||||||
|
net::iface::EthernetInterfaceBuilder::new(c.resources.ethernet)
|
||||||
.ethernet_addr(hardware_addr)
|
.ethernet_addr(hardware_addr)
|
||||||
.neighbor_cache(neighbor_cache)
|
.neighbor_cache(neighbor_cache)
|
||||||
.ip_addrs(&mut ip_addrs[..])
|
.ip_addrs(&mut ip_addrs[..])
|
||||||
.finalize();
|
.finalize();
|
||||||
let mut socket_set_entries: [_; 8] = Default::default();
|
let mut socket_set_entries: [_; 8] = Default::default();
|
||||||
let mut sockets = net::socket::SocketSet::new(&mut socket_set_entries[..]);
|
let mut sockets =
|
||||||
|
net::socket::SocketSet::new(&mut socket_set_entries[..]);
|
||||||
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, tcp_handle0);
|
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, tcp_handle0);
|
||||||
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, tcp_handle1);
|
create_socket!(sockets, tcp_rx_storage0, tcp_tx_storage0, tcp_handle1);
|
||||||
|
|
||||||
|
@ -170,41 +179,55 @@ const APP: () = {
|
||||||
time += 1;
|
time += 1;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let socket = &mut *sockets.get::<net::socket::TcpSocket>(tcp_handle0);
|
let socket =
|
||||||
|
&mut *sockets.get::<net::socket::TcpSocket>(tcp_handle0);
|
||||||
if socket.state() == net::socket::TcpState::CloseWait {
|
if socket.state() == net::socket::TcpState::CloseWait {
|
||||||
socket.close();
|
socket.close();
|
||||||
} else if !(socket.is_open() || socket.is_listening()) {
|
} else if !(socket.is_open() || socket.is_listening()) {
|
||||||
socket.listen(1234).unwrap_or_else(|e| warn!("TCP listen error: {:?}", e));
|
socket
|
||||||
|
.listen(1234)
|
||||||
|
.unwrap_or_else(|e| warn!("TCP listen error: {:?}", e));
|
||||||
} else if tick && socket.can_send() {
|
} else if tick && socket.can_send() {
|
||||||
let s = iir_state.lock(|iir_state| Status {
|
let s = iir_state.lock(|iir_state| Status {
|
||||||
t: time,
|
t: time,
|
||||||
x0: iir_state[0][0],
|
x0: iir_state[0][0],
|
||||||
y0: iir_state[0][2],
|
y0: iir_state[0][2],
|
||||||
x1: iir_state[1][0],
|
x1: iir_state[1][0],
|
||||||
y1: iir_state[1][2]
|
y1: iir_state[1][2],
|
||||||
});
|
});
|
||||||
json_reply(socket, &s);
|
json_reply(socket, &s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let socket = &mut *sockets.get::<net::socket::TcpSocket>(tcp_handle1);
|
let socket =
|
||||||
|
&mut *sockets.get::<net::socket::TcpSocket>(tcp_handle1);
|
||||||
if socket.state() == net::socket::TcpState::CloseWait {
|
if socket.state() == net::socket::TcpState::CloseWait {
|
||||||
socket.close();
|
socket.close();
|
||||||
} else if !(socket.is_open() || socket.is_listening()) {
|
} else if !(socket.is_open() || socket.is_listening()) {
|
||||||
socket.listen(1235).unwrap_or_else(|e| warn!("TCP listen error: {:?}", e));
|
socket
|
||||||
|
.listen(1235)
|
||||||
|
.unwrap_or_else(|e| warn!("TCP listen error: {:?}", e));
|
||||||
} else {
|
} else {
|
||||||
server.poll(socket, |req: &Request| {
|
server.poll(socket, |req: &Request| {
|
||||||
if req.channel < 2 {
|
if req.channel < 2 {
|
||||||
iir_ch.lock(|iir_ch| iir_ch[req.channel as usize] = req.iir);
|
iir_ch.lock(|iir_ch| {
|
||||||
|
iir_ch[req.channel as usize] = req.iir
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !match iface.poll(&mut sockets, net::time::Instant::from_millis(time as i64)) {
|
if !match iface.poll(
|
||||||
|
&mut sockets,
|
||||||
|
net::time::Instant::from_millis(time as i64),
|
||||||
|
) {
|
||||||
Ok(changed) => changed,
|
Ok(changed) => changed,
|
||||||
Err(net::Error::Unrecognized) => true,
|
Err(net::Error::Unrecognized) => true,
|
||||||
Err(e) => { info!("iface poll error: {:?}", e); true }
|
Err(e) => {
|
||||||
|
info!("iface poll error: {:?}", e);
|
||||||
|
true
|
||||||
|
}
|
||||||
} {
|
} {
|
||||||
// cortex_m::asm::wfi();
|
// cortex_m::asm::wfi();
|
||||||
}
|
}
|
||||||
|
@ -296,7 +319,7 @@ struct Status {
|
||||||
x0: f32,
|
x0: f32,
|
||||||
y0: f32,
|
y0: f32,
|
||||||
x1: f32,
|
x1: f32,
|
||||||
y1: f32
|
y1: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_reply<T: Serialize>(socket: &mut net::socket::TcpSocket, msg: &T) {
|
fn json_reply<T: Serialize>(socket: &mut net::socket::TcpSocket, msg: &T) {
|
||||||
|
@ -312,17 +335,26 @@ struct Server {
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self { data: Vec::new(), discard: false }
|
Self {
|
||||||
|
data: Vec::new(),
|
||||||
|
discard: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll<T, F, R>(&mut self, socket: &mut net::socket::TcpSocket, f: F) -> Option<R>
|
fn poll<T, F, R>(
|
||||||
|
&mut self,
|
||||||
|
socket: &mut net::socket::TcpSocket,
|
||||||
|
f: F,
|
||||||
|
) -> Option<R>
|
||||||
where
|
where
|
||||||
T: DeserializeOwned,
|
T: DeserializeOwned,
|
||||||
F: FnOnce(&T) -> R,
|
F: FnOnce(&T) -> R,
|
||||||
{
|
{
|
||||||
while socket.can_recv() {
|
while socket.can_recv() {
|
||||||
let found = socket.recv(|buf| {
|
let found = socket
|
||||||
let (len, found) = match buf.iter().position(|&c| c as char == '\n') {
|
.recv(|buf| {
|
||||||
|
let (len, found) =
|
||||||
|
match buf.iter().position(|&c| c as char == '\n') {
|
||||||
Some(end) => (end + 1, true),
|
Some(end) => (end + 1, true),
|
||||||
None => (buf.len(), false),
|
None => (buf.len(), false),
|
||||||
};
|
};
|
||||||
|
@ -333,11 +365,18 @@ impl Server {
|
||||||
self.data.extend_from_slice(&buf[..len]).unwrap();
|
self.data.extend_from_slice(&buf[..len]).unwrap();
|
||||||
}
|
}
|
||||||
(len, found)
|
(len, found)
|
||||||
}).unwrap();
|
})
|
||||||
|
.unwrap();
|
||||||
if found {
|
if found {
|
||||||
if self.discard {
|
if self.discard {
|
||||||
self.discard = false;
|
self.discard = false;
|
||||||
json_reply(socket, &Response { code: 520, message: "command buffer overflow" });
|
json_reply(
|
||||||
|
socket,
|
||||||
|
&Response {
|
||||||
|
code: 520,
|
||||||
|
message: "command buffer overflow",
|
||||||
|
},
|
||||||
|
);
|
||||||
self.data.clear();
|
self.data.clear();
|
||||||
} else {
|
} else {
|
||||||
let r = from_slice::<T>(&self.data[..self.data.len() - 1]);
|
let r = from_slice::<T>(&self.data[..self.data.len() - 1]);
|
||||||
|
@ -345,13 +384,25 @@ impl Server {
|
||||||
match r {
|
match r {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
let r = f(&res);
|
let r = f(&res);
|
||||||
json_reply(socket, &Response { code: 200, message: "ok" });
|
json_reply(
|
||||||
return Some(r);
|
socket,
|
||||||
|
&Response {
|
||||||
|
code: 200,
|
||||||
|
message: "ok",
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
return Some(r);
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("parse error {:?}", err);
|
warn!("parse error {:?}", err);
|
||||||
json_reply(socket, &Response { code: 550, message: "parse error" });
|
json_reply(
|
||||||
|
socket,
|
||||||
|
&Response {
|
||||||
|
code: 550,
|
||||||
|
message: "parse error",
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue