Giter VIP home page Giter VIP logo

fdcan's Introduction

STM32 Peripheral Access Crates

CI crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io Matrix

This repository provides Rust device support crates for all STM32 microcontrollers, providing a safe API to that device's peripherals using svd2rust and a community-built collection of patches to the basic SVD files. There is one crate per device family, and each supported device is a feature-gated module in that crate. These crates are commonly known as peripheral access crates or "PACs".

To view the generated code that makes up each crate, visit the stm32-rs-nightlies repository, which is automatically rebuilt on every commit to stm32-rs master. The stm32-rs repository contains the patches to the underlying SVD files and the tooling to generate the crates.

While these crates are widely used, not every register of every device will have been tested on hardware, and so errors or omissions may remain. We can't make any guarantee of correctness. Please report any bugs you find!

You can see current coverage status for each chip here. Coverage means that individual fields are documented with possible values, but even devices with low coverage should have every register and field available in the API. That page also allows you to drill down into each field on each register on each peripheral.

Using Device Crates In Your Own Project

In your own project's Cargo.toml:

[dependencies.stm32f4]
version = "0.15.1"
features = ["stm32f405", "rt"]

The rt feature is optional but helpful. See svd2rust for details.

Then, in your code:

use stm32f4::stm32f405;

let mut peripherals = stm32f405::Peripherals::take().unwrap();

Refer to svd2rust documentation for further usage.

Replace stm32f4 and stm32f405 with your own device; see the individual crate READMEs for the complete list of supported devices. All current STM32 devices should be supported to some level.

Using Latest "Nightly" Builds

Whenever the master branch of this repository is updated, all device crates are built and deployed to the stm32-rs-nightlies repository. You can use this in your Cargo.toml:

[dependencies.stm32f4]
git = "https://github.com/stm32-rs/stm32-rs-nightlies"
features = ["stm32f405", "rt"]

The nightlies should always build and be as stable as the latest release, but contain the latest patches and updates.

Generating Device Crates / Building Locally

  • Install svd2rust, svdtools, and form:
    • On x86-64 Linux, run make install to download pre-built binaries at the current version used by stm32-rs
    • Otherwise, build using cargo (double check versions against scripts/tool_install.sh):
      • cargo install form --version 0.12.1
      • cargo install svdtools --version 0.3.18
      • cargo install svd2rust --version 0.33.4
  • Install rustfmt: rustup component add rustfmt
  • Generate patched SVD files: make patch (you probably want -j for all make invocations)
    • Alternatively you could install cargo-make runner and then use it instead of make. Works on MS Windows natively:
      • cargo install cargo-make
      • cargo make patch
  • Generate svd2rust device crates: make svd2rust
  • Optional: Format device crates: make form

Motivation and Objectives

This project serves two purposes:

  • Create a source of high-quality STM32 SVD files, with manufacturer errors and inconsistencies fixed. These files could be used with svd2rust or other tools, or in other projects. They should hopefully be useful in their own right.
  • Create and publish svd2rust-generated crates covering all STM32s, using the SVD files.

When this project began, many individual crates existed for specific STM32 devices, typically maintained separately with hand-edited updates to the SVD files. This project hopes to reduce that duplication of effort and centralise the community's STM32 device support in one place.

Helping

This project is still young and there's a lot to do!

  • More peripheral patches need to be written, most of all. See what we've got in peripherals/ and grab a reference manual!
  • Also everything needs testing, and you can't so easily automate finding bugs in the SVD files...

Supported Device Families

crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io crates.io

Please see the individual crate READMEs for the full list of devices each crate supports. All SVDs released by ST for STM32 devices are covered, so probably your device is supported to some extent!

Devices that are nearly identical, like the STM32F405/F415, are supported by ST under a single SVD file STM32F405, so if you can't find your exact device check if its sibling is supported instead. The crate READMEs make this clear.

Many peripherals are not yet patched to provide the type-safe friendly-name interface (enumerated values); please consider helping out with this!

Check out the full list of supported devices here.

Adding New Devices

  • Update SVD zips in svd/vendor to include new SVDs.
  • Run make extract to extract the new zip files.
  • Add new YAML file in devices/ with the new SVD path and include any required SVD patches for this device, such as renaming or merging fields.
  • Add the new devices to stm32_part_table.yaml.
  • Add the new devices to scripts/makecrates.py.
  • You can run scripts/matchperipherals.py script to find out what existing peripherals could be cleanly applied to this new SVD. If they look sensible, you can include them in your device YAML. This requires a Python environment with the pyyaml and svdtools dependencies. Example command: python scripts/matchperipherals.py peripherals/rcc devices/stm32h562.yaml
  • Re-run scripts/makecrates.py devices/ to update the crates with the new devices.
  • Run make to rebuild, which will make a patched SVD and then run svd2rust on it to generate the final library.

