cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F405: GPIO config cannot be changed without EXTI DeInit

felix239955
Associate II
Posted on June 12, 2015 at 15:18

Hi,

i have PC9 (and some other pins) configured for PWM output and also use an external interrupt on that same pin to get an IRQ on every edge of the PWM. This all works perfectly fine, although in the reference manual RM0090 under point 8.3.8 one can read: ''To use external interrupt lines, the port must be configured in input mode[...]''.

Only when I try to change the GPIO configuration of this pin, I am running into very strange problems.

For example if I want to change the I/O type of PC9 in the MODER register from alternate function to input, this modification is not applied and I can also see that the register value is not altered when debugging. Instead the pin is now sitting on round about 1,6V (Vcc/2) with a rather large ripple on top.

After this failed reconfiguration I get stuck in an endless loop of ISRs from EXTI9_5 (the external interrupt for PC9), which seems to be caused by the voltage floating right around the threshold level.

This leads to a few questions:

1. What exactly can prevent the GPIO registers from getting changed?

2. How can I reconfigure the GPIO without fully disabling and afterwards re-enabling the external interrupt?

3. Why does PC9 put out about half the supply voltage, how is this even possible and how do I prevent this strange behaviour?

I would appreciate it, if anybody could help me out.

Thanks in advance!

Felix

#stm32f4-exti-pwm
7 REPLIES 7
Chris1
Senior III
Posted on June 12, 2015 at 22:18

It sounds like you are using a single pin for input and output (driven by a timer, presumably), simultaneously?

It's surprising if that appears to work at all.

So when you try to set the pin to input only, what is driving the pin?

felix239955
Associate II
Posted on June 12, 2015 at 23:19

Thanks for your reply Chris!

In this application the pin PC9 is solely used as an output to control a highside switch, but can be configured as PWM (timer controlled) or simple digital output during the program execution. Between these configuration changes I firstly want to switch to input mode, because it is the safe reset state of the pin, when the output is currently not used.

Apart from that, the external interrupts are enabled and the interrupt line for the pin numbers 5 to 9 is mapped to PC9 also. Because I need every edge of the output signal, the trigger is set for rising and falling edges

I tried all this well before I found the passage in the manual, that i quoted above, but like I said, it works perfectly until I want to change the GPIO configuration.

The Pin is not driven from the rest of the circuit, only pulled down via 100k, so the 1,6V that I mentioned before must somehow be generated inside the STM32 output stage. It may also be a very weak output of the full supply voltage, which is then halfed by the external pulldown. I will check that in detail on Monday.

Regards,

Felix

Chris1
Senior III
Posted on June 12, 2015 at 23:52

Interesting.

It may be worth checking to see if the GPIO locking mechanism has been engaged.

Posted on June 13, 2015 at 01:27

The internal pull up/down is supposed to be in the 10K range. I don't see this halving the output.

It's more likely two push-pull drivers fighting, but without better insight into the circuit as built it's hard to say from here. Could you have some shorts or soldering issues? Are multiple boards showing the same or different behaviour? Also, other than locking mechanism, or disabling the clock, I can't say I've experienced the peripheral bits being impossible to modify. I guess I'd need to see specifically the code doing it now, or if things like bit-banding were being used.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
felix239955
Associate II
Posted on June 15, 2015 at 14:43

Thanks for your replies!

Today I completely disconnected the pin (PC9) from the rest of our circuit to exclude it from the problem and also double checked for bad solder joints and shorts to other pins, but the error stills remains unchanged. In the error case the voltage of the pin is a rather clean sine wave with a frequency of 168MHz (system clock) and a voltage between 0,5V and 2,6V, so floating around 1,6V like I mentioned before. We firstly discovered the problem of getting stuck in an infinite interrupt handler loop on another board. I did not measure the output voltage of the pin on this other board, but the problem itself is reproducible. We do not use the locking function anywhere but it was the first thing I checked for, after I realized that the GPIO configuration cannot be changed. According to my debugger the LCKR register remains in its reset state all the time. The peripheral clocks of the output (AHB1) and the external interrupt (APB2) are not getting disabled, except in a de-init function that is never called in the current code. I must admit, that I had never heard of bit-banding before but according to what I read about it, we do not use it. At the moment all changes in the registers are exclusively done via the functions in the standard peripheral library which is provided by ST and based on the CMSIS. Here is a little piece of the code, where the pin should be reconfigured. This is used many times elsewhere and normally working.

{
GPIO_InitTypeDef Struct;
… 
Struct.GPIO_Pin = PinPort.ui16Pin;
Struct.GPIO_Mode = GPIO_Mode_IN;
Struct.GPIO_Speed = GPIO_Speed_2MHz;
Struct.GPIO_OType = GPIO_OType_PP;
Struct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOx, &Struct);
}

