beginning of refactoring
This commit is contained in:
2
memory.x
2
memory.x
@@ -1,8 +1,6 @@
|
||||
MEMORY
|
||||
{
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
/* TODO Adjust these memory regions to match your device memory layout */
|
||||
/* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */
|
||||
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 32K
|
||||
}
|
||||
|
16
src/lib/board.rs
Normal file
16
src/lib/board.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use crate::gpio::ports::{Port, PortOptions, UsablePort, setup_gpio_port};
|
||||
|
||||
pub struct UsableBoard;
|
||||
impl UsableBoard {
|
||||
pub fn setup_gpio_port(&self, port: Port, options: PortOptions) -> UsablePort {
|
||||
setup_gpio_port(port, options)
|
||||
}
|
||||
|
||||
// TODO: check page 704 for timers
|
||||
// TODO: impl Drop trait so that tasks all run before the main function ends?
|
||||
// TODO: examine page 670 for when (if) I do interrupts
|
||||
}
|
||||
|
||||
pub fn setup_board() -> UsableBoard {
|
||||
UsableBoard
|
||||
}
|
2
src/lib/gpio/mod.rs
Normal file
2
src/lib/gpio/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod pins;
|
||||
pub mod ports;
|
304
src/lib/gpio/pins.rs
Normal file
304
src/lib/gpio/pins.rs
Normal file
@@ -0,0 +1,304 @@
|
||||
use crate::{memory, L};
|
||||
|
||||
use super::ports::Port;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Pin {
|
||||
Zero = 0,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Three = 3,
|
||||
Four = 4,
|
||||
Five = 5,
|
||||
Six = 6,
|
||||
Seven = 7,
|
||||
}
|
||||
|
||||
pub enum Function {
|
||||
Analog,
|
||||
Digital,
|
||||
CAN,
|
||||
I2C,
|
||||
PWM,
|
||||
UART,
|
||||
}
|
||||
|
||||
/// Page 1351 of data sheet
|
||||
pub struct ReadablePinOptions {
|
||||
pub function: Function,
|
||||
pub pull_up: Option<bool>,
|
||||
}
|
||||
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 as u32)) }
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ReadablePin {
|
||||
data_address: *mut u32,
|
||||
bit: Pin,
|
||||
}
|
||||
impl ReadablePin {
|
||||
pub fn read(&self) -> bool {
|
||||
let current = unsafe { memory::read(self.data_address) };
|
||||
current & (1 << self.bit as u32) != 0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WritablePinOptions {
|
||||
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 as u32)) }
|
||||
}
|
||||
pub fn write_all(&mut self, values: [bool; N]) {
|
||||
unsafe {
|
||||
memory::write_bits(
|
||||
self.data_address,
|
||||
&self.pins.map(|pin| pin.bit as u32),
|
||||
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 as u32));
|
||||
}
|
||||
}
|
||||
pub fn set_all(&mut self) {
|
||||
unsafe {
|
||||
memory::set_bits(self.data_address, &self.pins.map(|pin| pin.bit as u32));
|
||||
}
|
||||
}
|
||||
pub fn toggle_all(&mut self) {
|
||||
unsafe {
|
||||
memory::toggle_bits(self.data_address, &self.pins.map(|pin| pin.bit as u32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct WritablePin {
|
||||
data_address: *mut u32,
|
||||
bit: Pin,
|
||||
}
|
||||
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 as u32]);
|
||||
}
|
||||
}
|
||||
pub fn set(&mut self) {
|
||||
unsafe {
|
||||
memory::set_bits(self.data_address, &[self.bit as u32]);
|
||||
}
|
||||
}
|
||||
pub fn toggle(&mut self) {
|
||||
unsafe {
|
||||
memory::toggle_bits(self.data_address, &[self.bit as u32]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Page 684 of the data sheet for how the lock mechanism works
|
||||
const UNLOCK: u32 = 0x4C4F434B;
|
||||
|
||||
fn setup_pins() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn setup_readable_pins<const N: usize>(
|
||||
port: Port,
|
||||
pins: [Pin; N],
|
||||
options: ReadablePinOptions,
|
||||
) -> ReadablePins<N> {
|
||||
// Unlock the pins
|
||||
unsafe {
|
||||
memory::write(port.lock(), UNLOCK);
|
||||
|
||||
memory::set_bits(port.commit(), &pins.map(|bit| bit as u32));
|
||||
}
|
||||
|
||||
// Disable analog when it's not selected (and enable analog if it is)
|
||||
match options.function {
|
||||
Function::Analog => unsafe {
|
||||
memory::set_bits(port.analog_mode_select(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
_ => unsafe {
|
||||
memory::clear_bits(port.analog_mode_select(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
}
|
||||
|
||||
unsafe {
|
||||
memory::clear_bits(port.direction(), &pins.map(|bit| bit as u32));
|
||||
}
|
||||
|
||||
for pin in pins {
|
||||
let mut memory_bits = [0; 4];
|
||||
|
||||
let min = (pin as u32) * 4;
|
||||
let max = min + 4;
|
||||
let range = min..max;
|
||||
|
||||
for (i, memory_bit) in range.enumerate() {
|
||||
memory_bits[i] = memory_bit;
|
||||
}
|
||||
|
||||
let values = match options.function {
|
||||
Function::Analog => todo!(),
|
||||
Function::Digital => [L, L, L, L],
|
||||
Function::CAN => todo!(),
|
||||
Function::I2C => todo!(),
|
||||
Function::PWM => todo!(),
|
||||
Function::UART => todo!(),
|
||||
};
|
||||
unsafe {
|
||||
memory::write_bits(port.port_control(), &memory_bits, values);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure pull-up and pull-down resistors
|
||||
match options.pull_up {
|
||||
Some(true) => unsafe {
|
||||
memory::set_bits(port.pull_up_select(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
Some(false) => unsafe {
|
||||
memory::set_bits(port.pull_down_select(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
None => {
|
||||
unsafe {
|
||||
memory::clear_bits(port.pull_up_select(), &pins.map(|bit| bit as u32));
|
||||
}
|
||||
unsafe {
|
||||
memory::clear_bits(port.pull_down_select(), &pins.map(|bit| bit as u32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match options.function {
|
||||
Function::Digital => unsafe {
|
||||
memory::set_bits(port.digital_enable(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
Function::Analog => unsafe {
|
||||
memory::clear_bits(port.digital_enable(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
let data_address = port.data();
|
||||
|
||||
let pins: [ReadablePin; N] = pins.map(|bit| ReadablePin { data_address, bit });
|
||||
|
||||
ReadablePins { data_address, pins }
|
||||
}
|
||||
|
||||
pub fn setup_writable_pins<const N: usize>(
|
||||
port: Port,
|
||||
pins: [Pin; N],
|
||||
options: WritablePinOptions,
|
||||
) -> WritablePins<N> {
|
||||
// Unlock the pins
|
||||
unsafe {
|
||||
memory::write(port.lock(), UNLOCK);
|
||||
|
||||
memory::set_bits(port.commit(), &pins.map(|bit| bit as u32));
|
||||
}
|
||||
|
||||
// Disable analog when it's not selected (and enable analog if it is)
|
||||
match options.function {
|
||||
Function::Analog => unsafe {
|
||||
memory::set_bits(port.analog_mode_select(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
_ => unsafe {
|
||||
memory::clear_bits(port.analog_mode_select(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
}
|
||||
|
||||
unsafe {
|
||||
memory::set_bits(port.direction(), &pins.map(|bit| bit as u32));
|
||||
}
|
||||
|
||||
for pin in pins {
|
||||
let mut memory_bits = [0; 4];
|
||||
|
||||
let min = (pin as u32) * 4;
|
||||
let max = min + 3;
|
||||
let range = min..=max;
|
||||
|
||||
for (i, memory_bit) in range.enumerate() {
|
||||
memory_bits[i] = memory_bit;
|
||||
}
|
||||
|
||||
let values = match options.function {
|
||||
Function::Analog => todo!(),
|
||||
Function::Digital => [L, L, L, L],
|
||||
Function::CAN => todo!(),
|
||||
Function::I2C => todo!(),
|
||||
Function::PWM => todo!(),
|
||||
Function::UART => todo!(),
|
||||
};
|
||||
unsafe {
|
||||
memory::write_bits(port.port_control(), &memory_bits, values);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check page 671 or 682 (+ more prob) for a table showing initial pin states
|
||||
|
||||
// TODO: finish
|
||||
|
||||
match options.function {
|
||||
Function::Analog | Function::Digital => unsafe {
|
||||
memory::clear_bits(
|
||||
port.alternate_function_select(),
|
||||
&pins.map(|bit| bit as u32),
|
||||
);
|
||||
},
|
||||
_ => unsafe {
|
||||
memory::set_bits(
|
||||
port.alternate_function_select(),
|
||||
&pins.map(|bit| bit as u32),
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
match options.function {
|
||||
Function::Digital => unsafe {
|
||||
memory::set_bits(port.digital_enable(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
Function::Analog => unsafe {
|
||||
memory::clear_bits(port.digital_enable(), &pins.map(|bit| bit as u32));
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
let data_address = port.data();
|
||||
|
||||
let pins: [WritablePin; N] = pins.map(|bit| WritablePin { data_address, bit });
|
||||
|
||||
WritablePins { data_address, pins }
|
||||
}
|
162
src/lib/gpio/ports.rs
Normal file
162
src/lib/gpio/ports.rs
Normal file
@@ -0,0 +1,162 @@
|
||||
use crate::{
|
||||
memory, Pin, ReadablePinOptions, ReadablePins, registers, WritablePinOptions, WritablePins,
|
||||
};
|
||||
|
||||
use super::pins::{setup_readable_pins, setup_writable_pins};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Port {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
}
|
||||
|
||||
pub struct PortOptions;
|
||||
|
||||
impl Port {
|
||||
/// The starting point of memory addresses corresponding to this GPIO register
|
||||
///
|
||||
/// Modeled after page 660 of data sheet (GPIO Register Map)
|
||||
fn base(&self) -> u32 {
|
||||
match self {
|
||||
Port::A => 0x4000_4000,
|
||||
Port::B => 0x4000_5000,
|
||||
Port::C => 0x4000_6000,
|
||||
Port::D => 0x4000_7000,
|
||||
Port::E => 0x4002_4000,
|
||||
Port::F => 0x4002_5000,
|
||||
}
|
||||
}
|
||||
|
||||
/// The memory address of the alternate function select (AFSEL) register for this port
|
||||
///
|
||||
/// Page 671 of data sheet
|
||||
pub(super) fn alternate_function_select(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x420;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
/// The memory address of the analog mode select (AMSEL) register for this port
|
||||
///
|
||||
/// Page 687 of data sheet
|
||||
pub(super) fn analog_mode_select(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x52C;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
/// The memory address of the commit (CR) register for this port
|
||||
///
|
||||
/// Page 685 of data sheet
|
||||
pub(super) fn commit(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x524;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
/// The memory address of the data (DATA) register for this port
|
||||
///
|
||||
/// Page 662 of data sheet
|
||||
pub(super) fn data(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x3FC;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
/// The memory address of the digital enable (DEN) register for this port
|
||||
///
|
||||
/// Page 682 of data sheet
|
||||
pub(super) fn digital_enable(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x51C;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
/// The memory address of the direction (DIR) register for this port
|
||||
///
|
||||
/// Page 663 of data sheet
|
||||
pub(super) fn direction(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x400;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
/// The memory address of the lock (LOCK) register
|
||||
///
|
||||
/// Page 684 of data sheet
|
||||
pub(super) fn lock(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x520;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
/// The memory address of the port control (PCTL) register for this port
|
||||
///
|
||||
/// Page 688 of data sheet
|
||||
pub(super) fn port_control(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x52C;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
/// The memory address of the pull-down resistor select (PDR) register for this port
|
||||
/// Page 679 of data sheet
|
||||
pub(super) fn pull_down_select(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x514;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
/// The memory address of the pull-up resistor select (PUR) register for this port
|
||||
/// Page 677 of data sheet
|
||||
pub(super) fn pull_up_select(&self) -> *mut u32 {
|
||||
const OFFSET: u32 = 0x510;
|
||||
(self.base() + OFFSET) as *mut u32
|
||||
}
|
||||
|
||||
// TODO: examine page 690 (ADC) for applicability
|
||||
// Note to self: page 1351 of data sheet for PWM
|
||||
// Apparently also for ADC!
|
||||
}
|
||||
|
||||
impl Port {
|
||||
/// The corresponding bit for this port in system's run-mode clock gate control (RCGC) register
|
||||
fn run_mode_clock_gate_control(&self) -> u32 {
|
||||
match self {
|
||||
Port::A => 0,
|
||||
Port::B => 1,
|
||||
Port::C => 2,
|
||||
Port::D => 3,
|
||||
Port::E => 4,
|
||||
Port::F => 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UsablePort {
|
||||
port: Port,
|
||||
}
|
||||
|
||||
impl UsablePort {
|
||||
pub fn setup_readable_pins<const N: usize>(
|
||||
&self,
|
||||
pins: [Pin; N],
|
||||
options: ReadablePinOptions,
|
||||
) -> ReadablePins<N> {
|
||||
setup_readable_pins(self.port, pins, options)
|
||||
}
|
||||
|
||||
pub fn setup_writable_pins<const N: usize>(
|
||||
&self,
|
||||
pins: [Pin; N],
|
||||
options: WritablePinOptions,
|
||||
) -> WritablePins<N> {
|
||||
setup_writable_pins(self.port, pins, options)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_gpio_port(port: Port, options: PortOptions) -> UsablePort {
|
||||
unsafe {
|
||||
memory::set_bits(
|
||||
registers::system::RCGCGPIO,
|
||||
&[port.run_mode_clock_gate_control() as u32],
|
||||
);
|
||||
}
|
||||
|
||||
UsablePort { port }
|
||||
}
|
@@ -32,7 +32,7 @@ pub unsafe fn write_bits<const N: usize>(address: *mut u32, bits: &[u32; N], val
|
||||
// TODO: look up accumulate or reduce or something
|
||||
for (bit, set) in bits.iter().zip(values) {
|
||||
if set {
|
||||
new |= (1 << bit);
|
||||
new |= 1 << bit;
|
||||
} else {
|
||||
new &= !(1 << bit);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ pub unsafe fn set_bits(address: *mut u32, bits: &[u32]) {
|
||||
|
||||
// TODO: look up accumulate or reduce or something
|
||||
for bit in bits {
|
||||
new |= (1 << bit);
|
||||
new |= 1 << bit;
|
||||
}
|
||||
|
||||
new
|
||||
@@ -79,7 +79,7 @@ pub unsafe fn toggle_bits(address: *mut u32, bits: &[u32]) {
|
||||
|
||||
// TODO: look up accumulate or reduce or something
|
||||
for bit in bits {
|
||||
new ^= (1 << bit);
|
||||
new ^= 1 << bit;
|
||||
}
|
||||
|
||||
new
|
||||
|
458
src/lib/mod.rs
458
src/lib/mod.rs
@@ -1,459 +1,13 @@
|
||||
#![no_std]
|
||||
|
||||
mod board;
|
||||
mod gpio;
|
||||
mod memory;
|
||||
mod registers;
|
||||
|
||||
pub use board::setup_board;
|
||||
pub use gpio::pins::{Function, Pin, ReadablePin, ReadablePinOptions, ReadablePins, WritablePin, WritablePinOptions, WritablePins};
|
||||
pub use gpio::ports::{Port, PortOptions, UsablePort};
|
||||
|
||||
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() as u32],
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
// TODO: refactor to just be self.base() + offset all the time - no matching
|
||||
impl PortIO {
|
||||
/// The memory address of the alternate function select (AFSEL) register for this port
|
||||
fn alternate_function_select(&self) -> *mut u32 {
|
||||
match self.port {
|
||||
Port::A => registers::gpio::afsel::PORT_A,
|
||||
Port::F => registers::gpio::afsel::PORT_F,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 digital enable (DEN) register for this port
|
||||
fn digital_enable(&self) -> *mut u32 {
|
||||
match self.port {
|
||||
Port::A => registers::gpio::den::PORT_A,
|
||||
Port::F => registers::gpio::den::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 {
|
||||
// TODO: refactor into private setup_pins function
|
||||
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.map(|bit| bit as u32));
|
||||
}
|
||||
|
||||
// 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.map(|bit| bit as u32));
|
||||
},
|
||||
_ => unsafe {
|
||||
memory::clear_bits(self.analog_mode_select(), &bits.map(|bit| bit as u32));
|
||||
},
|
||||
}
|
||||
|
||||
unsafe {
|
||||
memory::clear_bits(self.direction(), &bits.map(|bit| bit as u32));
|
||||
}
|
||||
|
||||
|
||||
|
||||
for bit in bits {
|
||||
let mut memory_bits = [0; 4];
|
||||
|
||||
let min = (*bit as u32) * 4;
|
||||
let max = min + 3;
|
||||
let range = min..=max;
|
||||
|
||||
for (i, memory_bit) in range.enumerate() {
|
||||
memory_bits[i] = memory_bit;
|
||||
}
|
||||
|
||||
let values = match options.function {
|
||||
Function::Analog => todo!(),
|
||||
Function::Digital => [L, L, L, L],
|
||||
Function::CAN => todo!(),
|
||||
Function::I2C => todo!(),
|
||||
Function::PWM => todo!(),
|
||||
Function::UART => todo!(),
|
||||
};
|
||||
unsafe {
|
||||
memory::write_bits(self.port_control(), &memory_bits, values);
|
||||
}
|
||||
}
|
||||
|
||||
// Configure pull-up and pull-down resistors
|
||||
match options.pull_up {
|
||||
Some(true) => {
|
||||
unsafe {
|
||||
memory::set_bits(self.pull_up_select(), &bits.map(|bit| bit as u32));
|
||||
}
|
||||
},
|
||||
Some(false) => {
|
||||
unsafe {
|
||||
memory::set_bits(self.pull_down_select(), &bits.map(|bit| bit as u32));
|
||||
}
|
||||
},
|
||||
None => {
|
||||
unsafe {
|
||||
memory::clear_bits(self.pull_up_select(), &bits.map(|bit| bit as u32));
|
||||
}
|
||||
unsafe {
|
||||
memory::clear_bits(self.pull_down_select(), &bits.map(|bit| bit as u32));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
match options.function {
|
||||
Function::Digital => unsafe {
|
||||
memory::set_bits(self.digital_enable(), &bits.map(|bit| bit as u32));
|
||||
},
|
||||
Function::Analog => unsafe {
|
||||
memory::clear_bits(self.digital_enable(), &bits.map(|bit| bit as u32));
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
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.map(|bit| bit as u32));
|
||||
}
|
||||
|
||||
// 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.map(|bit| bit as u32));
|
||||
},
|
||||
_ => unsafe {
|
||||
memory::clear_bits(self.analog_mode_select(), &bits.map(|bit| bit as u32));
|
||||
},
|
||||
}
|
||||
|
||||
unsafe {
|
||||
memory::set_bits(self.direction(), &bits.map(|bit| bit as u32));
|
||||
}
|
||||
|
||||
for bit in bits {
|
||||
let mut memory_bits = [0; 4];
|
||||
|
||||
let min = (*bit as u32) * 4;
|
||||
let max = min + 3;
|
||||
let range = min..=max;
|
||||
|
||||
for (i, memory_bit) in range.enumerate() {
|
||||
memory_bits[i] = memory_bit;
|
||||
}
|
||||
|
||||
let values = match options.function {
|
||||
Function::Analog => todo!(),
|
||||
Function::Digital => [L, L, L, L],
|
||||
Function::CAN => todo!(),
|
||||
Function::I2C => todo!(),
|
||||
Function::PWM => todo!(),
|
||||
Function::UART => todo!(),
|
||||
};
|
||||
unsafe {
|
||||
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
|
||||
|
||||
match options.function {
|
||||
Function::Analog | Function::Digital => {
|
||||
unsafe {
|
||||
memory::clear_bits(self.alternate_function_select(), &bits.map(|bit| bit as u32));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
unsafe {
|
||||
memory::set_bits(self.alternate_function_select(), &bits.map(|bit| bit as u32));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
match options.function {
|
||||
Function::Digital => unsafe {
|
||||
memory::set_bits(self.digital_enable(), &bits.map(|bit| bit as u32));
|
||||
},
|
||||
Function::Analog => unsafe {
|
||||
memory::clear_bits(self.digital_enable(), &bits.map(|bit| bit as u32));
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
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 pull_up: Option<bool>,
|
||||
}
|
||||
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 as u32)) }
|
||||
}
|
||||
}
|
||||
#[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 as u32)) }
|
||||
}
|
||||
pub fn write_all(&mut self, values: [bool; N]) {
|
||||
unsafe {
|
||||
memory::write_bits(
|
||||
self.data_address,
|
||||
&self.pins.map(|pin| pin.bit as u32),
|
||||
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 as u32));
|
||||
}
|
||||
}
|
||||
pub fn set_all(&mut self) {
|
||||
unsafe {
|
||||
memory::set_bits(self.data_address, &self.pins.map(|pin| pin.bit as u32));
|
||||
}
|
||||
}
|
||||
pub fn toggle_all(&mut self) {
|
||||
unsafe {
|
||||
memory::toggle_bits(self.data_address, &self.pins.map(|pin| pin.bit as u32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 as u32]);
|
||||
}
|
||||
}
|
||||
pub fn set(&mut self) {
|
||||
unsafe {
|
||||
memory::set_bits(self.data_address, &[self.bit as u32]);
|
||||
}
|
||||
}
|
||||
pub fn toggle(&mut self) {
|
||||
unsafe {
|
||||
memory::toggle_bits(self.data_address, &[self.bit as u32]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_board() -> Board {
|
||||
Board
|
||||
}
|
||||
|
@@ -5,98 +5,6 @@
|
||||
|
||||
// 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 = 0x3FC;
|
||||
|
||||
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;
|
||||
|
19
src/main.rs
19
src/main.rs
@@ -5,23 +5,26 @@ use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use test_cortex_m4_rust::{
|
||||
setup_board, Bit, Function, Port, PortSetup, ReadablePinSetup, WritablePinSetup, H, L,
|
||||
setup_board, Function, Pin, Port, PortOptions, ReadablePinOptions, WritablePinOptions, H, L,
|
||||
};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let board = setup_board();
|
||||
let port_f = board.setup_gpio_port(Port::F, PortSetup);
|
||||
let port_f = board.setup_gpio_port(Port::F, PortOptions);
|
||||
|
||||
let switches = port_f.setup_readable_pins(&[Bit::Zero, Bit::Four], ReadablePinSetup {
|
||||
function: Function::Digital,
|
||||
pull_up: Some(true),
|
||||
});
|
||||
let switches = port_f.setup_readable_pins(
|
||||
[Pin::Zero, Pin::Four],
|
||||
ReadablePinOptions {
|
||||
function: Function::Digital,
|
||||
pull_up: Some(true),
|
||||
},
|
||||
);
|
||||
let [sw1, sw2] = switches.pins();
|
||||
|
||||
let mut rgb_led = port_f.setup_writable_pins(
|
||||
&[Bit::One, Bit::Three, Bit::Two],
|
||||
WritablePinSetup {
|
||||
[Pin::One, Pin::Three, Pin::Two],
|
||||
WritablePinOptions {
|
||||
function: Function::Digital,
|
||||
},
|
||||
);
|
||||
|
Reference in New Issue
Block a user