stm32-rs / stm32f1xx-hal

A Rust embedded-hal HAL impl for the STM32F1 family based on japarics stm32f103xx-hal
Apache License 2.0
575 stars 179 forks source link

Blink example & non-default RCC config #71

Closed andreyk0 closed 5 years ago

andreyk0 commented 5 years ago

Sorry for a possibly really dumb question but can't seem to figure it out, in the blinky.rs example if I change

let clocks = rcc.cfgr.freeze(&mut flash.acr);

to

    let clocks = rcc.cfgr.use_hse(time::Hertz(8_000_000)).freeze(&mut flash.acr);

it continues to work but if I additionally set any other frequency, e.g.

    let clocks = rcc.cfgr.use_hse(time::Hertz(8_000_000)).sysclk(time::Hertz(72_000_000)).freeze(&mut flash.acr);

it stops working.

I'm trying to figure out how to explicitly create a config like rcc_clock_setup_in_hse_8mhz_out_72mhz from https://github.com/libopencm3/libopencm3/blob/master/lib/stm32/f1/rcc.c#L845

Thanks!

burrbull commented 5 years ago

let clocks = rcc.cfgr.use_hse(time::Hertz(72_000_000)).freeze(&mut flash.acr); or shorter let clocks = rcc.cfgr.use_hse(72.mhz()).freeze(&mut flash.acr);

andreyk0 commented 5 years ago

Thanks but how does one actually control prescalers then? I have a pretty basic "blue pill" board with 8Mhz HSE clock. It looked to me as if the library let you specify HSE clock and the desired other frequencies and then it tries to compute/set prescalers. Do you happen to have an example with non-default prescaler config?

geomatsi commented 5 years ago

Hi @andreyk0

You may specify HSE frequency and other derived clocks including sysclk, pclk1, pclk2, adcclk. For instance, I have been testing ADC clocks for #67 using the following clock configurations:

let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(8.mhz()).pclk1(2.mhz()).adcclk(1.mhz()).freeze(&mut flash.acr);
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(16.mhz()).pclk1(4.mhz()).adcclk(2.mhz()).freeze(&mut flash.acr);
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(24.mhz()).pclk1(9.mhz()).adcclk(3.mhz()).freeze(&mut flash.acr);
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(24.mhz()).pclk1(12.mhz()).adcclk(4.mhz()).freeze(&mut flash.acr);
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(24.mhz()).pclk1(12.mhz()).adcclk(6.mhz()).freeze(&mut flash.acr);
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(36.mhz()).pclk1(12.mhz()).adcclk(6.mhz()).freeze(&mut flash.acr);
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(32.mhz()).pclk1(16.mhz()).adcclk(8.mhz()).freeze(&mut flash.acr);
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(40.mhz()).pclk1(20.mhz()).adcclk(10.mhz()).freeze(&mut flash.acr);
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(48.mhz()).pclk1(24.mhz()).adcclk(12.mhz()).freeze(&mut flash.acr);

Library will attempt to calculate prescalers and write their values into MCU registers. If provided values are not consistent, then one of the asserts in src/rcc.rs will be triggered. So you better use some kind of debug output (semihosting or itm) when adjusting clocks for your system.

Regards, Sergey

geomatsi commented 5 years ago

Hi @burrbull , You are probably talking about HSI which is internal 8MHz oscillator. HSE is external crystal oscillator: according to your picture it can be between 4 and 12 MHz. Blue Pill has external 8 MHz crystal on board.

IIRC by default HAL configures HSI as a clock source. But it can be changed using use_hse() call, specifying actual crystal frequency as a parameter.

Regards, Sergey

andreyk0 commented 5 years ago

@geomatsi nice, thanks for the example!

TheZoq2 commented 5 years ago

As was discussed in #67, more documentation for this would be nice. I'll try and look into adding that next week

andreyk0 commented 5 years ago

