├── .cargo └── config ├── .github └── workflows │ └── bootloader.yml ├── .run └── GDB.run.xml ├── Cargo.toml ├── LICENSE-MIT ├── README.md ├── build.rs ├── memory.x ├── openocd.cfg ├── openocd.gdb └── src ├── button.rs ├── flash.rs ├── interrupt.rs ├── led.rs ├── main.rs ├── rcc.rs ├── sdcard.rs ├── tim.rs ├── upgrade.rs └── usb_ttl.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7m-none-eabi" 3 | 4 | [target.thumbv7m-none-eabi] 5 | runner = "arm-none-eabi-gdb -q -x openocd.gdb" 6 | rustflags = [ 7 | "-C", "linker=arm-none-eabi-gcc", 8 | "-C", "link-arg=-Wl,-Tlink.x", 9 | "-C", "link-arg=-nostartfiles", 10 | ] -------------------------------------------------------------------------------- /.github/workflows/bootloader.yml: -------------------------------------------------------------------------------- 1 | name: bootloader 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | name: Build Bootloader 15 | runs-on: macos-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: install arm-none-eabi-gcc toolchain 20 | run: brew tap ArmMbed/homebrew-formulae && brew install arm-none-eabi-gcc 21 | 22 | - name: add thumbv7m-none-eabi target 23 | uses: actions-rs/toolchain@v1 24 | with: 25 | toolchain: nightly 26 | target: thumbv7m-none-eabi 27 | override: true 28 | 29 | - name: build bootloader 30 | uses: actions-rs/cargo@v1 31 | with: 32 | command: build 33 | args: --target thumbv7m-none-eabi 34 | 35 | - name: make bootloader dir 36 | run: mkdir bootloader 37 | 38 | - name: rename bootloader to bootloader.elf 39 | run: mv ./target/thumbv7m-none-eabi/debug/bootloader ./bootloader/bootloader.elf 40 | 41 | - name: turn elf to bin 42 | run: arm-none-eabi-objcopy -O binary ./bootloader/bootloader.elf ./bootloader/bootloader.bin 43 | 44 | - name: upload a build artifact 45 | uses: actions/upload-artifact@master 46 | with: 47 | name: bootloader 48 | path: ./bootloader 49 | -------------------------------------------------------------------------------- /.run/GDB.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bootloader" 3 | version = "0.1.2" 4 | authors = ["spxg "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | cortex-m = "0.6" 11 | cortex-m-rt = "0.6.12" 12 | fat32 = "0.2" 13 | 14 | [dependencies.stm32f4xx-hal] 15 | version = "0.8" 16 | features = ["stm32f407", "rt"] 17 | 18 | [dependencies.sdio_sdhc] 19 | version = "0.2" 20 | features = ["filesystem"] -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2020 Spxg 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This a in-application programming (IAP) bootloader 2 | 3 | ## Supported feature 4 | 5 | * Upgrade firmware from sd card 6 | * Use usb-ttl to show log 7 | 8 | ## Step 9 | 10 | ### Ready 11 | 12 | * A fat32 card (only sdhc card) 13 | * Rename your firmware to firmware.bin (the format is bin not elf) 14 | * Copy your firmware to the card's root 15 | * Insert card 16 | 17 | ### Power Up Board 18 | 19 | * Check the bootloader works well (green LED light) 20 | * Auto upgrade if card has `install` file in the root 21 | * OR press KEY0 to upgrade (green LED dark and red LED light) 22 | * Wait 23 | 24 | ## Attentions 25 | 26 | ### Partition information 27 | 28 | * Bootloader: 0x08000000 to 0x0801FFFF (128KB) 29 | * Firmware: 0x08020000 to 0x080FFFFF (896KB; Sector from 5 to 11) 30 | 31 | ### USB-TTL (CH340) 32 | 33 | * Baudrare: 115200 34 | * Data Bits: 8 35 | * Parity: none 36 | * Stop Bits: 1 37 | 38 | ### How to turn elf to bin 39 | 40 | ```arm-none-eabi-objcopy -O binary target/thumbv7m-none-eabi/debug/xxxx firmware.bin``` 41 | 42 | ### Console log 43 | 44 | ``` 45 | This is a IAP bootloader 46 | start to check for upgrade from sd card 47 | 48 | found firmware 49 | if you do nothing, it will boot os in 5 seconds 50 | if you want to upgrade, press the KEY0 51 | 52 | upgrading 53 | start to erase flash, it will take minutes 54 | erase flash successfully 55 | start to upgrade firmware 56 | upgrade successfully 57 | 58 | boot os 59 | ``` 60 | 61 | ## Download 62 | 63 | * Download artifacts from [Actions](https://github.com/play-stm32/bootloader/actions) 64 | 65 | ## Relevant Project 66 | 67 | * [sdio_sdhc](https://github.com/play-stm32/sdio_sdhc) 68 | * [fat32](https://github.com/play-stm32/fat32) 69 | 70 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rerun-if-changed=memory.x"); 3 | } -------------------------------------------------------------------------------- /memory.x: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | FLASH (rx) : ORIGIN = 0x08000000‬, LENGTH = 128K 4 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K 5 | } -------------------------------------------------------------------------------- /openocd.cfg: -------------------------------------------------------------------------------- 1 | source [find interface/cmsis-dap.cfg] 2 | source [find target/stm32f4x.cfg] 3 | -------------------------------------------------------------------------------- /openocd.gdb: -------------------------------------------------------------------------------- 1 | target extended-remote :3333 2 | 3 | # stop at the user entry point 4 | load 5 | 6 | # start the process but immediately halt the processor 7 | stepi 8 | -------------------------------------------------------------------------------- /src/button.rs: -------------------------------------------------------------------------------- 1 | use stm32f4xx_hal::stm32; 2 | 3 | /// KEY0 init (PE4) 4 | pub fn init(rcc: &mut stm32::RCC, gpioe: &mut stm32::GPIOE) { 5 | rcc.ahb1enr.modify(|_r, w| w.gpioeen().set_bit()); 6 | rcc.apb2enr.modify(|_r, w| w.syscfgen().set_bit()); 7 | 8 | gpioe.moder.modify(|_r, w| w.moder4().input()); 9 | gpioe.pupdr.modify(|_r, w| w.pupdr4().pull_up()); 10 | } 11 | 12 | /// enable EXTI interrupt 13 | pub fn enable_interrupt() { 14 | let syscfg_ptr = unsafe { &*stm32::SYSCFG::ptr() }; 15 | let exti_ptr = unsafe { &*stm32::EXTI::ptr() }; 16 | 17 | syscfg_ptr.exticr2.modify(|_r, w| unsafe { w.exti4().bits(0b0100) }); 18 | exti_ptr.ftsr.modify(|_r, w| w.tr4().set_bit()); 19 | exti_ptr.imr.write(|w| w.mr4().set_bit()) 20 | } 21 | 22 | /// disable EXTI interrupt 23 | pub fn disable_interrupt() { 24 | let syscfg_ptr = unsafe { &*stm32::SYSCFG::ptr() }; 25 | let exti_ptr = unsafe { &*stm32::EXTI::ptr() }; 26 | 27 | syscfg_ptr.exticr2.modify(|_r, w| unsafe { w.exti4().bits(0) }); 28 | exti_ptr.ftsr.modify(|_r, w| w.tr4().clear_bit()); 29 | exti_ptr.imr.write(|w| w.mr4().clear_bit()); 30 | clean_interrupt_flag(); 31 | } 32 | 33 | /// clean EXTI interrupt flag 34 | fn clean_interrupt_flag() { 35 | let ptr = unsafe { &*stm32::EXTI::ptr() }; 36 | 37 | ptr.pr.write(|w| w.pr4().set_bit()); 38 | } -------------------------------------------------------------------------------- /src/flash.rs: -------------------------------------------------------------------------------- 1 | use stm32f4xx_hal::stm32; 2 | 3 | /// unlock fpec, make flash can be written 4 | fn unlock_fpec() { 5 | let ptr = unsafe { &*stm32::FLASH::ptr() }; 6 | 7 | ptr.keyr.write(|w| w.key().bits(0x45670123)); 8 | ptr.keyr.write(|w| w.key().bits(0xCDEF89AB)); 9 | } 10 | 11 | /// lock flash, then flash can't be edited 12 | fn lock() { 13 | let ptr = unsafe { &*stm32::FLASH::ptr() }; 14 | 15 | wait_free(); 16 | ptr.cr.modify(|_r, w| w.lock().set_bit()); 17 | } 18 | 19 | /// erase firmware flash 20 | pub fn erase(start_sector: u8, end_sector: u8) { 21 | unlock_fpec(); 22 | let ptr = unsafe { &*stm32::FLASH::ptr() }; 23 | 24 | for sector in start_sector..=end_sector { 25 | wait_free(); 26 | ptr.cr.modify(|_r, w| w.ser().set_bit()); 27 | ptr.cr.modify(|_r, w| unsafe { w.snb().bits(sector) }); 28 | ptr.cr.modify(|_r, w| w.strt().set_bit()); 29 | } 30 | 31 | lock(); 32 | } 33 | 34 | /// write to firmware flash 35 | pub fn write(mut address: usize, buf: &[u8]) { 36 | unlock_fpec(); 37 | for i in 0..buf.len() { 38 | write_byte(address, buf[i]); 39 | address += 0x1; 40 | } 41 | lock(); 42 | } 43 | 44 | /// write to flash per byte 45 | fn write_byte(address: usize, data: u8) { 46 | let ptr = unsafe { &*stm32::FLASH::ptr() }; 47 | 48 | wait_free(); 49 | ptr.cr.write(|w| w.pg().set_bit()); 50 | unsafe { *(address as *mut u8) = data; } 51 | } 52 | 53 | /// wait flash free 54 | fn wait_free() { 55 | let ptr = unsafe { &*stm32::FLASH::ptr() }; 56 | while !ptr.sr.read().bsy().bit_is_clear() {} 57 | } -------------------------------------------------------------------------------- /src/interrupt.rs: -------------------------------------------------------------------------------- 1 | use cortex_m::peripheral::NVIC; 2 | use stm32f4xx_hal::interrupt; 3 | use stm32f4xx_hal::stm32; 4 | use crate::{tim, button, UPGRADE_FLAG}; 5 | 6 | /// NVIC enable 7 | pub fn nvic_enable() { 8 | unsafe { 9 | NVIC::unmask(stm32::interrupt::TIM2); 10 | NVIC::unmask(stm32::interrupt::EXTI4); 11 | } 12 | } 13 | 14 | /// NVIC disable 15 | pub fn nvic_disable() { 16 | NVIC::mask(stm32::interrupt::TIM2); 17 | NVIC::mask(stm32::interrupt::EXTI4); 18 | } 19 | 20 | /// handle TIM2 interrupt 21 | #[interrupt] 22 | fn TIM2() { 23 | tim::clean_interrupt_flag(); 24 | tim::disable_count(); 25 | } 26 | 27 | /// handle EXTI4 interrupt 28 | #[interrupt] 29 | fn EXTI4() { 30 | unsafe { UPGRADE_FLAG = true; } 31 | 32 | tim::clean_interrupt_flag(); 33 | tim::disable_count(); 34 | button::disable_interrupt(); 35 | } -------------------------------------------------------------------------------- /src/led.rs: -------------------------------------------------------------------------------- 1 | use stm32f4xx_hal::stm32; 2 | 3 | /// led init 4 | pub fn init(rcc: &mut stm32::RCC, gpiof: &mut stm32::GPIOF) { 5 | // gpiof enable 6 | rcc.ahb1enr.modify(|_r, w| w.gpiofen().set_bit()); 7 | 8 | // set output mode 9 | gpiof.moder.modify(|_r, w| w.moder9().output().moder10().output()); 10 | 11 | // set push pull mode 12 | gpiof.otyper.modify(|_r, w| w.ot9().push_pull().ot10().push_pull()); 13 | 14 | // set speed 15 | gpiof.ospeedr.modify(|_r, w| w.ospeedr9().high_speed().ospeedr10().high_speed()); 16 | 17 | green_dark(); 18 | red_dark(); 19 | } 20 | 21 | /// red led light 22 | pub fn red_light() { 23 | let ptr = unsafe { &*stm32::GPIOF::ptr() }; 24 | ptr.bsrr.write(|w| w.br9().reset()); 25 | } 26 | 27 | /// red led dark 28 | pub fn red_dark() { 29 | let ptr = unsafe { &*stm32::GPIOF::ptr() }; 30 | ptr.bsrr.write(|w| w.bs9().set()); 31 | } 32 | 33 | /// green led light 34 | pub fn green_light() { 35 | let ptr = unsafe { &*stm32::GPIOF::ptr() }; 36 | ptr.bsrr.write(|w| w.br10().reset()); 37 | } 38 | 39 | /// green led dark 40 | pub fn green_dark() { 41 | let ptr = unsafe { &*stm32::GPIOF::ptr() }; 42 | ptr.bsrr.write(|w| w.bs10().set()); 43 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(panic_info_message)] 2 | #![feature(llvm_asm)] 3 | #![no_std] 4 | #![no_main] 5 | 6 | /// this is a IAP bootloader only for my stm32 7 | /// it should update firmware from tf card 8 | /// you can edit these codes to make it work on your device 9 | 10 | mod rcc; 11 | mod sdcard; 12 | mod usb_ttl; 13 | mod flash; 14 | mod upgrade; 15 | mod tim; 16 | mod interrupt; 17 | mod button; 18 | mod led; 19 | 20 | use stm32f4xx_hal::stm32; 21 | use core::panic::PanicInfo; 22 | use core::fmt::Write; 23 | use crate::usb_ttl::USART1; 24 | 25 | static mut UPGRADE_FLAG: bool = false; 26 | 27 | const OS_START_ADDRESS: usize = 0x08020000; 28 | const MSP_ADDRESS: *mut usize = OS_START_ADDRESS as *mut usize; 29 | const VECTOR_ADDRESS: *mut usize = (OS_START_ADDRESS + 0x4) as *mut usize; 30 | 31 | #[no_mangle] 32 | fn main() { 33 | let mut dp = stm32::Peripherals::take().unwrap(); 34 | rcc::clock_init(&mut dp.RCC, &mut dp.FLASH); 35 | sdcard::init(&mut dp.RCC, &mut dp.GPIOC, &mut dp.GPIOD); 36 | usb_ttl::init(&mut dp.RCC, &mut dp.GPIOA, &mut dp.USART1); 37 | led::init(&mut dp.RCC, &mut dp.GPIOF); 38 | button::init(&mut dp.RCC, &mut dp.GPIOE); 39 | tim::init(&mut dp.RCC, &mut dp.TIM2); 40 | interrupt::nvic_enable(); 41 | led::green_light(); 42 | 43 | writeln!(USART1, "This is a IAP bootloader").unwrap(); 44 | writeln!(USART1, "start to check for upgrade from sd card").unwrap(); 45 | writeln!(USART1, "").unwrap(); 46 | 47 | upgrade::check_and_upgrade(); 48 | 49 | interrupt::nvic_disable(); 50 | button::disable_interrupt(); 51 | tim::clean_interrupt_flag(); 52 | tim::disable_count(); 53 | led::green_dark(); 54 | led::red_dark(); 55 | 56 | boot_os(MSP_ADDRESS, VECTOR_ADDRESS); 57 | } 58 | 59 | /// boot the os 60 | fn boot_os(msp: *mut usize, pc: *mut usize) { 61 | writeln!(USART1, "boot os").unwrap(); 62 | unsafe { 63 | llvm_asm!( 64 | "msr msp, $0 65 | mov pc, $1" 66 | ::"{r0}"(*msp), "{r1}"(*pc) 67 | ::) 68 | } 69 | } 70 | 71 | #[panic_handler] 72 | pub unsafe extern "C" fn panic_fmt(info: &PanicInfo) -> ! { 73 | writeln!(USART1, "{}, {}", info.message().unwrap(), info.location().unwrap()).unwrap(); 74 | loop {} 75 | } -------------------------------------------------------------------------------- /src/rcc.rs: -------------------------------------------------------------------------------- 1 | use stm32f4xx_hal::stm32; 2 | 3 | /// init rcc clock 4 | pub fn clock_init(rcc: &mut stm32::RCC, flash: &mut stm32::FLASH) { 5 | // enable hse 6 | // enable hsi to make flash can be written 7 | rcc.cr.modify(|_r, w| w.hseon().set_bit().hsion().set_bit()); 8 | 9 | // wait until hse is stable 10 | // wait until hsi is stable 11 | while rcc.cr.read().hserdy().bit_is_clear() {} 12 | while rcc.cr.read().hsirdy().bit_is_clear() {} 13 | 14 | // AHB = SYSCLK / 1 = 168MHz(MAX) 15 | // APB1 = SYSCLK / 4 = 42MHz(MAX) 16 | // APB2 = SYSCLK / 2 = 84MHz(MAX) 17 | rcc.cfgr.modify(|_r, w| w.hpre().div1().ppre1().div4().ppre2().div2()); 18 | 19 | // VOC(input) = PLL(input) / PLLM(8) = 1MHz 20 | // VOC(output) = VOC(input) * PLLN(336) = 336MHz 21 | // PLL(output) = VOC(output) / PLLP(2) = 168MHz 22 | // PLL2(output) = VOC(output) / PLLQ(7) = 48MHz 23 | rcc.pllcfgr.modify(|_r, w| unsafe { 24 | w.pllsrc().set_bit().pllm().bits(8).plln().bits(336).pllp().div2().pllq().bits(7) 25 | }); 26 | 27 | // HCLK = 168MHz, set latency ws5, enable prefetch 28 | flash.acr.modify(|_r, w| w.latency().ws5().prften().set_bit()); 29 | 30 | // pll enable 31 | rcc.cr.modify(|_r, w| w.pllon().set_bit().plli2son().set_bit()); 32 | 33 | // wait until pll is stable 34 | while rcc.cr.read().pllrdy().bit_is_clear() {} 35 | while rcc.cr.read().plli2son().bit_is_clear() {} 36 | 37 | // switch sysclk to pll 38 | rcc.cfgr.modify(|_r, w| w.sw().pll()); 39 | 40 | // wait until sysclk is stable, 0b02 = 2 41 | while rcc.cfgr.read().sws().bits() != 2 {} 42 | } -------------------------------------------------------------------------------- /src/sdcard.rs: -------------------------------------------------------------------------------- 1 | use stm32f4xx_hal::stm32; 2 | 3 | pub fn init( 4 | rcc: &mut stm32::RCC, 5 | gpioc: &mut stm32::GPIOC, 6 | gpiod: &mut stm32::GPIOD, 7 | ) { 8 | // gpioc gpiod enable 9 | rcc.ahb1enr.modify(|_r, w| w.gpiocen().set_bit().gpioden().set_bit()); 10 | 11 | gpioc.afrh.modify(|_r, w| 12 | w.afrh8().af12() 13 | .afrh9().af12() 14 | .afrh10().af12() 15 | .afrh11().af12() 16 | .afrh12().af12()); 17 | gpiod.afrl.modify(|_r, w| w.afrl2().af12()); 18 | 19 | gpioc.moder.modify(|_r, w| 20 | w.moder8().alternate() 21 | .moder9().alternate() 22 | .moder10().alternate() 23 | .moder11().alternate() 24 | .moder12().alternate()); 25 | gpiod.moder.modify(|_r, w| w.moder2().alternate()); 26 | 27 | gpioc.ospeedr.modify(|_r, w| 28 | w.ospeedr8().high_speed() 29 | .ospeedr9().high_speed() 30 | .ospeedr10().high_speed() 31 | .ospeedr11().high_speed() 32 | .ospeedr12().high_speed()); 33 | gpiod.ospeedr.modify(|_r, w| w.ospeedr2().high_speed()); 34 | 35 | gpioc.otyper.modify(|_r, w| 36 | w.ot8().push_pull() 37 | .ot9().push_pull() 38 | .ot10().push_pull() 39 | .ot11().push_pull() 40 | .ot12().push_pull()); 41 | gpiod.otyper.modify(|_r, w| w.ot2().push_pull()); 42 | 43 | gpioc.pupdr.modify(|_r, w| 44 | w.pupdr8().pull_up() 45 | .pupdr9().pull_up() 46 | .pupdr10().pull_up() 47 | .pupdr11().pull_up() 48 | .pupdr12().pull_up()); 49 | gpiod.pupdr.modify(|_r, w| w.pupdr2().pull_up()); 50 | } -------------------------------------------------------------------------------- /src/tim.rs: -------------------------------------------------------------------------------- 1 | use stm32f4xx_hal::stm32; 2 | 3 | /// init tim2 4 | pub fn init(rcc: &mut stm32::RCC, tim: &mut stm32::TIM2) { 5 | rcc.apb1enr.modify(|_r, w| w.tim2en().set_bit()); 6 | 7 | // 84MHz / 8400 = 10KHz 8 | // 1 / 10KHz * 50000 = 5 9 | tim.psc.write(|w| w.psc().bits(8400)); 10 | tim.arr.write(|w| w.arr().bits(50000)); 11 | 12 | // load arr, update request source 13 | tim.cr1.modify(|_r, w| w.arpe().set_bit().urs().set_bit()); 14 | 15 | // update interrupt enable 16 | tim.dier.modify(|_r, w| w.uie().set_bit()); 17 | 18 | // update generation 19 | tim.egr.write(|w| w.ug().set_bit()); 20 | } 21 | 22 | /// enable tim count 23 | pub fn enable_count() { 24 | let ptr = unsafe { &*stm32::TIM2::ptr() }; 25 | 26 | // enable 27 | ptr.cr1.modify(|_r, w| w.cen().set_bit()); 28 | } 29 | 30 | /// clean flag 31 | pub fn clean_interrupt_flag() { 32 | let ptr = unsafe { &*stm32::TIM2::ptr() }; 33 | 34 | // clean update interrupt flag 35 | ptr.sr.modify(|_r, w| w.uif().clear_bit()); 36 | } 37 | 38 | /// disable tim count 39 | pub fn disable_count() { 40 | let ptr = unsafe { &*stm32::TIM2::ptr() }; 41 | 42 | // disable 43 | ptr.cr1.modify(|_r, w| w.cen().clear_bit()); 44 | } 45 | 46 | pub fn is_disable() -> bool { 47 | let ptr = unsafe { &*stm32::TIM2::ptr() }; 48 | 49 | // read 50 | ptr.cr1.read().cen().bit_is_clear() 51 | } -------------------------------------------------------------------------------- /src/upgrade.rs: -------------------------------------------------------------------------------- 1 | use sdio_sdhc::sdcard::Card; 2 | use fat32::volume::Volume; 3 | use core::fmt::Write; 4 | use crate::usb_ttl::USART1; 5 | use crate::{flash, tim, button, OS_START_ADDRESS, UPGRADE_FLAG, led}; 6 | 7 | pub fn check_and_upgrade() { 8 | // Card from sdio_sdhc 9 | match Card::init() { 10 | Ok(card) => { 11 | // Volume from fat32 12 | let cont = Volume::new(card); 13 | // into root dir 14 | let mut root = cont.root_dir(); 15 | match root.open_file("firmware.bin") { 16 | Ok(file) => { 17 | let upgrade = || { 18 | led::green_dark(); 19 | led::red_light(); 20 | 21 | let mut addr = OS_START_ADDRESS; 22 | 23 | writeln!(USART1, "upgrading").unwrap(); 24 | writeln!(USART1, "start to erase flash, it will take minutes").unwrap(); 25 | flash::erase(5, 11); 26 | writeln!(USART1, "erase flash successfully").unwrap(); 27 | 28 | writeln!(USART1, "start to upgrade firmware").unwrap(); 29 | for (buf, len) in file.read_per_sector() { 30 | flash::write(addr, &buf[0..len]); 31 | addr += len; 32 | } 33 | 34 | writeln!(USART1, "upgrade successfully").unwrap(); 35 | writeln!(USART1, "").unwrap(); 36 | }; 37 | 38 | if let None = root.exist("install") { 39 | writeln!(USART1, "found firmware").unwrap(); 40 | writeln!(USART1, "if you do nothing, it will boot os in 5 seconds").unwrap(); 41 | writeln!(USART1, "if you want to upgrade, press the KEY0").unwrap(); 42 | writeln!(USART1, "").unwrap(); 43 | 44 | tim::enable_count(); 45 | button::enable_interrupt(); 46 | 47 | // delay 5 seconds 48 | while !tim::is_disable() {} 49 | if unsafe { UPGRADE_FLAG } { upgrade(); } 50 | } else { 51 | writeln!(USART1, "found install file, auto upgrade").unwrap(); 52 | upgrade(); 53 | writeln!(USART1, "delete install file").unwrap(); 54 | writeln!(USART1, "").unwrap(); 55 | root.delete_file("install").unwrap(); 56 | } 57 | } 58 | Err(_) => { 59 | writeln!(USART1, "No Found Firmware").unwrap(); 60 | } 61 | } 62 | } 63 | Err(_) => { 64 | writeln!(USART1, "No Found Card").unwrap(); 65 | } 66 | }; 67 | } -------------------------------------------------------------------------------- /src/usb_ttl.rs: -------------------------------------------------------------------------------- 1 | use stm32f4xx_hal::stm32; 2 | use core::fmt::{Write, Error}; 3 | use core::str; 4 | 5 | /// init usart1 to send message 6 | pub fn init( 7 | rcc: &mut stm32::RCC, 8 | gpioa: &mut stm32::GPIOA, 9 | usart1: &mut stm32::USART1, 10 | ) { 11 | rcc.apb2enr.modify(|_r, w| w.usart1en().set_bit()); 12 | rcc.ahb1enr.modify(|_r, w| w.gpioaen().set_bit()); 13 | 14 | // PA9(Tx) alternate push 15 | gpioa.afrh.write(|w| w.afrh9().af7()); 16 | gpioa.moder.modify(|_r, w| w.moder9().alternate()); 17 | gpioa.ospeedr.modify(|_r, w| w.ospeedr9().high_speed()); 18 | gpioa.pupdr.modify(|_r, w| w.pupdr9().pull_up()); 19 | gpioa.otyper.modify(|_r, w| w.ot9().push_pull()); 20 | 21 | // configurate usart baudrate 22 | // USARTDIV = FCLK (PCLK2 for USART1) / baudrate / 16 23 | // = 84M / 115200 / 16 = 45.57291 24 | // DIV_MANTISSA = USARTDIV (integer part) 25 | // = 45 26 | // DIV_FRACTION = USARTDIV (fraction part) * 16 27 | // = 0.57291 * 16 = 9.16656 28 | usart1.brr.write(|w| w.div_mantissa().bits(45).div_fraction().bits(9)); 29 | usart1.cr1.write(|w| w.ue().set_bit().te().set_bit()); 30 | } 31 | 32 | pub struct USART1; 33 | 34 | /// impl Write for USART1, can use write! and writeln! 35 | impl Write for USART1 { 36 | fn write_str(&mut self, s: &str) -> Result<(), Error> { 37 | let ptr = unsafe { &*stm32::USART1::ptr() }; 38 | 39 | for ch in s.bytes() { 40 | while ptr.sr.read().txe().bit_is_clear() {} 41 | unsafe { 42 | ptr.dr.write(|w| w.bits(ch as u32)); 43 | } 44 | } 45 | 46 | Ok(()) 47 | } 48 | } --------------------------------------------------------------------------------