If adding a new STM32 family (not just a new device to an existing family), complete these steps as well:

  • Add the new devices to the CRATES field in Makefile.
  • Update this Readme to include the new devices.
  • Add the devices to workflows/ci.yaml and workflows/nightlies.yaml.

Updating Existing Devices/Peripherals

  • Using Linux, run make extract at least once to pull the SVDs out.
  • Edit the device or peripheral YAML (see below for format).
  • Using Linux, run make to rebuild all the crates using svd patch and svd2rust.
  • Test your new stuff compiles: cd stm32f4; cargo build --features stm32f405

If you've added a new peripheral, consider using the matchperipherals.py script to see which devices it would cleanly apply to.

To generate a new peripheral file from scratch, consider using periphtemplate.py, which creates an empty peripheral file based on a single SVD file, with registers and fields ready to be populated. For single bit wide fields with names ending in 'E' or 'D' it additionally generates sample "Enabled"/"Disabled" entries to save time.

Device and Peripheral YAML Format

Please see the svdtools documentation for full details of the patch file format.

Style Guide

  • Enumerated values should be named in the past tense ("enabled", "masked", etc).
  • Descriptions should start with capital letters but do not end with a period

Releasing

Notes for maintainers:

  1. Create PR preparing for new release:
    • Update CHANGELOG.md with changes since last release and new contributors
    • Update README.md to bump version number in example snippet
    • Update scripts/makecrates.py to update version number for generated PACs
  2. Merge PR once CI passes, pull master locally.
  3. make clean
  4. make -j16 form
  5. for f in stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32h7 stm32l0 stm32l1 stm32l4 stm32l5 stm32g0 stm32g4 stm32mp1 stm32wl stm32wb; cd $f; pwd; cargo publish --allow-dirty --no-default-features; cd ..; end
  6. git tag -a vX.X.X -m vX.X.X
  7. git push vX.X.X

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

fdcan's People

Contributors

abestanis avatar antoine31400 avatar bors[bot] avatar ho-ho-ho avatar luctius avatar mahu-wm avatar richardeoin avatar yorickdewid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

fdcan's Issues

I could not get to send/receive FDCAN frame (with BRS enabled)

Hi !

We are currently trying to use your crate to send/receive FDCAN frame, with bitrate_switch enabled, using a NUCLEO-H7A3ZI-Q board (it is a STM32H7A3ZI on it).

The standard CAN and the FDCAN (with no BRS) seem to work well, but we could not get to make BRS working.
We do not know where the issue come from, neither if the issue come from the crate.

Did someone try and succeed to send/receive FDCAN (BRS enabled) frame ?

Thanks !

Undefined behaviour in write_mailbox?

I think the following is undefined behaviour, since we should call 'as_mut_ptr', instead of 'as_ptr' (and lbuffer should be mut)?

fdcan/src/lib.rs

Lines 1333 to 1342 in 1045c9c

let lbuffer = [0_u32; 16];
let data = unsafe {
slice::from_raw_parts_mut(
lbuffer.as_ptr() as *mut u8,
tx_header.len as usize,
)
};
data[..tx_header.len as usize]
.copy_from_slice(&buffer[..tx_header.len as usize]);

Failure modes of `FdCan.transmit`

Hi! I'm attempting to diagnose a FAIL on Fdcan.transmit() after a few successful transmissions. The docs list it as a Rusult with Infallible type. It is failing, so I'm not sure what this means in context. The trigger for the failure is also using USB, which is probably not supported due to shared message ram.

TX_COMPLETE interrupt is not triggered

I had some difficulties getting the TX_COOMPLETE interrupt to work. Here is an excerpt from my test code.

can.enable_interrupt_line(InterruptLine::_0, true);
can.enable_interrupts(Interrupts::RX_FIFO0_NEW_MSG | Interrupts::TX_COMPLETE);
unsafe {
    cp.NVIC.set_priority(interrupt::FDCAN1_IT0, 1);
    NVIC::unmask(interrupt::FDCAN1_IT0);
}

If I now send and receive CAN bus datagrams, RX_FIFO0_NEW_MSG is triggered, but not TX_COMPLETE.

