rust-embedded-community / tm4c-hal

An Embedded HAL and general chip support for the TM4C123/LM4F120. Replaces the old lm4f120 crate.
Apache License 2.0
40 stars 26 forks source link

Delays through default configuration of SYST based delay are 2x longer than expected #13

Closed LeonardMH closed 5 years ago

LeonardMH commented 5 years ago

Environment

See https://github.com/thejpster/tm4c123x-hal/issues/12#issue-377004594. Otherwise my main.rs is all that has changed:

#![no_std]
#![no_main]

// you can put a breakpoint on `rust_begin_unwind` to catch panics
extern crate panic_halt;

use tm4c123x_hal::prelude::*;
use tm4c123x_hal::delay::Delay;
use cortex_m_rt::entry;

use tm4c123x_hal::i2c::I2c;

#[entry]
fn main() -> ! {
    let cp = cortex_m::Peripherals::take().unwrap();
    let dp = tm4c123x::Peripherals::take().unwrap();

    // set up a delay controller based on systick
    let mut sysctl = dp.SYSCTL.constrain();
    let clocks = sysctl.clock_setup.freeze();
    let mut delay = Delay::new(cp.SYST, &clocks);

    // Set up GPIOs that we're going to use
    let pf = dp.GPIO_PORTF.split(&sysctl.power_control);
    let mut red_led = pf.pf1.into_push_pull_output();
    let mut blu_led = pf.pf2.into_push_pull_output();
    let mut grn_led = pf.pf3.into_push_pull_output();

    // set up I2C1
    let mut pa = dp.GPIO_PORTA.split(&sysctl.power_control);
    let scl1 = pa.pa6.into_af_push_pull(&mut pa.control);
    let sda1 = pa.pa7.into_af_open_drain(&mut pa.control);
    let mut i2c1 = I2c::i2c1(dp.I2C1, (scl1, sda1), 400.khz(), &clocks, &sysctl.power_control);

    // set up I2C2
    let mut pe = dp.GPIO_PORTE.split(&sysctl.power_control);
    let scl2 = pe.pe4.into_af_push_pull(&mut pe.control);
    let sda2 = pe.pe5.into_af_open_drain(&mut pe.control);
    let mut i2c2 = I2c::i2c2(dp.I2C2, (scl2, sda2), 400.khz(), &clocks, &sysctl.power_control);

    loop {
        // do an i2c transaction forever
        match i2c1.write(0x38, &[0x55, 0, 1, 2, 3]) {
            Ok(_) => { red_led.set_low(); }
            Err(_) => { red_led.set_high(); }
        };

        // TODO: Any delay I pass to this is being scaled by 2x (so 100ms == 200ms, 200ms==400ms)
        blu_led.set_high();
        delay.delay_ms(50u32);
        blu_led.set_low();
    }
}

When I run this I see:

image

thejpster commented 5 years ago

I'm curious if this is just me misunderstanding how the precision internal oscillator is configured, or something more fundamental. Is there any chance you could you retry with:

    let mut sc = p.SYSCTL.constrain();
    sc.clock_setup.oscillator = sysctl::Oscillator::Main(
        sysctl::CrystalFrequency::_16mhz,
        sysctl::SystemClock::UsePll(sysctl::PllOutputFrequency::_80_00mhz),
    );
    let clocks = sc.clock_setup.freeze();

I know this configuration is good as I can generate valid VGA signals at 20MHz on a divide by 4 on the SPI. If your delays are still wrong with this setup, it's the Delay code. If your delays look good, it's probably the oscillator configuration or clock calculations.

dtwood commented 5 years ago

From the port to the tm4c129x, I think https://github.com/thejpster/tm4c123x-hal/blob/4e38c58addebe599c0d32b20af844d5cd0c8b720/src/sysctl.rs#L828 https://github.com/thejpster/tm4c123x-hal/blob/4e38c58addebe599c0d32b20af844d5cd0c8b720/src/sysctl.rs#L856 https://github.com/thejpster/tm4c123x-hal/blob/4e38c58addebe599c0d32b20af844d5cd0c8b720/src/sysctl.rs#L881 and https://github.com/thejpster/tm4c123x-hal/blob/4e38c58addebe599c0d32b20af844d5cd0c8b720/src/sysctl.rs#L898 should be w.sysdiv().bits((div as u16 - 1) as u8); or similar.

A divider of /1 should be achieved by writing 0 into the sysdiv register, therefore I believe that the 1 that this code is writing gets treated as /2.