1

How could I create an interrupt for a blue pill from scratch?

I do not want to use any sort of special library. Also, I use Keil IDE, thus, by "building from scratch" I refer rather not to use any extra library than to assemble the project without the help of an IDE.

I tried to find resources, but no success. Could anybody help me and at least provide some information/bibliography for me? I would be grateful.

Moreover, by "strange library" I mean any other library than the stmf32f1xx.h header. I would like to fire an interrupt when one of the pins' input value toggles. In order to do this, on AVR MCUs it was very simple as long as only a few register values should be changed. Unfortunately, I don't know how an interrupt within an ARM MCU functions and in which registers should I write what values.

Also, a better understanding of the ARM MCU's interrupt mechanism would make me more prepared for tackling debouncing issues.

25
  • What do you mean by "How could I create an interrupt"? What exactly do you want to do? Most interrupt related stuff is described in the Cortex®-M3 programming manual. Commented Apr 17, 2021 at 19:14
  • you need to master the tool. you will need to control the vector table, and figure out how to do this in kiel and/or how their sandbox works. start with the systick timer exception and worry about interrupts later. Commented Apr 18, 2021 at 1:55
  • Of course poll your way through interrupts and only enable it to the core as the very last thing...follow the interrupt from the peripheral to the interrupt controller, how to clear the interrupt, etc...use one of the timers, which means learn the timer first. Interrupts are like the 20th of 40th thing you want to learn not the second or third. Commented Apr 18, 2021 at 1:56
  • you want the cortex-m3 technical reference manual from arm. and the armv7m architectural reference manual from arm. and the reference manual from ST for this part. Commented Apr 18, 2021 at 1:57
  • I find it SIGNIFICANTLY easier to get the uart working so I can print stuff out, register values, etc, to see how the peripherals work, particular the status registers...(not printf of course roll your own, printf is massive and something you want to start to avoid with bare metal (as with most system calls)) Commented Apr 18, 2021 at 2:00

1 Answer 1

2

I am not going to take you entirely literally when you mandate "no libraries", because no one who wants to get work done and knows what they are doing on Cortex-M would do that - and I will assume at least that you will use the CMSIS - a common API provided for all ARM Cortex-M devices, and which makes your code more, not less portable.

All the CMSIS code is provided as source, rather than static library, so there is nothing hidden and if you chose not to use it, you can see how it works and replicate that functionality (needlessly) if you wish.

In the CMSIS default implementations are provided as "weak-links" that can be overridden by user code simply by defining a function of the pre-defined name to override the default. The default implementation is generally an infinite loop - so that unhandled interrupts are "trapped" so you can intervene with your debugger or wait for a watchdog reset for example.

The Cortex-M core interrupt handlers and exception handlers have common names across all Cortex-M parts:

Reset_Handler      
NMI_Handler        
HardFault_Handler  
MemManage_Handler  
BusFault_Handler   
UsageFault_Handler 
SVC_Handler        
DebugMon_Handler   
PendSV_Handler     
SysTick_Handler    

Peripheral interrupt handlers have names defined by the vendor, but the naming convention is <interrupt_source>_IRQHandler. For example on STM32F1xx EXTI0_IRQHandler is the shared external interrupt assigned to bit zero of GPIO ports.

To implement an CMSIS interrupt handler, all you need do is:

  1. Implement the interrupt handler function using the CMSIS handler function name
  2. Enable the interrupt in the NVIC (interrupt controller).

There other are things you might do such as assign the interrupt priority scheme (the split between preempt priorities and subpriorities), but lets keep it simple for the time being.

Because it is ubiquitous to all Cortex-M parts, and because it is useful in almost any non-trivial application an illustration using the SYSTICK interrupt is useful as a starting point.

#include "stm32f1xx.h"
  
volatile uint32_t msTicks = 0 ;
  
void SysTick_Handler(void)  
{
    msTicks++ ;
}
  
int main (void)  
{
    if( SysTick_Config( SystemCoreClock / 1000 ) != 0 ) // 1ms tick
    {
        // Error Handling 
    }
  
    ...

}

SysTick_Config() is another CMSIS function. In core_cm3.h it looks like this:

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}

So let's say you have a external interrupt source on the falling edge of GPIOA pin 0, then you would use the STM32 EXTI0 interrupt. The minimal handler would look like:

void EXTI0_IRQHandler(void)
{
    EXTI->PR |= (1<<0);                           // clear pending interrupt

    // Handle interrupt...
}

Setting up the EXTI requires enabling the GPIO and the EXTI itself as well as the NVIC:

RCC->APB2ENR |= RCC_APB2ENR_IOPAEN ;           // enable clock for GPIOA
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN ;           // enable clock for Alternate Function
AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0 ;        // set pin to use

EXTI->IMR = EXTI_IMR_MR0 ;             // unmask interrupt
EXTI->EMR = EXTI_EMR_MR0 ;             // unmask event
EXTI->FTSR = EXTI_FTSR_TR0 ;           // set falling edge

NVIC->ISER[0] |= (1 << (EXTI0_IRQChannel & 0x1F));    // enable interrupt EXTI 0

The peripheral registers and structures are defined in stm32f10weakx.h, and the "weak" default peripheral handlers to be overridden are in startup_stm32f10x_cl.s for your specific part. Any handlers you override must match these symbol names exactly.

All the peripheral interrupt sources and how to configure them is defined un the ST Reference Manual RM0008.

All the Cortex-M core specific stuff - systtick, NVIC, exception handlers etc. is provided by ARM at https://developer.arm.com/ip-products/processors/cortex-m/cortex-m3

CMSIS for CM3 is documented at https://developer.arm.com/documentation/dui0552/a/

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.