//! Hardware initialization and control wrappers
use stm32f1xx_hal as hal;
use hal::{
prelude::*,
stm32::{self, Peripherals},
timer::{Timer, Tim4NoRemap},
i2c::{BlockingI2c, DutyCycle, Mode},
qei::{Qei, QeiOptions},
};
// re-export some of the types from the HAL we're not wrapping
pub use hal::timer::{Event, CounterHz};
pub use stm32::{TIM2, TIM3};
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306, mode::BufferedGraphicsMode};
// Imports for specifying the types of pins
// The Pin type takes 4 parameters; config, low/high register, bank name, and number
// E.g. Pin, CRL, 'A', 0> for PA0 in input mode with no pull resistor.
use hal::gpio::{Pin, Input, Output, Floating, PushPull, CRL, CRH, Alternate, OpenDrain};
//
// The Board peripheral collection
//
/// The managed peripherals for this Blue Pill project board
pub struct Board {
pub encoder: Encoder,
pub display: Display,
pub led: UserLed,
pub poll_timer: CounterHz,
pub blink_timer: CounterHz,
}
impl Board {
/// Set up clocks and pin mappings for peripherals on the board
///
/// This returns a plain struct containing all the managed
/// peripherals so that it can be destructured.
pub fn init(periphs: Peripherals) -> Self {
// Set up board clocks
let mut flash = periphs.FLASH.constrain();
let rcc = periphs.RCC.constrain();
let clocks = rcc
.cfgr
.use_hse(8.MHz())
.sysclk(72.MHz())
.hclk(72.MHz())
.pclk1(36.MHz())
.pclk2(72.MHz())
.adcclk(12.MHz())
.freeze(&mut flash.acr);
// LED is on pin C13, configure it for output
let mut gpioc = periphs.GPIOC.split();
let led_pin = gpioc.pc13;
let led = UserLed::new(led_pin, &mut gpioc.crh);
let mut gpiob = periphs.GPIOB.split();
// Rotary encoder uses TIM4 on B6 and B7, and B8 as the encoder button.
let enc_clk = gpiob.pb6;
let enc_dt = gpiob.pb7;
let enc_button = gpiob.pb8;
let mut afio = periphs.AFIO.constrain();
let encoder = Encoder::new(
periphs.TIM4,
enc_clk,
enc_dt,
enc_button,
&mut afio.mapr,
&clocks,
);
// Use TIM2 for the control polling task
let poll_timer = Timer::new(periphs.TIM2, &clocks).counter_hz();
// Use TIM3 for the LED blinker task
let blink_timer = Timer::new(periphs.TIM3, &clocks).counter_hz();
// I2C for display is on gpiob B10 and B11
let scl = gpiob.pb10.into_alternate_open_drain(&mut gpiob.crh);
let sda = gpiob.pb11.into_alternate_open_drain(&mut gpiob.crh);
// Configure the I2C peripheral itself
let i2c = BlockingI2c::i2c2(
periphs.I2C2,
(scl, sda),
Mode::Fast {
frequency: 400_000.Hz(),
duty_cycle: DutyCycle::Ratio2to1,
},
clocks,
1000,
10,
1000,
1000,
);
let display = Display::new(i2c);
Self {
encoder,
display,
led,
poll_timer,
blink_timer,
}
}
}
/// The user-controllable green LED
pub struct UserLed(Pin