Bootstrap

高低电平触发,(边沿触发)上升沿触发和下降沿触发 中断区别

外部中断可以分为电平触发和边缘触发两种,那么这两种中断有什么区别。中断基本概念是:

  1. CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);
  2. CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);

待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断。通俗点说:你正在家里做家务,突然有人来找你,打断了你的进程,在你们交谈完之后,你就又回去继续进行你的家务活,这其中被打断的过程,就叫做中断,而在中断结束之后,你则会继续进行本来应该做的事情

1. 高低电平触发:

1.1 低电平触发:

低电平触发中断顾名思义,就是检测到引脚为低电平就触发,从而进入中断函数中处理这个中断,并且在低电平保持的时间内持续触发。假设是低电平触发,只要引脚为低电平时间内中断一直有效,那么就会一直进入中断,直到电平变化为高电平。

1.2 高电平触发:

则是检测为高电平就触发,其余与低电平触发相同

注意事项:

1.电平触发中断,如果在电平没有恢复之前就退出中断程序,那么会在退出后又再次进入中断。只要不退出是不会重复触发的。也就是重复触发只有在退出中断后才会再次触发,不用担心这次还没进行完,中断已经重新触发的情况

2.低电平触发是即时的,当外部中断信号撤消时,中断申请信号随之消失。如果在外部中断信号申请期间,CPU来不及响应此中断,那么有可能这次中断申请就漏掉了。也就是说假设低/高电平的时间很短。CPU没来得及相应,那么这次的电平中断申请就可能不会检测到

3.如果想要电平触发中断也只进行一次,通常的做法是在中断进入后就关闭中断响应,等后面需要的时候再打开。

2. 边沿触发

这里要先了解下:从低电平到高电平,  叫做上升;从高电平到低电平, 叫做下降

2.1 上升沿触发

数字电平从低电平(数字“0”)变为高电平(数字“1”)的那一瞬间叫作上升沿。 上升沿触发是当信号有上升沿时的开关动作,当电位由低变高而触发输出变化的就叫上升沿触发。也就是当测到的信号电位是从低到高也就是上升时就触发,叫做上升沿触发。

2.2 下降沿触发

数字电路中,数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间叫作下降沿。 [1]  下降沿触发是当信号有下降沿时的开关动作,当电位由高变低而触发输出变化的就叫下降沿触发。也就是当测到的信号电位是从高到低也就是下降时就触发,叫做下降沿触发。

那么我们可以很好的理解两种触发:上升沿触发 就是当电压从低变高时触发中断;下降沿触发 就是当电压从高变低时触发中断。

当然,上升沿与下降沿检测的是电平变化的一瞬间,就会产生中断,这个时间是us级别的,但是如果中断引脚检测到一直保持低/高电平,则无法产生下次中断,也就是中断只会触发一次,只有在下次电平发生变化时才会重新触发中断。

还需要注意的是中断在上升或者下降沿的时候进行中断检查,检查过程是通用的数据信号采集检测原理。硬件设置的检测周期可能是有所变化的,如果在检测时间周期内(比方说3个时间周期,us级别)发现待检测信号的梯度变化不足够大,这种低端相应的硬件可能无法触发硬件边沿中断信号。

注意事项:

1 边沿触发就是单片机在上一次机器周期内,检测到中断引脚口为高电平,这一次机器周期内检测到为低电平,则会申请产生中断,所以为us级别

2 下降沿触发是锁存中断信号的,由D触发器记忆,意即:即使当时CPU来不及响应中断,外部中断信号撤消后,由于D触发器的记忆作用,消失的中断信号仍然有效,直到中断被响应并进入中断ISR,记忆的中断信号才会由硬件清除。 这也是为什么边沿触发只能触发一次的原因。

3.对于单片机的中断引脚,如果你另一端接的是VCC 则需要设置成上升沿或者高电平触发 如果你接的是GND 就可以设置成下降沿或者低电平触发

区别:我们可以理解,电平触发在你一直按着按键的时候会一直进入中断,边沿触发则是只会触发一次,再次按下才会重新触发,这就给我们不同的应用功能提供了选择,使得我们可以在不同个工作下选择适合的模式,边沿触发适用于对对时间要求高的,比如中断中有计数之类的(GATE门控位置1时),而电平触发则适合报警装置。

3. 操作系统实现

初始化中断向量表时,有一个重要的操作就是set_irq_handler。这个函数参数一般为handle_edge_irq或者handle_level_irq。对于边缘中断,使用handle_edge_irq作参数;对于电平中断,使用handle_level_irq作为中断。那这两个函数有什么区别呢?

3.1 handle_edge_irq

kernel源代码中的注释为:

c代码

/**
* handle_edge_irq - edge type IRQ handler
* @irq: the interrupt number
* @desc: the interrupt description structure for this irq
*
* Interrupt occures on the falling and/or rising edge of a hardware
* signal. The occurence is latched into the irq controller hardware
* and must be acked in order to be reenabled. After the ack another
* interrupt can happen on the same source even before the first one
* is handled by the assosiacted event handler. If this happens it
* might be necessary to disable (mask) the interrupt depending on the
* controller hardware. This requires to reenable the interrupt inside
* of the loop which handles the interrupts which have arrived while
* the handler was running. If all pending interrupts are handled, the
* loop is left.
*/

