diff --git a/minimal_drivers/README.md b/minimal_drivers/README.md index 4ca70cf..fda361d 100644 --- a/minimal_drivers/README.md +++ b/minimal_drivers/README.md @@ -4,13 +4,18 @@ - [Pre-requisites](#pre-requisites) - [GPIO](#gpio) - [Changelog](#changelog) + - [L0 Layer - Controller](#l0-layer---controller) + - [Global](#global) + - [Utility](#utility) + - [STM32L475xx](#stm32l475xx) - [L2 Layer - Utilities](#l2-layer---utilities) - [Own implementation](#own-implementation) - [Crates.io](#cratesio) - [L3 Layer - Interfaces](#l3-layer---interfaces) + - [Rust interfaces used](#rust-interfaces-used) - [L3 Layer - Drivers](#l3-layer---drivers) - [L3 Layer - Miscellaneous](#l3-layer---miscellaneous) - - [L4](#l4) + - [L4 Sensor / Actuator](#l4-sensor--actuator) # Minimal Drivers @@ -40,6 +45,26 @@ This code has been tested on - L4 Sensor - L5 Application +``` +application v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l5) +├── l0 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l0) +│ [build-dependencies] +│ └── bindgen v0.63.0 +├── l3 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l3) +│ ├── l0 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l0) (*) +│ └── l2 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l2) +└── l4 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l4) + └── l3 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l3) (*) + +l0 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l0) (*) + +l2 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l2) (*) + +l3 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l3) (*) + +l4 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l4) (*) +``` + ## Pre-requisites - Pre-requisites from `minimal_controller_peripheral` @@ -60,7 +85,7 @@ This code has been tested on graph BT; subgraph Port GPIOA-H - subgraph Periphal + subgraph Peripheral GPIO subgraph Register MODER @@ -73,9 +98,27 @@ graph BT; end end ``` - # Changelog +## L0 Layer - Controller + +### Global + +- System Clock + - Every microcontroller will have a system clock in Hz + +### Utility + +- Common macros for port and register access + - `get_port!` + - `read_register!` + - `write_register!` + +### STM32L475xx + +- Controller initialization + - For now it just updates the System Clock so that it can be used by upper layer (drivers etc) + ## L2 Layer - Utilities ### Own implementation @@ -89,23 +132,29 @@ graph BT; - GpioIn - GpioOut - UsartIn -- UsartOut - UsartInOut -- Port - - Generic interface that creates a port using base address and peripheral register layout - - In C it would be the equivalent of `GPIO_TypeDef * gpio = (GPIO_TypeDef *)BASE_ADDRESS` + +### Rust interfaces used + +- Write + - Used instead of creating out own UsartOut interface ## L3 Layer - Drivers - RCC - GPIO + - [x] Input + - [x] Output +- USART + - [x] Read + - [x] Write ## L3 Layer - Miscellaneous - Singleton - Safe access to global ports -## L4 +## L4 Sensor / Actuator - Led - Button diff --git a/minimal_drivers/l0/build.rs b/minimal_drivers/l0/build.rs index ad6f5b5..ce7d158 100644 --- a/minimal_drivers/l0/build.rs +++ b/minimal_drivers/l0/build.rs @@ -16,6 +16,8 @@ fn parse_header(input_filename: &str, output_path: &PathBuf) { .wrap_unsafe_ops(true) .translate_enum_integer_types(true) .explicit_padding(false) + .generate_block(true) + .default_enum_style(bindgen::EnumVariation::ModuleConsts) .parse_callbacks(Box::new(bindgen::CargoCallbacks)) .generate() .expect("Unable to generate bindings"); @@ -35,6 +37,9 @@ fn main() { }; // TODO, Make this user configurable to support multiple microcontroller formats + // NOTE, This controller.rs contains both + // - Architecture considerations (ARM specific peripherals) + // - Microcontroller considerations (STM32 specific peripherals) const PARSE_INPUT_FILE: &str = "device/controller/stm32l475xx.h"; const OUTPUT_FILE: &str = "src/controller.rs"; if should_parse { diff --git a/minimal_drivers/l0/src/entry_point.rs b/minimal_drivers/l0/src/entry_point.rs index 27190e2..add8c0f 100644 --- a/minimal_drivers/l0/src/entry_point.rs +++ b/minimal_drivers/l0/src/entry_point.rs @@ -1,3 +1,5 @@ +use crate::chip::controller_init; + #[link_section = ".vector_table.reset_vector"] #[no_mangle] pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset; @@ -5,6 +7,7 @@ pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset; // NOTE, All the externed modules come here #[no_mangle] pub unsafe extern "C" fn Reset() -> ! { + // Data and BSS sections extern "C" { // .data section static mut __data_end__: u8; @@ -16,7 +19,7 @@ pub unsafe extern "C" fn Reset() -> ! { static mut __bss_end__: u8; } - // data + // Copy from VMA to LMA let vma_data_end = &__data_end__ as *const u8; let vma_data_start = &__data_start__ as *const u8; let lma_data_start = &__etext as *const u8; @@ -24,16 +27,22 @@ pub unsafe extern "C" fn Reset() -> ! { // core::ptr::copy_nonoverlapping(lma_data_start, &mut __data_start__ as *mut u8, count); core::ptr::copy_nonoverlapping(lma_data_start, vma_data_start as *mut u8, count); - // end + // Write 0 to .bss section let bss_end = &__bss_end__ as *const u8; let bss_start = &__bss_start__ as *const u8; let count = bss_end as usize - bss_start as usize; // core::ptr::write_bytes(&mut __bss_start__ as *mut u8, 0, count); core::ptr::write_bytes(bss_start as *mut u8, 0, count); + // Controller level startup system initialization + + // Jump to L0 controller system init + // TODO, Jump to L4 board init + // Jump to L5 main function extern "Rust" { fn main() -> !; } + controller_init(); main(); } @@ -68,7 +77,7 @@ pub static EXCEPTIONS: [Vector; 14] = [ Vector { reserved: 0 }, Vector { reserved: 0 }, Vector { handler: SVCall }, - Vector { reserved: 0 }, + Vector { reserved: 0 }, // Debug Monitor Handler comes here Vector { reserved: 0 }, Vector { handler: PendSV }, Vector { handler: SysTick }, diff --git a/minimal_drivers/l0/src/global.rs b/minimal_drivers/l0/src/global.rs new file mode 100644 index 0000000..bb52ecd --- /dev/null +++ b/minimal_drivers/l0/src/global.rs @@ -0,0 +1,8 @@ +use core::sync::atomic::AtomicU32; + +pub static SYSTEM_CLOCK: AtomicU32 = AtomicU32::new(4_000_000); + +pub fn get_system_clock() -> u32 { + use core::sync::atomic::Ordering; + SYSTEM_CLOCK.load(Ordering::SeqCst) +} diff --git a/minimal_drivers/l0/src/lib.rs b/minimal_drivers/l0/src/lib.rs index 1f0c362..9968b88 100644 --- a/minimal_drivers/l0/src/lib.rs +++ b/minimal_drivers/l0/src/lib.rs @@ -8,9 +8,23 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] +// * Private to l0 mod entry_point; +mod global; mod rust_entry_point; -// Generated controller bindings -mod controller; -pub use controller::*; +// TODO, Add features +// #[cfg(feature = "stm32l475xx")] +mod stm32l475xx; +use stm32l475xx as chip; + +// * Public APIs usable when l0 is a dependency +mod utility; // Macro export makes macros always public +pub use global::get_system_clock; + +// Generated controller bindings from C to Rust +pub mod controller; + +// Whats the difference between the chip module and controller module +// controller.rs contains bindings for ARM Architecture + Chip specific peripheral structs (STM32L475xx) +// chip.rs contains only Chip specific functionality (STM32L475xx) diff --git a/minimal_drivers/l0/src/stm32l475xx/mod.rs b/minimal_drivers/l0/src/stm32l475xx/mod.rs new file mode 100644 index 0000000..644f321 --- /dev/null +++ b/minimal_drivers/l0/src/stm32l475xx/mod.rs @@ -0,0 +1,2 @@ +mod private; +pub use private::*; diff --git a/minimal_drivers/l0/src/stm32l475xx/private.rs b/minimal_drivers/l0/src/stm32l475xx/private.rs new file mode 100644 index 0000000..2896e72 --- /dev/null +++ b/minimal_drivers/l0/src/stm32l475xx/private.rs @@ -0,0 +1,30 @@ +use crate::{get_port, global::SYSTEM_CLOCK, read_register}; +use core::ptr::read_volatile; +use core::sync::atomic::Ordering; + +use crate::controller::{RCC_TypeDef, RCC_BASE}; + +pub fn controller_init() { + // Update the System clock + let rcc_port = get_port!(RCC_TypeDef, RCC_BASE); + let cr_data = read_register!(rcc_port.CR); + let msi_range = (cr_data >> 4) & 0xF; + let system_clock: u32 = match msi_range { + 0 => todo!(), + 1 => todo!(), + 2 => todo!(), + 3 => todo!(), + 4 => 1_000_000, + 5 => 2_000_000, + 6 => 4_000_000, + 7 => todo!(), + 8 => todo!(), + 9 => todo!(), + 10 => todo!(), + 11 => todo!(), + _ => unreachable!(), + }; + SYSTEM_CLOCK.store(system_clock, Ordering::SeqCst) + + // TODO, Do more things +} diff --git a/minimal_drivers/l0/src/utility/mod.rs b/minimal_drivers/l0/src/utility/mod.rs new file mode 100644 index 0000000..88e2c38 --- /dev/null +++ b/minimal_drivers/l0/src/utility/mod.rs @@ -0,0 +1,21 @@ +#[macro_export] +macro_rules! get_port { + ($register_map_struct:ident, $address:ident) => { + unsafe { &mut *($address as *mut $register_map_struct) } + }; +} + +#[macro_export] +macro_rules! read_register { + ($register:expr) => { + unsafe { read_volatile(&$register) } + }; +} + +// TODO, Overload this macro to support $data:literal +#[macro_export] +macro_rules! write_register { + ($register:expr, $data:expr) => { + unsafe { write_volatile(&mut $register, $data) } + }; +} diff --git a/minimal_drivers/l2/Cargo.toml b/minimal_drivers/l2/Cargo.toml index a1631fb..877c130 100644 --- a/minimal_drivers/l2/Cargo.toml +++ b/minimal_drivers/l2/Cargo.toml @@ -8,6 +8,5 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -l0 = { path = "../l0" } # Add libraries here bitflags = "1.3.2" diff --git a/minimal_drivers/l3/src/interfaces/generic_interface.rs b/minimal_drivers/l3/src/interfaces/generic_interface.rs index ec3717d..961e572 100644 --- a/minimal_drivers/l3/src/interfaces/generic_interface.rs +++ b/minimal_drivers/l3/src/interfaces/generic_interface.rs @@ -1,10 +1,3 @@ -pub trait Port { - fn get_port(&self) -> &'static mut T { - let mutable_ref = unsafe { &mut *(B as *mut T) }; - mutable_ref - } -} - pub trait PeripheralConfiguration { type Config; type Register; diff --git a/minimal_drivers/l3/src/interfaces/mod.rs b/minimal_drivers/l3/src/interfaces/mod.rs index c739dee..7fbf6bc 100644 --- a/minimal_drivers/l3/src/interfaces/mod.rs +++ b/minimal_drivers/l3/src/interfaces/mod.rs @@ -4,5 +4,8 @@ pub use generic_interface::*; pub mod gpio_interface; pub use gpio_interface::*; +pub mod usart_interface; +pub use usart_interface::*; + pub mod utility_interface; pub use utility_interface::*; diff --git a/minimal_drivers/l3/src/interfaces/usart_interface.rs b/minimal_drivers/l3/src/interfaces/usart_interface.rs new file mode 100644 index 0000000..90f0ff3 --- /dev/null +++ b/minimal_drivers/l3/src/interfaces/usart_interface.rs @@ -0,0 +1,9 @@ +use core::fmt::Write; + +// * Instead of having an `UsartOut` trait, we can use the Rust core library `core::fmt::Write` trait + +pub trait UsartIn { + fn read_character(&mut self) -> char; +} + +pub trait UsartInOut: UsartIn + Write {} diff --git a/minimal_drivers/l3/src/lib.rs b/minimal_drivers/l3/src/lib.rs index 43f7995..c50440c 100644 --- a/minimal_drivers/l3/src/lib.rs +++ b/minimal_drivers/l3/src/lib.rs @@ -3,8 +3,8 @@ mod interfaces; pub use interfaces::*; -pub mod peripheral; -pub use peripheral::*; +pub mod singleton; +pub use singleton::*; // TODO, Add features // #[cfg(feature = "stm32l475xx")] diff --git a/minimal_drivers/l3/src/peripheral.rs b/minimal_drivers/l3/src/singleton.rs similarity index 100% rename from minimal_drivers/l3/src/peripheral.rs rename to minimal_drivers/l3/src/singleton.rs diff --git a/minimal_drivers/l3/src/stm32l475xx/gpio.rs b/minimal_drivers/l3/src/stm32l475xx/gpio.rs index a30c748..f69d5d7 100644 --- a/minimal_drivers/l3/src/stm32l475xx/gpio.rs +++ b/minimal_drivers/l3/src/stm32l475xx/gpio.rs @@ -2,11 +2,9 @@ use core::ptr::{read_volatile, write_volatile}; -use crate::{EnumToNum, GpioIn, GpioOut, GpioValue, PeripheralConfiguration, Port, Singleton}; -use l0::{ - GPIO_TypeDef, GPIOA_BASE, GPIOB_BASE, GPIOC_BASE, GPIOD_BASE, GPIOE_BASE, GPIOF_BASE, - GPIOG_BASE, GPIOH_BASE, -}; +use crate::Singleton; +use crate::{EnumToNum, GpioIn, GpioOut, GpioValue, PeripheralConfiguration}; +use l0::{controller::*, get_port, read_register, write_register}; pub enum GPIOMode { Input, @@ -116,13 +114,12 @@ impl EnumToNum for GPIOAlternate { } } -pub struct GPIORegister { +pub struct GPIORegister { + port: &'static mut GPIO_TypeDef, pin: u32, } -impl Port for GPIORegister {} - -impl GPIORegister { +impl GPIORegister { fn via_config(&mut self, config: &GPIOConfig) { self.set_moder(&config.moder); self.set_otyper(&config.otyper); @@ -132,62 +129,62 @@ impl GPIORegister { } fn set_moder(&mut self, moder: &GPIOMode) { - let mut moder_data = unsafe { read_volatile(&self.get_port().MODER) }; + let mut moder_data = read_register!(self.port.MODER); moder_data &= !(0x3 << self.pin * 2); // clear mode register moder_data |= moder.to_num() << self.pin * 2; - unsafe { write_volatile(&mut self.get_port().MODER, moder_data) }; + write_register!(self.port.MODER, moder_data); } fn set_otyper(&mut self, otyper: &GPIOType) { - let mut otyper_data = unsafe { read_volatile(&self.get_port().OTYPER) }; + let mut otyper_data = read_register!(self.port.OTYPER); otyper_data &= !(0x1 << self.pin); // clear type register otyper_data |= otyper.to_num() << self.pin; - unsafe { write_volatile(&mut self.get_port().OTYPER, otyper_data) }; + write_register!(self.port.OTYPER, otyper_data); } fn set_ospeedr(&mut self, ospeedr: &GPIOSpeed) { - let mut ospeedr_data = unsafe { read_volatile(&self.get_port().OSPEEDR) }; + let mut ospeedr_data = read_register!(self.port.OSPEEDR); ospeedr_data &= !(0x3 << self.pin * 2); // clear ospeedr register ospeedr_data |= ospeedr.to_num() << self.pin * 2; - unsafe { write_volatile(&mut self.get_port().OSPEEDR, ospeedr_data) }; + write_register!(self.port.OSPEEDR, ospeedr_data); } fn set_pupdr(&mut self, pupdr: &GPIOPull) { - let mut pupdr_data = unsafe { read_volatile(&self.get_port().PUPDR) }; + let mut pupdr_data = read_register!(self.port.PUPDR); pupdr_data &= !(0x3 << self.pin * 2); pupdr_data |= pupdr.to_num() << self.pin * 2; - unsafe { write_volatile(&mut self.get_port().PUPDR, pupdr_data) }; + write_register!(self.port.PUPDR, pupdr_data); } fn set_afr(&mut self, afr: &GPIOAlternate) { - let (register, pin) = if self.pin > 7 { + let (index, pin) = if self.pin > 7 { // Use AFRH - (&mut self.get_port().AFR[1], self.pin - 7) + (1, self.pin - 7) } else { // Use AFRL - (&mut self.get_port().AFR[0], self.pin) + (0, self.pin) }; - let mut afr_data = unsafe { read_volatile(register) }; + let mut afr_data = read_register!(self.port.AFR[index]); afr_data &= !(0xF << (pin << 2)); afr_data |= afr.to_num() << (pin << 2); - unsafe { write_volatile(register, afr_data) }; + write_register!(self.port.AFR[index], afr_data); } fn set_bsrr(&mut self) { - let mut bsrr_data = unsafe { read_volatile(&self.get_port().BSRR) }; + let mut bsrr_data = read_register!(self.port.BSRR); bsrr_data |= 1 << self.pin; - unsafe { write_volatile(&mut self.get_port().BSRR, bsrr_data) }; + write_register!(self.port.BSRR, bsrr_data); } fn set_brr(&mut self) { - let mut brr_data = unsafe { read_volatile(&self.get_port().BRR) }; + let mut brr_data = read_register!(self.port.BRR); brr_data |= 1 << self.pin; - unsafe { write_volatile(&mut self.get_port().BRR, brr_data) }; + write_register!(self.port.BRR, brr_data); } } -impl GpioOut for GPIORegister { +impl GpioOut for GPIORegister { fn write(&mut self, value: GpioValue) { match value { GpioValue::Low => self.set_brr(), @@ -196,9 +193,9 @@ impl GpioOut for GPIORegister { } } -impl GpioIn for GPIORegister { +impl GpioIn for GPIORegister { fn read(&self) -> GpioValue { - let idr = unsafe { read_volatile(&self.get_port().IDR) }; + let idr = read_register!(self.port.IDR); let value = (idr >> self.pin) & 0x01; let value = match value { 0x0 => GpioValue::Low, @@ -213,7 +210,7 @@ impl GpioIn for GPIORegister { pub struct GPIOPeripheral; impl GPIOPeripheral { - pub fn configure_as_output(&self, pin: u32) -> impl GpioOut { + pub fn configure_for_output(&self, pin: u32) -> impl GpioOut { let config = GPIOConfig { pin, moder: GPIOMode::Output, @@ -225,7 +222,7 @@ impl GPIOPeripheral { self.configure(&config) } - pub fn configure_as_input(&self, pin: u32) -> impl GpioIn { + pub fn configure_for_input(&self, pin: u32) -> impl GpioIn { let config = GPIOConfig { pin, moder: GPIOMode::Input, @@ -236,6 +233,18 @@ impl GPIOPeripheral { }; self.configure(&config) } + + pub fn configure_for_usart(&self, afr: GPIOAlternate, pin: u32) -> GPIORegister { + let config = GPIOConfig { + pin, + moder: GPIOMode::AlternateFunction, + otyper: GPIOType::PushPull, + pupdr: GPIOPull::NoPullupOrPulldown, + ospeedr: GPIOSpeed::VeryHighSpeed, + afr, + }; + self.configure(&config) + } } pub struct GPIOConfig { @@ -249,10 +258,11 @@ pub struct GPIOConfig { impl PeripheralConfiguration for GPIOPeripheral { type Config = GPIOConfig; - type Register = GPIORegister; + type Register = GPIORegister; fn configure(&self, configuration: &Self::Config) -> Self::Register { - let mut gpio = GPIORegister:: { + let mut gpio = GPIORegister { + port: get_port!(GPIO_TypeDef, B), pin: configuration.pin, }; gpio.via_config(&configuration); diff --git a/minimal_drivers/l3/src/stm32l475xx/mod.rs b/minimal_drivers/l3/src/stm32l475xx/mod.rs index ad970ff..68dd1da 100644 --- a/minimal_drivers/l3/src/stm32l475xx/mod.rs +++ b/minimal_drivers/l3/src/stm32l475xx/mod.rs @@ -1,5 +1,9 @@ -mod gpio; mod rcc; +pub use rcc::*; +// Contains interfaces +mod gpio; pub use gpio::*; -pub use rcc::*; + +mod usart; +pub use usart::*; diff --git a/minimal_drivers/l3/src/stm32l475xx/rcc.rs b/minimal_drivers/l3/src/stm32l475xx/rcc.rs index 78c5542..dc68dc9 100644 --- a/minimal_drivers/l3/src/stm32l475xx/rcc.rs +++ b/minimal_drivers/l3/src/stm32l475xx/rcc.rs @@ -2,10 +2,13 @@ use core::ptr::{read_volatile, write_volatile}; -use l0::{RCC_TypeDef, RCC_BASE}; +use l0::{ + controller::{RCC_TypeDef, RCC_BASE}, + get_port, read_register, write_register, +}; use l2::bitflags; -use crate::{Port, Singleton}; +use crate::Singleton; bitflags! { pub struct RCC_AHB2ENR : u32 { @@ -29,28 +32,34 @@ bitflags! { } } -// Put functionality here i.e various valid configurations for your port -pub struct RCCPeripheral; +pub struct RCCRegister { + port: &'static mut RCC_TypeDef, +} -impl RCCPeripheral { +impl RCCRegister { pub fn set_ahb2enr(&mut self, ahb2: RCC_AHB2ENR) { - let mut ahb2enr = unsafe { read_volatile(&mut self.get_port().AHB2ENR) }; - ahb2enr |= ahb2.bits(); - unsafe { - write_volatile(&mut self.get_port().AHB2ENR, ahb2enr); - } + let mut ahb2enr_data = read_register!(self.port.AHB2ENR); + ahb2enr_data |= ahb2.bits(); + write_register!(self.port.AHB2ENR, ahb2enr_data); } pub fn set_apb2enr(&mut self, apb2: RCC_APB2ENR) { - let mut apb2enr = unsafe { read_volatile(&mut self.get_port().AHB2ENR) }; - apb2enr |= apb2.bits(); - unsafe { - write_volatile(&mut self.get_port().AHB2ENR, apb2enr); - } + let mut apb2enr_data = read_register!(self.port.APB2ENR); + apb2enr_data |= apb2.bits(); + write_register!(self.port.APB2ENR, apb2enr_data); } } -impl Port for RCCPeripheral {} +// Put functionality here i.e various valid configurations for your port +pub struct RCCPeripheral; + +impl RCCPeripheral { + pub fn get_register(&self) -> RCCRegister { + RCCRegister { + port: get_port!(RCC_TypeDef, B), + } + } +} // Create established ports here diff --git a/minimal_drivers/l3/src/stm32l475xx/usart.rs b/minimal_drivers/l3/src/stm32l475xx/usart.rs new file mode 100644 index 0000000..0263050 --- /dev/null +++ b/minimal_drivers/l3/src/stm32l475xx/usart.rs @@ -0,0 +1,195 @@ +#![allow(non_camel_case_types)] + +use core::{ + fmt::Write, + ptr::{read_volatile, write_volatile}, +}; + +use l0::{ + controller::{USART_TypeDef, USART1_BASE}, + get_port, get_system_clock, read_register, write_register, +}; +use l2::bitflags; + +use crate::{PeripheralConfiguration, Singleton, UsartIn, UsartInOut}; + +bitflags! { + pub struct USART_CR1 : u32 { + const UE = 1 << 0; + const RE = 1 << 2; + const TE = 1 << 3; + const M0 = 1 << 12; + const OVER8 = 1 << 15; + const M1 = 1 << 28; + } +} + +bitflags! { + pub struct USART_CR2 : u32 { + const STOP = 3 << 12; + } +} + +pub enum USARTWordLength { + Len8, + Len9, + Len7, +} + +pub enum USARTStopBit { + Bit1_0, + Bit0_5, + Bit2_0, + Bit1_5, +} + +pub enum USARTMode { + Inactive, + RxOnly, + TxOnly, + RxTx, +} + +pub struct USARTRegister { + port: &'static mut USART_TypeDef, +} + +impl USARTRegister { + fn via_configure(&mut self, config: &USARTConfig) { + // Disable USART + self.reset_cr1(USART_CR1::UE); + + // Baud rate + let system_clock = get_system_clock(); + let usartdiv = system_clock / config.baud_rate; + write_register!(self.port.BRR, usartdiv); + + // Stop bits + let mut cr2_data = read_register!(self.port.CR2); + cr2_data &= (!USART_CR2::STOP).bits(); + cr2_data |= match config.stop_bit { + USARTStopBit::Bit1_0 => 0 << 12, + USARTStopBit::Bit0_5 => 1 << 12, + USARTStopBit::Bit2_0 => 2 << 12, + USARTStopBit::Bit1_5 => 3 << 12, + }; + write_register!(self.port.CR2, cr2_data); + + // Set word length, usart mode and enable + let mut cr1_data = read_register!(self.port.CR1); + cr1_data &= (!USART_CR1::all()).bits(); + + // Set word length + cr1_data |= match config.word_length { + USARTWordLength::Len8 => 0, + USARTWordLength::Len9 => USART_CR1::M0.bits(), + USARTWordLength::Len7 => USART_CR1::M1.bits(), + }; + + // Set Mode + cr1_data |= match config.mode { + USARTMode::Inactive => 0, + USARTMode::RxOnly => USART_CR1::RE.bits(), + USARTMode::TxOnly => USART_CR1::TE.bits(), + USARTMode::RxTx => (USART_CR1::RE | USART_CR1::TE).bits(), + }; + + // Enable + cr1_data |= USART_CR1::UE.bits(); + + write_register!(self.port.CR1, cr1_data); + } + + fn reset_cr1(&mut self, cr1: USART_CR1) { + let mut cr1_data = read_register!(self.port.CR1); + cr1_data &= !(cr1.bits()); + write_register!(self.port.CR1, cr1_data); + } +} + +impl UsartIn for USARTRegister { + fn read_character(&mut self) -> char { + const ISR_RXNE: u32 = 5; + while (read_register!(self.port.ISR) & 1 << ISR_RXNE) == 0 {} + let data = read_register!(self.port.RDR) as u8; + data as char + } +} + +impl Write for USARTRegister { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + let bit_not_set = |bit: u32| { + let isr_data = read_register!(self.port.ISR); + isr_data & (1 << bit) == 0 + }; + + const ISR_TXE: u32 = 7; + const ISR_TC: u32 = 6; + s.chars().for_each(|c| { + while bit_not_set(ISR_TXE) {} + write_register!(self.port.TDR, c as u16); + }); + while bit_not_set(ISR_TC) {} + Ok(()) + } +} + +impl UsartInOut for USARTRegister {} + +// Put functionality here i.e various valid configurations for your peripheral +pub struct USARTPeripheral {} + +impl USARTPeripheral { + pub fn configure_default_rx(&self) -> impl UsartIn { + self.configure(&USARTConfig { + mode: USARTMode::RxOnly, + word_length: USARTWordLength::Len8, + stop_bit: USARTStopBit::Bit1_0, + baud_rate: 115200, + }) + } + + pub fn configure_default_tx(&self) -> impl Write { + self.configure(&USARTConfig { + mode: USARTMode::TxOnly, + word_length: USARTWordLength::Len8, + stop_bit: USARTStopBit::Bit1_0, + baud_rate: 115200, + }) + } + + pub fn configure_default_rx_tx(&self) -> impl UsartInOut { + self.configure(&USARTConfig { + mode: USARTMode::RxTx, + word_length: USARTWordLength::Len8, + stop_bit: USARTStopBit::Bit1_0, + baud_rate: 115200, + }) + } +} + +pub struct USARTConfig { + mode: USARTMode, + word_length: USARTWordLength, + stop_bit: USARTStopBit, + baud_rate: u32, +} + +impl PeripheralConfiguration for USARTPeripheral { + type Config = USARTConfig; + type Register = USARTRegister; + + fn configure(&self, configuration: &Self::Config) -> Self::Register { + let mut usart = USARTRegister { + port: get_port!(USART_TypeDef, B), + }; + usart.via_configure(&configuration); + usart + } +} + +// Create established ports here + +type USART1 = USARTPeripheral; + +pub static USART1_PORT: Singleton = Singleton::new(USART1 {}); diff --git a/minimal_drivers/l4/Cargo.toml b/minimal_drivers/l4/Cargo.toml index b468f18..2f4bbdd 100644 --- a/minimal_drivers/l4/Cargo.toml +++ b/minimal_drivers/l4/Cargo.toml @@ -8,6 +8,4 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -l0 = { path = "../l0" } -l2 = { path = "../l2" } l3 = { path = "../l3" } diff --git a/minimal_drivers/l5/Cargo.toml b/minimal_drivers/l5/Cargo.toml index 5f95fcc..e9b764f 100644 --- a/minimal_drivers/l5/Cargo.toml +++ b/minimal_drivers/l5/Cargo.toml @@ -9,6 +9,5 @@ readme = "README.md" [dependencies] l0 = { path = "../l0" } -l2 = { path = "../l2" } l3 = { path = "../l3" } l4 = { path = "../l4" } diff --git a/minimal_drivers/l5/src/main.rs b/minimal_drivers/l5/src/main.rs index 6093d05..56d1910 100644 --- a/minimal_drivers/l5/src/main.rs +++ b/minimal_drivers/l5/src/main.rs @@ -20,27 +20,37 @@ pub fn spin_delay(delay: u32) { #[cfg(all(target_arch = "arm", target_os = "none"))] #[no_mangle] fn main() -> ! { + use core::fmt::Write; + use l0::*; use l3::*; use l4::*; - // Activate clock control for GPIOA and GPIOC - let mut rcc_peripheral = RCC_PORT.take(); - rcc_peripheral.set_ahb2enr(RCC_AHB2ENR::GPIOAEN | RCC_AHB2ENR::GPIOCEN); + let mut rcc_register = RCC_PORT.take().get_register(); + // Activate clock control for GPIOA, GPIOB and GPIOC and USART1EN + rcc_register.set_ahb2enr(RCC_AHB2ENR::GPIOAEN | RCC_AHB2ENR::GPIOBEN | RCC_AHB2ENR::GPIOCEN); + rcc_register.set_apb2enr(RCC_APB2ENR::USART1EN); let gpioa_peripheral = GPIOA_PORT.take(); - let gpioc_peripheral = GPIOC_PORT.take(); - // Configure GPIOA port and Pin 5 as output - let mut gpio_out_at_pin5 = gpioa_peripheral.configure_as_output(5); + let mut gpio_out_at_pin5 = gpioa_peripheral.configure_for_output(5); + + let gpiob_peripheral = GPIOB_PORT.take(); + // Configure GPIOB port Pin 6 and Pin 7 for USART + gpiob_peripheral.configure_for_usart(GPIOAlternate::AF7, 6); + gpiob_peripheral.configure_for_usart(GPIOAlternate::AF7, 7); + let usart1_rx_tx: &mut dyn UsartInOut = &mut USART1_PORT.take().configure_default_rx_tx(); + + let gpioc_peripheral = GPIOC_PORT.take(); // Configure GPIOC port and Pin 13 as input - let mut gpio_in_at_pin13 = gpioc_peripheral.configure_as_input(13); + let mut gpio_in_at_pin13 = gpioc_peripheral.configure_for_input(13); // Created led module let mut led = Led::new(&mut gpio_out_at_pin5); let button = Button::new(&mut gpio_in_at_pin13, GpioValue::High); let mut time; + let mut counter = 0; loop { if button.pressed() { time = 20_000; @@ -49,9 +59,24 @@ fn main() -> ! { } led.on(); + // Can also use write! and writeln! + usart1_rx_tx + .write_fmt(format_args!("LED ON: {}\r\n", counter)) + .unwrap(); + usart1_rx_tx + .write_str("Waiting for a character\r\n> ") + .unwrap(); + let char = usart1_rx_tx.read_character(); + usart1_rx_tx + .write_fmt(format_args!("Character: {:#?}\r\n", char)) + .unwrap(); spin_delay(time); led.off(); + usart1_rx_tx + .write_fmt(format_args!("LED OFF: {}\r\n", counter)) + .unwrap(); spin_delay(time); + counter += 1; } }