Skip to content

Commit 5ee909f

Browse files
authored
Merge pull request #130 from tstellanova/add_rng_support
Add Rng interface and an example that uses it.
2 parents 234e9ce + 3739431 commit 5ee909f

File tree

6 files changed

+262
-0
lines changed

6 files changed

+262
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1818
- Added USB driver.
1919
- PLL48Clk configuration.
2020
- Added bit-banding implementation.
21+
- Added support for RNG peripheral and rand_core, and an example that uses it.
2122

2223
## [v0.6.0] - 2019-10-19
2324

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ default-target = "x86_64-unknown-linux-gnu"
3030
cortex-m = ">=0.5.8,<0.7"
3131
cortex-m-rt = "0.6.10"
3232
nb = "0.1.2"
33+
rand_core = "0.5.1"
3334
stm32f4 = "0.9.0"
3435
synopsys-usb-otg = { version = "0.1.0", features = ["cortex-m"], optional = true }
3536

@@ -107,3 +108,7 @@ required-features = ["rt", "stm32f411"]
107108
[[example]]
108109
name = "stopwatch-with-ssd1306-and-interrupts"
109110
required-features = ["rt", "stm32f411"]
111+
112+
[[example]]
113+
name = "rng-display"
114+
required-features = ["rt", "stm32f407"]

examples/rng-display.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//! Generate random numbers using the RNG peripheral and display the values.
2+
//! This example is specifically tuned to run correctly on the
3+
//! stm32f4-discovery board (model STM32F407G-DISC1)
4+
//! This example requires the `rt` feature to be enabled. For example:
5+
//!
6+
//! ```bash
7+
//! cargo run --release --features stm32f407,rt --example rng-display
8+
//! ```
9+
//!
10+
//! Note that this example requires the `--release` build flag because it is too
11+
//! large to fit in the default `memory.x` file provided with this crate.
12+
13+
#![no_std]
14+
#![no_main]
15+
16+
use stm32f4xx_hal as hal;
17+
18+
#[cfg(not(debug_assertions))]
19+
use panic_halt as _;
20+
#[cfg(debug_assertions)]
21+
use panic_semihosting as _;
22+
23+
use cortex_m_rt::ExceptionFrame;
24+
use cortex_m_rt::{entry, exception};
25+
use embedded_graphics::{fonts::Font6x8, prelude::*};
26+
27+
use ssd1306::{prelude::*, Builder as SSD1306Builder};
28+
29+
use hal::{i2c::I2c, prelude::*, stm32};
30+
use rand_core::RngCore;
31+
32+
use arrayvec::ArrayString;
33+
use core::fmt;
34+
35+
// dimensions of SSD1306 OLED display known to work
36+
pub const SCREEN_WIDTH: i32 = 128;
37+
pub const SCREEN_HEIGHT: i32 = 64;
38+
pub const FONT_HEIGHT: i32 = 8;
39+
/// height of embedded font, in pixels
40+
pub const VCENTER_PIX: i32 = (SCREEN_HEIGHT - FONT_HEIGHT) / 2;
41+
pub const HINSET_PIX: i32 = 20;
42+
43+
#[entry]
44+
fn main() -> ! {
45+
if let (Some(dp), Some(cp)) = (
46+
stm32::Peripherals::take(),
47+
cortex_m::peripheral::Peripherals::take(),
48+
) {
49+
// Set up the system clock.
50+
let rcc = dp.RCC.constrain();
51+
52+
// Clock configuration is critical for RNG to work properly; otherwise
53+
// RNG_SR CECS bit will constantly report an error (if RNG_CLK < HCLK/16)
54+
// here we pick a simple clock configuration that ensures the pll48clk,
55+
// from which RNG_CLK is derived, is about 48 MHz
56+
let clocks = rcc
57+
.cfgr
58+
.use_hse(8.mhz()) //discovery board has 8 MHz crystal for HSE
59+
.sysclk(128.mhz())
60+
.freeze();
61+
62+
let mut delay_source = hal::delay::Delay::new(cp.SYST, clocks);
63+
64+
// Set up I2C1: SCL is PB8 and SDA is PB9; they are set to Alternate Function 4
65+
// as per the STM32F407 datasheet. Pin assignment as per the
66+
// stm32f4-discovery (ST32F407G-DISC1) board.
67+
let gpiob = dp.GPIOB.split();
68+
let scl = gpiob.pb8.into_alternate_af4().set_open_drain();
69+
let sda = gpiob.pb9.into_alternate_af4().set_open_drain();
70+
let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 400.khz(), clocks);
71+
72+
// Set up the display
73+
let mut disp: GraphicsMode<_> = SSD1306Builder::new().connect_i2c(i2c).into();
74+
disp.init().unwrap();
75+
76+
// enable the RNG peripheral and its clock
77+
// this will panic if the clock configuration is unsuitable
78+
let mut rand_source = dp.RNG.constrain(clocks);
79+
let mut format_buf = ArrayString::<[u8; 20]>::new();
80+
loop {
81+
//this will continuously report an error if RNG_CLK < HCLK/16
82+
let rand_val = rand_source.next_u32();
83+
84+
format_buf.clear();
85+
if fmt::write(&mut format_buf, format_args!("{}", rand_val)).is_ok() {
86+
disp.draw(
87+
Font6x8::render_str(format_buf.as_str())
88+
.with_stroke(Some(1u8.into()))
89+
.translate(Coord::new(HINSET_PIX, VCENTER_PIX))
90+
.into_iter(),
91+
);
92+
}
93+
disp.flush().unwrap();
94+
//delay a little while between refreshes so the display is readable
95+
delay_source.delay_ms(100u8);
96+
}
97+
}
98+
99+
loop {}
100+
}
101+
102+
#[exception]
103+
fn HardFault(ef: &ExceptionFrame) -> ! {
104+
panic!("{:#?}", ef);
105+
}

