blinky, working systick and delay

This commit is contained in:
topquark12 2022-10-20 20:57:24 +08:00
parent 710b3ff5d5
commit fc90593f84
11 changed files with 347 additions and 10 deletions

37
Cargo.lock generated
View File

@ -41,6 +41,12 @@ dependencies = [
"stable_deref_trait", "stable_deref_trait",
] ]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]] [[package]]
name = "bare-metal" name = "bare-metal"
version = "0.2.5" version = "0.2.5"
@ -253,12 +259,16 @@ dependencies = [
"cortex-m-log", "cortex-m-log",
"cortex-m-rt", "cortex-m-rt",
"cortex-m-semihosting 0.5.0", "cortex-m-semihosting 0.5.0",
"fugit",
"log", "log",
"nb 1.0.0", "nb 1.0.0",
"num-traits",
"panic-halt", "panic-halt",
"smoltcp", "smoltcp",
"stm32-eth", "stm32-eth",
"stm32f4xx-hal", "stm32f4xx-hal",
"usb-device",
"usbd-serial",
] ]
[[package]] [[package]]
@ -267,6 +277,12 @@ version = "0.2.135"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
[[package]]
name = "libm"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.17"
@ -297,6 +313,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" 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]] [[package]]
name = "num_threads" name = "num_threads"
version = "0.1.6" version = "0.1.6"
@ -482,6 +508,17 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" 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]] [[package]]
name = "vcell" name = "vcell"
version = "0.1.3" version = "0.1.3"

View File

@ -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"] } stm32f4xx-hal = { version = "0.13.2", features = ["rt", "stm32f407", "usb_fs"] }
stm32-eth = { version = "0.3.0", features = ["stm32f407"] } stm32-eth = { version = "0.3.0", features = ["stm32f407"] }
smoltcp = { version = "0.8.0", default-features = false, features = ["proto-ipv4", "socket-tcp", "log", "medium-ethernet"] } 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] [features]

View File

@ -18,16 +18,16 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1659446231, "lastModified": 1666164185,
"narHash": "sha256-hekabNdTdgR/iLsgce5TGWmfIDZ86qjPhxDg/8TlzhE=", "narHash": "sha256-5v+YB4ijeUfg5LCz9ck4gIpCPhIS+qn02OyPJO48bCE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "eabc38219184cc3e04a974fe31857d8e0eac098d", "rev": "c5203abb1329f7ea084c04acda330ca75d5b9fb5",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-21.11", "ref": "nixos-22.05",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }

View File

@ -1,7 +1,7 @@
{ {
description = "Firmware for kirdy"; 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; }; inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; };
outputs = { self, nixpkgs, mozilla-overlay }: outputs = { self, nixpkgs, mozilla-overlay }:

View File

87
src/device/gpio.rs Normal file
View File

@ -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<Input<Floating>>,
PA7<Input<Floating>>,
PB11<Input<Floating>>,
PG13<Input<Floating>>,
PB13<Input<Floating>>,
PC4<Input<Floating>>,
PC5<Input<Floating>>,
>;
impl thermostatPins for thermostat {
type DacSpi = Dac0Spi;
type DacSync = PE4<Output<PushPull>>;
type Shdn = PE10<Output<PushPull>>;
type VRefPin = PA0<Analog>;
type ItecPin = PA6<Analog>;
type DacFeedbackPin = PA4<Analog>;
type TecUMeasPin = PC2<Analog>;
}
pub struct thermostatPinSet {
pub dac_spi: Transfer<u8>,
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();
}
}

View File

@ -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() { pub fn init_log() {
use log::LevelFilter; use log::LevelFilter;
use cortex_m_log::log::{Logger, init}; use cortex_m_log::log::{Logger, init};

4
src/device/mod.rs Normal file
View File

@ -0,0 +1,4 @@
pub mod log;
pub mod usb;
pub mod timer;
// pub mod init_gpio;

51
src/device/timer.rs Normal file
View File

@ -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<RefCell<u32>> = 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 {}
}

103
src/device/usb.rs Normal file
View File

@ -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<UsbBusAllocator<Bus<USB>>> = MaybeUninit::uninit();
// static mut SERIAL_DEV: Option<(SerialPort<'static, Bus<USB>>, UsbDevice<'static, Bus<USB>>)> = None;
static mut STATE: Option<State> = None;
pub struct State {
serial: SerialPort<'static, Bus<USB>>,
dev: UsbDevice<'static, Bus<USB>>,
}
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(())
}
}

View File

@ -3,16 +3,57 @@
use panic_halt as _; use panic_halt as _;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use stm32f4xx_hal as _; use stm32f4xx_hal::{
use log::{info}; 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 device::timer;
use init_log::init_log;
#[cfg(not(feature = "semihosting"))]
const WATCHDOG_PERIOD: u32 = 1000;
#[cfg(feature = "semihosting")]
const WATCHDOG_PERIOD: u32 = 30000;
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
init_log(); init_log();
info!("Kirdy init"); 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();
}
} }