The function “GPIO-Init�? is part of the standard peripheral library, provided by ST.


uint32_t GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

{

uint32_t ui32Ret = 1;


uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;


/* Check the parameters */

assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));

assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));

assert_param(IS_GPIO_PUPD(GPIO_InitStruct->GPIO_PuPd));


/* -------------------------Configure the port pins---------------- */

/*-- GPIO Mode Configuration --*/

for
(pinpos = 0x00; pinpos < 0x10; pinpos++)

{

pos = ((uint32_t)0x01) << pinpos;

/* Get the port pins position */

currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;


if
(currentpin == pos)

{

GPIOx->MODER &= ~(GPIO_MODER_MODER0 << (pinpos * 2));

GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (pinpos * 2));


if
((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF))

{

/* Check Speed mode parameters */

assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));


/* Speed mode configuration */

GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pinpos * 2));

GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed) << (pinpos * 2));


/* Check Output mode parameters */

assert_param(IS_GPIO_OTYPE(GPIO_InitStruct->GPIO_OType));


/* Output mode configuration*/

GPIOx->OTYPER &= ~((GPIO_OTYPER_OT_0) << ((uint16_t)pinpos)) ;

GPIOx->OTYPER |= (uint16_t)(((uint16_t)GPIO_InitStruct->GPIO_OType) << ((uint16_t)pinpos));

}


/* Pull-up Pull down resistor configuration*/

GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)pinpos * 2));

GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (pinpos * 2));


ui32Ret = 0;

}

}


return
(ui32Ret);

}

Regards, Felix
Chris1
Senior III
Posted on June 15, 2015 at 23:03 If you completely disconnected the pin from the circuit, it could appear to have just about any voltage less than V DD. Does it have an unexpected voltage if it is pulled low (and not connected to anything else except the pull-down resistor) ? Does your ''

Struct

'' structure have all the right values when GPIO_Init() is called? For example, is Struct.GPIO_Pin = 0x0200 (GPIO_Pin_9)?
felix239955
Associate II
Posted on June 17, 2015 at 11:50

I found the cause of the problem yesterday and wanted to make sure that it is solved before making the solution public, so sorry for the late answer.

I stretched the code step by step to only have one instruction per line for easier debugging. These modifications made it somehow possible to step further trough the code. Unlike I thought, the error did not occur in the GPIO reconfiguration itself and also the MODER register was modified correctly, but it occured a few lines later during the configuration of the alternate function. Another example, that you cannot always trust your debugger. In the function below, out of stm32f4xx_gpio.c so again part of the peripheral library, the alternate function is always set to the reset state AF0 first and then changed again to the desired AF. In case of PC9 and also PA8 (currently not used) AF0 is NOT a safe reset state but the MOC (clock output) is activated. This is a rather dumb practice, because it produces at least one pulse on the pin. In my case the rising edge of this puls instantly triggered the external interrupt, which then prevented the register from being changed again. Because of the ongoing output of the system clock on PC9 the corresponding external interrupt is rapidly triggered again and again, which causes the CPU to get stuck in an infinite loop of interrupt handlers.


void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF)

{ 

uint32_t temp = 0x00;

uint32_t temp_2 = 0x00;


/* Check the parameters */

assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));

assert_param(IS_GPIO_AF(GPIO_AF));


temp = ((uint32_t)(GPIO_AF) << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4)) ;

GPIOx->AFR[GPIO_PinSource >> 0x03] &= ~((uint32_t)0xF << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4));

//Error is happening here

temp_2 = GPIOx->AFR[GPIO_PinSource >> 0x03] | temp;

GPIOx->AFR[GPIO_PinSource >> 0x03] = temp_2;

} 

My solution for this problem can be seen below. The current value of the whole register is now stored in a variable, modified there and then written back to the register. Therefore the register is modified only once and never set back to reset state unintentionally.


void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF)

{

uint32_t temp_1 = 0x00;

uint32_t temp_2 = 0x00;

uint32_t temp_3 = 0x00;


/* Check the parameters */

assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));

assert_param(IS_GPIO_AF(GPIO_AF));


temp_1 = ((uint32_t)(GPIO_AF) << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4)) ;

temp_2 = GPIOx->AFR[GPIO_PinSource >> 0x03];

temp_2 &= ~((uint32_t)0xF << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4));


temp_3 = temp_1 | temp_2;


GPIOx->AFR[GPIO_PinSource >> 0x03] = temp_3;

}

The MCO function is limited to 100MHz, but still “tries�? to put out our 168MHz system clock. The output transistors may simply not be able to fully switch that fast and therefore behave like a low-pass filter, so the strange sinewave seems also to be explainable. With the modfied function the problem is now solved. Thanks for your help! Regards, Felix Should I rename the thread title? The current title is quite missleading now, because the error was somewhere else.