diff --git a/.gitignore b/.gitignore index b25267d..549dfbb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,12 +4,4 @@ Cargo.lock target/ -# editor files -.vscode/* -!.vscode/*.md -!.vscode/*.svd -!.vscode/launch.json -!.vscode/tasks.json -!.vscode/extensions.json - .history/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..86cf0e1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +// https://github.com/rust-lang/vscode-rust/issues/729#issuecomment-635977755 +{ + "rust-analyzer.cargo.target": "thumbv7em-none-eabihf", + "rust-analyzer.checkOnSave.allTargets": false +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index dd5728e..05cc8bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,9 @@ panic-halt = "0.2.0" # features = ["stm32f303", "rt"] # version = "0.7.1" +[lib] +path = "src/lib/mod.rs" + # this lets you use `cargo fix`! [[bin]] name = "test-cortex-m4-rust" diff --git a/new-main.bin b/new-main.bin deleted file mode 100755 index f154b74..0000000 Binary files a/new-main.bin and /dev/null differ diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index ef9c0b2..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,56 +0,0 @@ -#![no_std] - -pub struct Board; - -impl Board { - pub fn setup_gpio_port(port: Port, options: PortSetup) -> PortIO { - PortIO - } -} - -pub enum Port { - A, - F, -} - -pub struct PortSetup; - -pub struct PortIO; - -impl PortIO { - pub fn setup_pin(pin: Pin, options: PinSetup) -> PinIO { - PinIO - } -} - -pub enum Pin { - Zero = 0, - One = 1, - Two = 2, - Three = 3, - Four = 4, - Five = 5, - Six = 6, - Seven = 7, -} - -pub struct PinSetup; - -pub struct PinIO; - -impl PinIO { - pub fn clear(&mut self) { - todo!(); - } - pub fn set(&mut self) { - todo!(); - } - pub fn toggle(&mut self) { - todo!(); - } -} - - -pub fn setup_board() -> Board { - Board -} diff --git a/src/lib/memory.rs b/src/lib/memory.rs new file mode 100644 index 0000000..8ca199f --- /dev/null +++ b/src/lib/memory.rs @@ -0,0 +1,83 @@ +//! Interact with memory + +use core::ptr; + +use crate::{Bit, L}; + +pub unsafe fn read(address: *mut u32) -> u32 { + ptr::read_volatile(address) +} +pub unsafe fn write(address: *mut u32, new: u32) { + ptr::write_volatile(address, new); +} + +pub unsafe fn update u32>(address: *mut u32, updater: Updater) { + write(address, updater(read(address))); +} + +pub unsafe fn read_bits(address: *mut u32, bits: &[u32; N]) -> [bool; N] { + let current = read(address); + let mut result = [L; N]; + + // TODO: look up accumulate or reduce or something + for (i, bit) in bits.iter().enumerate() { + result[i] = (current & (1 << bit)) != 0; + } + + result +} +pub unsafe fn write_bits(address: *mut u32, bits: &[u32; N], values: [bool; N]) { + update(address, |current| { + let mut new = current; + // TODO: look up accumulate or reduce or something + for (bit, set) in bits.iter().zip(values) { + if set { + new |= (1 << bit); + } else { + new &= !(1 << bit); + } + } + new + }) +} + +pub unsafe fn update_bits [bool; N]>(address: *mut u32, bits: &[Bit; N], updater: Updater) { + write_bits(address, bits, updater(read_bits(address, bits))) +} + +pub unsafe fn set_bits(address: *mut u32, bits: &[Bit]) { + update(address, |current| { + let mut new = current; + + // TODO: look up accumulate or reduce or something + for bit in bits { + new |= (1 << bit); + } + + new + }) +} +pub unsafe fn clear_bits(address: *mut u32, bits: &[Bit]) { + update(address, |current| { + let mut new = current; + + // TODO: look up accumulate or reduce or something + for bit in bits { + new &= !(1 << bit); + } + + new + }) +} +pub unsafe fn toggle_bits(address: *mut u32, bits: &[Bit]) { + update(address, |current| { + let mut new = current; + + // TODO: look up accumulate or reduce or something + for bit in bits { + new ^= (1 << bit); + } + + new + }) +} diff --git a/src/lib/mod.rs b/src/lib/mod.rs new file mode 100644 index 0000000..10751a1 --- /dev/null +++ b/src/lib/mod.rs @@ -0,0 +1,343 @@ +#![no_std] + +mod memory; +mod registers; + +pub const H: bool = true; +pub const L: bool = false; + +pub struct Board; +// TODO: check page 704 for timers +// TODO: impl Drop trait so that tasks all run before the main function ends? + +impl Board { + pub fn setup_gpio_port(&self, port: Port, options: PortSetup) -> PortIO { + let port_io = PortIO { port }; + + unsafe { + memory::set_bits(registers::system::RCGCGPIO, &[port_io.run_mode_clock_gate_control()]); + } + + port_io + } +} + +// Page 684 of the data sheet for how the lock mechanism works +const UNLOCK: u32 = 0x4C4F434B; + +pub enum Port { + A, + B, + C, + D, + E, + F, +} + +pub struct PortSetup; + +pub struct PortIO { + port: Port, +} + +impl PortIO { + /// The memory address of the analog mode select (AMSEL) register for this port + fn analog_mode_select(&self) -> *mut u32 { + match self.port { + Port::A => registers::gpio::amsel::PORT_A, + Port::F => registers::gpio::amsel::PORT_F, + _ => todo!(), + } + } + + /// The memory address of the commit (CR register for this port + fn commit(&self) -> *mut u32 { + match self.port { + Port::A => registers::gpio::cr::PORT_A, + Port::F => registers::gpio::cr::PORT_F, + _ => todo!(), + } + } + + /// The memory address of the data (DATA) register for this port + fn data(&self) -> *mut u32 { + match self.port { + Port::A => registers::gpio::data::PORT_A, + Port::F => registers::gpio::data::PORT_F, + _ => todo!(), + } + } + + /// The memory address of the direction (DIR) register for this port + fn direction(&self) -> *mut u32 { + match self.port { + Port::A => registers::gpio::dir::PORT_A, + Port::F => registers::gpio::dir::PORT_F, + _ => todo!(), + } + } + + /// The memory address of the lock (LOCK) register + fn lock(&self) -> *mut u32 { + match self.port { + Port::A => registers::gpio::lock::PORT_A, + Port::F => registers::gpio::lock::PORT_F, + _ => todo!(), + } + } + + /// The memory address of the port control (PCTL) register for this port + fn port_control(&self) -> *mut u32 { + match self.port { + Port::A => registers::gpio::pctl::PORT_A, + Port::F => registers::gpio::pctl::PORT_F, + _ => todo!(), + + } + } + + /// The memory address of the pull-down resistor select (PDR) register for this port + fn pull_down_select(&self) -> *mut u32 { + match self.port { + Port::A => registers::gpio::pdr::PORT_A, + Port::F => registers::gpio::pdr::PORT_F, + _ => todo!(), + } + } + + /// The memory address of the pull-up resistor select (PUR) register for this port + fn pull_up_select(&self) -> *mut u32 { + match self.port { + Port::A => registers::gpio::pur::PORT_A, + Port::F => registers::gpio::pur::PORT_F, + _ => todo!(), + } + } + + // Note to self: page 1351 of data sheet for PWM + // Apparently also for ADC! +} + + +impl PortIO { + /// The corresponding bit for this port in system's run-mode clock gate control (RCGC) register + fn run_mode_clock_gate_control(&self) -> Bit { + match self.port { + Port::A => Bit::Zero, + Port::B => Bit::One, + Port::C => Bit::Two, + Port::D => Bit::Three, + Port::E => Bit::Four, + Port::F => Bit::Five, + } + } +} + +impl PortIO { + pub fn setup_readable_pins( + &self, + bits: &[Bit; N], + options: ReadablePinSetup, + ) -> ReadablePins { + // Unlock the pins + unsafe { + memory::write(self.lock(), UNLOCK); + + memory::set_bits(self.commit(), bits); + } + + // Disable analog when it's not selected (and enable analog if it is) + match options.function { + Function::Analog => unsafe { + memory::set_bits(self.analog_mode_select(), bits); + }, + _ => unsafe { + memory::clear_bits(self.analog_mode_select(), bits); + }, + } + + unsafe { + memory::clear_bits(self.direction(), bits); + } + + // TODO: finish + + let data_address = self.data(); + + let pins: [ReadablePin; N] = bits.map(|bit| ReadablePin { data_address, bit }); + + ReadablePins { data_address, pins } + } + + pub fn setup_writable_pins( + &self, + bits: &[Bit; N], + options: WritablePinSetup, + ) -> WritablePins { + // Unlock the pins + unsafe { + memory::write(self.lock(), UNLOCK); + + memory::set_bits(self.commit(), bits); + } + + // Disable analog when it's not selected (and enable analog if it is) + match options.function { + Function::Analog => unsafe { + memory::set_bits(self.analog_mode_select(), bits); + }, + _ => unsafe { + memory::clear_bits(self.analog_mode_select(), bits); + }, + } + + unsafe { + memory::set_bits(self.direction(), bits); + } + + unsafe { + for bit in bits { + let memory_bits = [0; N]; + let values = match options.function { + Function::Analog => todo!(), + Function::Digital => todo!(), + Function::CAN => todo!(), + Function::I2C => todo!(), + Function::PWM => todo!(), + Function::UART => todo!(), + }; + memory::write_bits(self.port_control(), memory_bits, values); + } + } + + // TODO: check page 671 or 682 (+ more prob) for a table showing initial pin states + + // TODO: finish + + let data_address = self.data(); + + let pins: [WritablePin; N] = bits.map(|bit| WritablePin { data_address, bit }); + + WritablePins { data_address, pins } + } +} + +#[derive(Clone, Copy)] +pub enum Bit { + Zero = 0, + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + Six = 6, + Seven = 7, +} + +/// Page 1351 of data sheet +pub enum Function { + Analog, + Digital, + CAN, + I2C, + PWM, + UART, +} + +pub struct ReadablePinSetup { + pub function: Function, +} +pub struct ReadablePins { + data_address: *mut u32, + pins: [ReadablePin; N], +} +impl ReadablePins { + pub fn pins(&self) -> [ReadablePin; N] { + self.pins + } + + pub fn read_all(&self) -> [bool; N] { + unsafe { memory::read_bits(self.data_address, &self.pins.map(|pin| pin.bit)) } + } +} +#[derive(Clone, Copy)] +pub struct ReadablePin { + data_address: *mut u32, + bit: Bit, +} +impl ReadablePin { + pub fn read(&self) -> bool { + let current = unsafe { memory::read(self.data_address) }; + current & (1 << self.bit as u32) != 0 + } +} + +pub struct WritablePinSetup { + pub function: Function, +} +pub struct WritablePins { + data_address: *mut u32, + pins: [WritablePin; N], +} +impl WritablePins { + pub fn pins(&self) -> [WritablePin; N] { + self.pins + } + + pub fn read_all(&self) -> [bool; N] { + unsafe { memory::read_bits(self.data_address, &self.pins.map(|pin| pin.bit)) } + } + pub fn write_all(&mut self, values: [bool; N]) { + unsafe { memory::write_bits(self.data_address, &self.pins.map(|pin| pin.bit), values) } + } + pub fn update_all [bool; N]>(&mut self, updater: Updater) { + self.write_all(updater(self.read_all())); + } + + pub fn clear_all(&mut self) { + unsafe { + memory::clear_bits(self.data_address, &self.pins.map(|pin| pin.bit)); + } + } + pub fn set_all(&mut self) { + unsafe { + memory::set_bits(self.data_address, &self.pins.map(|pin| pin.bit)); + } + } + pub fn toggle_all(&mut self) { + unsafe { + memory::toggle_bits(self.data_address, &self.pins.map(|pin| pin.bit)); + } + } +} + +#[derive(Clone, Copy)] +pub struct WritablePin { + data_address: *mut u32, + bit: Bit, +} +impl WritablePin { + pub fn read(&self) -> bool { + let current = unsafe { memory::read(self.data_address) }; + current & (1 << self.bit as u32) != 0 + } + pub fn clear(&mut self) { + unsafe { + memory::clear_bits(self.data_address, &[self.bit]); + } + } + pub fn set(&mut self) { + unsafe { + memory::set_bits(self.data_address, &[self.bit]); + } + } + pub fn toggle(&mut self) { + unsafe { + memory::toggle_bits(self.data_address, &[self.bit]); + } + } +} + +pub fn setup_board() -> Board { + Board +} diff --git a/src/lib/registers.rs b/src/lib/registers.rs new file mode 100644 index 0000000..b7834ba --- /dev/null +++ b/src/lib/registers.rs @@ -0,0 +1,106 @@ +//! Memory addresses of registers +//! Data sheet: https://www.ti.com/lit/ds/spms376e/spms376e.pdf + +// TODO: check page 92-94 for more features ("Memory Map" table)! + +// TODO: check page 1230 onward for PWM + +/// Modeled after page 660 of data sheet (GPIO Register Map) +pub mod gpio { + mod base { + pub const PORT_A: u32 = 0x4000_4000; + pub const PORT_F: u32 = 0x4002_5000; + } + + /// Page 671 of data sheet + pub mod afsel { + const OFFSET: u32 = 0x420; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + /// Page 687 of data sheet + pub mod amsel { + const OFFSET: u32 = 0x52C; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + /// Page 685 of data sheet + pub mod cr { + const OFFSET: u32 = 0x524; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + /// Page 662 of data sheet + pub mod data { + const OFFSET: u32 = 0x000; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + /// Page 682 of data sheet + pub mod den { + const OFFSET: u32 = 0x51C; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + /// Page 663 of data sheet + pub mod dir { + const OFFSET: u32 = 0x400; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + /// Page 684 of data sheet + pub mod lock { + const OFFSET: u32 = 0x520; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + /// Page 688 of data sheet + pub mod pctl { + const OFFSET: u32 = 0x52C; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + /// Page 679 of data sheet + pub mod pdr { + const OFFSET: u32 = 0x514; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + /// Page 677 of data sheet + pub mod pur { + const OFFSET: u32 = 0x510; + + pub const PORT_A: *mut u32 = (super::base::PORT_A + OFFSET) as *mut u32; + pub const PORT_F: *mut u32 = (super::base::PORT_F + OFFSET) as *mut u32; + } + + // TODO: examine page 670 for when (if) I do interrupts +} + +// TODO: examine page 690 (ADC) for applicability + +/// Page 231 of data sheet +pub mod system { + const BASE: u32 = 0x400F_E000; + + // TODO: page 340 + pub const RCGCGPIO: *mut u32 = (BASE + 0x608) as *mut u32; +} diff --git a/src/main.rs b/src/main.rs index e19fe3a..dc1a174 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ use core::ptr; use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics use cortex_m_rt::entry; +use test_cortex_m4_rust::{Function, Bit, ReadablePinSetup, WritablePinSetup, PortSetup, Port, setup_board, H, L}; +// use test_cortex_m4_rust::setup_board; // Begin .h file contents @@ -15,9 +17,9 @@ const GPIO_PORTF_DIR_R: *mut u32 = 0x40025400 as *mut u32; const GPIO_PORTF_AFSEL_R: *mut u32 = 0x40025420 as *mut u32; const GPIO_PORTF_PUR_R: *mut u32 = 0x40025510 as *mut u32; const GPIO_PORTF_DEN_R: *mut u32 = 0x4002551C as *mut u32; -const GPIO_PORTF_LOCK_R: *mut u32 = 0x40025520 as *mut u32; -const GPIO_PORTF_CR_R: *mut u32 = 0x40025524 as *mut u32; -const GPIO_PORTF_AMSEL_R: *mut u32 = 0x40025528 as *mut u32; +const GPIOLOCK_PORT_F: *mut u32 = 0x40025520 as *mut u32; +const GPIOCR_PORT_F: *mut u32 = 0x40025524 as *mut u32; +const GPIOAMSEL_PORT_F: *mut u32 = 0x40025528 as *mut u32; const GPIO_PORTF_PCTL_R: *mut u32 = 0x4002552C as *mut u32; const SYSCTL_RCGCPIO_R: *mut u32 = 0x400FE608 as *mut u32; @@ -41,26 +43,26 @@ const BLUE: u8 = 0b0000_0100; fn setup_port_f() { // 1) activate clock for Port F - unsafe { - ptr::write_volatile( - SYSCTL_RCGCPIO_R, - ptr::read_volatile(SYSCTL_RCGCPIO_R) | 0x00_00_00_20, - ); - } + // unsafe { + // ptr::write_volatile( + // SYSCTL_RCGCPIO_R, + // ptr::read_volatile(SYSCTL_RCGCPIO_R) | 0x00_00_00_20, + // ); + // } // Delay - for _ in 0u8..2u8 {} + // for _ in 0u8..2u8 {} // 2) unlock GPIO Port F - unsafe { - ptr::write_volatile(GPIO_PORTF_LOCK_R, 0x4C4F434B); - // allow changes to PF4-0 - // only PF0 needs to be unlocked, other bits can't be locked - ptr::write_volatile(GPIO_PORTF_CR_R, 0b0001_1111); - } + // unsafe { + // ptr::write_volatile(GPIOLOCK_PORT_F, 0x4C4F434B); + // // allow changes to PF4-0 + // // only PF0 needs to be unlocked, other bits can't be locked + // ptr::write_volatile(GPIOCR_PORT_F, 0b0001_1111); + // } // 3) disable analog on PF - unsafe { ptr::write_volatile(GPIO_PORTF_AMSEL_R, 0x00) } + // unsafe { ptr::write_volatile(GPIOAMSEL_PORT_F, 0x00) } // 4) PCTL GPIO on PF4-0 unsafe { @@ -68,9 +70,9 @@ fn setup_port_f() { } // 5) PF4,PF0 in, PF3-1 out - unsafe { - ptr::write_volatile(GPIO_PORTF_DIR_R, 0x0E); - } + // unsafe { + // ptr::write_volatile(GPIO_PORTF_DIR_R, 0x0E); + // } // 6) disable alt funct on PF7-0 unsafe { ptr::write_volatile(GPIO_PORTF_AFSEL_R, 0x00); @@ -97,6 +99,26 @@ fn output_to_port_f(value: u8) { } } +// #[entry] +// fn main() -> ! { +// let board = setup_board(); +// let port_f = board.setup_gpio_port(Port::F, PortSetup); + +// let switches = port_f.setup_readable_pins([Bit::Zero, Bit::Four], ReadablePinSetup); +// let [sw1, sw2] = switches.pins(); + +// let mut rgb_led = port_f.setup_writable_pins(&[Bit::One, Bit::Three, Bit::Two], WritablePinSetup { drive: PinDrive::Digital }); + +// loop { +// match switches.read_all() { +// [L, L] => rgb_led.write_all([L, H, L]), +// [L, H] => rgb_led.write_all([L, L, H]), +// [H, L] => rgb_led.write_all([L, H, L]), +// [H, H] => rgb_led.write_all([L, L, L]), +// } +// } +// } + #[entry] fn main() -> ! { setup_port_f(); @@ -105,12 +127,14 @@ fn main() -> ! { let status = input_from_port_f(); match status { - 0x01 => output_to_port_f(BLUE), - 0x10 => output_to_port_f(RED), - 0x00 => output_to_port_f(GREEN), + 0x00 => output_to_port_f(RED | GREEN), + 0x01 => output_to_port_f(RED), + 0x10 => output_to_port_f(GREEN), 0x11 => output_to_port_f(BLACK), // Impossible case - _ => output_to_port_f(RED | BLUE), + _ => output_to_port_f(BLUE), } } } + +// TODO: implement an extremely simple example of the task system (using a timer trigger) that is a traffic light (green -> yellow -> red -> green...)