src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ pub mod otg_fs;
122122
)
123123
))]
124124
pub mod otg_hs;
125+
126+
#[cfg(all(
127+
feature = "device-selected",
128+
not(any(
129+
feature = "stm32f401",
130+
feature = "stm32f410",
131+
feature = "stm32f411",
132+
feature = "stm32f446",
133+
))
134+
))]
135+
pub mod rng;
136+
125137
#[cfg(feature = "device-selected")]
126138
pub mod prelude;
127139
#[cfg(feature = "device-selected")]

src/prelude.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,14 @@ pub use embedded_hal::prelude::*;
77
pub use crate::gpio::GpioExt as _stm32f4xx_hal_gpio_GpioExt;
88
pub use crate::i2c::Pins as _stm32f4xx_hal_i2c_Pins;
99
pub use crate::rcc::RccExt as _stm32f4xx_hal_rcc_RccExt;
10+
#[cfg(all(
11+
feature = "device-selected",
12+
not(any(
13+
feature = "stm32f401",
14+
feature = "stm32f410",
15+
feature = "stm32f411",
16+
feature = "stm32f446",
17+
))
18+
))]
19+
pub use crate::rng::RngExt as _stm32f4xx_hal_rng_RngExt;
1020
pub use crate::time::U32Ext as _stm32f4xx_hal_time_U32Ext;

