├── .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 |
5 |
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 | }
--------------------------------------------------------------------------------