Skip to content
Snippets Groups Projects
Commit c03d4f7d authored by Per's avatar Per
Browse files

bare5

parent 2ce8a8f3
Branches
No related tags found
No related merge requests found
//! bare5.rs
//!
//! C Like Peripheral API
//!
//! What it covers:
//! - abstractions in Rust
//! - structs and implementations
#![no_std]
#![no_main]
extern crate panic_halt;
extern crate cortex_m;
use cortex_m_rt::entry;
// C like API...
mod stm32f40x {
#[allow(dead_code)]
use core::{cell, ptr};
#[rustfmt::skip]
mod address {
pub const PERIPH_BASE: u32 = 0x40000000;
pub const AHB1PERIPH_BASE: u32 = PERIPH_BASE + 0x00020000;
pub const RCC_BASE: u32 = AHB1PERIPH_BASE + 0x3800;
pub const GPIOA_BASE: u32 = AHB1PERIPH_BASE + 0x0000;
}
use address::*;
pub struct VolatileCell<T> {
value: cell::UnsafeCell<T>,
}
impl<T> VolatileCell<T> {
#[inline(always)]
pub fn read(&self) -> T
where
T: Copy,
{
unsafe { ptr::read_volatile(self.value.get()) }
}
#[inline(always)]
pub fn write(&self, value: T)
where
T: Copy,
{
unsafe { ptr::write_volatile(self.value.get(), value) }
}
}
// modify (reads, modifies a field, and writes the volatile cell)
//
// parameters:
// offset (field offset)
// width (field width)
// value (new value that the field should take)
//
// impl VolatileCell<u32> {
// #[inline(always)]
// pub fn modify(&self, offset: u8, width: u8, value: u32) {
// // your code here
// }
// }
#[repr(C)]
#[allow(non_snake_case)]
#[rustfmt::skip]
pub struct RCC {
pub CR: VolatileCell<u32>, // < RCC clock control register, Address offset: 0x00
pub PLLCFGR: VolatileCell<u32>, // < RCC PLL configuration register, Address offset: 0x04
pub CFGR: VolatileCell<u32>, // < RCC clock configuration register, Address offset: 0x08
pub CIR: VolatileCell<u32>, // < RCC clock interrupt register, Address offset: 0x0C
pub AHB1RSTR: VolatileCell<u32>, // < RCC AHB1 peripheral reset register, Address offset: 0x10
pub AHB2RSTR: VolatileCell<u32>, // < RCC AHB2 peripheral reset register, Address offset: 0x14
pub AHB3RSTR: VolatileCell<u32>, // < RCC AHB3 peripheral reset register, Address offset: 0x18
pub RESERVED0: VolatileCell<u32>, // < Reserved, 0x1C
pub APB1RSTR: VolatileCell<u32>, // < RCC APB1 peripheral reset register, Address offset: 0x20
pub APB2RSTR: VolatileCell<u32>, // < RCC APB2 peripheral reset register, Address offset: 0x24
pub RESERVED1: [VolatileCell<u32>; 2], // < Reserved, 0x28-0x2C
pub AHB1ENR: VolatileCell<u32>, // < RCC AHB1 peripheral clock register, Address offset: 0x30
pub AHB2ENR: VolatileCell<u32>, // < RCC AHB2 peripheral clock register, Address offset: 0x34
pub AHB3ENR: VolatileCell<u32>, // < RCC AHB3 peripheral clock register, Address offset: 0x38
pub RESERVED2: VolatileCell<u32>, // < Reserved, 0x3C
pub APB1ENR: VolatileCell<u32>, // < RCC APB1 peripheral clock enable register, Address offset: 0x40
pub APB2ENR: VolatileCell<u32>, // < RCC APB2 peripheral clock enable register, Address offset: 0x44
pub RESERVED3: [VolatileCell<u32>; 2], // < Reserved, 0x48-0x4C
pub AHB1LPENR: VolatileCell<u32>, // < RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50
pub AHB2LPENR: VolatileCell<u32>, // < RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54
pub AHB3LPENR: VolatileCell<u32>, // < RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58
pub RESERVED4: VolatileCell<u32>, // < Reserved, 0x5C
pub APB1LPENR: VolatileCell<u32>, // < RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60
pub APB2LPENR: VolatileCell<u32>, // < RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64
pub RESERVED5: [VolatileCell<u32>; 2], // < Reserved, 0x68-0x6C
pub BDCR: VolatileCell<u32>, // < RCC Backup domain control register, Address offset: 0x70
pub CSR: VolatileCell<u32>, // < RCC clock control & status register, Address offset: 0x74
pub RESERVED6: [VolatileCell<u32>; 2], // < Reserved, 0x78-0x7C
pub SSCGR: VolatileCell<u32>, // < RCC spread spectrum clock generation register, Address offset: 0x80
pub PLLI2SCFGR: VolatileCell<u32>, // < RCC PLLI2S configuration register, Address offset: 0x84
}
impl RCC {
pub fn get() -> *mut RCC {
address::RCC_BASE as *mut RCC
}
}
#[repr(C)]
#[allow(non_snake_case)]
#[rustfmt::skip]
pub struct GPIOA {
pub MODER: VolatileCell<u32>, // < GPIO port mode register, Address offset: 0x00
pub OTYPER: VolatileCell<u32>, // < GPIO port output type register, Address offset: 0x04
pub OSPEEDR: VolatileCell<u32>, // < GPIO port output speed register, Address offset: 0x08
pub PUPDR: VolatileCell<u32>, // < GPIO port pull-up/pull-down register, Address offset: 0x0C
pub IDR: VolatileCell<u32>, // < GPIO port input data register, Address offset: 0x10
pub ODR: VolatileCell<u32>, // < GPIO port output data register, Address offset: 0x14
pub BSRRL: VolatileCell<u16>, // < GPIO port bit set/reset low register, Address offset: 0x18
pub BSRRH: VolatileCell<u16>, // < GPIO port bit set/reset high register, Address offset: 0x1A
pub LCKR: VolatileCell<u32>, // < GPIO port configuration lock register, Address offset: 0x1C
pub AFR: [VolatileCell<u32>;2], // < GPIO alternate function registers, Address offset: 0x20-0x24
}
impl GPIOA {
pub fn get() -> *mut GPIOA {
GPIOA_BASE as *mut GPIOA
}
}
}
use stm32f40x::*;
// see the Reference Manual RM0368 (www.st.com/resource/en/reference_manual/dm00096844.pdf)
// rcc, chapter 6
// gpio, chapter 8
fn wait(i: u32) {
for _ in 0..i {
cortex_m::asm::nop(); // no operation (cannot be optimized out)
}
}
// simple test of Your `modify`
//fn test() {
// let t:VolatileCell<u32> = unsafe { core::mem::uninitialized() };
// t.write(0);
// assert!(t.read() == 0);
// t.modify(3, 3, 0b10101);
// //
// // 10101
// // ..0111000
// // ---------
// // 000101000
// assert!(t.read() == 0b101 << 3);
// t.modify(4, 3, 0b10001);
// // 000101000
// // 111
// // 001
// // 000011000
// assert!(t.read() == 0b011 << 3);
// if << is used, your code will panic in dev (debug), but not in release mode
// t.modify(32, 3, 1);
//}
// system startup, can be hidden from the user
#[entry]
fn main() -> ! {
let rcc = unsafe { &mut *RCC::get() }; // get the reference to RCC in memory
let gpioa = unsafe { &mut *GPIOA::get() }; // get the reference to GPIOA in memory
// test(); // uncomment to run test
idle(rcc, gpioa);
loop {
continue;
}
}
// user application
fn idle(rcc: &mut RCC, gpioa: &mut GPIOA) {
// power on GPIOA
let r = rcc.AHB1ENR.read(); // read
rcc.AHB1ENR.write(r | 1 << (0)); // set enable
// configure PA5 as output
let r = gpioa.MODER.read() & !(0b11 << (5 * 2)); // read and mask
gpioa.MODER.write(r | 0b01 << (5 * 2)); // set output mode
loop {
// set PA5 high
gpioa.BSRRH.write(1 << 5); // set bit, output hight (turn on led)
// alternatively to set the bit high we can
// read the value, or with PA5 (bit 5) and write back
// gpioa.ODR.write(gpioa.ODR.read() | (1 << 5));
wait(10_000);
// set PA5 low
gpioa.BSRRL.write(1 << 5); // clear bit, output low (turn off led)
// alternatively to clear the bit we can
// read the value, mask out PA5 (bit 5) and write back
// gpioa.ODR.write(gpioa.ODR.read() & !(1 << 5));
wait(10_000);
}
}
// 0. Build and run the application.
//
// > cargo build --example bare5
// (or use the vscode)
//
// 1. C like API.
// Using C the .h files are used for defining interfaces, like function signatures (prototypes),
// structs and macros (but usually not the functions themselves).
//
// Here is a peripheral abstraction quite similar to what you would find in the .h files
// provided by ST (and other companies). Actually, the file presented here is mostly a
// cut/paste/replace of the stm32f40x.h, just Rustified.
//
// In this case we pass mutable pointers of the peripherals to the `idle` function.
//
// In the loop we access PA5 through bit set/clear operations.
// Comment out those operations and uncomment the the ODR accesses.
// (They should have the same behavior, but is a bit less efficient.)
//
// Run and see that the program behaves the same.
//
// Commit your answers (bare5_1)
//
// 2. Extend the read/write API with a `modify` for u32, taking the
// - address (&mut u32),
// - field offset (in bits, u8),
// - field width (in bits, u8),
// - and value (u32).
//
// Implement and check that running `test` gives you expected behavior.
//
// Change the code into using your new API.
//
// Run and see that the program behaves the same.
//
// Commit your answers (bare5_2)
//
// Discussion:
// As with arithmetic operations, default semantics differ in between
// debug/dev and release builds. E.g., debug << rhs is checked, rhs must be less
// than 32 (for 32 bit datatypes).
//
// Notice, over-shifting (where bits are spilled) is always considered legal,
// its just the shift amount that is checked.
// There are explicit unchecked versions available if so wanted.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment