New library to define and access microcontroller peripherals

I recently wrote two libraries, and want to share them.

The first one is peripherals, that allows to define peripherals with macros, a bit like tock-registers. It is designed to make it easy to define multiple similar devices that share peripherals in the same crate.

The generated API is based on types with operator overloading, which means that you modify register by combining fields with |.

The other library is msp430-periph. It uses peripherals and provide access to the peripherals of all msp430s. Basic examples can be seen at github.com/kellda/msp430-periph/tree/master/examples.

Happy to answer questions and receive feedback and ideas of improvement.

Edit: fixed links now that I'am allowed more than two

Can you talk more about the difference between these new crates and embedded-hal or its device support crates (e.g. stm32f4)?

For example, in which scenarios would I want to choose one over the other, what are the design decisions that make these new crates stand out?

Of course !

So re. embedded-hal, it is one level lower. msp430-periph is really a PAC, not a HAL. Maybe I will write a hal on to of it one day.

Re. stm32f4, it is exactly want type of crate msp430-periph aims to be. Obviously they are not for the same family of microcontrollers, but there are (few) svd2rust-generated crates for MSP340s and nothing prevents from writing/generating a crate based on peripherals for STM32s.

So here are IMO the main differences:

Macro-based

It uses macros to define peripherals, registers, and register fields, which makes the source code readable and editable by hand (and quite smaller).

svd2rust generates code from patched svd files. When an error is found in the svd file, the patch is updated and the crate regenerated. With peripherals, one can just fix the error in the PAC crate source code.

Shared peripherals

peripherals is really designed to allow devices that have similar peripherals to use the same types for these. This should make HAL (and any generic code) quite easier to write, with way less copy-paste from one device to another.

Zero-sized struct instead of aliased struct

This one is a bit of an implementation detail. A lot of crates define struct that have the same layout in memory that the related registers. peripherals use only zero-sized struct, which avoid issues such as deal with rustc/LLVM bug around LLVM's `dereferenceable` and volatile ops by japaric · Pull Request #387 · rust-embedded/wg · GitHub, and makes easy to move registers out of peripherals.

API

The API is heavily based on operator overloading. A quick example:

With peripherals:

let p1 = p.port_1;

// set P0 high and P6 low
p1.pout.modify(POUT0(true) | POUT6(false));

// Set P0 and P6 as outputs
p1.pdir.modify(PDIR0(true) | PDIR6(true));

With svd2rust:

let p12 = p.PORT_1_2;

// set P0 high and P6 low
p12.p1out
   .modify(|_, w| w.p0().set_bit().p6().clear_bit());

// Set P0 and P6 as outputs
p12.p1dir
   .modify(|_, w| w.p0().set_bit().p6().set_bit());

Usage

Re. which crate to use, I think it primarily depends on how much time you're ready to spend in writing/generating PAC crates, and which API you prefer.

Currently, using peripherals with a non-MSP430 target would require to manually write peripherals definitions, or to write code to generate these. On MSP430 targets, there isn't much svd2rust PAC crates, although they aren't hard to generate, so it really depends on API preference.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.