diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85c414e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +Cargo.lock +target/ +.cargo/ +.github/ +.idea/ diff --git a/Cargo.toml b/Cargo.toml index 3544ce4..2635702 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,12 +5,14 @@ categories = ["embedded", "no-std"] authors = [ "Thomas Campistron ", "Debilausaure", + "Devin Brite " ] edition = "2018" [dependencies] volatile = "*" bit_field = "*" +r0 = "0.2.2" [features] default = [] diff --git a/doc/K20P64M72SF1.pdf b/doc/K20P64M72SF1.pdf new file mode 100644 index 0000000..69ce5f9 Binary files /dev/null and b/doc/K20P64M72SF1.pdf differ diff --git a/doc/teensy_3.2.pdf b/doc/teensy_3.2.pdf deleted file mode 100644 index e69de29..0000000 diff --git a/examples/blink_dynamic_clocks.rs b/examples/blink_dynamic_clocks.rs new file mode 100644 index 0000000..0997183 --- /dev/null +++ b/examples/blink_dynamic_clocks.rs @@ -0,0 +1,33 @@ +#![feature(stdsimd)] +#![no_std] +#![no_main] + +use teensy::mcg::{CpuFreq, Mcg}; +use teensy::sim::Sim; +use teensy::*; + +define_panic! {blink} + +#[no_mangle] +fn main() { + let mut led = unsafe { make_pin!(led).make_gpio().with_output() }; + + loop { + change_clocks(CpuFreq::High); + led.toggle(); + sleep::delay(72_000_000 * 5); // 5s at 72MHz -> 3.75s at 96MHz + led.toggle(); + sleep::delay(72_000_000 * 5); // 5s at 72MHz -> 3.75s at 96MHz + + change_clocks(CpuFreq::Reduced); + led.toggle(); + sleep::delay(72_000_000 * 5); // 5s at 72MHz -> 7.5s at 48MHz + led.toggle(); + sleep::delay(72_000_000 * 5); // 5s at 72MHz -> 7.5s at 48MHz + } +} + +fn change_clocks(freq: mcg::CpuFreq) { + let (sim, mcg): (&mut Sim, &mut Mcg) = unsafe { (sim::Sim::new(), mcg::Mcg::new()) }; + mcg.set_clocks(freq, sim); +} diff --git a/layout.ld b/layout.ld index 5ec1529..5a7f28d 100644 --- a/layout.ld +++ b/layout.ld @@ -24,28 +24,41 @@ SECTIONS { PROVIDE(_stack_top = ORIGIN(RAM) + LENGTH(RAM)); - .vector_table ORIGIN(FLASH) : { + .text ORIGIN(FLASH) : { /* Initial stack pointer */ LONG(_stack_top); + /* TODO: add other items from vector_table before interrupts */ /* Interrupts */ KEEP(*(.vector_table.interrupts)); - } > FLASH - - - - .text : { . = 0x400; KEEP(*(.flashconfig*)) . = ALIGN(4); *(.text*) - } > FLASH = 0xFF + } > FLASH .rodata : ALIGN(4){ *(.rodata .rodata.*); . = ALIGN(4); } > FLASH + /* uninitialized static vars */ + .bss : ALIGN(4) + { + _sbss = .; + *(.bss.*); + _ebss = ALIGN(4); + } > RAM + + /* initialized static vars */ + .data : ALIGN(4) + { + _sdata = .; + *(.data.*); + _edata = ALIGN(4); + } > RAM AT > FLASH + _sidata = LOADADDR(.data); + /DISCARD/ : { *(.ARM.*) } diff --git a/src/boot.rs b/src/boot.rs index 01a780e..8e2590d 100644 --- a/src/boot.rs +++ b/src/boot.rs @@ -1,3 +1,4 @@ +#![no_builtins] //! Here is the most important file of this crate. //! When you add this crate as a dependency it will move the bootloader to the good section. Enable //! **all** the port, the clock at 72MHz and disable the watchdog and **then** call your `main`. @@ -5,6 +6,7 @@ //! this crate. use crate::*; +use r0::{init_data, zero_bss}; /// The first function to be executed by the teensy /// Enable all the clocks: @@ -15,8 +17,18 @@ use crate::*; /// use all the ports. /// Disable the watchdog. #[no_mangle] -extern "C" fn __boot() { +extern "C" fn __reset() { + extern "C" { + static mut _sbss: u32; + static mut _ebss: u32; + static mut _sdata: u32; + static mut _edata: u32; + static _sidata: u32; + } + unsafe { + zero_bss(&mut _sbss, &mut _ebss); + init_data(&mut _sdata, &mut _edata, &_sidata); init(); main(); } @@ -26,17 +38,17 @@ extern "C" fn __boot() { #[cfg(not(feature = "manual_init"))] #[no_mangle] fn init() { - let (wdog, sim, mcg, osc) = unsafe { + let (sim, mcg, osc) = unsafe { + + watchdog::Watchdog::new().disable(); + ( - watchdog::Watchdog::new(), sim::Sim::new(), mcg::Mcg::new(), osc::Osc::new(), ) }; - wdog.disable(); - // Enable the crystal oscillator with 10pf of capacitance osc.enable(10); // Turn on all the port clock gate @@ -46,33 +58,14 @@ fn init() { sim.enable_clock(sim::Clock::PortD); sim.enable_clock(sim::Clock::PortE); - // Set our clocks: - // core: 72Mhz - // peripheral: 36MHz - // flash: 24MHz - sim.set_dividers(1, 2, 3); - // We would also set the USB divider here if we wanted to use it. - - // Now we can start setting up the MCG for our needs. - if let mcg::Clock::Fei(mut fei) = mcg.clock() { - // Our 16MHz xtal is "very fast", and needs to be divided - // by 512 to be in the acceptable FLL range. - fei.enable_xtal(mcg::OscRange::VeryHigh); - let fbe = fei.use_external(512); - - // PLL is 27/6 * xtal == 72MHz - let pbe = fbe.enable_pll(27, 6); - pbe.use_pll(); - } else { - panic!("Somehow the clock wasn't in FEI mode"); - } + mcg.set_clocks(mcg::CpuFreq::Default, sim); } /// This is the Interrupt Descriptor Table #[link_section = ".vector_table.interrupts"] #[no_mangle] pub static _INTERRUPTS: [unsafe extern "C" fn(); 111] = [ - __boot, // TODO: Move this to a different vector? + __reset, // TODO: Move this to a different vector? interrupts::isr_non_maskable, interrupts::isr_hard_fault, interrupts::isr_memmanage_fault, diff --git a/src/mcg.rs b/src/mcg.rs index e21814b..014602d 100644 --- a/src/mcg.rs +++ b/src/mcg.rs @@ -1,10 +1,10 @@ use core::mem; +use crate::sim; use bit_field::BitField; use volatile::Volatile; -/// TODO This value should not be hardcoded and should change depending on the choosen frequency -pub const F_CPU: u32 = 72_000_000; +pub static mut F_CPU: u32 = 72_000_000; #[repr(C, packed)] pub struct Mcg { @@ -42,6 +42,14 @@ enum OscSource { External = 2, } +pub enum CpuFreq { + /// Core/Bus/Flash speeds + High, // 96/48/24 (96MHz PLL) + Default, // 72/36/24 (72MHz PLL) + Reduced, // 48/48/24 (96MHz PLL) + Low, // 24/24/24 (96MHz PLL) +} + pub struct Fei { mcg: &'static mut Mcg, } @@ -107,6 +115,8 @@ pub struct Fbe { } impl Fbe { + // numerator / denominator * xtal (16MHz) = clock speed?? + // enable phase-locked loop pub fn enable_pll(self, numerator: u8, denominator: u8) -> Pbe { if numerator < 24 || numerator > 55 { panic!("Invalid PLL VCO divide factor: {}", numerator); @@ -117,11 +127,15 @@ impl Fbe { } self.mcg.c5.update(|c5| { + // set PLL external reference divide factor c5.set_bits(0..5, denominator - 1); + // Subtract 1 here to make math easier }); self.mcg.c6.update(|c6| { + // set VCO divider c6.set_bits(0..5, numerator - 24); + // select PLL c6.set_bit(6, true); }); @@ -139,7 +153,7 @@ pub struct Pbe { } impl Pbe { - pub fn use_pll(self) { + pub fn use_pll(self) -> Pee { self.mcg.c1.update(|c1| { c1.set_bits(6..8, OscSource::LockedLoop as u8); }); @@ -151,6 +165,42 @@ impl Pbe { // which would be invalid to set, we just check for the known // value "3" here. while self.mcg.s.read().get_bits(2..4) != 3 {} + Pee { mcg: self.mcg } + } + + pub fn disable_pll(self) -> Fbe { + self.mcg.c6.update(|c6| { + // select FLL + c6.set_bit(6, false); + }); + + // Wait for PLL to be disabled + while self.mcg.s.read().get_bit(5) {} + // Wait for the PLL to be "unlocked" + while self.mcg.s.read().get_bit(6) {} + + Fbe { mcg: self.mcg } + } +} + +pub struct Pee { + mcg: &'static mut Mcg, +} + +impl Pee { + fn use_external(self) -> Pbe { + self.mcg.c1.update(|c1| { + c1.set_bits(6..8, OscSource::External as u8); + }); + + // mcg.c1 and mcg.s have slightly different behaviors. In c1, + // we use one value to indicate "Use whichever LL is + // enabled". In s, it is differentiated between the FLL at 0, + // and the PLL at 3. Instead of adding a value to OscSource + // which would be invalid to set, we just check for the known + // value "0" here. + while self.mcg.s.read().get_bits(2..4) != 2 {} + return Pbe { mcg: self.mcg }; } } @@ -158,6 +208,7 @@ pub enum Clock { Fei(Fei), Fbe(Fbe), Pbe(Pbe), + Pee(Pee), } impl Mcg { @@ -166,11 +217,82 @@ impl Mcg { let fll_internal = self.c1.read().get_bit(2); let pll_enabled = self.c6.read().get_bit(6); + // TODO: match all possible MCG clock modes before panic match (fll_internal, pll_enabled, source) { (true, false, OscSource::LockedLoop) => Clock::Fei(Fei { mcg: self }), (false, false, OscSource::External) => Clock::Fbe(Fbe { mcg: self }), (_, true, OscSource::External) => Clock::Pbe(Pbe { mcg: self }), + (_, true, OscSource::LockedLoop) => Clock::Pee(Pee { mcg: self }), _ => panic!("The current clock mode cannot be represented as a known struct"), } } + + pub fn set_clocks(&'static mut self, clock: CpuFreq, sim: &mut sim::Sim) -> Pee { + match clock { + CpuFreq::High => { + unsafe { + F_CPU = 96_000_000; + } + // Set our clocks: 96/48/24 + sim.set_dividers(1, 2, 4); + // We would also set the USB divider here if we wanted to use it. + let fbe = self.mode_to_fbe(); + let pbe = fbe.enable_pll(24, 4); // 16MHz / 4 * 24 = 96MHz + pbe.use_pll() + } + CpuFreq::Default => { + unsafe { + F_CPU = 72_000_000; + } + // Set our clocks: 72/36/24 + sim.set_dividers(1, 2, 3); + // We would also set the USB divider here if we wanted to use it. + let fbe = self.mode_to_fbe(); + let pbe = fbe.enable_pll(27, 6); // 16MHz / 6 * 27 = 72MHz + pbe.use_pll() + } + CpuFreq::Reduced => { + unsafe { + F_CPU = 48_000_000; + } + // Set our clocks: 48/48/24 + sim.set_dividers(2, 2, 4); + // We would also set the USB divider here if we wanted to use it. + let fbe = self.mode_to_fbe(); + let pbe = fbe.enable_pll(24, 4); // 16MHz / 4 * 24 = 96MHz + pbe.use_pll() + } + CpuFreq::Low => { + unsafe { + F_CPU = 24_000_000; + } + // Set our clocks: 24/24/24 + sim.set_dividers(4, 4, 4); + // We would also set the USB divider here if we wanted to use it. + let fbe = self.mode_to_fbe(); + let pbe = fbe.enable_pll(24, 4); // 16MHz / 4 * 24 = 96MHz + pbe.use_pll() + } + } + } + + pub fn mode_to_fbe(&'static mut self) -> Fbe { + return match self.clock() { + Clock::Fei(mut fei) => { + // Our 16MHz xtal is "very fast", and needs to be divided + // by 512 to be in the acceptable FLL range. + // 31.25 kHz to 39.0625 kHz + fei.enable_xtal(OscRange::VeryHigh); + // (literally the only valid divisor on teensy) + fei.use_external(512) // 16MHz/512 = 31.25KHz + } + Clock::Fbe(fbe) => fbe, + Clock::Pbe(pbe) => pbe.disable_pll(), + Clock::Pee(pee) => { + let pbe = pee.use_external(); + let fbe = pbe.disable_pll(); + return fbe; + } + }; + } } diff --git a/src/panic.rs b/src/panic.rs index 41d4491..81573c4 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -62,7 +62,7 @@ macro_rules! blink_panic { loop { led.toggle(); - sleep::sleep_ms(500); + sleep::sleep_ms(50); } } }; diff --git a/src/port/macros.rs b/src/port/macros.rs index 794a9b0..dea5d1d 100644 --- a/src/port/macros.rs +++ b/src/port/macros.rs @@ -27,213 +27,213 @@ macro_rules! make_pin { }; // ===== special pin ===== (led) => { - teensy::port::Port::new(teensy::port::PortName::C).pin(5) + crate::port::Port::new(crate::port::PortName::C).pin(5) }; // ===== analog pins ===== (A17) => { - teensy::port::Pin::new(28) + crate::port::Pin::new(28) }; (A16) => { - teensy::port::Pin::new(27) + crate::port::Pin::new(27) }; (A15) => { - teensy::port::Pin::new(26) + crate::port::Pin::new(26) }; (A18) => { - teensy::port::Pin::new(29) + crate::port::Pin::new(29) }; (A19) => { - teensy::port::Pin::new(30) + crate::port::Pin::new(30) }; (A20) => { - teensy::port::Pin::new(31) + crate::port::Pin::new(31) }; (A9) => { - teensy::port::Pin::new(23) + crate::port::Pin::new(23) }; (A8) => { - teensy::port::Pin::new(22) + crate::port::Pin::new(22) }; (A7) => { - teensy::port::Pin::new(21) + crate::port::Pin::new(21) }; (A6) => { - teensy::port::Pin::new(20) + crate::port::Pin::new(20) }; (A5) => { - teensy::port::Pin::new(19) + crate::port::Pin::new(19) }; (A4) => { - teensy::port::Pin::new(18) + crate::port::Pin::new(18) }; (A3) => { - teensy::port::Pin::new(17) + crate::port::Pin::new(17) }; (A2) => { - teensy::port::Pin::new(16) + crate::port::Pin::new(16) }; (A1) => { - teensy::port::Pin::new(15) + crate::port::Pin::new(15) }; (A0) => { - teensy::port::Pin::new(14) + crate::port::Pin::new(14) }; // ===== I2C port ===== (SCL0) => { - teensy::port::Pin::new(19) + crate::port::Pin::new(19) }; (SDA0) => { - teensy::port::Pin::new(18) + crate::port::Pin::new(18) }; (SCL1) => { - teensy::port::Pin::new(29) + crate::port::Pin::new(29) }; (SDA1) => { - teensy::port::Pin::new(30) + crate::port::Pin::new(30) }; // ===== serial port ===== (RX) => { - teensy::port::Pin::new(3) + crate::port::Pin::new(3) }; (TX) => { - teensy::port::Pin::new(4) + crate::port::Pin::new(4) }; (RX1) => { - teensy::port::Pin::new(0) + crate::port::Pin::new(0) }; (TX1) => { - teensy::port::Pin::new(1) + crate::port::Pin::new(1) }; (RX2) => { - teensy::port::Pin::new(9) + crate::port::Pin::new(9) }; (TX2) => { - teensy::port::Pin::new(10) + crate::port::Pin::new(10) }; (RX3) => { - teensy::port::Pin::new(7) + crate::port::Pin::new(7) }; (TX3) => { - teensy::port::Pin::new(8) + crate::port::Pin::new(8) }; // ===== SPI port ===== (CS) => { - teensy::port::Pin::new(10) + crate::port::Pin::new(10) }; (DOUT) => { - teensy::port::Pin::new(11) + crate::port::Pin::new(11) }; (DIN) => { - teensy::port::Pin::new(12) + crate::port::Pin::new(12) }; (SCK) => { - teensy::port::Pin::new(13) + crate::port::Pin::new(13) }; // ===== Schematic view ===== (PTA4) => { - teensy::port::Pin::new(33) + crate::port::Pin::new(33) }; (PTB18) => { - teensy::port::Pin::new(32) + crate::port::Pin::new(32) }; (PTE0) => { - teensy::port::Pin::new(31) + crate::port::Pin::new(31) }; (PTC11) => { - teensy::port::Pin::new(30) + crate::port::Pin::new(30) }; (PTC10) => { - teensy::port::Pin::new(29) + crate::port::Pin::new(29) }; (PTC8) => { - teensy::port::Pin::new(28) + crate::port::Pin::new(28) }; (PTC9) => { - teensy::port::Pin::new(27) + crate::port::Pin::new(27) }; (PTE1) => { - teensy::port::Pin::new(26) + crate::port::Pin::new(26) }; (PTB19) => { - teensy::port::Pin::new(25) + crate::port::Pin::new(25) }; (PTA5) => { - teensy::port::Pin::new(24) + crate::port::Pin::new(24) }; (PTC2) => { - teensy::port::Pin::new(23) + crate::port::Pin::new(23) }; (PTC1) => { - teensy::port::Pin::new(22) + crate::port::Pin::new(22) }; (PTD6) => { - teensy::port::Pin::new(21) + crate::port::Pin::new(21) }; (PTD5) => { - teensy::port::Pin::new(20) + crate::port::Pin::new(20) }; (PTB2) => { - teensy::port::Pin::new(19) + crate::port::Pin::new(19) }; (PTB3) => { - teensy::port::Pin::new(18) + crate::port::Pin::new(18) }; (PTB1) => { - teensy::port::Pin::new(17) + crate::port::Pin::new(17) }; (PTB0) => { - teensy::port::Pin::new(16) + crate::port::Pin::new(16) }; (PTC0) => { - teensy::port::Pin::new(15) + crate::port::Pin::new(15) }; (PTD1) => { - teensy::port::Pin::new(14) + crate::port::Pin::new(14) }; (PTC5) => { - teensy::port::Pin::new(13) + crate::port::Pin::new(13) }; (PTC7) => { - teensy::port::Pin::new(12) + crate::port::Pin::new(12) }; (PTC6) => { - teensy::port::Pin::new(11) + crate::port::Pin::new(11) }; (PTC4) => { - teensy::port::Pin::new(10) + crate::port::Pin::new(10) }; (PTC3) => { - teensy::port::Pin::new(9) + crate::port::Pin::new(9) }; (PTD3) => { - teensy::port::Pin::new(8) + crate::port::Pin::new(8) }; (PTD2) => { - teensy::port::Pin::new(7) + crate::port::Pin::new(7) }; (PTD4) => { - teensy::port::Pin::new(6) + crate::port::Pin::new(6) }; (PTD7) => { - teensy::port::Pin::new(5) + crate::port::Pin::new(5) }; (PTA13) => { - teensy::port::Pin::new(4) + crate::port::Pin::new(4) }; (PTA12) => { - teensy::port::Pin::new(3) + crate::port::Pin::new(3) }; (PTD0) => { - teensy::port::Pin::new(2) + crate::port::Pin::new(2) }; (PTB17) => { - teensy::port::Pin::new(1) + crate::port::Pin::new(1) }; (PTB16) => { - teensy::port::Pin::new(0) + crate::port::Pin::new(0) }; // ===== digital pins ===== ($n:expr) => { - teensy::port::Pin::new($n) + crate::port::Pin::new($n) }; } diff --git a/src/port/mod.rs b/src/port/mod.rs index 26a94c2..f15e8bc 100644 --- a/src/port/mod.rs +++ b/src/port/mod.rs @@ -55,10 +55,10 @@ //! gpio.high(); //! ``` +pub use self::gpio::Gpio; pub use self::pin::Pin; pub use self::port::Port; pub use self::port::PortName; -pub use self::gpio::Gpio; mod gpio; mod pin; diff --git a/src/sim.rs b/src/sim.rs index 487cf9b..b6ac003 100644 --- a/src/sim.rs +++ b/src/sim.rs @@ -38,7 +38,7 @@ pub struct Sim { pub scgc6: Volatile, pub scgc7: Volatile, clkdiv1: Volatile, - clkviv2: Volatile, + clkdiv2: Volatile, fcfg1: Volatile, fcfg2: Volatile, uidh: Volatile, diff --git a/src/sleep.rs b/src/sleep.rs index 3a18235..1e1c6fb 100644 --- a/src/sleep.rs +++ b/src/sleep.rs @@ -22,13 +22,13 @@ pub fn delay(n: u64) { /// not properly work at all with anything below 5 MHz #[inline] pub fn sleep_us(microseconds: u32) { - (0..microseconds).for_each(|_| { + (0..microseconds).for_each(|_| unsafe { let mut inner = crate::mcg::F_CPU / 5_000_000; // This loop should take 5 cycles while inner != 0 { inner -= 1; - unsafe { + { __nop(); } } @@ -38,23 +38,24 @@ pub fn sleep_us(microseconds: u32) { /// For milliseconds, this loop approach is not too far off reality as long /// as it runs uninterrupted. pub fn sleep_ms(milliseconds: u32) { - (0..milliseconds).for_each(|_| { + (0..milliseconds).for_each(|_| unsafe { let mut inner = crate::mcg::F_CPU / 10_000; // This loop should take 10 cycles: // inner -= 1 sub: 1 cycle // inner != 0 beq: 1 cycle if not taken - // 6*nop nop: 1 cycle + // 7*nop nop: 1 cycle // endwhile b: 1 + (1-3) cycles while inner != 0 { inner -= 1; - unsafe { + { __nop(); // 1 __nop(); // 2 __nop(); // 3 __nop(); // 4 __nop(); // 5 __nop(); // 6 + __nop(); // 7 } } });