1、根据中断入口跳转方法的不同,分为向量中断和非向量中断;
2、采用向量中断的 CPU 通常为不同的中断分配不同的中断号,当检测到某中断号的中断到来后,就自动跳转到与该中断号对应的地址执行。不同中断号的中断有不同的入口地址;
3、非向量中断的多个中断共享一个入口地址,进入该入口地址后再通过软件判断中断标志来识别具体是哪个中断;
4、向量中断由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务程序入口地址;
5、定时器工作原理:
中断向量与向量地址
中断号——中段的编号
什么是中断向量?我们可以把它理解成中断服务程序的入口地址。
比如说在 x86 系列当中,我们可以把中断向量理解成中断服务程序的段地址和偏移量组成的一个向量。
有的时候也指程序状态字,比如说 CPU 发生中断的时候,一些非体系结构寄存器或者是表示程序状态的一些寄存器,这些寄存器指令无法进行读取,那么在计算机的内部就把它集成成一个字,这个字我们把它称为程序状态字。那这个时候所谓中断向量,实际上就是指,和中断服务程序相关的入口地址,包括段地址,或者是偏移量,也包括执行中断服务程序的时候需要的一些状态信息。这就是中断向量。实际上,中断服务程序的入口地址可以由中断向量来生成。
向量地址,是指中断向量保存的内存单元的地址,比如说中断服务程序入口地址所保存的内存单元的地址。或者是我们可以利用一条跳转指令,通过这个跳转指令,把它跳转到中断服务程序。那这个时候,它的向量地址就是指这条跳转指令在内存当中保存的地址,我们把这个称为向量地址。
我们看一下,要形成向量地址,我们用一个硬部件来做,这个文件叫做中断向量地址形成部件。
它的输入(的值)是由排队器输出得到的——就是我们刚才讲的那个链式排队器,或者是其他类型的中断优先级的排队器。要记住,排队器输出的结果当中只有一位是高电平,其他的各位都是低电平。有多少个中断源,或者是对应了多少个中断服务程序,那么就会有多少位输入。
它的输出对应了中断向量地址(向量地址),也就是我们刚才讲的中断服务程序入口地址所保存的那个内存单元的地址,或者是那条跳转指令在内存当中保存的地址。
以S3C24XX为例:
根据中断入口跳转方法的不同,中断可分为向量中断和非向量中断。采用向量中断的CPU通常为不同的中断分配不同的中断号,当检测到某中断号中断到来后,就自动跳转到与该中断号对应的地址执行。不同的中断号的中断有不同的入口地址。非向量中断的多个中断共享一个入口地址,进入该入口地址后,再通过软件判断中断标志来识别具体是哪个中断。
也就是说,向量中断由硬件提供中断服务程序的入口地址,非向量中断由软件提供中断服务程序入口地址。
-------------------------------------------------------------------------------
asm_do_IRQ为中断的C语言入口函数:
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc *desc = irq_desc + irq;
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (irq >= NR_IRQS)
desc = &bad_irq_desc;
irq_enter();
desc_handle_irq(irq, desc);
/* AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
-------------------------------------------------------------------------------
其中,desc_handle_irq函数调用desc 结构中的handle_irq成员函数,它就是irq_desc[irq].handle_irq。
另外,asm_do_IRQ函数中参数irq的取值范围为(IRQ_EINT0~IRQ_ADCPARENT),共有32个取值。它可能对应一个实际的中断号, 也可能是一组中断的中断号,这是由芯片的特性决定的。
发生中断时INTPND寄存器的某一位被置1,INTOFFSET寄存器中记录了是哪一位(0~31),中断向量调用asm_do_IRQ之前根据INTOFFSET寄存器的值确定irq参数。
每一个实际的中断在irq_desc数组中都有一项与它对应,它们的数目不止32。
当asm_do_IRQ函数中参数irq表示的是一组中断时,irq_desc[irq].handle_irq成员函数还需要进一步分辨出是哪一个中断(假设中断号为52,对应IRQ_EINT8/S3C2410_IRQ(36)),然后调用irq_desc[52].handle_irq来进行处理。
-------------------------------------------------------------------------------
下面以外部中断EINT8~EINT23为例进一步分析:
它们被触发时,INTOFFSET寄存器中的值都是5,asm_do_IRQ函数中参数irq的值为IRQ_EINT8t23(21),则asm_do_IRQ函数中将调用irq_desc[IRQ_EINT8t23].handle_irq。
irq_desc[IRQ_EINT8t23].handle_irq在s3c24xx_init_irq函数中初始化中断体系结构的时候被设为s3c_irq_demux_extint8。
set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
s3c_irq_demux_extint8函数首先读取EINTPEND、EINTMASK寄存器,确定发生了哪些中断,重新计算它们的中断号,然后调用desc 数组项中的handle_irq成员函数。
比如发生中断时对应的是EINT8,查看芯片手册可知,对应EINTPEND寄存器的第8位被置1,
所以irq=8+(IRQ_EINT4-4)=8+(48-4)=52,正好对应IRQ_EINT8/S3C2410_IRQ(36)=52,也就是说对应数组项irq_desc[52]。
s3c_irq_demux_extint8(unsigned int irq, struct irq_desc *desc)
{
unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND);
unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);
eintpnd &= ~eintmsk;
eintpnd &= ~0xff; /* ignore lower irqs */
/* we may as well handle all the pending IRQs here */
while (eintpnd) {
irq = __ffs(eintpnd);
eintpnd &= ~(1<<irq);
irq += (IRQ_EINT4 - 4);
desc_handle_irq(irq, irq_desc + irq);
}
}
-------------------------------------------------------------------------------
/* we keep the first set of CPU IRQs out of the range of
* the ISA space, so that the PC104 has them to itself
* and we don't end up having to do horrible things to the
* standard ISA drivers....
*/
#define S3C2410_CPUIRQ_OFFSET (16)
#define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)
/* main cpu interrupts */
#define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */
#define IRQ_EINT1 S3C2410_IRQ(1)
#define IRQ_EINT2 S3C2410_IRQ(2)
#define IRQ_EINT3 S3C2410_IRQ(3)
#define IRQ_EINT4t7 S3C2410_IRQ(4) /* 20 */
#define IRQ_EINT8t23 S3C2410_IRQ(5)
#define IRQ_RESERVED6 S3C2410_IRQ(6) /* for s3c2410 */
#define IRQ_CAM S3C2410_IRQ(6) /* for s3c2440,s3c2443 */
#define IRQ_BATT_FLT S3C2410_IRQ(7)
#define IRQ_TICK S3C2410_IRQ(8) /* 24 */
#define IRQ_WDT S3C2410_IRQ(9) /* WDT/AC97 for s3c2443 */
#define IRQ_TIMER0 S3C2410_IRQ(10)
#define IRQ_TIMER1 S3C2410_IRQ(11)
#define IRQ_TIMER2 S3C2410_IRQ(12)
#define IRQ_TIMER3 S3C2410_IRQ(13)
#define IRQ_TIMER4 S3C2410_IRQ(14)
#define IRQ_UART2 S3C2410_IRQ(15)
#define IRQ_LCD S3C2410_IRQ(16) /* 32 */
#define IRQ_DMA0 S3C2410_IRQ(17) /* IRQ_DMA for s3c2443 */
#define IRQ_DMA1 S3C2410_IRQ(18)
#define IRQ_DMA2 S3C2410_IRQ(19)
#define IRQ_DMA3 S3C2410_IRQ(20)
#define IRQ_SDI S3C2410_IRQ(21)
#define IRQ_SPI0 S3C2410_IRQ(22)
#define IRQ_UART1 S3C2410_IRQ(23)
#define IRQ_RESERVED24 S3C2410_IRQ(24) /* 40 */
#define IRQ_NFCON S3C2410_IRQ(24) /* for s3c2440 */
#define IRQ_USBD S3C2410_IRQ(25)
#define IRQ_USBH S3C2410_IRQ(26)
#define IRQ_IIC S3C2410_IRQ(27)
#define IRQ_UART0 S3C2410_IRQ(28) /* 44 */
#define IRQ_SPI1 S3C2410_IRQ(29)
#define IRQ_RTC S3C2410_IRQ(30)
#define IRQ_ADCPARENT S3C2410_IRQ(31)
/* interrupts generated from the external interrupts sources */
#define IRQ_EINT4 S3C2410_IRQ(32) /* 48 */
#define IRQ_EINT5 S3C2410_IRQ(33)
#define IRQ_EINT6 S3C2410_IRQ(34)
#define IRQ_EINT7 S3C2410_IRQ(35)
#define IRQ_EINT8 S3C2410_IRQ(36)
#define IRQ_EINT9 S3C2410_IRQ(37)
#define IRQ_EINT10 S3C2410_IRQ(38)
#define IRQ_EINT11 S3C2410_IRQ(39)
#define IRQ_EINT12 S3C2410_IRQ(40)
#define IRQ_EINT13 S3C2410_IRQ(41)
#define IRQ_EINT14 S3C2410_IRQ(42)
#define IRQ_EINT15 S3C2410_IRQ(43)
#define IRQ_EINT16 S3C2410_IRQ(44)
#define IRQ_EINT17 S3C2410_IRQ(45)
#define IRQ_EINT18 S3C2410_IRQ(46)
#define IRQ_EINT19 S3C2410_IRQ(47)
#define IRQ_EINT20 S3C2410_IRQ(48) /* 64 */
#define IRQ_EINT21 S3C2410_IRQ(49)
#define IRQ_EINT22 S3C2410_IRQ(50)
#define IRQ_EINT23 S3C2410_IRQ(51)
-------------------------------------------------------------------------------
1、根据中断入口跳转方法的不同,分为向量中断和非向量中断;
2、采用向量中断的 CPU 通常为不同的中断分配不同的中断号,当检测到某中断号的中断到来后,就自动跳转到与该中断号对应的地址执行。不同中断号的中断有不同的入口地址;
3、非向量中断的多个中断共享一个入口地址,进入该入口地址后再通过软件判断中断标志来识别具体是哪个中断;
4、向量中断由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务程序入口地址;
5、定时器工作原理:
转载于:https://www.cnblogs.com/dq23578/p/7229320.html
在编写中断服务程序(ISR)的时候需要知道中断号,用于通知51内核,这个中断服务程序是为了响应哪个中断的。那么,从以上这句简单的描述可以看出,中断号必然对应于中断服务程序的入口地址,而入口地址又与中断向量(Interrupt Vector)有对应关系,所以:
(1)、中断号即是中断向量从小到大的编号,从0开始;
(2)、中断向量0x0000是上电复位,不计入中断向量的编号;
(3)、特别注意,有些中断源的默认中断优先级并不是中断向量从小到大的编号,所以默认的中断优先级并不能作为中断号使用。
什么是中断号
// Wake-up interrupt handler
void resume_isr(void) interrupt WKUP_VECT
{
EZUSB_CLEAR_RSMIRQ();
}
↑,这是EZ-USB的唤醒中断的中断服务程序,其中”WKUP_VECT”就是Resume中断的中断号,中断号用关键词”interrupt”标记。
EZ-USB的中断号
在固件源代码中,中断号的声明是:
// 中断名称 中断号
#define INT0_VECT 0
#define TMR0_VECT 1
#define INT1_VECT 2
#define TMR1_VECT 3
#define COM0_VECT 4
#define TMR2_VECT 5
#define WKUP_VECT 6
#define COM1_VECT 7
#define USB_VECT 8
#define I2C_VECT 9
#define INT4_VECT 10
#define INT5_VECT 11
#define INT6_VECT 12
文档中的描述:

可以看出,固件源码中的中断号和文档中的“Natural Priority”是不一致的,而和”Interrupt Vector”的从小到大的排列顺序是一致的。
这里,没有中断向量0x0000的中断,在标准的C51中他被用作上电复位,下面再举一个中断向量0x0000的例子。
C8051F34X的中断号


这是SILLICON LABS的C8051F34X芯片的中断号,他包含0x0000中断向量,但是不计入“排序”。官方给出的UART0的中断服务程序证明了这一点:
void UART0_Interrupt (void) interrupt 4
{
if (RI0 == 1)
{
if( UART_Buffer_Size == 0) { // If new word is entered
UART_Input_First = 0; }
RI0 = 0; // Clear interrupt flag
Byte = SBUF0; // Read a character from UART
if (UART_Buffer_Size < UART_BUFFERSIZE)
{
UART_Buffer[UART_Input_First] = Byte; // Store in array
UART_Buffer_Size++; // Update array's size
UART_Input_First++; // Update counter
}
}
if (TI0 == 1) // Check if transmit flag is set
{
TI0 = 0; // Clear interrupt flag
if (UART_Buffer_Size != 1) // If buffer not empty
{
// If a new word is being output
if ( UART_Buffer_Size == UART_Input_First ) {
UART_Output_First = 0; }
// Store a character in the variable byte
Byte = UART_Buffer[UART_Output_First];
if ((Byte >= 0x61) && (Byte <= 0x7A)) { // If upper case letter
Byte -= 32; }
SBUF0 = Byte; // Transmit to Hyperterminal
UART_Output_First++; // Update counter
UART_Buffer_Size--; // Decrease array size
}
else
{
UART_Buffer_Size = 0; // Set the array size to 0
TX_Ready = 1; // Indicate transmission complete
}
}
}