Interrupt occurs from change that occurred before interrupt is enabled - c

I'm using an Arduino Uno with the Atmega328p microcontroller. I'm trying to use INT1 as a software interrupt. I manually set INT1's associated PORTD3 high or low depending on external info. What I want is to set the pin high or low at the startup of the device and then enable the interrupt on the pin without causing an interrupt if i set the pin high before enabling the interrupt.
It doesnt seem to matter where I enabled the interrupt--if i changed the state of the pin at some point the interrupt will occur once its enabled. Here is a snippet of the code:
int main(void)
{
DDRD |= (1<<DDD7)|(1<<DDD3);//7 for siren 3 for software int1
USART_Init(MYUBRR);//Initialize USART
while(door!='C' && door!='O'){//get door state on startup
door = getDoorState();
}
if(door=='O')
PORTD |= 1<<PORTD3;
else
PORTD &= ~(1<<PORTD3);
EIFR &= ~(1<<INTF1);//clear flag bit before enable, I'd heard this may help????
EIMSK |= (1<<INT1);//enable door switch interrupt
EICRA |= (1<<ISC00)|(1<<ISC10);//int1 and int0 set for any logical change
sei();//global interrupt enable
while (1)
{}
}
As soon as the global interrupt is enabled by a call to sei() the interrupt will occur if PORTD3 is high, regardless of where PORTD3 was set high or where sei() is. Calling sei() should never cause an interrupt in this code, ideally.

4386427 is correct. The bit is cleared by setting it to a one, not zero. Seems counter-intuitive to me so it threw me off but it works now.
EIFR |= (1<<INTF1);

EIFR &= ~(1<<INTF1) is incorrect.
The correct way of doing this is EIFR = 1<<INTF1.
Datasheet says: the flag is cleared by writing a '1' to it.

Related

How to setup the interrupt handler in stm32?

void EXTI4_15_IRQHandler()
{
if(EXTI->PR & EXTI_PR_PR8)
{
EXTI->PR |= EXTI_PR_PR8;
// handle interrupt here
GPIOA->BSRR |= GPIO_BSRR_BS_10;
Delay(500);
GPIOA->BSRR |= GPIO_BSRR_BR_10;
Delay(500);
}
}
the interrupt init is initialized in the main.c and the setup is correct.
Is there anything that i am missing in the handler function?
You have posted only interrupt handler function, which is not a big help on its own. Basically, your MCU either enters it and executes it entirely or not. So if it (the interrupt handler) doesn't run, it means, the interrupt is not triggered. It means, the problem is somewhere else, so I will go over the entire logic of getting EXTI interrupt on STM32, make sure you have all of that done.
In order to get EXTI interrupts working, we need to connect 3 internal peripherals together: GPIO, EXTI and NVIC. You haven't indicated a specific microcontroller, but they all (STM32) do it in the same/similar way, so I will use STM32F746 as an example, since I have it here on my table.
First, you need to set up EXTI. If you want an interrupt on pin GPIOx8, you need to set bits 8 in the appropriate places in the EXTI registers depending on the event you want to trigger an interrupt.
Second of all, you need to connect GPIO port to EXTI. Do you want an interrupt on PA8? Or PB8? Or PC8? EXTI only understands that it's "Pin 8". It doesn't know if it's PA8 or PB8. That you do in SYSCFG peripheral. In the register EXTI->EXTICR3 you need to set port for EXTI8. This is where you decide that it's PB8 and not PA8 that triggers the interrupt, or whatever port you have.
Now, upon the GPIO behavior event defined in EXTI, the EXTI will report an EXTI9_5 event to NVIC. For now, NVIC will set pending bit if EXTI interrupt event occurs, but it will not execute the interrupt, because it's not activated. So we need to configure NVIC and activate EXTI9_5 interrupt there. At this point your interrupt should work.
To recap, the sequence of actions is the following:
Configure EXTI with the number of pin, on which you want interrupt. Number of pin, but not a GPIO port. For you, it will be pin 8.
Configure SYSCFG to select GPIO port for that EXTI pin. Configure pin 8 to be pin PB8 and not PA8 (or whatever port the interrupt is on).
Activate the corresponding NVIC interrupt.
From this also follows, that you can't have interrupts on PA8 and PB8 at the same time.
EDIT: having delays in the ISR is highly inadvisable. Also, make sure your input signal on the interrupt pin is debounced.
EDIT2: I assumed it goes without saying that every peripheral you use requires clock to be supplied to it.

