Skip to content

Commit 732c1e6

Browse files
authored
Minimal USART Driver (#6)
1 parent d1db5b5 commit 732c1e6

File tree

21 files changed

+467
-85
lines changed

21 files changed

+467
-85
lines changed

minimal_drivers/README.md

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
- [Pre-requisites](#pre-requisites)
55
- [GPIO](#gpio)
66
- [Changelog](#changelog)
7+
- [L0 Layer - Controller](#l0-layer---controller)
8+
- [Global](#global)
9+
- [Utility](#utility)
10+
- [STM32L475xx](#stm32l475xx)
711
- [L2 Layer - Utilities](#l2-layer---utilities)
812
- [Own implementation](#own-implementation)
913
- [Crates.io](#cratesio)
1014
- [L3 Layer - Interfaces](#l3-layer---interfaces)
15+
- [Rust interfaces used](#rust-interfaces-used)
1116
- [L3 Layer - Drivers](#l3-layer---drivers)
1217
- [L3 Layer - Miscellaneous](#l3-layer---miscellaneous)
13-
- [L4](#l4)
18+
- [L4 Sensor / Actuator](#l4-sensor--actuator)
1419

1520
# Minimal Drivers
1621

@@ -40,6 +45,26 @@ This code has been tested on
4045
- L4 Sensor
4146
- L5 Application
4247

48+
```
49+
application v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l5)
50+
├── l0 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l0)
51+
│ [build-dependencies]
52+
│ └── bindgen v0.63.0
53+
├── l3 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l3)
54+
│ ├── l0 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l0) (*)
55+
│ └── l2 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l2)
56+
└── l4 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l4)
57+
└── l3 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l3) (*)
58+
59+
l0 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l0) (*)
60+
61+
l2 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l2) (*)
62+
63+
l3 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l3) (*)
64+
65+
l4 v0.1.0 (D:\Repositories\lowlevel_rust\minimal_drivers\l4) (*)
66+
```
67+
4368
## Pre-requisites
4469

4570
- Pre-requisites from `minimal_controller_peripheral`
@@ -60,7 +85,7 @@ This code has been tested on
6085
graph BT;
6186
subgraph Port
6287
GPIOA-H
63-
subgraph Periphal
88+
subgraph Peripheral
6489
GPIO
6590
subgraph Register
6691
MODER
@@ -73,9 +98,27 @@ graph BT;
7398
end
7499
end
75100
```
76-
77101
# Changelog
78102

103+
## L0 Layer - Controller
104+
105+
### Global
106+
107+
- System Clock
108+
- Every microcontroller will have a system clock in Hz
109+
110+
### Utility
111+
112+
- Common macros for port and register access
113+
- `get_port!`
114+
- `read_register!`
115+
- `write_register!`
116+
117+
### STM32L475xx
118+
119+
- Controller initialization
120+
- For now it just updates the System Clock so that it can be used by upper layer (drivers etc)
121+
79122
## L2 Layer - Utilities
80123

81124
### Own implementation
@@ -89,23 +132,29 @@ graph BT;
89132
- GpioIn
90133
- GpioOut
91134
- UsartIn
92-
- UsartOut
93135
- UsartInOut
94-
- Port
95-
- Generic interface that creates a port using base address and peripheral register layout
96-
- In C it would be the equivalent of `GPIO_TypeDef * gpio = (GPIO_TypeDef *)BASE_ADDRESS`
136+
137+
### Rust interfaces used
138+
139+
- Write
140+
- Used instead of creating out own UsartOut interface
97141

98142
## L3 Layer - Drivers
99143

100144
- RCC
101145
- GPIO
146+
- [x] Input
147+
- [x] Output
148+
- USART
149+
- [x] Read
150+
- [x] Write
102151

103152
## L3 Layer - Miscellaneous
104153

105154
- Singleton
106155
- Safe access to global ports
107156

108-
## L4
157+
## L4 Sensor / Actuator
109158

110159
- Led
111160
- Button

minimal_drivers/l0/build.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ fn parse_header(input_filename: &str, output_path: &PathBuf) {
1616
.wrap_unsafe_ops(true)
1717
.translate_enum_integer_types(true)
1818
.explicit_padding(false)
19+
.generate_block(true)
20+
.default_enum_style(bindgen::EnumVariation::ModuleConsts)
1921
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
2022
.generate()
2123
.expect("Unable to generate bindings");
@@ -35,6 +37,9 @@ fn main() {
3537
};
3638

3739
// TODO, Make this user configurable to support multiple microcontroller formats
40+
// NOTE, This controller.rs contains both
41+
// - Architecture considerations (ARM specific peripherals)
42+
// - Microcontroller considerations (STM32 specific peripherals)
3843
const PARSE_INPUT_FILE: &str = "device/controller/stm32l475xx.h";
3944
const OUTPUT_FILE: &str = "src/controller.rs";
4045
if should_parse {

minimal_drivers/l0/src/entry_point.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
use crate::chip::controller_init;
2+
13
#[link_section = ".vector_table.reset_vector"]
24
#[no_mangle]
35
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
46

57
// NOTE, All the externed modules come here
68
#[no_mangle]
79
pub unsafe extern "C" fn Reset() -> ! {
10+
// Data and BSS sections
811
extern "C" {
912
// .data section
1013
static mut __data_end__: u8;
@@ -16,24 +19,30 @@ pub unsafe extern "C" fn Reset() -> ! {
1619
static mut __bss_end__: u8;
1720
}
1821

19-
// data
22+
// Copy from VMA to LMA
2023
let vma_data_end = &__data_end__ as *const u8;
2124
let vma_data_start = &__data_start__ as *const u8;
2225
let lma_data_start = &__etext as *const u8;
2326
let count: usize = vma_data_end as usize - vma_data_start as usize;
2427
// core::ptr::copy_nonoverlapping(lma_data_start, &mut __data_start__ as *mut u8, count);
2528
core::ptr::copy_nonoverlapping(lma_data_start, vma_data_start as *mut u8, count);
2629

27-
// end
30+
// Write 0 to .bss section
2831
let bss_end = &__bss_end__ as *const u8;
2932
let bss_start = &__bss_start__ as *const u8;
3033
let count = bss_end as usize - bss_start as usize;
3134
// core::ptr::write_bytes(&mut __bss_start__ as *mut u8, 0, count);
3235
core::ptr::write_bytes(bss_start as *mut u8, 0, count);
3336

37+
// Controller level startup system initialization
38+
39+
// Jump to L0 controller system init
40+
// TODO, Jump to L4 board init
41+
// Jump to L5 main function
3442
extern "Rust" {
3543
fn main() -> !;
3644
}
45+
controller_init();
3746
main();
3847
}
3948

@@ -68,7 +77,7 @@ pub static EXCEPTIONS: [Vector; 14] = [
6877
Vector { reserved: 0 },
6978
Vector { reserved: 0 },
7079
Vector { handler: SVCall },
71-
Vector { reserved: 0 },
80+
Vector { reserved: 0 }, // Debug Monitor Handler comes here
7281
Vector { reserved: 0 },
7382
Vector { handler: PendSV },
7483
Vector { handler: SysTick },

minimal_drivers/l0/src/global.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use core::sync::atomic::AtomicU32;
2+
3+
pub static SYSTEM_CLOCK: AtomicU32 = AtomicU32::new(4_000_000);
4+
5+
pub fn get_system_clock() -> u32 {
6+
use core::sync::atomic::Ordering;
7+
SYSTEM_CLOCK.load(Ordering::SeqCst)
8+
}

minimal_drivers/l0/src/lib.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,23 @@
88
#![allow(non_camel_case_types)]
99
#![allow(non_snake_case)]
1010

11+
// * Private to l0
1112
mod entry_point;
13+
mod global;
1214
mod rust_entry_point;
1315

14-
// Generated controller bindings
15-
mod controller;
16-
pub use controller::*;
16+
// TODO, Add features
17+
// #[cfg(feature = "stm32l475xx")]
18+
mod stm32l475xx;
19+
use stm32l475xx as chip;
20+
21+
// * Public APIs usable when l0 is a dependency
22+
mod utility; // Macro export makes macros always public
23+
pub use global::get_system_clock;
24+
25+
// Generated controller bindings from C to Rust
26+
pub mod controller;
27+
28+
// Whats the difference between the chip module and controller module
29+
// controller.rs contains bindings for ARM Architecture + Chip specific peripheral structs (STM32L475xx)
30+
// chip.rs contains only Chip specific functionality (STM32L475xx)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mod private;
2+
pub use private::*;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use crate::{get_port, global::SYSTEM_CLOCK, read_register};
2+
use core::ptr::read_volatile;
3+
use core::sync::atomic::Ordering;
4+
5+
use crate::controller::{RCC_TypeDef, RCC_BASE};
6+
7+
pub fn controller_init() {
8+
// Update the System clock
9+
let rcc_port = get_port!(RCC_TypeDef, RCC_BASE);
10+
let cr_data = read_register!(rcc_port.CR);
11+
let msi_range = (cr_data >> 4) & 0xF;
12+
let system_clock: u32 = match msi_range {
13+
0 => todo!(),
14+
1 => todo!(),
15+
2 => todo!(),
16+
3 => todo!(),
17+
4 => 1_000_000,
18+
5 => 2_000_000,
19+
6 => 4_000_000,
20+
7 => todo!(),
21+
8 => todo!(),
22+
9 => todo!(),
23+
10 => todo!(),
24+
11 => todo!(),
25+
_ => unreachable!(),
26+
};
27+
SYSTEM_CLOCK.store(system_clock, Ordering::SeqCst)
28+
29+
// TODO, Do more things
30+
}

minimal_drivers/l0/src/utility/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#[macro_export]
2+
macro_rules! get_port {
3+
($register_map_struct:ident, $address:ident) => {
4+
unsafe { &mut *($address as *mut $register_map_struct) }
5+
};
6+
}
7+
8+
#[macro_export]
9+
macro_rules! read_register {
10+
($register:expr) => {
11+
unsafe { read_volatile(&$register) }
12+
};
13+
}
14+
15+
// TODO, Overload this macro to support $data:literal
16+
#[macro_export]
17+
macro_rules! write_register {
18+
($register:expr, $data:expr) => {
19+
unsafe { write_volatile(&mut $register, $data) }
20+
};
21+
}

minimal_drivers/l2/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,5 @@ readme = "README.md"
88
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
99

1010
[dependencies]
11-
l0 = { path = "../l0" }
1211
# Add libraries here
1312
bitflags = "1.3.2"

minimal_drivers/l3/src/interfaces/generic_interface.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
pub trait Port<T, const B: u32> {
2-
fn get_port(&self) -> &'static mut T {
3-
let mutable_ref = unsafe { &mut *(B as *mut T) };
4-
mutable_ref
5-
}
6-
}
7-
81
pub trait PeripheralConfiguration {
92
type Config;
103
type Register;

minimal_drivers/l3/src/interfaces/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ pub use generic_interface::*;
44
pub mod gpio_interface;
55
pub use gpio_interface::*;
66

7+
pub mod usart_interface;
8+
pub use usart_interface::*;
9+
710
pub mod utility_interface;
811
pub use utility_interface::*;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use core::fmt::Write;
2+
3+
// * Instead of having an `UsartOut` trait, we can use the Rust core library `core::fmt::Write` trait
4+
5+
pub trait UsartIn {
6+
fn read_character(&mut self) -> char;
7+
}
8+
9+
pub trait UsartInOut: UsartIn + Write {}

minimal_drivers/l3/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
mod interfaces;
44
pub use interfaces::*;
55

6-
pub mod peripheral;
7-
pub use peripheral::*;
6+
pub mod singleton;
7+
pub use singleton::*;
88

99
// TODO, Add features
1010
// #[cfg(feature = "stm32l475xx")]

0 commit comments

Comments
 (0)