This commit is contained in:
J / Jacob Babich
2022-04-13 01:49:16 -04:00
parent 79de6fab06
commit 816520ed77
9 changed files with 588 additions and 88 deletions

8
.gitignore vendored
View File

@@ -4,12 +4,4 @@
Cargo.lock Cargo.lock
target/ target/
# editor files
.vscode/*
!.vscode/*.md
!.vscode/*.svd
!.vscode/launch.json
!.vscode/tasks.json
!.vscode/extensions.json
.history/ .history/

5
.vscode/settings.json vendored Normal file
View File

@@ -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
}

View File

@@ -24,6 +24,9 @@ panic-halt = "0.2.0"
# features = ["stm32f303", "rt"] # features = ["stm32f303", "rt"]
# version = "0.7.1" # version = "0.7.1"
[lib]
path = "src/lib/mod.rs"
# this lets you use `cargo fix`! # this lets you use `cargo fix`!
[[bin]] [[bin]]
name = "test-cortex-m4-rust" name = "test-cortex-m4-rust"

Binary file not shown.

View File

@@ -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
}

83
src/lib/memory.rs Normal file
View File

@@ -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<Updater: Fn(u32) -> u32>(address: *mut u32, updater: Updater) {
write(address, updater(read(address)));
}
pub unsafe fn read_bits<const N: usize>(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<const N: usize>(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<const N: usize, Updater: Fn([bool; N]) -> [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
})
}

343
src/lib/mod.rs Normal file
View File

@@ -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<const N: usize>(
&self,
bits: &[Bit; N],
options: ReadablePinSetup,
) -> ReadablePins<N> {
// 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<const N: usize>(
&self,
bits: &[Bit; N],
options: WritablePinSetup,
) -> WritablePins<N> {
// 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<const N: usize> {
data_address: *mut u32,
pins: [ReadablePin; N],
}
impl<const N: usize> ReadablePins<N> {
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<const N: usize> {
data_address: *mut u32,
pins: [WritablePin; N],
}
impl<const N: usize> WritablePins<N> {
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<Updater: Fn([bool; N]) -> [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
}

106
src/lib/registers.rs Normal file
View File

@@ -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;
}

View File

@@ -6,6 +6,8 @@ use core::ptr;
use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics
use cortex_m_rt::entry; 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 // 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_AFSEL_R: *mut u32 = 0x40025420 as *mut u32;
const GPIO_PORTF_PUR_R: *mut u32 = 0x40025510 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_DEN_R: *mut u32 = 0x4002551C as *mut u32;
const GPIO_PORTF_LOCK_R: *mut u32 = 0x40025520 as *mut u32; const GPIOLOCK_PORT_F: *mut u32 = 0x40025520 as *mut u32;
const GPIO_PORTF_CR_R: *mut u32 = 0x40025524 as *mut u32; const GPIOCR_PORT_F: *mut u32 = 0x40025524 as *mut u32;
const GPIO_PORTF_AMSEL_R: *mut u32 = 0x40025528 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 GPIO_PORTF_PCTL_R: *mut u32 = 0x4002552C as *mut u32;
const SYSCTL_RCGCPIO_R: *mut u32 = 0x400FE608 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() { fn setup_port_f() {
// 1) activate clock for Port F // 1) activate clock for Port F
unsafe { // unsafe {
ptr::write_volatile( // ptr::write_volatile(
SYSCTL_RCGCPIO_R, // SYSCTL_RCGCPIO_R,
ptr::read_volatile(SYSCTL_RCGCPIO_R) | 0x00_00_00_20, // ptr::read_volatile(SYSCTL_RCGCPIO_R) | 0x00_00_00_20,
); // );
} // }
// Delay // Delay
for _ in 0u8..2u8 {} // for _ in 0u8..2u8 {}
// 2) unlock GPIO Port F // 2) unlock GPIO Port F
unsafe { // unsafe {
ptr::write_volatile(GPIO_PORTF_LOCK_R, 0x4C4F434B); // ptr::write_volatile(GPIOLOCK_PORT_F, 0x4C4F434B);
// allow changes to PF4-0 // // allow changes to PF4-0
// only PF0 needs to be unlocked, other bits can't be locked // // only PF0 needs to be unlocked, other bits can't be locked
ptr::write_volatile(GPIO_PORTF_CR_R, 0b0001_1111); // ptr::write_volatile(GPIOCR_PORT_F, 0b0001_1111);
} // }
// 3) disable analog on PF // 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 // 4) PCTL GPIO on PF4-0
unsafe { unsafe {
@@ -68,9 +70,9 @@ fn setup_port_f() {
} }
// 5) PF4,PF0 in, PF3-1 out // 5) PF4,PF0 in, PF3-1 out
unsafe { // unsafe {
ptr::write_volatile(GPIO_PORTF_DIR_R, 0x0E); // ptr::write_volatile(GPIO_PORTF_DIR_R, 0x0E);
} // }
// 6) disable alt funct on PF7-0 // 6) disable alt funct on PF7-0
unsafe { unsafe {
ptr::write_volatile(GPIO_PORTF_AFSEL_R, 0x00); 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] #[entry]
fn main() -> ! { fn main() -> ! {
setup_port_f(); setup_port_f();
@@ -105,12 +127,14 @@ fn main() -> ! {
let status = input_from_port_f(); let status = input_from_port_f();
match status { match status {
0x01 => output_to_port_f(BLUE), 0x00 => output_to_port_f(RED | GREEN),
0x10 => output_to_port_f(RED), 0x01 => output_to_port_f(RED),
0x00 => output_to_port_f(GREEN), 0x10 => output_to_port_f(GREEN),
0x11 => output_to_port_f(BLACK), 0x11 => output_to_port_f(BLACK),
// Impossible case // 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...)