STM32 Bare Metal C - Can't get LED to work

So I'm currently following a course for the STM32 Microprocessor however, I seem to fail at even the most basic thing: turning a LED on. The complete code is at the bottom of this post.
Important:
The hardware is functioning properly.
I am using a STM32L432KC.
First of all, we have to figure out on what pin the built-in LED is. According to the manufacturers manual, the LED should be on pin D13 (PB3).
Okay so we're looking for PB3. According to the datasheet of the STM32L432KC, PB3 is on the B port and therefore connected to the high performance bus as suggested in the image below.
Cool. So our bus is AHB2 and we're working with GPIOB. Now we have to enable the clock on that bus using the RCC_AHB3ENR register. Now, this is the part where I'm probably going to make mistakes (as this post otherwise wouldn't exist), so please pay close attention. If I understand correctly, I want bit 1 to be set to 1 as this indicates that 'GPIOBEN' is set to 'IO port B clock enabled.'.
This leads me to believe that I should set the bus register as follows:
RCC->AHB2ENR |= 0x2;
Next up I have to set the mode of the GPIO pin to output. According to the course and my documentation, this is done using GPIOx_MODER.
This leads me to believe that I should set the GPIO mode as follows:
GPIOB->MODER |= 0x40;
And last but not least to turn the actual LED on we have to set the output data register which is GPIOx_ODR.
This leads me to believe that I should set the data as follows:
GPIOB->ODR = 0x8;
I'm not sure where I'm going wrong but this is the first time I'm working with registers on such a deep level. I must be overlooking something but I've tried multiple examples and had no success. All help is appreciated.
This is the complete code:
// PB3 - User LED
// RCC->AHB2ENR
// GPIOx_MODER
// GPIOx_ODR
#include "stm32l4xx.h"
int main(void)
{
RCC->AHB2ENR |= 0x2;
GPIOB->MODER |= 0x40;
while(1)
{
GPIOB->ODR = 0x8;
}
}
Your mode register is not configured correctly. Your line of code
GPIOB->MODER |= 0x40;
can only set bits, it cannot clear them. And you have too many bits set, as the reset value of each pair is 11 and the entire register is FFFF FFFF for ports C-E, FFFF FEBF for port B.
You should use
GPIOB->MODER = (GPIOB->MODER & 0xFFFFFF3F) | 0x00000040;
although because the reset state is guaranteed, this will also work:
GPIOB->MODER &= 0xFFFFFF7F; // equivalently, ~0x0080
The note in the documentation of 11 analog mode (reset state) is not accurate for all pins. Several are in 10 alternate function mode at reset, including PB3. So you need to both clear one bit and set one.

Why is the PLL not locking? Is my clock configuration correct?

I am using the stm32l412kb for UART communication. I am trying to configure the USART2 peripheral clock to a 72MHz frequency. The stm32, after reset, uses the MSI at 4MHz, which I then use the PLL to extend to 72MHz when reaching the peripheral.
The code holds at the first PLLRDY check, as the PLL I assume is not locking. Could this be due to a too high a frequency output? Have I configure everything correctly? How do I know that the PLL is then being used instead of the 4MHz MSI, or the 24MHz HSE?
'''
void configureClocks(){
/*Clock Configuration
* The MSI (at 4MHz) is used as system clock source after startup from Reset.
* */
/*Turning on the medium speed internal clock (making sure it's on)*/
RCC->CR |= RCC_CR_MSION;
RCC->CR |= RCC_CR_MSIPLLEN;
/*Waiting until clock is ready*/
while(!(RCC->CR & RCC_CR_MSIRDY));
/*Selecting the MSI (0010) as the MCU clock output*/
RCC->CFGR &= RCC_CFGR_MCOSEL_Msk;
RCC->CFGR |= (0b0010<<RCC_CFGR_MCOSEL_Pos);
/*Turn off PLL to allow to make changes*/
RCC->CR &= ~(RCC_CR_PLLON_Msk);
/*Make sure PLL is locked*/
while(!(RCC->CR & RCC_CR_PLLRDY));
/*At 4Mhz, (4*36/2 = 72Mhz)*/
RCC->PLLCFGR &= ~(RCC_PLLCFGR_PLLN_Msk | RCC_PLLCFGR_PLLM_Msk);
RCC->PLLCFGR |= (2 << RCC_PLLCFGR_PLLM_Pos) | (36 << RCC_PLLCFGR_PLLN_Pos);
/*Turning back on the PLL clock*/
RCC->CR |= RCC_CR_PLLON;
/*Waiting until clock is ready*/
while(!(RCC->CR & RCC_CR_PLLRDY));
/*Selecting the PLL (0101) as the microcontroller clock output*/
RCC->CFGR &= RCC_CFGR_MCOSEL_Msk;
RCC->CFGR |= (0b0101<<RCC_CFGR_MCOSEL_Pos);
/*Enabling the USART2 peripheral clock.*/
RCC->APB1ENR1 &= ~(RCC_APB1ENR1_USART2EN_Msk);
RCC->APB1ENR1 |= (0b1 << RCC_APB1ENR1_USART2EN_Pos);
/*Enabling the GPIOA port peripheral clock*/
RCC->AHB2ENR &= ~(RCC_AHB2ENR_GPIOAEN_Msk);
RCC->AHB2ENR |= (0b1 << RCC_AHB2ENR_GPIOAEN_Pos);
return;
}
'''
Your responses are always much appreciated,
Many thanks,
Harry
Update, thanks to comments:
The first PLL check has been changed from:
while(!(RCC->CR & RCC_CR_MSIRDY));
to:
while(RCC->CR & RCC_CR_MSIRDY);
However, the PLL check still gets stuck on the second one.
Please refer to the Reference Manual (pdf) section 6.2.3 "MSI clock", "Hardware auto calibration with LSE (PLL-mode)" and section 6.4.1 "Clock control register (RCC_CR)"
There is in your code:
RCC->CR |= RCC_CR_MSIPLLEN;
But before enabling PLL mode on the MSI clock you have two things to be done:
External low-frequency resonator or oscillator should be installed (e.g. 32768Hz clock quartz)
As said in the Bit 2 MSIPLLEN description: MSIPLLEN must be enabled after LSE is enabled (LSEON enabled) and ready (LSERDY set
by hardware).There is a hardware protection to avoid enabling MSIPLLEN if LSE is not
ready.
So, if you have LSE installed, first you have to turn it on, and wait until it ready:
RCC->BDCR |= (RCC_BDCR_LSEON);
/*Make sure LSE is ready*/
while(!(RCC->BDCR & RCC_BDCR_LSERDY));
But probably you don't have to use PLL function of the MSI, because USART much more tolerant to frequency deviations. Then MSI-PLL mode should be kept disabled.
STM32 MCUs have some protection mechanics to avoid wrongly switch the clock source. Some bits cannot be set until clock source is ready, or cannot be cleared if the clock source is in use. They are described in the reference manual in bit descriptions.
So, please, carefully compare all steps you're doing to the manual.
UPD
As pointed out in another answer
/*Turn off PLL to allow to make changes*/
RCC->CR &= ~(RCC_CR_PLLON_Msk);
/*Make sure PLL is locked*/
while(!(RCC->CR & RCC_CR_PLLRDY));
You cannot have PLL locked when it is disabled. So, the while-loop will run forever.
UPD2
Before enabling the PLL you forget to set up it's source (bits PLLSRC in PLLCFGR). I.e.:
// set MSI as the source for PLL
RCC->PLLCFGR = (RCC->PLLCFGR & ~RCC_PLLCFGR_PLLSRC_Msk) | RCC_PLLCFGR_PLLSRC_MSI;
After disabling the PLL with RCC->CR &= ~(RCC_CR_PLLON_Msk); wait until PLLRDY is cleared.
Your code does the opposite, waits until PLLRDY is set, meaning it's locked. But you've just disabled it, so it's not going to lock.
After setting up PLLCFGR, turn it back on, and wait until PLLRDY is set. This part looks OK in the code.
When the PLL is running at the required speed, you should set the System clock switch (RCC_CFGR_SW) to PLL instead of the Microcontroller clock output to have your system run on the PLL clock.
Microcontroller clock output does something else. It can be connected to an external pin, to output the clock signal for using it outside the MCU, e.g. to synchronise several MCUs.
I always use Cube to see the clock tree. I also use registers - but that tool is very convenient and it prevents many stupid errors as it will let you know if the values are outside the recommended ones.
PS it should be a comment but I wanted to put the picture. So please do not UV or accept

STM32L1x Stop mode + RTC too much current

I am able to put my stm32L1xDiscovery board in STOP mode with RTC running.
according to the datasheet this should draw about 1.3 µA. But my application draws 3.3 µA.
I noticed I did not put the FLASH in a low power mode during sleep. But when I did this, nothing changed.
This is what I use to go into STOP mode:
SCB->SCR |= ((uint32_t)SCB_SCR_SLEEPDEEP);
RCC->APB1ENR |= RCC_APB1Periph_PWR;
PWR->CR |= ((uint32_t)(PWR_CR_LPSDSR|PWR_CR_ULP)); // ULP seems to have no effect on power consumption
RCC->APB1ENR &= ~RCC_APB1Periph_PWR;
FLASH->ACR |= SLEEP_PD; // seems to have no effect at all on power consumption
__WFI();
Any idea what I am missing here?
If you use discovery board your measurement may be not clear because a lot of other components consumpt some energy. It's may be protection diode, driver of 3.3V line or second MCU with ST-LINK/V2 embedded debug tool.
Where did you measure the power consumption? You should do it betwen JP1 pins 1 & 2 (Pin 2 is connected directly to Vdd). That should show the power drawn by the MCU, and of course anything that is powered by the output pins.
The trick is to properly disconnect and shut down all pins (except the wakeup source) as well as all clocks that are not needed.
Set FLASH->ACR |= SLEEP_PD
Enable all GPIO clocks
Put all unneded pins to analog mode
Disable ALL clocks except RCC_APB1ENR_PWREN and wakeup GPIOs in RCC->xxxLPENR
Then start the thing without the debugger, ST-Link (CN3) jumpers removed.
... and there might be other issues. It's hard to get it right.

Only two IO port interrupts for Xmega (INT0 and INT1)

In avr Xmega device, there are only two IO port interrupt vectors INT0 and INT1.
I have three switches that should generate three interrupts.
I have written an interrupt code for the first switch using INT0. Now, I can write the second interrupt using INT1 which will exhaust my vectors.
Below is my code for INT0 ISR:
ISR (PORTD_INT0_vect){
PORTD.INTFLAGS = 0x01; // clear INT0IF flag.
PORTD_OUT = PORTD_OUT | (1<<4); // led on.
}
Can I redefine this ISR to enable interrupt for second switch?
The registers that I set in main function are as follows:
PORTD.INT0MASK = 0x04; // PD2 is the source of interrupt for INT0.
PORTD.INTCTRL = 0x03; // Disable INT1, enable INT0 and place it a high-priority level.
PORTD.PIN2CTRL = 0x03; // configure PD2 pin to interrupt on the low level of signal.
PMIC.CTRL = 0x07; // enable high,medium, and low level interrupts.
sei(); // enable interrupt globally.
The idea of the external interrupts is that you get to know that something happened, but not what happened. In your ISR you have to read out the state of the inputs the switches are connected to and determine based on the readout, what to do. To detect, if the state did alter, keep a copy of the ports input status and do an XOR with the newly read out value (do this for a whole port, not just for single pins).
I'm not expert of XMEGA, but I can suggest, for example using PD1 as interrupt pin:
PORTD.INT0MASK = 0x04; // PD2 is the source of interrupt for INT0.
PORTD.INT1MASK = 0x02; // PD1 is the source of interrupt for INT1.
PORTD.INTCTRL = 0x0F; // Enable INT1, enable INT0 and place them a high-priority level.
PORTD.PIN2CTRL = 0x03; // configure PD2 pin to interrupt on the low level of signal.PORTD.PIN1CTRL = 0x03; // configure PD1 pin to interrupt on the low level of signal.
Every port has 2 interrupts. You can use more ports if you need more interrupts than 2. You can connect the third switch to a second port and use the INT0 there.
How about polling input(s) from your code, in a loop or from timer interrupt routine(s)? This way you can handle as many input signals as you have inputs capable of sensing the signal change.
The limiting factor, though is the frequency of polling and the delay the polling software introduces.
Of course I assume you want to observe high/low levels of the signal. For more elaborate signal sensing (rising, fallsing, frequency change) the use of ADC is unavoidable.
I hope this helped.

Resources