commit 4d043688d3c7f6be1c8178516bdd085d07f32b07 Author: Levi Pearson Date: Tue Mar 10 21:21:14 2020 -0600 Commit of some working but not optimal code diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..c77c017 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,25 @@ +[target.thumbv7m-none-eabi] +# uncomment ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system +# runner = "arm-none-eabi-gdb -q -x openocd.gdb" +runner = "gdb-multiarch" +# runner = "gdb -q -x openocd.gdb" + +rustflags = [ + # LLD (shipped with the Rust toolchain) is used as the default linker + "-C", "link-arg=-Tlink.x", + + # if you run into problems with LLD switch to the GNU linker by commenting out + # this line + # "-C", "linker=arm-none-eabi-ld", + + # if you need to link to pre-compiled C libraries provided by a C toolchain + # use GCC as the linker by commenting out both lines above and then + # uncommenting the three lines below + # "-C", "linker=arm-none-eabi-gcc", + # "-C", "link-arg=-Wl,-Tlink.x", + # "-C", "link-arg=-nostartfiles", +] + +[build] +target = "thumbv7m-none-eabi" # Cortex-M3 diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..7c72d4f --- /dev/null +++ b/.gdbinit @@ -0,0 +1,6 @@ +target remote :3333 + +monitor arm semihosting enable + +load +step diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..90a8c33 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "blue_pill_more" +version = "0.1.0" +authors = ["Levi Pearson "] +description = "Crate for STM32F103 Blue Pill boards with peripherals" +categories = ["embedded", "no-std"] +edition = "2018" + +[dependencies] +cortex-m = "0.6.2" +cortex-m-rt = "0.6.12" +panic-halt = "0.2.0" +embedded-hal = "0.2.3" +nb = "0.1.2" +ssd1306 = "0.3.0-alpha.4" +switch-hal = "0.3.2" +rotary-encoder-hal = "0.2.1" + +[dependencies.arrayvec] +version = "0.5.1" +default-features = false + +[dependencies.embedded-graphics] +version = "0.6.0-alpha.3" + +[dependencies.stm32f1] +version = "0.10.0" +features = ["stm32f103", "rt"] + +[dependencies.stm32f1xx-hal] +version = "0.5.3" +features = ["rt", "stm32f103", "medium"] + +[[bin]] +name = "blue_pill_more" +test = false +bench = false + +[profile.release] +lto = true +codegen-units = 1 +debug = true diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..6a7b3b2 --- /dev/null +++ b/memory.x @@ -0,0 +1,32 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x08000000, LENGTH = 64K + RAM : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Example of putting non-initialized variables into custom RAM locations. */ +/* This assumes you have defined a region RAM2 above, and in the Rust + sources added the attribute `#[link_section = ".ram2bss"]` to the data + you want to place there. */ +/* Note that the section will not be zero-initialized by the runtime! */ +/* SECTIONS { + .ram2bss (NOLOAD) : ALIGN(4) { + *(.ram2bss); + . = ALIGN(4); + } > RAM2 + } INSERT AFTER .bss; +*/ diff --git a/openocd.cfg b/openocd.cfg new file mode 100644 index 0000000..88b2e9c --- /dev/null +++ b/openocd.cfg @@ -0,0 +1,12 @@ +# Sample OpenOCD configuration for the blue pill board + +# Depending on the hardware revision you got you'll have to pick ONE of these +# interfaces. At any time only one interface should be commented out. + +# Revision C (newer revision) +# source [find interface/stlink-v2-1.cfg] + +# Revision A and B (older revisions) +source [find interface/stlink-v2.cfg] + +source [find target/stm32f1x.cfg] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..aa71156 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,239 @@ +#![no_std] +#![no_main] + +use core::mem::MaybeUninit; + +use panic_halt as _; + +use cortex_m_rt::entry; + +use cortex_m::peripheral::NVIC; + +use stm32f1xx_hal::{ + afio, + device::EXTI, + delay::Delay, + i2c::{BlockingI2c, DutyCycle, Mode}, + gpio::{ + Input, + Floating, + ExtiPin, + Edge, + gpioa::{PA8, PA9}, + }, + pac::{ + CorePeripherals, + Peripherals, + Interrupt, + interrupt, + }, + prelude::*, +}; + +use embedded_hal::digital::v2::InputPin; + +use switch_hal::{IntoSwitch, InputSwitch, OutputSwitch, ToggleableOutputSwitch}; + +use ssd1306::{ + prelude::*, + Builder, +}; + +use embedded_graphics::{ + fonts::{Font6x8, Text}, + pixelcolor::BinaryColor, + prelude::*, + primitives::Circle, + style::{PrimitiveStyle, TextStyle}, +}; + +use arrayvec::ArrayString; +use core::fmt::Write; + +static mut CLK_PIN: MaybeUninit>> = MaybeUninit::uninit(); +static mut DT_PIN: MaybeUninit>> = MaybeUninit::uninit(); +static mut COUNT: i32 = 0; + +#[entry] +fn main() -> ! { + // Get access to the core peripherals from the cortex-m crate + let mut core_periph = CorePeripherals::take().unwrap(); + + // Get access to the device specific peripherals from the peripheral access crate + let dev_periph = Peripherals::take().unwrap(); + + // RCC is the primary clock control peripheral, but FLASH is also involved in setting + // up clock speed because the wait states must be increased at higher clock rates + let mut rcc = dev_periph.RCC.constrain(); + let mut flash = dev_periph.FLASH.constrain(); + let mut afio = dev_periph.AFIO.constrain(&mut rcc.apb2); + + // Peripherals often need to know the clock settings to be properly configured, so + // we configure the clock and "freeze" the configuration so we can pass it + let clocks = rcc + .cfgr + .use_hse(8.mhz()) // Use High Speed External 8Mhz crystal oscillator + .sysclk(72.mhz()) // Use the PLL to multiply SYSCLK to 72MHz + .hclk(72.mhz()) // Leave AHB prescaler at /1 + .pclk1(36.mhz()) // Use the APB1 prescaler to divide the clock to 36MHz (max supported) + .pclk2(72.mhz()) // Leave the APB2 prescaler at /1 + .adcclk(12.mhz()) // ADC prescaler of /6 (max speed of 14MHz, but /4 gives 18MHz) + .freeze(&mut flash.acr); + + // In order to have precisely-timed delays, we can use the core SysTick clock as a + // delay provider + let mut delay = Delay::new(core_periph.SYST, clocks); + + // Acquire the necessary gpio peripherals + let mut gpioa = dev_periph.GPIOA.split(&mut rcc.apb2); + let mut gpiob = dev_periph.GPIOB.split(&mut rcc.apb2); + let mut gpioc = dev_periph.GPIOC.split(&mut rcc.apb2); + + // Configure the rotary encoder pins A8, A9 and switch pin A10 + let clk = gpioa.pa8 + .into_floating_input(&mut gpioa.crh); + let dt = gpioa.pa9 + .into_floating_input(&mut gpioa.crh); + let sw = gpioa.pa10 + .into_floating_input(&mut gpioa.crh) + .into_active_low_switch(); + init_encoder_pins(clk, dt, &mut afio, &dev_periph.EXTI); + + // Configure pin C13 to drive the "PC13" LED as an active-low switch + let mut led = gpioc.pc13 + .into_push_pull_output(&mut gpioc.crh) + .into_active_low_switch(); + + // Configure the I2C pins we are using to the correct mode + 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( + dev_periph.I2C2, + (scl, sda), + Mode::Fast { + frequency: 400_000.hz(), + duty_cycle: DutyCycle::Ratio2to1, + }, + clocks, + &mut rcc.apb1, + 1000, + 10, + 1000, + 1000, + ); + + // Turn the LED on via the OutputPin trait + led.on().unwrap(); + + // Initialize the display + let mut display: GraphicsMode<_> = Builder::new() + .with_i2c_addr(0x3c) + .connect_i2c(i2c) + .into(); + display.init().unwrap(); + + // Every set of commands to the display is buffered until we flush it + display.flush().unwrap(); + + // We will draw a fixed message on the screen and also a moving circle + + const C_RADIUS: i32 = 8; + const DISPLAY_W: i32 = 128; + const DISPLAY_H: i32 = 64; + + let mut cx = 20; + let mut cy = 20; + let t = Text::new("Hello Rust!", Point::new(20, 16)) + .into_styled(TextStyle::new(Font6x8, BinaryColor::On)); + + // Very brief delay before the loop, just to show how + delay.delay_us(10_u8); + + // Enable interrupts + unsafe { + core_periph.NVIC.set_priority(Interrupt::EXTI9_5, 1); + NVIC::unmask(Interrupt::EXTI9_5); + } + NVIC::unpend(Interrupt::EXTI9_5); + + let mut button_last = false; + // Microcontroller programs never exit main, so we must loop! + loop { + // Check our inputs + let counter = unsafe { &mut COUNT }; + let button = sw.is_active().unwrap(); + if button_last && !button { + led.toggle().unwrap(); + } + button_last = button; + + let c = Circle::new(Point::new(cx, cy), C_RADIUS as u32) + .into_styled(PrimitiveStyle::with_fill(BinaryColor::On)); + + let mut textbuf = ArrayString::<[u8; 15]>::new(); + write!(&mut textbuf, "count: {}", *counter).unwrap(); + let count = Text::new(&textbuf, Point::new(20, 36)) + .into_styled(TextStyle::new(Font6x8, BinaryColor::On)); + display.clear(); + c.draw(&mut display); + t.draw(&mut display); + count.draw(&mut display); + display.flush().unwrap(); + + // Move down+right, come back from the other side if we go off-screen + cx = *counter; + //cx += 1; + //if cx > (DISPLAY_W + C_RADIUS) { cx = -C_RADIUS }; + cy += 1; + if cy > (DISPLAY_H + C_RADIUS) { cy = -C_RADIUS }; + + } +} + +fn init_encoder_pins( + clk: PA8>, + dt: PA9>, + afio: &mut afio::Parts, + exti: &EXTI, +) { + let clk_pin = unsafe { &mut *CLK_PIN.as_mut_ptr() }; + let dt_pin = unsafe { &mut *DT_PIN.as_mut_ptr() }; + + *clk_pin = clk; + *dt_pin = dt; + + clk_pin.make_interrupt_source(afio); + clk_pin.trigger_on_edge(exti, Edge::RISING_FALLING); + clk_pin.enable_interrupt(exti); +} + +#[interrupt] +fn EXTI9_5() { + static mut PREV_A: bool = false; + static mut PREV_C: bool = false; + + let clk_pin = unsafe { &mut *CLK_PIN.as_mut_ptr() }; + let dt_pin = unsafe { &mut *DT_PIN.as_mut_ptr() }; + let counter = unsafe { &mut COUNT }; + + if clk_pin.check_interrupt() { + let a = clk_pin.is_high().unwrap(); + let b = dt_pin.is_high().unwrap(); + // When A is changing, B should be stable + if a != *PREV_A { + *PREV_A = a; + // Capture B as C whenever it changes from C's last value + if b != *PREV_C { + *PREV_C = b; + if a == b { // Up + *counter = counter.wrapping_add(1); + } else { // Down + *counter = counter.wrapping_add(-1); + } + } + } + clk_pin.clear_interrupt_pending_bit(); + } +}