Got some time to play more (sorry, I'm new to this), so far only one configuration works for me:

let clocks = rcc .cfgr .use_hse(8.mhz()) .sysclk(8.mhz()) .pclk1(2.mhz()) .adcclk(1.mhz()) .freeze(&mut flash.acr);

All of the other ones result in a hang. E.g. this hangs: https://github.com/andreyk0/stm32_blink/blob/master/src/main.rs#L35

% make debug
cargo build
warning: unused manifest key: profile.development
    Finished dev [unoptimized + debuginfo] target(s) in 0.10s
gdb-multiarch -x openocd.gdb -q target/thumbv7m-none-eabi/debug/stm32_blink
Reading symbols from target/thumbv7m-none-eabi/debug/stm32_blink...
0xfffffffe in ?? ()
Breakpoint 1 at 0x8003000: file /home/andrey/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.8/src/lib.rs, line 540.
Breakpoint 2 at 0x800321a: file /home/andrey/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.8/src/lib.rs, line 530.
Breakpoint 3 at 0x8002e78: file /home/andrey/.cargo/registry/src/github.com-1ecc6299db9ec823/panic-halt-0.2.0/src/lib.rs, line 33.
semihosting is enabled
Loading section .vector_table, size 0x130 lma 0x8000000
Loading section .text, size 0x30fa lma 0x8000130
Loading section .rodata, size 0xbac lma 0x8003230
Start address 0x8002fba, load size 15830
Transfer rate: 16 KB/sec, 5276 bytes/write.
Note: automatically using hardware breakpoints for read-only addresses.
DefaultPreInit () at /home/andrey/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.8/src/lib.rs:546
546     pub unsafe extern "C" fn DefaultPreInit() {}
(gdb) cont
Continuing.
^C
Program received signal SIGINT, Interrupt.
0xfffffffe in ?? ()
(gdb) bt
#0  0xfffffffe in ?? ()
#1  <signal handler called>
#2  stm32f1::stm32f103::rcc::cfgr::<impl stm32f1::stm32f103::rcc::CFGR>::modify (self=0x40021004, f=...)
    at /home/andrey/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f1-0.7.1/src/stm32f103/rcc/cfgr/mod.rs:20
#3  0x0800228e in stm32f1xx_hal::rcc::CFGR::freeze (self=..., acr=0x20004e74) at /home/andrey/work/3rdparty/embedded/Rust/stm32-rs-stm32f1xx-hal/src/rcc.rs:310
#4  0x080005ba in main () at src/main.rs:35
(gdb) 

Longer session: https://github.com/andreyk0/stm32_blink/blob/master/eg.gdb.session CPU is STM32F103C8T6, 8Mhz HW clock. I must be missing some config detail here but so far haven't noticed which yet. My local stm32-rs-stm32f1xx-hal is at 5ad17fc7097618400c2b0afc23215e25e7372410 at the moment.

Thanks for looking at this!

geomatsi commented 5 years ago

Hmm.. The same minimal example works fine on my BluePill board:

#[entry]
fn main() -> ! {
    let dp = hal::stm32::Peripherals::take().unwrap();
    let mut rcc = dp.RCC.constrain();
    let mut flash = dp.FLASH.constrain();

    let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(16.mhz()).pclk1(4.mhz()).adcclk(2.mhz()).freeze(&mut flash.acr);
    //let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(36.mhz()).pclk1(12.mhz()).adcclk(6.mhz()).freeze(&mut flash.acr);
    //let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(56.mhz()).pclk1(28.mhz()).adcclk(14.mhz()).freeze(&mut flash.acr);

    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);

    loop {
        hprintln!("Hello World!").unwrap();

        led.set_high();
        delay(5000);
        led.set_low();
        delay(500);
    }
}

fn delay(count: u32) {
    for _ in 0..count {
        cm::asm::nop();
    }
}

I used the same versions of dependencies as in your case. So I have no idea what could go wrong in your case. Could you please show the dump of registers after you system stucks ? You may use openocd telnet console to halt system and dump registers.

The only difference that I noticed so far is the content of memory file. Here is the content of my memory.x:

$ cat memory.x
MEMORY
{
    FLASH : ORIGIN = 0x08000000, LENGTH = 64K
    RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

_stack_start = ORIGIN(RAM) + LENGTH(RAM);
_stext = ORIGIN(FLASH) + 0x400;
_heap_size = 1024;
andreyk0 commented 5 years ago

Thanks! I took your memory settings (just in case) but no luck, updated https://github.com/andreyk0/stm32_blink/blob/master/eg.gdb.session with a new debug session (single steps before hang). Everything seems fine until it touches SW register and then PC becomes corrupt right after that and it jumps off the rails. Sorry to waste your time with this, must be something really dumb that I'm doing but I don't see it. Feel free to ignore me, I'll keep poking when I have free time, it's as good of a starting point as any for me to try to figure out this system. Thanks again for your help!

geomatsi commented 5 years ago

Just in case, which rust toolchain are you using ?

andreyk0 commented 5 years ago

I think these are the main parts, anything else that matters?

% cargo --version
cargo 1.35.0 (6f3e9c367 2019-04-04)

% rustc --version
rustc 1.35.0 (3c235d560 2019-05-20)

% dpkg -l | grep arm-none-eabi
ii  binutils-arm-none-eabi                     2.32-1ubuntu4+11                        amd64        GNU assembler, linker and binary utilities for ARM Cortex-R/M processors
ii  gcc-arm-none-eabi                          15:7-2018-q2-6                          amd64        GCC cross compiler for ARM Cortex-R/M processors
ii  libnewlib-arm-none-eabi                    3.1.0.20181231-1                        all          C library and math library compiled for bare metal using Cortex A/R/M
ii  libstdc++-arm-none-eabi-newlib             15:7-2018-q2-5+12                       all          GNU Standard C++ Library v3 for ARM Cortex-R/M processors (newlib)
% 
andreyk0 commented 5 years ago

Hmm, maybe there's something wrong with the toolchain or the hardware, tried git clean -fdx everywhere and they sysclk(16.mhz()) blinked but then tried one more config and it hung. Did a quick memory dump and compared registers with the docs - seem allright. Sorry, please ignore this for now, this is most likely something weird in my environment. I'll go step-by-step and check everything again when I have a bit more time.

therealprof commented 5 years ago

The other possibility that comes to mind is a fake/non STM32 chip.

andreyk0 commented 5 years ago

Had some time to play with it again this weekend, the only diff is I ran rustup update everything seems to work now. Still not sure what it was but thanks for the help. Compared to RTOS as well, the only tiny diff I've noticed is the treatment of "don't care" 0xxx bits. RTOS seems to zero them all out while Rust code only zeroes out the significant bit. According to the docs it shouldn't matter thought.

Rust HPRE: 0111 0xxx: SYSCLK not divided
RTOS HPRE: 0000 0xxx: SYSCLK not divided
Rust PPRE2: 011  0xx HCLK not divided
RTOS PPRE2: 000  0xx HCLK not divided
pedrovanzella commented 4 years ago

I think I know what's going on here: If we set the clock to 72mhz we get this panic: panicked at 'assertion failed: rvr < (1 << 24)', src/timer.rs:268:9

The line before that one sets rvr as: let rvr = self.clk.0 / timeout.into().0 - 1;

If I set the count down frequency to 10.hz() I don't get this panic anymore. It looks like it wants the hclk (which should be clk.0 means) divided by the timeout (in our case, 1) to be less than 16777217 ((1 << 24) + 1).

therealprof commented 4 years ago

@pedrovanzella The SysTick counter can only from 24 bits down and it does this with clock frequency by (implementation) default. At 72MHz this gives you a maximum interval time of 0.233s (or 1.864s if you change the SysTick clock to be HCLK/8 instead). If you need longer countdowns you need to set it to a lower value and add up the number of cycles yourself. Or you use the delay functions instead which will do this for you.

pedrovanzella commented 4 years ago

@therealprof That makes total sense. Thanks for the explanation!