how to enable external interrupt of 8051?
Each of the 8051s interrupts has its own bit in the interrupt enable (IE) special function register (SFR) and is enabled by setting the corresponding bit. The code examples below are in 8051 assembly as well as C to provide a general idea of what is taking place.
To enable external interrupt 0 (EX0) you need to set bit 0 of IE.
SETB EX0 or ORL IE,#01 or MOV IE,#01
To enable external interrupt 1 (EX1) you need to set bit 3 of IE.
SETB EX1 or ORL IE,#08 or MOV IE,#08
Interrupts then need to be globally enabled by setting bit 7 of IE, which is the global interupt enable/disable bit (EA). If necessary, you can set the priority of the external interrupts to high via the interrupt priority (IP) SFR.
SETB EA or ORL IE,#80
Example in C:
#define IE (*(volatile unsigned char *)0xA8)
#define BIT(x) (1 << (x))
...
IE &= ~BIT(7); /* clear bit 7 of IE (EA) to disable interrupts */
...
IE |= BIT(0); /* set bit 0 of IE (EX0) to enable external interrupt 0 */
...
IE |= BIT(1); /* set bit 3 of IE (EX1) to enable external interrupt 1 */
...
IE ^= BIT(7) /* toggle bit 7 of IE (EA) to re-enable interrupts */
or
IE = 0x89; /* enable both external interrupts and globally enable interrupts */
The various 8051 C compiler vendors often define their own methods of setting up interrupt functions. You may need to consult the documentation for your specific compiler.
To define an interrupt function using the Keil C51 Compiler (pdf link to application note), an interrupt number and register bank is specified where the interrupt number corresponds to a specific interrupt vector address.
void my_external_interrupt_0_routine(void) interrupt 0 using 2
{
/* do something */
}
To define an interrupt function using the 8051 IAR C/C++ Compiler (icc8051) (pdf link to reference guide), the __interrupt keyword and the #pragma vector directive can be used.
#pragma vector=0x03
__interrupt void my_external_interrupt_0_routine(void)
{
/* do something */
}
If you are new to the 8051, there is a wealth of information available at www.8052.com. I would also recommend The 8051/8052 Microcontroller: Architecture, Assembly Language, and Hardware Interfacing written by Craig Steiner, the webmaster and author of 8052.com.
Enable the corresponding bit of external interrupt in IE register.
If it is level triggering, just write the subroutine appropriate to this interrupt, or else enable the TCON register bit corresponding to the edge triggered interrupt – whether it is INT0 or INT1.
Related
I am writing code with the STM323 ide and using the STM32f303re nucleo board. I configured pin PA7 as an interrupt, however it does not have its own interrupt handler like previous development boards i've worked with.
As you can see, this one interrupt handler handles interrupts from pins 9-5, thus if any of these pins are triggered they will call the same interrupt. I need to be able to perform different functions depending on which pin is triggered. Is there a way to know which specific pin was causing the interrupt?
You can use EXTI_GetITStatus, to check which line causing interrupt.
/* Handle PA7 interrupt */
void EXTI9_5_IRQHandler(void) {
/* Make sure that interrupt flag is set */
if (EXTI_GetITStatus(EXTI_Line7) != RESET) {
/* Do your stuff when PA7 is changed */
/* Clear interrupt flag */
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
Do not forget to clear flag after.
For the cases when multiple pins share an interrupt, when the interrupt fires, you need to check what pin specifically caused an interrupt. Unlike pin 1 interrupt, where the interrupt itself means it was pin 1 and you can process it right away, in this case an interrupt means "it's either pin 5, or 6, or 7, or 8, or 9", so in the ISR you need to check "was it pin 5? or 6?..."
I think it's a good opportunity to look directly into the registers of EXTI peripheral.
If you open the reference manual of your MCU on page 299, section 14.3.6, you can see this EXTI_PR1 register holds pending bits for lines 0..31. Those bits are marked as rc_w1, which from the start of the document means (reference manual, page 46):
read/clear (rc_w1)
Software can read as well as clear this bit by writing 1. Writing ‘0’
has no effect on the bit value.
So the logic is the following: if an interrupt of lines 5..9 occurred, you need to check what bit specifically in that register is set to 1, and then you write 1 there to reset it. This will clear the flag.
void EXTI9_5_IRQHandler(void)
{
if(EXTI_PR1 & (1U<<5U)) //check if it's line 5, returns 0 if PR5 is 0, otherwise returns non-zero, which is true
{
EXTI_PR1 |= (1U<<5U); //write 1 to that bit to clear it so interrupt doesn't fire again once ISR is finished
do_stuff_if_it's_pin5();
}
}
Alternatively, instead of 1U<<5U you should be able to use EXTI_PR1_PR5, so the code would look a little easier to read, like this:
void EXTI9_5_IRQHandler(void)
{
if(EXTI_PR1 & EXTI_PR1_PR5) //check if it's line 5, returns 0 if PR5 is 0, otherwise returns non-zero, which is true
{
EXTI_PR1 |= EXTI_PR1_PR5; //write 1 to that bit to clear it so interrupt doesn't fire again once ISR is finished
do_stuff_if_it's_pin5();
}
}
This is what the functions provided by #Lime7 do behind the scenes, I suspect (can't check, but it makes logical sense).
I don't have that microcontroller to test it, but it should work.
Other answers already covered the interrupt flags. I'm going to add some info regarding CubeMX and HAL, since it looks like you are using them.
When you enable a GPIO interrupt in CubeMX, the code generator adds a line in the appropriate IRQ handler function. When multiple interrupts are enabled, the generated code looks like so (comments removed):
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
}
The HAL_GPIO_EXTI_IRQHandler() function checks the interrupt status for the given pin and calls HAL_GPIO_EXTI_Callback() function if the flag is set.
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
In the HAL flow, you don't typically write code directly in the IRQ handler, but implement the HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) function. The pin that triggered the interrupt is provided as a parameter for your convenience.
Is there any way to disable all irq from Cortex M3 MCU except one ?
My issue is that I have a system running several kinds of irq with various priority levels and I want to disable all irq except one in a particular state.
I know I can disable all irq by using "__disable_irq()" instruction but I can't enable one irq after calling this instruction if I didn't call "__enable_irq()" before.
Thanks for your help,
Regards
Use the BASEPRI register to disable all interrupts below the specified priority level.
This is a core register, described in the Cortex-M3 Programming Manual.
CMSIS provides the __get_BASEPRI() and __set_BASEPRI() functions to manipulate its value.
Note that bits 7-4 are used, the priority value must be shifted left by 4. To disable all interrupts with priority 1 or lower, use
__set_BASEPRI(1 << 4);
and to enable all, set it to 0
__set_BASEPRI(0);
You should of course set interrupt priorities accordingly, ensuring that no other interrupt has priority 0.
Other than by disabling all the enabled interrupts you don't want, no.
__disable_irq() is implemented as CPSID I, which turns off all exceptions which can have a priority set (those configured in the NVIC), it achieves this by changing the PRIMASK register (setting bit 0) within the CPU. There is no way to tell this to only enable a specific interrupt.
I'm looking into how to make an operating system for Raspberry Pi, and in the OSDev barebones tutorial for the Raspberry Pi, the following code is used to clear any pending interrupts.
// Clear pending interrupts.
mmio_write(UART0_ICR, 0x7FF);
The function mmio_write is as follows.
static inline void mmio_write(uint32_t reg, uint32_t data){
*(volatile uint32_t *)reg = data;
}
Why is this value significant, and how does it clear the interrupts?
This line:
// Clear pending interrupts.
mmio_write(UART0_ICR, 0x7FF);
will write value of 0x7FF to the register at address UART0_ICR.
Most likely, UART0_ICR is a value defined some else where in the code, which refers to the address of a corresponding register inside the BCM2835, maybe something similar to this:
#define UART0_ICR (UART0_BASE + 0x44)
Why is this value significant, and how does it clear the interrupts?
To understand this, you need to read the data sheet of the device, which is BCM2835 in your case (a quick search gives me this link). Look at the UART register section, you can see the description for UART_ICR register, which is the Interupt Clear Register.
It's quite obvious from the bit table description of that register: it's a 32-bit register, bit 31-11 is not used, while bit from 0 to 10 (11 bit) is used as flags for clearing various kinds of interrupt. Writing value of 0x7FF to this register is literally setting on all those flags, hence it will clear all the pending UART interrupts.
I'm currently taking a microprocessors course that uses the MSP430g2553 and I've noticed that the example C code our professor writes makes extensive use of global variables.
What would be the reasoning behind this?
(I've always been told to use globals only when absolutely necessary, so I assume something about the structure of microprocessors makes them necessary.)
UPDATE: I forgot to include sample code. Here's an early example c program we saw in class:
#include <msp430g2553.h>
volatile unsigned int blink_interval; // number of WDT interrupts per blink of LED
volatile unsigned int blink_counter; // down counter for interrupt handler
int main(void) {
// setup the watchdog timer as an interval timer
WDTCTL =(WDTPW + // (bits 15-8) password
// bit 7=0 => watchdog timer on
// bit 6=0 => NMI on rising edge (not used here)
// bit 5=0 => RST/NMI pin does a reset (not used here)
WDTTMSEL + // (bit 4) select interval timer mode
WDTCNTCL + // (bit 3) clear watchdog timer counter
0 // bit 2=0 => SMCLK is the source
+1 // bits 1-0 = 01 => source/8K
);
IE1 |= WDTIE; // enable the WDT interrupt (in the system interrupt register IE1)
P1DIR |= 0x01; // Set P1.0 to output direction
// initialize the state variables
blink_interval=67; // the number of WDT interrupts per toggle of P1.0
blink_counter=blink_interval; // initialize the counter
_bis_SR_register(GIE+LPM0_bits); // enable interrupts and also turn the CPU off!
}
// ===== Watchdog Timer Interrupt Handler =====
// This event handler is called to handle the watchdog timer interrupt,
// which is occurring regularly at intervals of about 8K/1.1MHz ~= 7.4ms.
interrupt void WDT_interval_handler(){
if (--blink_counter==0){ // decrement the counter and act only if it has reached 0
P1OUT ^= 1; // toggle LED on P1.0
blink_counter=blink_interval; // reset the down counter
}
}
ISR_VECTOR(WDT_interval_handler, ".int10")
According to MSP430 data sheet, it has up to 512 KB flash memory (for program storage) and 66 KB ram (for data storage). Since you haven't provided any code sample, it is highly possible that your prof may have wanted to use the ram in an optimal fashion. There may be some functions declared, using the same data as an input or he just defined variables in global region without regarding any performance issue and totally unintentional (which is I don't think so, but it is a probability too). The important point you should care is, always try to use these limited resources in an efficient way, especially on an embedded device.
There is nothing special about microcontrollers that makes global variables necessary. Global variables are undesirable in embedded systems for all the same reasons they're undesirable in other systems. Jack Ganssle explains the reasons in his blog post on globals.
Yes, microcontrollers have a limited amount of RAM but that's not an excuse to use global variables freely.
Ask your instructor why he/she is using global variables. Perhaps your instructor is more concerned with teaching you about microprocessors than demonstrating good software design.
With an ARM Cortex-M3, such as an NXP LPC1788, why would someone use the Interrupt Set-Pending Register(s) or Interrupt Clear-Pending Registers?
Can someone provide a simple, canonical example of using these registers?
The only use case I can think of is the triggering of a low-priority software excaption form a high priority IRQHandler - like the GPIO interrupt handler.
Normally you would use PendSV for that, but when you have more than one task or priority level you can use any unused peripherial exception vector. Could be useful in programs that use the Sleep-on-Exit feature - where the µC will only run in exception handlers.
// Example for LPC17xx
void ETHERNET_Handler (void)
{
// toggle LED on P0.4
LPC_GPIO0->FIODIR0 ^= (1<<4);
}
void main(void)
{
// set Ethernet IRQ to loewst Priority
NVIC_SetPriority(ENET_IRQn,31);
NVIC_EnableIRQ(ENET_IRQn);
NVIC_SetPendingIRQ(ENET_IRQn); // trigger Ethernet IRQ Handler
// ...
while (1);
}