src/rng.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
use core::cmp;
2+
use core::mem;
3+
4+
use crate::hal::blocking::rng;
5+
use crate::rcc::Clocks;
6+
use crate::stm32;
7+
use crate::stm32::RNG;
8+
use crate::time::U32Ext;
9+
use core::num::NonZeroU32;
10+
use core::ops::Shl;
11+
use rand_core::RngCore;
12+
13+
#[derive(Debug)]
14+
pub enum ErrorKind {
15+
/// The RNG_CLK was not correctly detected (fRNG_CLK< fHCLK/16).
16+
/// See CECS in RNG peripheral documentation.
17+
ClockError = 2,
18+
/// RNG detected more than 64 consecutive bits of the same value (0 or 1) OR
19+
/// more than 32 consecutive 01 pairs.
20+
/// See SECS in RNG peripheral documentation.
21+
SeedError = 4,
22+
}
23+
24+
impl From<ErrorKind> for rand_core::Error {
25+
fn from(err: ErrorKind) -> rand_core::Error {
26+
let err_code = NonZeroU32::new(rand_core::Error::CUSTOM_START + err as u32).unwrap();
27+
rand_core::Error::from(err_code)
28+
}
29+
}
30+
31+
pub trait RngExt {
32+
fn constrain(self, clocks: Clocks) -> Rng;
33+
}
34+
35+
impl RngExt for RNG {
36+
/// Enable RNG_CLK and the RNG peripheral.
37+
/// Note that clocks must already be configured such that RNG_CLK is not less than 1/16 HCLK,
38+
/// otherwise all reads of the RNG would return a ClockError (CECS error).
39+
/// This function will panic if pll48clk < 1/16 hclk.
40+
fn constrain(self, clocks: Clocks) -> Rng {
41+
let rcc = unsafe { &*stm32::RCC::ptr() };
42+
43+
cortex_m::interrupt::free(|_| {
44+
// enable RNG_CLK (peripheral clock)
45+
rcc.ahb2enr.modify(|_, w| w.rngen().enabled());
46+
// give RNG_CLK time to start
47+
let _ = rcc.ahb2enr.read().rngen().is_enabled();
48+
49+
// reset the RNG
50+
rcc.ahb2rstr.modify(|_, w| w.rngrst().set_bit());
51+
rcc.ahb2rstr.modify(|_, w| w.rngrst().clear_bit());
52+
53+
// verify the clock configuration is valid
54+
let hclk = clocks.hclk();
55+
let rng_clk = clocks.pll48clk().unwrap_or(0u32.hz());
56+
assert!(rng_clk.0 >= (hclk.0 / 16));
57+
58+
// enable the RNG peripheral
59+
self.cr.modify(|_, w| w.rngen().set_bit());
60+
});
61+
62+
Rng { rb: self }
63+
}
64+
}
65+
66+
pub struct Rng {
67+
rb: RNG,
68+
}
69+
70+
impl Rng {
71+
/// Returns 32 bits of random data from RNDATA, or error.
72+
/// May fail if, for example RNG_CLK is misconfigured.
73+
fn next_random_word(&mut self) -> Result<u32, ErrorKind> {
74+
loop {
75+
let status = self.rb.sr.read();
76+
if status.cecs().bit() {
77+
return Err(ErrorKind::ClockError);
78+
}
79+
if status.secs().bit() {
80+
return Err(ErrorKind::SeedError);
81+
}
82+
if status.drdy().bit() {
83+
return Ok(self.rb.dr.read().rndata().bits());
84+
}
85+
}
86+
}
87+
88+
pub fn release(self) -> RNG {
89+
self.rb
90+
}
91+
}
92+
93+
impl rng::Read for Rng {
94+
type Error = rand_core::Error;
95+
96+
fn read(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> {
97+
self.try_fill_bytes(buffer)
98+
}
99+
}
100+
101+
impl RngCore for Rng {
102+
fn next_u32(&mut self) -> u32 {
103+
self.next_random_word().unwrap()
104+
}
105+
106+
fn next_u64(&mut self) -> u64 {
107+
let w1 = self.next_u32();
108+
let w2 = self.next_u32();
109+
(w1 as u64).shl(32) | (w2 as u64)
110+
}
111+
112+
fn fill_bytes(&mut self, dest: &mut [u8]) {
113+
self.try_fill_bytes(dest).unwrap()
114+
}
115+
116+
/// Fills buffer with random values, or returns an error
117+
fn try_fill_bytes(&mut self, buffer: &mut [u8]) -> Result<(), rand_core::Error> {
118+
const BATCH_SIZE: usize = 4 / mem::size_of::<u8>();
119+
let mut i = 0_usize;
120+
while i < buffer.len() {
121+
let random_word = self.next_random_word()?;
122+
let bytes = random_word.to_ne_bytes();
123+
let n = cmp::min(BATCH_SIZE, buffer.len() - i);
124+
buffer[i..i + n].copy_from_slice(&bytes[..n]);
125+
i += n;
126+
}
127+
Ok(())
128+
}
129+
}

0 commit comments

Comments
 (0)