下面翻译为中文:

中断发生在硬件信号的上升沿/下降沿。中断事件保存在中断控制器中并且必须被立刻响应以重新打开中断。响应之后,即使第一个中断还没有被相关的中断函数处理完,另一个中断也可能在同一个中断源发生。如果这种情况发生的话就有必要关闭中断。这样,在中断处理函数运行时发生的中断就会被标记为阻塞,在中断处理循环处理完这些阻塞的中断之后,就有必要在循环内部重新打开中断。如果全部的阻塞中断处理完毕,就可以离开中断处理循环。

响应中断条件是:中断使能中断标志同时成立时。一般来讲,响应中断后,有硬件清标志软件清标志两种.(如果硬件不能清标志,说明书会说明)。单片机要靠查询中断标志来判断是否要进入中断,如果你不清除中断标志,本次中断退出,单片机又会检测到中断标志,因此重复进入中断。当然,这样情况中断后面响应过来的硬件中断系统也是没有办法做出处理的,会被挡在外边。

3.2 handle_level_irq

c代码

/**
* handle_level_irq - Level type irq handler
* @irq: the interrupt number
* @desc: the interrupt description structure for this irq
*
* Level type interrupts are active as long as the hardware line has
* the active level. This may require to mask the interrupt and unmask
* it after the associated handler has acknowledged the device, so the
* interrupt line is back to inactive.
*/

一旦硬件线路达到了激活电平,电平类型的中断就会被触发。这可能需要屏蔽中断,并且在相关的中断处理程序响应了设备之后重新打开中断,这样中断线才能恢复反激活状态。

对比上述两个函数可见,对于电平中断,一进入中断处理程序就会立刻屏蔽中断,直到退出中断的时候才会unmask中断;而对于边缘中断,除非在处理当前中断时有一个新的中断被触发否则不会屏蔽中断。这两个处理函数的特点刚好和这两种中断的特点一致:对于电平中断,只要中断脚在激活电平电平,就会不断地触发中断;而边缘中断只在上升沿或者下降沿被触发。

下面是linux 2.6.32版本kernel的代码。

//觉得很奇怪,既然电平中断一开始就mask了中断,
//退出的时候才unmask,怎么会与同类型的其他中断冲突呢?

c代码


void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
struct irqaction *action;
irqreturn_t action_ret; 

spin_lock(&desc->lock);
//一开始以为对于电平中断ack不会被调用,但是发现
//mask_ack_irq会间接调用ack
mask_ack_irq(desc, irq); 

if (unlikely(desc->status & IRQ_INPROGRESS))
goto out_unlock;
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
kstat_incr_irqs_this_cpu(irq, desc); 

/*
* If its disabled or no action available
* keep it masked and get out of here
*/
action = desc->action;
if (unlikely(!action || (desc->status & IRQ_DISABLED)))
goto out_unlock; 

desc->status |= IRQ_INPROGRESS;
spin_unlock(&desc->lock); 

action_ret = handle_IRQ_event(irq, action);
if (!noirqdebug)
note_interrupt(irq, desc, action_ret); 

spin_lock(&desc->lock);
desc->status &= ~IRQ_INPROGRESS; 

if (unlikely(desc->status & IRQ_ONESHOT))
desc->status |= IRQ_MASKED;
else if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
desc->chip->unmask(irq);
out_unlock:
spin_unlock(&desc->lock);
}
EXPORT_SYMBOL_GPL(handle_level_irq); 

void
handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
spin_lock(&desc->lock); 

desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); 

/*
* If we're currently running this IRQ, or its disabled,
* we shouldn't process the IRQ. Mark it pending, handle
* the necessary masking and go out
*/
//正如注释中所说,在处理一个中断时下一个同中断源的中断会被标记为阻塞,
//然后屏蔽这个中断源的中断。注意:只有在这个时候才会屏蔽中断,否则
//linux不会做无谓的屏蔽。
if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
!desc->action)) {
desc->status |= (IRQ_PENDING | IRQ_MASKED);
mask_ack_irq(desc, irq);
goto out_unlock;
}
kstat_incr_irqs_this_cpu(irq, desc); 

/* Start handling the irq */
if (desc->chip->ack)
desc->chip->ack(irq); 

/* Mark the IRQ currently inprogress.*/
desc->status |= IRQ_INPROGRESS; 

do {
struct irqaction *action = desc->action;
irqreturn_t action_ret; 

if (unlikely(!action)) {
desc->chip->mask(irq);
goto out_unlock;

/*
* When another irq arrived while we were handling
* one, we could have masked the irq.
* Renable it, if it was not disabled in meantime.
*/
//因为这时中断处理函数没有运行,所以可以unmask中断
if (unlikely((desc->status &
(IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
(IRQ_PENDING | IRQ_MASKED))) {
desc->chip->unmask(irq);
desc->status &= ~IRQ_MASKED;

desc->status &= ~IRQ_PENDING;
spin_unlock(&desc->lock);
action_ret = handle_IRQ_event(irq, action);
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
spin_lock(&desc->lock); 

} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING); 

desc->status &= ~IRQ_INPROGRESS;
out_unlock:
spin_unlock(&desc->lock);
}

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;