Giter Club home page Giter Club logo

Comments (21)

aarongowatch avatar aarongowatch commented on September 18, 2024 1

This is an old thread, but I implemented this or a similar abstraction for Embassy, but perhaps embedded-hal is a more appropriate place to implement these generalized abstractions? These abstractions are beneficial to my use case, and I can open a PR if there's interest.

from embedded-hal.

therealprof avatar therealprof commented on September 18, 2024

Good stuff you're adding a new ticket for this very important topic.

There's from my side at least three important aspects to cover:

  • Interrupt generation configuration (certainly a HAL topic)
  • Interrupt handler setup
  • Interrupt handling (maybe a HAL topic)

All of this has to be done manually right now but at least for the setup of the interrupt handlers there's already some support (for Cortex-M MCUs) in the cortex-m-rt and cortex-m-rtfm crates with the largest problems being:

  • The interrupt handler definition is static and happens on a program level
  • The interrupt generation configuration is completely disconnected from the interrupt handler itself and happens dynamically somewhere in the program

Ideally there would be a possibility for an embedded-hal implementation to insert a static interrupt handler definition at compile time from a macro call inside the program. Only at this level the compiler can ensure that the required resources are still available and the HAL implementation that the interrupt generation setup and the interrupt handler itself match up and are will be operational.

One alternative might be to always use a veneer for all interrupt handlers and allow the HAL to assign the real interrupt handler at runtime. Of course higher level Cortex-Ms and other architectures may allow to change the interrupt handlers on the fly instead of being fixed addresses in flash.

from embedded-hal.

nordmoen avatar nordmoen commented on September 18, 2024

Just want to chime in and say that this is sorely needed. I'm developing a small crate for HC-SR04 which ideally should use interrupts to measure return value from sensor. At the moment I have implemented it as a non-blocking type where users are responsible for calling an update function to inform the sensor of the interrupt event. This is one use-case where some form of abstract interrupt handling would be beneficial.

Without much experience in embedded work, the workflow I would imagine is something along the line of users passing a type implementing Nr and exposing a crate that contains either a macro or function which can setup the interrupt to call a selected method from the sensor crate. For my own use I would not mind that, initially at least, the user of the sensor crate would be responsible for configuring the interrupt (e.g. manage the correct line for interrupt, etc.), but letting the sensor crate select the, possibly private, function to handle the interrupt and so on.

from embedded-hal.

therealprof avatar therealprof commented on September 18, 2024

@nordmoen Unfortunately it doesn't quite work like this. First of all each platform has a completely different way of setting up the interrupts and different capabilities when it comes to them (e.g. some platforms have a number of interrupts and you're completely free to map them to certain "events" happing in the system, while others have very specific interrupts mapped to exact peripherals).

Then there's a lot of options depending on user choice, e.g. if you want to have an interrupt when a button is pressed you'll need to tell the system whether it is pulled low or high and also whether you'd like it edge-triggered or level-triggered; some also allow debouncing, etc.

Then there's different ways how the interrupts work in the system, e.g. Cortex-M0 have a fixed interrupt/exception handler table at the beginning of the flash, whereas Cortex-M3 or higher have the same but can can change the address of the interrupt handler table and then there're other chips allowing to completely dynamically remap interrupts.

And of course, once you successfully arrived in the interrupt handler, you still need to know how to deal with the interrupt, i.e. figure out what exactly the cause of the interrupt was and also clear it, which again, is completely MCU dependent...

from embedded-hal.

puzrin avatar puzrin commented on September 18, 2024

IMHO at HAL level of abstraction interrupts are not self-sufficient. Every interrupt is a part of hardware block (timer, dma, spi, i2c, ...) with specific details.

  1. I think it would be preferable for end users to have events support in existing modules instead of abstract interrupts.
  2. After adding event-like support to existing modules it will be more clear if it worth to generalise interrupts API or not.

from embedded-hal.

jcsoo avatar jcsoo commented on September 18, 2024

To me, embedded-hal describes the interface that drivers provide to user code, and concepts such as interrupts and DMA are implementation details that are the responsibility of the driver.

That doesn't mean that these traits are optimal for every purpose. If they aren't, one can use a more suitable driver-specific API or write a custom driver that does exactly what is needed.

I do think there will ultimately be a handful of traits for each hardware protocol - we already have blocking and non-blocking variants, and I can also see zero-copy ownership-passing interfaces as well as command queues and scatter-gather for some types of hardware.

However, even those more complex traits must abstract away the details of how drivers are implemented and instead focus on the protocol that the client and driver agree to.

from embedded-hal.

therealprof avatar therealprof commented on September 18, 2024

