cancel
Showing results for 
Search instead for 
Did you mean: 

Configuring EXTI->EXTICR on Nucleo STM32G031K8 (Cortex M0+)

lab1
Associate II

Good afternoon,

I am attempting to write a bare metal C driver for an interrupt specifically on GPIOA8 for the nucleo-g031k8 (Cortex M0+). I believe there is an issue with how I am changing the register EXTICR in the EXTI. According to figure 29 (pg 322 of RM) I need to adjust the EXTI_EXTICR1.EXTI8 mux to access the PA8 gpio.But when I view the cmsis header file for this chip (stm32g031xx.h) there is no cr1 for pin 8. The only available register is EXTI_EXTICR3_EXTI8. Why is this the case? I would expect the CMSIS header file to follow the RM0444 manual you provided for the chip and have an available EXTICR1_EXTI8 variable available but there is none. Things get even more confusing when I debug the chip and step through the program. According to the debugger, EXTI_EXTICR3_EXTI8 doesn't actually adjust CR3 but instead changes the CR4 register. If any of you could provide a detailed explanation about the EXTICR in this reference manual specifically what m and x mean in the manual and how I am supposed to use this register I think this will fix my issue. I have pasted my code below. If you could explain what I'm doing wrong here, that would be a great help!

 

Important documents:

  • Cortex M0 generic user guide
  • RM0444 STM32G0x1 advanced Arm®-based 32-bit MCUs

 

void pa8_exti_init(void){

/*Disable global interrupts*/

__disable_irq();

 

/*Enable clock access for GPOIA*/

RCC->IOPENR |= RCC_IOPENR_GPIOAEN;

 

/*Set PA8 as input*/

GPIOA->MODER &= ~GPIO_MODER_MODE8_Msk; // ~(11) = 00: input

 

/*Enable clock access for SYSCFG*/

RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN;

 

/*Select PORTA8 for EXTI*/

EXTI->EXTICR[3] &= ~EXTI_EXTICR3_EXTI8_Msk;

EXTI->EXTICR[3] |= EXTI_EXTICR3_EXTI8

 

/*Unmask EXTI8*/

EXTI->IMR1 |= EXTI_IMR1_IM8;

 

/*Select falling edge trigger*/

EXTI->FTSR1 |= EXTI_FTSR1_FT8 ;

 

/*Enable EXTI8 line in NVIC*/

NVIC_EnableIRQ(EXTI4_15_IRQn);

 

/*Enable global interrupts*/

__enable_irq();

}

1 ACCEPTED SOLUTION

Accepted Solutions

how is works on a C0 (similar) (edited):

 

void EXTI4_15_IRQHandler(void) {
    if(EXTI->FPR1 & EXTI_FPR1_FPIF8) {
        // falling edge on PA8 -> IRQ pending
        blink();
        EXTI->FPR1 |= EXTI_FPR1_FPIF8; // clear pending bit
    }
}


// make PA8 EXTI interrupt
void init_EXTI() {
    RCC->IOPENR |= RCC_IOPENR_GPIOAEN; // enable peripheral clock
    (void)RCC->IOPENR; // read back to make sure that clock is on
    GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODE8_Msk) | (0 << GPIO_MODER_MODE8_Pos); // set PA8 to input mode

    // let m be the pin number  (0..15)
    // let x be the port number (A=0, B=1,...)
    // for PA8:
    int m = 8;  // variable name m corresponds to reference manual
    int x = 0;  // variable name x corresponds to reference manual

    EXTI->FTSR1 |= 1 << m; // enable falling edge for pin m (0..15)
    
    // there is 1 byte for each port number
    // that is 4 bytes per 32-bit register (layout chosen by CMSIS)
    EXTI->EXTICR[m / 4] &= ~(0xF << (m % 4)); // clear port bits for pin m
    EXTI->EXTICR[m / 4] |=   x << (m % 4);    // set port x for pin m

    EXTI->IMR1 |= EXTI_IMR1_IM8;    // EXTI CPU wakeup with interrupt mask register
    //EXTI->EMR1 |= EXTI_EMR1_EM8;    // EXTI CPU wakeup with event mask register

    NVIC_EnableIRQ(EXTI4_15_IRQn);
}

 

 

 

 

 

View solution in original post

7 REPLIES 7
TDK
Guru

EXTI0-3 are in EXTICR1.

EXTI4-7 are in EXTICR2.

EXTI8-11 are in EXTICR3.

EXTI12-15 are in EXTICR4.

 

I think the manual is consistent here with its "x" and "m" variables. Although perhaps a little confusing to unravel at first.

 

Note that EXTICR3 is EXTI->EXTICR[2], not 3. Zero based indices.

If you feel a post has answered your question, please click "Accept as Solution".
lab1
Associate II

