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.

Clifford
  • 88,407
  • 13
  • 85
  • 165
pauk
  • 350
  • 4
  • 15
  • 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](https://www.st.com/resource/en/programming_manual/cd00228163-stm32f10xxx20xxx21xxxl1xxxx-cortexm3-programming-manual-stmicroelectronics.pdf). – Codo Apr 17 '21 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. – old_timer Apr 18 '21 at 01: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. – old_timer Apr 18 '21 at 01: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. – old_timer Apr 18 '21 at 01: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)) – old_timer Apr 18 '21 at 02:00
  • I want to learn interrupts from scratch because I need to read the values from a couple of switches. Here I have two choices, either I approach a polling technique, or I use interrupts. I don't want to use polling as long as it is a very unpleasant way in order to solve this kind of problems, in other words, I could miss a toggling switch and even if I choose an adequate frequency in order to catch all the toggling switches, this requires a lot of CPU-time spent for this process. – pauk Apr 18 '21 at 06:15
  • That's why people created interrupts :-) .But I don't want to use strange libraries in order to do this. I thought that it is possible to create some interrupts in a very simple fashion from scratch. I created some interrupts for AVR controllers and I found it easy. Also, I found plenty of resources related to this topic. I couldn't assert the same in this case, for ARM controllers. – pauk Apr 18 '21 at 06:20
  • What constitutes a "strange library"? The CMSIS is ubiquitous for Cortex-M parts and included with Keil ARM MDK. You could write the NVIC registers directly but there is no real benefit. You in any case need to be clearer on your constraints. For example are you prepared to include the stm32f1xx.h header? Also the question is too broad; there are numerous interrupt sources. – Clifford Apr 18 '21 at 12:59
  • You have added information about what you actually want to do in comments, that should be in the question. Polling a switch in a timer interrupt is trivial from a CPU load point of view, and makes denouncing simpler. If you use an EXTI for the switch, you will get a flurry of interrupts for each throw of the switch. I am not saying not to do it, there are good reasons for doing so, such as hard real time response, but don't thing you are doing so because it is easier or more CPU efficient – Clifford Apr 18 '21 at 13:06
  • @Clifford By "strange library" I mean any other library than the stm32f1xx.h header. – pauk Apr 18 '21 at 13:47
  • I do not know exactly how an ARM works, instead, I know how to create interrupts on AVR from scratch, using only the registers. I thought that it was possible to do the same for ARM ucontrollers. Also, yes, I know how to tackle debouncing. – pauk Apr 18 '21 at 13:49
  • Nevertheless, @Clifford nobody recommended me some bibliography. I would like to receive some suggestions related to resources/tutorials for creating interrupts. – pauk Apr 18 '21 at 13:50
  • Moreover, @old_timer I have my own reasons to learn NOW interrupts from scratch on ARM, and yeah, they are verrrrry important, you cannot even receive a signal from a switch in an efficient way. There are a lot of reasons for which the polling method is very inefficient compared to the interrupt approach. As I said, that's why people invented interrupts :-). – pauk Apr 18 '21 at 14:08
  • That is to say, I didn't question WHAT should I learn related to the ARM MCUs, but my question was related to what bibliography should I read/learn in order to create interrupts. – pauk Apr 18 '21 at 14:28
  • you definitely do not want to use interrupts for switches and buttons, that is generally not. a good idea, depends on the design. – old_timer Apr 18 '21 at 14:28
  • based on the led experience I am trying to maximize your success and minimize frustration and failures. very very happy to see you avoiding the libraries and happy to help – old_timer Apr 18 '21 at 14:29
  • blink led, use systick to time the led and verify your clock speed, use the clock speed to attempt to get the uart to work, ideally with a scope, use the uart to print registers out then learn how to use one of the timers, in a polling fashion. use the systick timer to create periodic exceptions and work on overall infrastructure of the code with the vector table and handlers, uart helps. – old_timer Apr 18 '21 at 14:32
  • @old_timer I want to learn this because I have my own reasons. I tried to perform both the polling and the interrupt techniques on other microcontrollers. The problem is that I want to learn how to deal with interruptions on ARM. I didn't ask what are the pros and cons of using interrupts. I know that it is harder to deal with interrupts, and in some cases exhaustive, but maybe I would like to learn - educational purpose -, or maybe I face some of those situations where interrupts are much more efficient than polls. That's what I'm trying to say. – pauk Apr 18 '21 at 14:33
  • use the uart to see how one of the simpler timers really works, follow the roll over/under interrupt through to the interrupt controller using polling, dont enable to core until fully understood, then use the systick exception experience to get the vector table right and and there is sometimes one more step to clear the interrupt – old_timer Apr 18 '21 at 14:33
  • @pauk : Requests for resources are off topic on SO - it mist be a slow day here if your question has not been closed down yet. Google is a far better method of finding resources. – Clifford Apr 18 '21 at 14:33
  • doesnt matter how many decades of experience on however many platforms if you ware interested in success and success in the least amount of time, follow a process like this, otherwise you spend all day waiting for stacloverflow to just give you the answers, or try to work through failures borrowing other code from the internet. – old_timer Apr 18 '21 at 14:35
  • and you still have to try to interpret the answers, your two biggest problems right now are how to generate the exception interrupt and the bigger one is how does the build process work for the tools and sandbox you are using to get your handler in there. using gnu and really rolling your own is relatively trivial to using a sandbox, but ymmv I have not messed with arm tools since before the realview/allant thing happend...(well before they bought keil) – old_timer Apr 18 '21 at 14:36
  • your first question on this topic should be, here is the disassembly of the vector table I am using keil with these settings and I want to change the vector for a particular interrupt/exception and it is not working (After of course googling how do I change a vector with this tool in this mode for this target) – old_timer Apr 18 '21 at 14:38
  • or just look at the dozens to hundreds of examples out there that do this exact thing – old_timer Apr 18 '21 at 14:38
  • there are multiple solutions for each toolchain so you also need to indicate what general development path you are taking for a paritcular toolchian including a minimal example of what you are doing (more info than you showed on the led thing) – old_timer Apr 18 '21 at 14:39

1 Answers1

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/

Clifford
  • 88,407
  • 13
  • 85
  • 165