@jcsoo

To me, embedded-hal describes the interface that drivers provide to user code, and concepts such as interrupts and DMA are implementation details that are the responsibility of the driver.

And by "driver" you mean? At the moment they're they are the responsibility of the application. And while I do agree with everything you've said I still think we should at least have the capability of simplifying the use of interrupts with embedded-hal implementations.

from embedded-hal.

jcsoo avatar jcsoo commented on September 18, 2024

And by "driver" you mean? At the moment they're they are the responsibility of the application. And while I do agree with everything you've said I still think we should at least have the capability of simplifying the use of interrupts with embedded-hal implementations.

By "driver" I mean whatever concrete type is implementing the trait, and "client" covers the code that is calling the methods of the trait. "Application" covers a lot of territory and could include code on both sides of that boundary.

As far as interrupts go, I see two separate but related concerns.

First is the issue of hardware interrupts and the various methods of configuring them, especially how one dispatches to handlers at either build time or run time. That requires using a combination of vendor-specific code to configure the interrupts (maybe "Low-Level HAL" is a good term for this), perhaps a cross-platform build-time or link-time tool (RTFM and cortex-m-rt do some of this) to manage static dispatching, or alternatively, architecture-specific tools for managing vector relocation (as on Cortex-M) and/or run-time interrupt dispatch mechanisms.

Second is the question of how to structure an application as a whole to work well in an asynchronous environment. Embedded-HAL doesn't really deal with that at all - it simply supports a blocking or non-blocking model and pushes policy to either the driver or the code calling the interface. For some applications a busy loop or call to WFI is fine, but for others we start wanting some parts of an RTOS.

Both of those areas haven't had as much attention as Embedded-HAL itself because they involve much more policy and complexity than Embedded-HAL.

I'd be very interesting in seeing specific examples of applications that run into these issues. I've done some experimentation with both dynamic handler assignment using relocated vector tables and dispatchers, each of which has strengths and weaknesses, and I've also been working on coming up with a core set of primitives on which to base a simple RTOS.

from embedded-hal.

therealprof avatar therealprof commented on September 18, 2024

@jcsoo

"Application" covers a lot of territory and could include code on both sides of that boundary.

I respectfully disagree. The application (or more commonly called "firmware" in embedded systems) is the binary running on the system which sets up the hardware and drivers and binds all bits and pieces together to achieve whatever it is supposed to do in the end. While it could theoretically provide an HAL implementation consumed by the drivers it uses, this is not really the intended use case.

Both of those areas haven't had as much attention as Embedded-HAL itself because they involve much more policy and complexity than Embedded-HAL.

I agree with the two points but you're missing one important detail from my POV: the traits in embedded-hal serve as a common interface for HAL implementations, drivers and applications. And as such I would also expect a common interface to figure out which interrupts can be used by a peripheral, activate it and of course take ownership. embedded-hal seems like the natural place for such an interface.

from embedded-hal.

jcsoo avatar jcsoo commented on September 18, 2024

I agree with the two points but you're missing one important detail from my POV: the traits in embedded-hal serve as a common interface for HAL implementations, drivers and applications. And as such I would also expect a common interface to figure out which interrupts can be used by a peripheral, activate it and of course take ownership. embedded-hal seems like the natural place for such an interface.

My understanding is that embedded-hal is specifically about common ways of using drivers and explicitly excludes initializing and configuring them - see the documentation:

Out of scope

Initialization and configuration stuff like "ensure this serial interface and that SPI interface are not using the same pins". The HAL will focus on doing I/O.

Initialization and configuration (of which interrupt management is one part) is hugely important, but it's also incredibly broad with many, many possible approaches. Because of that I think it's better handled outside of this crate, which should keep its focus on defining a minimal set of interfaces for interoperability.

from embedded-hal.

therealprof avatar therealprof commented on September 18, 2024

Yes, the embedded-hal traits define the interface, they do not actually do anything (well, not a lot) by themselves.

from embedded-hal.

puzrin avatar puzrin commented on September 18, 2024

And as such I would also expect a common interface to figure out which interrupts can be used by a peripheral, activate it and of course take ownership. embedded-hal seems like the natural place for such an interface.

Do you mean that interrupts can be relocated in MCU on hardware level? That looks reasonable point to reflect support in independent interface.

from embedded-hal.

therealprof avatar therealprof commented on September 18, 2024

@puzrin Some (most?) MCU have mappable interrupts, e.g. STM32 with the EXTI but that's only a part of my point. When you're using interrupts there's a common set of operations you'll typically need to do:

  • Set it up
  • Activate it
  • Deactivate it
  • Check whether it has been tripped
  • Clear it
  • ...