You're answer is true and is shown in the CR2 register cmsis header file (stm32g031xx.h): 

lab1_0-1724283849564.png

However, I'm still pretty lost here. I probably should have phrased my question differently.

Because 0x00 is for gpio PA[m] how do I change this register to make PA8 the interrupt? using the provided formula m=(4 * (x -1))  where x is 8, m = 28. does this mean I need to make bit position 28 a 1 in this case? The RM calls the 8-bit sections EXTIm, EXTIm+1, etc... this confuses me because, as you said, CR3 (CR2 zero base) is EXTI8-11. which doesn't work with anything related to 28 that I can see. Not to mention CR3 is also m+2. 

> The RM calls the 8-bit sections EXTIm, EXTIm+1, etc...

Don't get confused by how the RM *calls* things, that's just names and does not necessarily perfectly reflect the functionality. And unfortunately they got the numbering very wrong. ST here shot itself into its own foot and confused the users unnecessarily.

Simply put, there's an array of 32 bytes, where each byte controls one EXTI source, nicely consecutively, so the byte at offset 0x60 selects between Px0 pins, byte at offset 0x61 for Px1 pins etc For purposes of documentation those registers are shown in groups of 4 as words. This grouping then results in the numbering confusion. However, EXTI registers *are* bytewise accessible, see beginning of the registers description subchapter, so going for that might perhaps result in less confusing notation.

JW

PS. For PAx, you don't need to set the respective EXTI mux selection at all, as 0 is its reset-default state (unless you want to swap between the sources in runtime).

KnarfB
Principal III

Note that EXTICR3 is EXTI->EXTICR[2], not 3. Zero based indices.

Yeah, but only when using the array index, not when using the register names. So EXTI_EXTICR3_EXTI8 is for EXTI->EXTICR[2]. 

A guarantee for fun.

KnarfB

how is works on a C0 (similar) (edited):

 

void EXTI4_15_IRQHandler(void) {
    if(EXTI->FPR1 & EXTI_FPR1_FPIF8) {
        // falling edge on PA8 -> IRQ pending
        blink();
        EXTI->FPR1 |= EXTI_FPR1_FPIF8; // clear pending bit
    }
}


// make PA8 EXTI interrupt
void init_EXTI() {
    RCC->IOPENR |= RCC_IOPENR_GPIOAEN; // enable peripheral clock
    (void)RCC->IOPENR; // read back to make sure that clock is on
    GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODE8_Msk) | (0 << GPIO_MODER_MODE8_Pos); // set PA8 to input mode

    // let m be the pin number  (0..15)
    // let x be the port number (A=0, B=1,...)
    // for PA8:
    int m = 8;  // variable name m corresponds to reference manual
    int x = 0;  // variable name x corresponds to reference manual

    EXTI->FTSR1 |= 1 << m; // enable falling edge for pin m (0..15)
    
    // there is 1 byte for each port number
    // that is 4 bytes per 32-bit register (layout chosen by CMSIS)
    EXTI->EXTICR[m / 4] &= ~(0xF << (m % 4)); // clear port bits for pin m
    EXTI->EXTICR[m / 4] |=   x << (m % 4);    // set port x for pin m

    EXTI->IMR1 |= EXTI_IMR1_IM8;    // EXTI CPU wakeup with interrupt mask register
    //EXTI->EMR1 |= EXTI_EMR1_EM8;    // EXTI CPU wakeup with event mask register

    NVIC_EnableIRQ(EXTI4_15_IRQn);
}

 

 

 

 

 

It means you need to set the EXTICR3_EXTI8 field to 0 (all zero bits).

Your original code should be changed to the following:

 

/*Select PORTA8 for EXTI*/
// clear all bits in EXTICR3_EXTI8
EXTI->EXTICR[2] &= ~EXTI_EXTICR3_EXTI8;
// set EXTICR3_EXTI8 field to 0 for port A
EXTI->EXTICR[2] |= 0 << EXTI_EXTICR3_EXTI8_Pos;

 

(Or do nothing as @waclawek.jan suggests as GPIOA is the default.)

If you feel a post has answered your question, please click "Accept as Solution".
lab1
Associate II

Thank you, everyone, for helping explain how this register is configured. I can't say I am a fan of the naming conventions used here or the explanation in the RM, but it's starting to make sense!

 

For future reference:

PA[m] is a NO OP since the reset state is already 0 for all bits, and PA is 0x00 (Everyone).

For EXTICRx,     m is the pin number 1 2 etc, and x is the port A, B, etc... (KnarfB).

The debugger displays the register name not the index which is why it shows a different exticr[] than whats written in the code. The debugger name is not zero-based, and the code calls the array position (Everyone)