After the following lines have been inserted, the TX_COMPLETE Intterupt is triggered on the STM32H743 system, as expected. However, this is certainly not the intended path.

unsafe {
    // FDCAN_TXBTIE Tx buffer transmission interrupt enable register
    core::ptr::write_volatile(0x4000a0e0 as *mut u32, 0xffff_ffff);
}

Have I overlooked something, or is there a mistake here?

Interrupts/registers do not match the H7 datasheet

The block reference manual (RM0433) lists different interrupts and registers than the current driver expects.

Found so far:

  • RM044 section 56.5.16 FDCAN_IE is different than what src/interrupt.rs lists.
  • RX FIFO (RXFnC.FnWM) watermark is never set which means it will default to 0. Any RAM index greater than 0 will be reported as 'overrun'.

HAL support

To make this crate useful and worth maintaining, it's helpful if it is used in at least a couple of HALs

  • stm32g4xx-hal
  • stm32h7xx-hal - PR here
  • stm32g0xx-hal

CI and bors

Continuous Integration and bors need to be added to the repository

  • Probably use Github Actions for CI Done
  • There's an Github Integration for bors

Expected FDCAN core major release 3

Does anyone know how to fix this? Using STM32G473. Thx!

Relevant line of code: https://github.com/stm32-rs/fdcan/blob/master/src/lib.rs#L554

Panic message:

ERROR panicked at 'Expected FDCAN core major release 3', C:\Users\david\.cargo\registry\src\github.com-1ecc6299db9ec823\fdcan-0.1.2\src\lib.rs:545:9

The CREL register, REL field bits I get when reading is 11. (0b1011) The total value of the CREL register is 3118262466, indicating:

Rel: 1011 (11)
Step: 1001 (9)
Substep: 1101 (13)
Year: 1100 (12)
Mon: 11101000 (232)
Day: 11000010 (194)

The name of the `set_interrupt_line_config` method is very confusing

I have setup a new can instance in loopback mode for FDCAN1 on my stm32h723zgt6. It compiles fine. The transmit in the main loop, however, never seems to trigger the rx interrupt to fire.

I'm including a main.rs for a stm32h7xx-hal build as an example below:

#![no_main]
#![no_std]

use core::{

num::{ NonZeroU8, NonZeroU16}

};

use stm32h7xx_hal::{

gpio::Speed,
interrupt,
pac,
prelude::*,
pwr::PowerConfiguration,
rcc::{Ccdr, PllConfigStrategy, rec}

};

use stm32h7xx_hal::{

rcc::{Rcc, RccExt},
stm32::SYSCFG

};

use cortex_m_rt::entry;

use fdcan::{

config::{DataBitTiming, NominalBitTiming},
id::StandardId,
filter::{StandardFilter, StandardFilterSlot},
frame::{FrameFormat, TxFrameHeader},
interrupt::{Interrupts,  InterruptLine},

};