By having a generic trait for interrupts in embedded-hal, the HAL implementation can provide specific implementations for the trait for the available interrupts and model them similarly to other peripherals thus simplifying the handling.

from embedded-hal.

puzrin avatar puzrin commented on September 18, 2024

For exti it could be reasonable to have exti hal, not abstract interrupts call. I can't understand what can be common with dma or timer there.

from embedded-hal.

therealprof avatar therealprof commented on September 18, 2024

EXTI is STM32 specific and thus not a good choice for a trait. The abstraction is not about the interrupt handler calls but the interrupt setup and handling; it would be great if the interrupt handlers could be abstracted, too, but that would require some serious magic which is beyond my skills.

I have an idea. Let me see if I can find some time later today to test it and come up with a proposal.

from embedded-hal.

puzrin avatar puzrin commented on September 18, 2024

The abstraction is not about the interrupt handler calls but the interrupt setup and handling

Ah, seems i got your point. It it's something like pin definitions, that may be useful to configure autogenerated mcu hal-s.

from embedded-hal.

therealprof avatar therealprof commented on September 18, 2024

I've added a rough cut of my idea here: https://github.com/therealprof/embedded-hal/tree/features/interrupts

This is what an exemplary implementation for the RTC0 interrupt on a NRF51 might look like (of course this should be composable with other drivers):

use nrf51::RTC0;

use hal::interrupts::IRQ;

pub struct Irq<I> {
    irq: I,
}

impl Irq<RTC0> {
    pub fn new(irq: RTC0, prescaler: u32) -> Self {
        let rtc0 = unsafe { &*RTC0::ptr() };
        rtc0.evtenset.write(|w| w.tick().set_bit());
        rtc0.prescaler.write(|w| unsafe { w.bits(prescaler) });
        Irq { irq }
    }
}

impl IRQ for Irq<RTC0> {
    fn enable(&mut self) {
        self.irq.evtenset.write(|w| w.tick().set_bit());
        self.irq.intenset.write(|w| w.tick().set_bit());
        self.irq.tasks_start.write(|w| unsafe { w.bits(1) });
    }

    fn disable(&mut self) {
        self.irq.evtenset.write(|w| w.tick().clear_bit());
        self.irq.intenset.write(|w| w.tick().clear_bit());
        self.irq.tasks_stop.write(|w| unsafe { w.bits(1) });
    }

    fn clear(&mut self) {
        self.irq.events_tick.write(|w| unsafe { w.bits(0) });
    }
}

To use it one would instantiate the component:

    let mut rtc0irq = interrupts::Irq::new(p.RTC0, 64_000);
    rtc0irq.enable();
    // plus whatever magic is needed to activate the interrupt, e.g. via NVIC
    // plus the magic to set up the interrupt handler

and in the interrupt handler one would clear it:

    rtc0irq.clear();

from embedded-hal.

thejpster avatar thejpster commented on September 18, 2024

This is an interesting idea, but I have a couple of questions.

  • How do you get the peripheral back so that you can call functions on it?
  • How do you propose access is gained to the rtcirq0 object in the interrupt, when it is likely to have been created on the stack?

from embedded-hal.

therealprof avatar therealprof commented on September 18, 2024

How do you get the peripheral back so that you can call functions on it?

I'd compose it into the HAL implementation if necessary.

How do you propose access is gained to the rtcirq0 object in the interrupt, when it is likely to have been created on the stack?

Using the usual static Mutex<RefCell<Option<>>> hack which is required anyway regardless whether this this new trait is utilised or not.

from embedded-hal.

bjc avatar bjc commented on September 18, 2024

Agree this seems like something that would ideally be provided by embedded-hal. Can we have a list, akin to #37 about requirements? Off the top of my head, we'd need enable, disable, and clear for a base trait. We could have a Pendable trait that also adds pend and unpend.

Would they need anything, at the HAL level, other than just the interrupt number?

While it would be nice to encapsulate the interrupt vector setup in the HAL, I don't think that's a requirement in order to proceed with a trait or two that describes the basic functions of the interrupt controller.

from embedded-hal.

rubberduck203 avatar rubberduck203 commented on September 18, 2024

I was recently working on an embedded linux program and the concept of an interrupt for linux-embedded-hal would be quite different than pend and unpend. In that world, what you get is not a static interrupt handler, but a mspc::Channel::Rx, which makes me wonder if some sort of no_std channel would be a better abstraction for drivers to consume than trying to abstract away the very hardware dependent interrupts.

from embedded-hal.

Related Issues (20)

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.