[ivory-search id="2373" title="AJAX Search Form"]
Search

Become a leader in the IoT community!

New DevHeads get a 320-point leaderboard boost when joining the DevHeads IoT Integration Community. In addition to learning and advising, active community leaders are rewarded with community recognition and free tech stuff. Start your Legendary Collaboration now!

Step 1 of 5

CREATE YOUR PROFILE *Required

OR
Step 2 of 5

WHAT BRINGS YOU TO DEVHEADS? *Choose 1 or more

Collaboration & Work 🤝
Learn & Grow 📚
Contribute Experience & Expertise 🔧
Step 3 of 5

WHAT'S YOUR INTEREST OR EXPERTISE? *Choose 1 or more

Hardware & Design 💡
Embedded Software 💻
Edge Networking
Step 4 of 5

Personalize your profile

Step 5 of 5

Read & agree to our COMMUNITY RULES

  1. We want this server to be a welcoming space! Treat everyone with respect. Absolutely no harassment, witch hunting, sexism, racism, or hate speech will be tolerated.
  2. If you see something against the rules or something that makes you feel unsafe, let staff know by messaging @admin in the "support-tickets" tab in the Live DevChat menu.
  3. No age-restricted, obscene or NSFW content. This includes text, images, or links featuring nudity, sex, hard violence, or other graphically disturbing content.
  4. No spam. This includes DMing fellow members.
  5. You must be over the age of 18 years old to participate in our community.
  6. Our community uses Answer Overflow to index content on the web. By posting in this channel your messages will be indexed on the worldwide web to help others find answers.
  7. You agree to our Terms of Service (https://www.devheads.io/terms-of-service/) and Privacy Policy (https://www.devheads.io/privacy-policy)
By clicking "Finish", you have read and agreed to the our Terms of Service and Privacy Policy.

How to Manually Program an Interrupt Service Routine (ISR) for STM32F4 MCU Without Using Libraries?

Good day everyone,
I have gone through the Datasheet and User Manual for my STM32F4 Microcontroller Unit (MCU) in great detail, including PM0214 for STM32F4xx MCUs. Additionally, I have searched various online resources that provide information on programming interrupts without using a library but didn’t find much guidance. My question is if NVIC’s integration with hardware essentials makes it difficult to program an interrupt manually by specifying ISR address and function without relying on any libraries? Everywhere I look there are functions like:

Enable IRQn using NVIC.

Setting the priority for a specified IRQn type can be achieved by using `NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)`.

What if someone desires to manually code an ISR for the purpose of learning?

Which steps do I need to take? Is there documentation available for reference? Would it be advisable or worthwhile to follow this approach?

  1. ke7c2mi#0000

    First things first – lets keep in mind there are multiple layers of abstraction in the ecosystem

    You have the ST high level and low level HALs – it is very common to not use these, or just use them as reference / guidance when implementing your own drivers

    Then there is CMSIS – this is a standard made by arm so we don’t reinvent the wheel a million times in how some basic functions are handled, taking care of some of the core Cortex-XX things. In general, use CMSIS and don’t throw it away – it is much less common to not use CMSIS than it is to not use whichever HAL

    `NVIC_SetPriority` comes from CMSIS – so typically we would always use it
    (it’s worth noting here, part of why Cortex-M3 was novel was ARM made not just the core, but a bunch of supporting stuff too, they mandated that there would be an NVIC and a Systick in all the systems and so these parts can be common even across different manufacturers, never mind parts – this is a huge step up from the older days. We can see some of the pain of the many interrupt handler implementations in the RISCV ecosystem today, though I suspect that will normalise over time)

    If you look at it though, all it does is poke a couple of registers, there is absolutely nothing stopping you poking those registers directly – we can definitely replace `NVIC_SetPriority` if we want to – I would go as far as to say this is easy.

    One other note on things to consider throwing away and things to keep – keep the low level device headers i.e. the ones which give names to the memory map: https://github.com/STMicroelectronics/cmsis_device_f4/blob/master/Include/stm32f407xx.h – we generate them now from svd

    To do an interrupt from scratch we need to go further though. At the end of the day, an interrupt is some function, with some specific calling convention, which exists at some explicit location in memory. We will see some systems let us point the NVIC to an interrupt table at various locations and know this can be somewhat configurable.

  2. ke7c2mi#0000

    But how do we define that table? Well we can see how ST do it here:

    https://github.com/STMicroelectronics/cmsis_device_f4/blob/master/Source/Templates/arm/startup_stm32f401xc.s

    “`
    ; Vector Table Mapped to Address 0 at Reset
    AREA RESET, DATA, READONLY
    EXPORT __Vectors
    EXPORT __Vectors_End
    EXPORT __Vectors_Size

    __Vectors DCD __initial_sp ; Top of Stack
    DCD Reset_Handler ; Reset Handler
    DCD NMI_Handler ; NMI Handler
    ….
    “`

    This is startup code, we see the table of vectors being declared at line 60, but that’s only half the story, we should also look at the linker file:

    https://github.com/STMicroelectronics/STM32CubeF4/blob/master/Projects/STM32446E-Nucleo/Templates/STM32CubeIDE/STM32F446RETX_FLASH.ld

    This is where the table actually gets placed at a specific location:

    “`
    /* The startup code into “ROM” Rom type memory */
    .isr_vector :
    {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
    } >ROM
    “`

    In English – put the symbol “.isr_vector” at the start of the ROM section – which is flash – which is where our table should be on startup

    … we’re still not done yet…

  3. ke7c2mi#0000

    Lets go back to the startup code and look inside the table… what is this?

    “`
    ; External Interrupts
    DCD WWDG_IRQHandler ; Window WatchDog interrupt ( wwdg1_it)
    DCD PVD_AVD_IRQHandler ; PVD/AVD through EXTI Line detection
    DCD TAMP_STAMP_IRQHandler ; Tamper and TimeStamps through the EXTI line
    DCD RTC_WKUP_IRQHandler ; RTC Wakeup through the EXTI line
    DCD FLASH_IRQHandler ; FLASH
    DCD RCC_IRQHandler ; RCC
    “`

    That’s all the symbols for all the interrupt handlers being declared… we are about to have some fun though because what is this….

    “`
    Default_Handler PROC

    EXPORT WWDG_IRQHandler [WEAK]
    EXPORT PVD_AVD_IRQHandler [WEAK]
    EXPORT TAMP_STAMP_IRQHandler [WEAK]
    EXPORT RTC_WKUP_IRQHandler [WEAK]
    “`

  4. ke7c2mi#0000

    This is declaring a “weak linkage” handler for each interrupt. It’s important to understand this weak linkage as it can be a source of confusion – it means “if no other thing provides this symbol, provide this value for it” – this lets the startup code install default handlers for every interrupt. When you declare the symbol in your code i.e. make a function `void RTC_WKUP_IRQHandler(void)…` you override that linkage, and now your function instead of the default handler will be there. I want to call this part out in particular. It is a common mistake in my experience for people to misname an interrupt vector – there are not really warnings when this happens because no symbol is missing, just the interrupt handler isn’t called anywhere – this will make it “just not work” and it wont be clear why – if you have a debugger you will see when the interrupt fires you get stuck in the default handler – that is the clue you messed up there!

    All of this is just a very long way to say – if you are using the ST startup code, you just need to define a function with a specific name and it will handle that interrupt – making your own interrupt handler for RTC wakeup events is as simple as creating the function `RTC_WKUP_IRQHandler` and doing any setup to get the interrupts

  5. ZacckOsiemo#0000

    I think you scared him 🙂 for sure all the info though.

  6. ZacckOsiemo#0000

    @ifreakio if you look at the code around this commit https://github.com/zacck/stm32f4xx_device_drivers/commit/094f0bd64252e6b5323cbf0b62e137d8039e0453 you will see interrupts from scratch

  7. ke7c2mi#0000

    This is really neat 🤓

  8. ZacckOsiemo#0000

    Ah it’s all just reading the reference manual and trying to code it up, mostly I fail.

CONTRIBUTE TO THIS THREAD

Leaderboard

RANKED BY XP

All time
  • 1.
    Avatar
    @Nayel115
    1620 XP
  • 2.
    Avatar
    @UcGee
    650 XP
  • 3.
    Avatar
    @melta101
    600 XP
  • 4.
    Avatar
    @lifegochi
    250 XP
  • 5.
    Avatar
    @Youuce
    180 XP
  • 6.
    Avatar
    @hemalchevli
    170 XP