#[entry]
fn main() -> ! {

let mut cortex_peripherals = cortex_m::Peripherals::take().unwrap();
let device_peripherals = pac::Peripherals::take().unwrap();

// Constrain and Freeze power
let pwr = device_peripherals.PWR.constrain();
let pwr_cfg: PowerConfiguration = pwr.ldo().freeze();

// Constrain and Freeze clock
let rcc: Rcc = device_peripherals.RCC.constrain();
let ccdr: Ccdr = setup_clocks(rcc, pwr_cfg, device_peripherals.SYSCFG);

//Setup GPIO
let gpio_d = device_peripherals.GPIOD.split(ccdr.peripheral.GPIOD);
let gpio_can_tx = gpio_d.pd1.into_alternate().speed(Speed::VeryHigh);
let gpio_can_rx = gpio_d.pd0.into_alternate().speed(Speed::VeryHigh);

//Setup CANbus

unsafe {

    cortex_peripherals.NVIC.set_priority(interrupt::FDCAN1_IT0, 1);
    cortex_m::peripheral::NVIC::unmask(interrupt::FDCAN1_IT0);
}

assert_eq!(ccdr.clocks.pll2_q_ck().unwrap().raw(), 32_000_000);

let fdcan_prec = ccdr
    .peripheral
    .FDCAN
    .kernel_clk_mux(rec::FdcanClkSel::Pll2Q);

let mut can = device_peripherals.FDCAN1.fdcan(gpio_can_tx, gpio_can_rx, fdcan_prec);

// Kernel Clock 32MHz, Bit rate: 500kBit/s, Sample Point 87.5%
// Value was calculated with http://www.bittiming.can-wiki.info/
// TODO: use the can_bit_timings crate
let nominal_bit_timing = NominalBitTiming {
    prescaler: NonZeroU16::new(4).unwrap(),
    seg1: NonZeroU8::new(13).unwrap(),
    seg2: NonZeroU8::new(2).unwrap(),
    sync_jump_width: NonZeroU8::new(1).unwrap(),
};

// Kernel Clock 32MHz, Bit rate: 1MBit/s, Sample Point 87.5%
// Value was calculated with http://www.bittiming.can-wiki.info/
// TODO: use the can_bit_timings crate
let _data_bit_timing = DataBitTiming {
    prescaler: NonZeroU8::new(2).unwrap(),
    seg1: NonZeroU8::new(13).unwrap(),
    seg2: NonZeroU8::new(2).unwrap(),
    sync_jump_width: NonZeroU8::new(1).unwrap(),
    transceiver_delay_compensation: true,
};

//Configure timing
can.set_nominal_bit_timing(nominal_bit_timing);

//Configure Filters");
can.set_standard_filter(
    StandardFilterSlot::_0,
    StandardFilter::accept_all_into_fifo0(),
);

can.enable_interrupt_line(InterruptLine::_0, true);
can.set_interrupt_line_config(Interrupts::all());
can.enable_interrupts(Interrupts::all());

let mut can = can.into_external_loopback();

loop {

    let buffer = [
        0xA1, 0xA2, 0xAA, 0xAA, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0,
        0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    ];

    let header = TxFrameHeader {
        len: 8,
        id: StandardId::new(0x1).unwrap().into(),
        frame_format: FrameFormat::Standard,
        bit_rate_switching: false,
        marker: None,
    };

    let result = can.transmit(header, &buffer);
}

}

fn setup_clocks(rcc: Rcc, pwrcfg: PowerConfiguration, dp_sys_cfg: SYSCFG) -> Ccdr{

rcc
.bypass_hse()
.use_hse(24.MHz())
.sys_ck(384.MHz())

.pll1_strategy(PllConfigStrategy::Iterative)
.pll1_p_ck(384.MHz())
.pll1_q_ck(48.MHz())        //USB
.pll1_r_ck(192.MHz())

.pll2_strategy(PllConfigStrategy::Iterative)
//.pll2_p_ck(1_500_000.Hz())
.pll2_p_ck(32.MHz())
.pll2_q_ck(32.MHz())        //FDCAN
.pll2_r_ck(96.MHz())

.freeze(pwrcfg, &dp_sys_cfg)

}

#[panic_handler]
fn panic_handler(_info: &core::panic::PanicInfo) -> ! {

loop {
}

}

#[interrupt]
fn FDCAN1_IT1()
{
let _never_fired = true;
}

#[interrupt]
fn FDCAN1_IT0()
{
let _never_fired = true;
}

On STM32G4 ILE bits are switched

I just noticed that on the stm32g4, the meanings of the ILE bits are switched. This is (by the reading of the reference manual) not the case on the H7.

fdcan_ile

(Read the fine print, RM0440 Rev 6)

Compile error: no `Interrupts` in `interrupt` + others

Using version 0.1.2. Compiling with this crate leads to this sequence of errors:

error[E0432]: unresolved imports `super::interrupt::Interrupt`, `super::interrupt::Interrupts`
--> C:\Users\david\.cargo\registry\src\github.com-1ecc6299db9ec823\fdcan-0.1.2\src\config.rs:1:28
 |
1 | pub use super::interrupt::{Interrupt, InterruptLine, Interrupts};
 |                            ^^^^^^^^^                 ^^^^^^^^^^ no `Interrupts` in `interrupt`
 |                            |
 |                            no `Interrupt` in `interrupt`

error[E0432]: unresolved imports `interrupt::Interrupt`, `interrupt::Interrupts`
 --> C:\Users\david\.cargo\registry\src\github.com-1ecc6299db9ec823\fdcan-0.1.2\src\lib.rs:59:17
  |
59 | use interrupt::{Interrupt, InterruptLine, Interrupts};
  |                 ^^^^^^^^^                 ^^^^^^^^^^ no `Interrupts` in `interrupt`
  |                 |
  |                 no `Interrupt` in `interrupt`
  |
help: consider importing this unresolved item instead
  |
