diff --git a/Cargo.lock b/Cargo.lock index 22cf644..b088d88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bare-metal" version = "0.2.5" @@ -253,12 +259,16 @@ dependencies = [ "cortex-m-log", "cortex-m-rt", "cortex-m-semihosting 0.5.0", + "fugit", "log", "nb 1.0.0", + "num-traits", "panic-halt", "smoltcp", "stm32-eth", "stm32f4xx-hal", + "usb-device", + "usbd-serial", ] [[package]] @@ -267,6 +277,12 @@ version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +[[package]] +name = "libm" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" + [[package]] name = "log" version = "0.4.17" @@ -297,6 +313,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -482,6 +508,17 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" +[[package]] +name = "usbd-serial" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db75519b86287f12dcf0d171c7cf4ecc839149fe9f3b720ac4cfce52959e1dfe" +dependencies = [ + "embedded-hal 0.2.7", + "nb 0.1.3", + "usb-device", +] + [[package]] name = "vcell" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 8eed748..81166b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,10 @@ cortex-m-log = { version = "0.7.0", features = ["log-integration", "semihosting" stm32f4xx-hal = { version = "0.13.2", features = ["rt", "stm32f407", "usb_fs"] } stm32-eth = { version = "0.3.0", features = ["stm32f407"] } smoltcp = { version = "0.8.0", default-features = false, features = ["proto-ipv4", "socket-tcp", "log", "medium-ethernet"] } +num-traits = { version = "0.2.15", default-features = false, features = ["libm"] } +usb-device = "0.2.9" +usbd-serial = "0.1.1" +fugit = "0.3.6" [features] diff --git a/flake.lock b/flake.lock index 6586fc9..9e751b7 100644 --- a/flake.lock +++ b/flake.lock @@ -18,16 +18,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1659446231, - "narHash": "sha256-hekabNdTdgR/iLsgce5TGWmfIDZ86qjPhxDg/8TlzhE=", + "lastModified": 1666164185, + "narHash": "sha256-5v+YB4ijeUfg5LCz9ck4gIpCPhIS+qn02OyPJO48bCE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "eabc38219184cc3e04a974fe31857d8e0eac098d", + "rev": "c5203abb1329f7ea084c04acda330ca75d5b9fb5", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-21.11", + "ref": "nixos-22.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 600309c..1ce69ee 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,7 @@ { description = "Firmware for kirdy"; - inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-21.11; + inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-22.05; inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; }; outputs = { self, nixpkgs, mozilla-overlay }: diff --git a/src/current_source/max5719.rs b/src/current_source/max5719.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/device/gpio.rs b/src/device/gpio.rs new file mode 100644 index 0000000..b9b1a19 --- /dev/null +++ b/src/device/gpio.rs @@ -0,0 +1,87 @@ +use stm32f4xx_hal::{ + adc::Adc, + gpio::{ + AF5, Alternate, AlternateOD, Analog, Floating, Input, + gpioa::*, + gpiob::*, + gpioc::*, + gpioe::*, + gpiof::*, + gpiog::*, + GpioExt, + Output, PushPull, + }, + hal::{self, blocking::spi::Transfer, digital::v2::OutputPin}, + otg_fs::USB, + rcc::Clocks, + pwm::{self, PwmChannels}, + spi::{Spi, NoMiso, TransferModeNormal}, + pac::{ + ADC1, + GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, + OTG_FS_GLOBAL, OTG_FS_DEVICE, OTG_FS_PWRCLK, + SPI1, SPI2, SPI3, + TIM4 + }, + timer::Timer, + time::U32Ext, +}; + +use stm32_eth::EthPins; +pub type EthernetPins = EthPins< + PA1>, + PA7>, + PB11>, + PG13>, + PB13>, + PC4>, + PC5>, +>; + +impl thermostatPins for thermostat { + type DacSpi = Dac0Spi; + type DacSync = PE4>; + type Shdn = PE10>; + type VRefPin = PA0; + type ItecPin = PA6; + type DacFeedbackPin = PA4; + type TecUMeasPin = PC2; +} + +pub struct thermostatPinSet { + pub dac_spi: Transfer, + pub dac_sync: OutputPin, + pub shdn: OutputPin, + pub vref_pin: VRefPin, + pub itec_pin: ItecPin, + pub dac_feedback_pin: DacFeedbackPin, + pub tec_u_meas_pin: TecUMeasPin, +} + +pub struct Pins { + pub thermostat: thermostatPinSet, +} + +impl Pins { + + pub fn setup( + clocks: Clocks, + tim4: TIM4, + gpioa: GPIOA, gpiob: GPIOB, gpioc: GPIOC, gpiod: GPIOD, gpioe: GPIOE, gpiof: GPIOF, gpiog: GPIOG, + spi1: SPI1, spi2: SPI2, spi3: SPI3, + adc1: ADC1, + otg_fs_global: OTG_FS_GLOBAL, otg_fs_device: OTG_FS_DEVICE, otg_fs_pwrclk: OTG_FS_PWRCLK, + ) -> (Self, EthernetPins, USB) { + let gpioa = gpioa.split(); + let gpiob = gpiob.split(); + let gpioc = gpioc.split(); + let gpiod = gpiod.split(); + let gpioe = gpioe.split(); + let gpiof = gpiof.split(); + let gpiog = gpiog.split(); + + + + } + +} \ No newline at end of file diff --git a/src/init_log.rs b/src/device/log.rs similarity index 62% rename from src/init_log.rs rename to src/device/log.rs index 8b18241..f4c2d92 100644 --- a/src/init_log.rs +++ b/src/device/log.rs @@ -1,4 +1,14 @@ +#[cfg(not(feature = "semihosting"))] +use super::usb; +#[cfg(not(feature = "semihosting"))] +pub fn init_log() { + static USB_LOGGER: usb::Logger = usb::Logger; + let _ = log::set_logger(&USB_LOGGER); + log::set_max_level(log::LevelFilter::Debug); +} + +#[cfg(feature = "semihosting")] pub fn init_log() { use log::LevelFilter; use cortex_m_log::log::{Logger, init}; diff --git a/src/device/mod.rs b/src/device/mod.rs new file mode 100644 index 0000000..37d3a02 --- /dev/null +++ b/src/device/mod.rs @@ -0,0 +1,4 @@ +pub mod log; +pub mod usb; +pub mod timer; +// pub mod init_gpio; \ No newline at end of file diff --git a/src/device/timer.rs b/src/device/timer.rs new file mode 100644 index 0000000..4bd607f --- /dev/null +++ b/src/device/timer.rs @@ -0,0 +1,51 @@ +use core::cell::RefCell; +use core::ops::Deref; +use cortex_m::interrupt::Mutex; +use cortex_m::peripheral::syst::SystClkSource; +use cortex_m_rt::exception; +use stm32f4xx_hal::{ + rcc::Clocks, + pac::SYST, +}; + +/// Rate in Hz +const TIMER_RATE: u32 = 1000; +/// Interval duration in milliseconds +const TIMER_DELTA: u32 = 1000 / TIMER_RATE; +/// Elapsed time in milliseconds +static TIMER_MS: Mutex> = Mutex::new(RefCell::new(0)); + +/// Setup SysTick exception +pub fn setup(mut syst: SYST, clocks: Clocks) { + syst.set_clock_source(SystClkSource::Core); + syst.set_reload(clocks.hclk().to_Hz() / TIMER_RATE - 1); + syst.enable_counter(); + syst.enable_interrupt(); +} + +/// SysTick exception (Timer) +#[exception] +fn SysTick() { + cortex_m::interrupt::free(|cs| { + *TIMER_MS.borrow(cs) + .borrow_mut() += TIMER_DELTA; + }); +} + +/// Obtain current time in milliseconds +pub fn now() -> u32 { + cortex_m::interrupt::free(|cs| { + *TIMER_MS.borrow(cs) + .borrow() + .deref() + }) +} + +/// block for `amount` milliseconds +pub fn sleep(amount: u32) { + if amount == 0 { + return; + } + let start = now(); + while now() - start <= amount - 1 {} +} diff --git a/src/device/usb.rs b/src/device/usb.rs new file mode 100644 index 0000000..11f18f8 --- /dev/null +++ b/src/device/usb.rs @@ -0,0 +1,103 @@ +use core::{fmt::{self, Write}, mem::MaybeUninit}; +use cortex_m::interrupt::free; +use stm32f4xx_hal::{ + otg_fs::{USB, UsbBus as Bus}, + pac::{interrupt, Interrupt, NVIC}, +}; +use usb_device::{ + class_prelude::{UsbBusAllocator}, + prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid}, +}; +use usbd_serial::SerialPort; +use log::{Record, Log, Metadata}; + +static mut EP_MEMORY: [u32; 1024] = [0; 1024]; + +static mut BUS: MaybeUninit>> = MaybeUninit::uninit(); +// static mut SERIAL_DEV: Option<(SerialPort<'static, Bus>, UsbDevice<'static, Bus>)> = None; +static mut STATE: Option = None; + +pub struct State { + serial: SerialPort<'static, Bus>, + dev: UsbDevice<'static, Bus>, +} + +impl State { + pub fn setup(usb: USB) { + unsafe { BUS.write(Bus::new(usb, &mut EP_MEMORY)) }; + + let bus = unsafe { BUS.assume_init_ref() }; + let serial = SerialPort::new(bus); + let dev = UsbDeviceBuilder::new(bus, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("M-Labs") + .product("thermostat") + .device_release(0x20) + .self_powered(true) + .device_class(usbd_serial::USB_CLASS_CDC) + .build(); + + free(|_| { + unsafe { STATE = Some(State { serial, dev }); } + }); + + unsafe { + NVIC::unmask(Interrupt::OTG_FS); + } + } + + pub fn get() -> Option<&'static mut Self> { + unsafe { STATE.as_mut() } + } + + pub fn poll() { + if let Some(ref mut s) = Self::get() { + if s.dev.poll(&mut [&mut s.serial]) { + // discard any input + let mut buf = [0u8; 64]; + let _ = s.serial.read(&mut buf); + } + } + } +} + +#[interrupt] +fn OTG_FS() { + free(|_| { + State::poll(); + }); +} + +pub struct Logger; + +impl Log for Logger { + fn enabled(&self, _: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let mut output = SerialOutput; + let _ = writeln!(&mut output, "{} - {}", record.level(), record.args()); + } + } + + fn flush(&self) { + if let Some(ref mut state) = State::get() { + let _ = free(|_| state.serial.flush()); + } + } +} + +pub struct SerialOutput; + +impl Write for SerialOutput { + fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> { + if let Some(ref mut state) = State::get() { + for chunk in s.as_bytes().chunks(16) { + free(|_| state.serial.write(chunk)) + .map_err(|_| fmt::Error)?; + } + } + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index de6f847..628f5e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,16 +3,57 @@ use panic_halt as _; use cortex_m_rt::entry; -use stm32f4xx_hal as _; -use log::{info}; +use stm32f4xx_hal::{ + watchdog::IndependentWatchdog, + rcc::RccExt, + pac::{CorePeripherals, Peripherals}, + time::MegaHertz, prelude::_stm32f4xx_hal_gpio_GpioExt, +}; +use log::info; +use fugit::ExtU32; +mod device; +use device::log::init_log; +// use setup::init_gpio::Pins; -mod init_log; -use init_log::init_log; +use device::timer; + +#[cfg(not(feature = "semihosting"))] +const WATCHDOG_PERIOD: u32 = 1000; +#[cfg(feature = "semihosting")] +const WATCHDOG_PERIOD: u32 = 30000; #[entry] fn main() -> ! { init_log(); info!("Kirdy init"); - loop {} + let mut core_perif = CorePeripherals::take().unwrap(); + core_perif.SCB.enable_icache(); + core_perif.SCB.enable_dcache(&mut core_perif.CPUID); + + let perif = Peripherals::take().unwrap(); + let clocks = perif.RCC.constrain() + .cfgr + .use_hse(MegaHertz::from_raw(8).convert()) + .sysclk(MegaHertz::from_raw(168).convert()) + .hclk(MegaHertz::from_raw(168).convert()) + .pclk1(MegaHertz::from_raw(32).convert()) + .pclk2(MegaHertz::from_raw(64).convert()) + .freeze(); + + let mut wd = IndependentWatchdog::new(perif.IWDG); + wd.start(WATCHDOG_PERIOD.millis()); + wd.feed(); + + timer::setup(core_perif.SYST, clocks); + + let gpioc = perif.GPIOC.split(); + let mut pc9 = gpioc.pc9.into_push_pull_output(); + + loop { + pc9.toggle(); + timer::sleep(1); + wd.feed(); + } + } \ No newline at end of file