59 | use interrupt::{crate::config::Interrupt;
  |                 ~~~~~~~~~~~~~~~~~~~~~~~~~
help: consider importing this unresolved item instead
  |
59 | use interrupt::{Interrupt, InterruptLine, crate::config::Interrupts;
  |                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~

error[E0433]: failed to resolve: use of undeclared crate or module `ir`
 --> C:\Users\david\.cargo\registry\src\github.com-1ecc6299db9ec823\fdcan-0.1.2\src\pac\fdcan.rs:63:24
  |
63 |     pub ir: crate::Reg<ir::IR_SPEC>,
  |                        ^^ use of undeclared crate or module `ir`

error[E0433]: failed to resolve: use of undeclared crate or module `ie`
 --> C:\Users\david\.cargo\registry\src\github.com-1ecc6299db9ec823\fdcan-0.1.2\src\pac\fdcan.rs:65:24
  |
65 |     pub ie: crate::Reg<ie::IE_SPEC>,
  |                        ^^ use of undeclared crate or module `ie`

error[E0433]: failed to resolve: use of undeclared crate or module `ils`
 --> C:\Users\david\.cargo\registry\src\github.com-1ecc6299db9ec823\fdcan-0.1.2\src\pac\fdcan.rs:67:25
  |
67 |     pub ils: crate::Reg<ils::ILS_SPEC>,
  |                         ^^^ use of undeclared crate or module `ils`

set_standard_filter doesn't seem to work

Trying to filter for arb ID 0x700 and still receiving all messages when I call can1.receive0(msg) later on

Have tried this

let arb_id = StandardId::new(0x700 as u16).unwrap().into();
let slot = StandardFilterSlot::_0;
let filter = StandardFilter {
    filter: FilterType::DedicatedSingle(arb_id),
    action: Action::StoreInFifo0,
};
can1.set_standard_filter(slot, filter);

and this

let filter = StandardFilter {
    filter: FilterType::BitMask {
         filter: 0x700,
         mask: 0x7ff,
    },
    action: Action::StoreInFifo0,
};

still receive all can messages

An example would be helpful. I came across this https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/can-echo.rs example, but it only shows use of accept_all_into_fifo0()

can.set_standard_filter(
        StandardFilterSlot::_0,
        StandardFilter::accept_all_into_fifo0(),
    );

Request an example

Hi! Are there any examples available? Thoughts on adding one or more to an examples folder? I'm attempting to learn how to use this lib, but am not sure where to start. Thanks!

Of note, from the docs page, it seems like most of the structs dummies for use in typestates. I gather that the following structs and their methods make up the primary API: FdCan, Tx, Rx, FdCanControl. Is this right?

embedded-can support

Hi,

I'm trying to understand the state of affairs with embedded-can but I'm having a hard time figuring it out. Can anyone shed some light?

Note that I've also created an issue in the embedded rust WG

Thanks,

Wrong DLC bits for fdcan frame

In message_ram/enums.rs, lines 41 to 56, the function that compute DLC bits should be modified.

See page 6 of document "FDCAN peripheral on STM32 devices - Application note" :
https://www.st.com/resource/en/application_note/an5348-fdcan-peripheral-on-stm32-devices-stmicroelectronics.pdf

Current function :

pub(crate) fn dlc(&self) -> u8 {
    match self {
        DataLength::Standard(l) => *l,
        DataLength::Fdcan(l) => match l {
            0..=8 => *l,
            9..=12 => 12,
            13..=16 => 16,
            17..=20 => 20,
            21..=24 => 24,
            25..=32 => 32,
            33..=48 => 48,
            49..=64 => 64,
            _ => panic!("DataLength > 64"),
        },
    }
}

New function proposed :

pub(crate) fn dlc(&self) -> u8 {
    match self {
        DataLength::Standard(l) => *l,
        DataLength::Fdcan(l) => match l {
            0..=8 => *l,
            9..=12 => 9,
            13..=16 => 10,
            17..=20 => 11,
            21..=24 => 12,
            25..=32 => 13,
            33..=48 => 14,
            49..=64 => 15,
            _ => panic!("DataLength > 64"),
        },
    }
}

Transmit and receive should use byte array

I think the FdCan::transmit and possibly the FdCan::receive0 and FdCan::receive1 should accept a byte array instead of a u32 vector. I understand the use of u32 for RAM alignment but I think this should be handled internally with an optional padding.

Public FdCan functions should not have to use lambda's if they are not necessary. This would make things more idiomatic Rust.

I can draft a PR.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.