查看: 3897|回复: 0

[分享] LPC800特色模块揭秘——引脚中断和引脚组合逻辑

[复制链接]

该用户从未签到

72

主题

80

帖子

0

版主

Rank: 7Rank: 7Rank: 7

积分
724
最后登录
2020-1-3
发表于 2018-11-26 09:34:48 | 显示全部楼层 |阅读模式
本帖最后由 eefishing 于 2018-11-23 17:01 编辑
本文是“LPC800特色模块揭秘”系列文章的第四篇,点击以下链接查看前三篇:
LPC800特色模块揭秘——输入输出引脚的配置
LPC800特色模块揭秘——开关矩阵
从系统框图解读LPC800的配置与特性

外部引脚可以触发芯片内部的中断,这是每一个通用MCU都具备的基本功能。

在LPC800中,所有外部引脚都可以配置为产生中断的触发源。每个引脚不但可以独立地触发中断,还可以和其它引脚的信号状态进行组合,由软件指定某种特定的组合触发中断。

前面几篇文章已经介绍了,引脚的特性配置由IOCON模块实现,开关矩阵则负责把引脚与片内外设对应起来。所有的数字信号,不管配置为输入还是输出,都可以被指定为引脚中断和引脚组合逻辑的一个输入选项。

下图给出了引脚中断和引脚组合逻辑模块(图中蓝色部分)与其它部分的关系示意。

引脚中断和引脚组合逻辑模块与开关矩阵(SWM)和引脚配置模块(IOCON)的关系示意图
11.png

本文只介绍引脚中断和引脚组合逻辑模块,即图中右下方的蓝色部分。
如果只希望了解“模式匹配引擎”,可以跳过以下关于中断功能的描述,直接从第3节开始阅读。



1、引脚中断功能和使用

任何引脚,只要它在开关矩阵或IOCON中被指定为数字引脚,不管是输入还是输出,这个引脚都可以被指定为引脚中断和引脚组合逻辑的输入端。

所有的LPC800产品,都允许有最多8个引脚作为引脚中断,和引脚组合逻辑的输入端。在SYSCON中通过8个PINTSEL寄存器,指定哪个引脚可以作为引脚中断的输入,软件只需把引脚编号写入对应的PINTSEL寄存器即可。对于PIO1_n的引脚,引脚编号为(32 + n)。

例如:
  // 指定PIO0_20为引脚中断0的输入源
  LPC_SYSCON->PINTSEL[0] = 20;
  // 指定PIO0_20为引脚中断3的输入源
  LPC_SYSCON->PINTSEL[3] = 20;
  // 指定PIO1_1为引脚中断6的输入源
  LPC_SYSCON->PINTSEL[6] = 33;
上面例子中,配置了PIO0_20引脚作为中断0的输入源,又作为中断3的输入源,即PIO0_20上的电平变化,可以同时触发两个中断。


1.1. 指定中断触发源和触发方式
在LPC800中有8个引脚中断向量,它们分别为PININT0_IRQ ~ PININT7_IRQ,每个PINTSEL寄存器指定的引脚对应一个中断向量。以下10个寄存器用于控制中断触发源和触发方式:

表1. 控制中断触发源和触发方式的寄存器
12.png

下面这个表格是按照要求的触发方式,标示出应该如何设置寄存器控制位。
表2. 触发方式的配置
12.png
表2中可以看出,SIENR和CIENR都是只写寄存器,一个用于设置IENR寄存器位,另一个用于清除IENR寄存器位;这两个寄存器的目的,是为了在修改IENR寄存器时的“读-修改-写”的操作,只需写操作,即可改变所有需要设置的位或需要清除的位。
同样,SIENF和CIENF也都是只写寄存器,用于设置IENF寄存器位。
由于独立的IENR和IENF寄存器,用户可以配置同一个信号的上升沿和下降沿都产生中断。在中断处理中,可以通过读出RISE和FALL寄存器判断是哪个边沿产生的中断。在RISE和/或FALL寄存器中写’1’可以清除中断状态,也可以在IST寄存器中写’1’ 清除中断状态。


1.2. 电平触发方式的使用

因为通过软件本身,在MCU内部不能清除电平触发所产生的中断,软件必须执行某种操作,让外部电路改变信号线上的电平,才能使MCU不再产生中断,所以使用电平中断时要小心处理。
可以通过在IST寄存器中写’1’的方式,改变触发的电平,从而间接地清除中断状态。例如当高电平触发中断后,在IST寄存器中写’1’,控制器将变为有低电平时产生中断,同时清除中断状态。

一般情况下,建议用边沿触发方式,通过软件处理实现电平中断的效果。对于高电平中断的控制方式,改变位在上升沿中断的中断处理程序返回之前,检测该信号线是否为高电平的方式实现相同的逻辑功能。同理,低电平中断的控制方式,可以用下降沿中断,再加上检测低电平的方式实现相同逻辑功能。



2、引脚中断的实用函数
为了方便使用,这里呈现几个实用函数,方便使用PINT功能。

2.1. 复位所有引脚中断寄存器
PinInt_Reset()函数的功能就是清除所有未处理的引脚中断标志,同时关闭所有的引脚中断。
13.png

2.2. 使能引脚的中断
共有4个函数分别使能引脚的中断为上升沿触发、下降沿触发、高电平触发和低电平触发,这些函数的功能是按照给定的输入参数,使能对应的引脚中断,它的输入参数是需要设置的引脚中断的位域值。
输入参数的第0位为’1’表示需要设置PININT0,输入参数的第1位为’1’表示需要设置PININT1,依次类推直至第7位。
注意,不要把PININTn和引脚编号混淆,使用以下函数之前,需要指定PININTn和引脚的关系,由LPC_SYSCON->PINTSEL[0]定义。
下面分别是这4个函数的代码。

■ 函数PinInt_Enable_Rising()
读出中断模式寄存器的内容,设置对应的位为’0’(边沿触发),然后使能上升沿中断

14.png

■ 函数PinInt_Enable_Falling()
读出中断模式寄存器的内容,设置对应的位为’0’(边沿触发),然后使能下降沿中断
14.png

■ 函数PinInt_Enable_High()
读出中断模式寄存器的内容,设置对应的位为’1’(电平触发),然后使能高电平中断
15.png

■ 函数PinInt_Enable_Low()
读出中断模式寄存器的内容,设置对应的位为’1’(电平触发),然后使能低电平中断
16.png

2.3. 关闭对应的引脚中断
函数PinInt_Disable()的功能是按照给定的输入参数,关闭对应的引脚中断,它的输入参数和上面那些函数的输入参数意义一致,是需要设置的引脚中断的位域值。
17.png

2.4. 清除对应的引脚中断标志
函数PinInt_Clear()的功能是按照给定的输入参数,清除对应的引脚中断标志,它的输入参数和上面那些函数的输入参数意义一致,是需要操作的引脚中断标志位的位域值。

通常这个函数是在中断处理函数中调用。
18.png



3、模式匹配引擎功能和使用

模式匹配引擎实现引脚的组合逻辑,组合逻辑结果可以通过状态位由软件检测,也可以是引脚中断的延伸,产生中断请求,还可以向CPU核心发送事件信号和/或在某个引脚上输出。

前面介绍的引脚中断功能,每个中断只能由来自某个单个引脚的状态变化而产生;而通过模式匹配引擎,根据多个引脚的组合逻辑运算结果,可以产生对应的中断。

例如可以实现一个键盘,键盘的每个按键可以单独产生中断,用于判断哪个键被按下,也可以使用组合逻辑功能,当某个特定的按键组合被按下,才能产生中断,这样可以更加方便地检测诸如Ctrl-C、Ctrl-V这样的组合功能。


3.1. 模式匹配引擎
一个逻辑运算表达式或布尔运算表达式,是由布尔变量经基本的”与”、”或”、”非”和”异或”等布尔运算构成。
下面是一些布尔运算表达式的例子:
例1: A*B + B*C + A*C
例2: D*C + A*B*C + A*D*E
例3: A + B*C*D + B*E*F + G*A*D

在LPC800的模式匹配引擎中,每个布尔变量与一个输入引脚一一对应,最多允许有8个布尔变量参与运算,同时所有变量出现次数的总和不能多于8个。
LPC800的模式匹配引擎可以直接支持”与”、”或”、”非”运算,但不能原生支持”异或”运算,需要由软件配置实现”异或”运算,如下:
A ^ B = A* /B + /A *B
在上述例1中总共有3个布尔变量:A、B和C,它们出现的次数总和为6次,可以由模式匹配引擎实现。例2有A~D共5个布尔变量,出现的总次数为8次,也可以由模式匹配引擎实现。但例3中有A~G共7个布尔变量,出现的总次数为10次,不能由模式匹配引擎实现。

3.2. 布尔项的实现
在模式匹配引擎中,布尔变量的每一次出现以一个布尔项来实现,在用户手册中“布尔项”以slice表示。
内部实现中,只有8个布尔项(slice),因此所有变量出现次数的总和不能多于8个。

对每一个布尔项,用户可以按照输入信号的不同变化,选择多达8种不同的条件。这些条件分别是:
① 恒为“高”。这种情况与输入信号无关,对应的布尔项始终为“高”。一般是按照应用逻辑,用于设置闲置的布尔项。
② 锁存的上升沿。从输入信号出现一个上升沿,到再次写入模式匹配引擎的控制寄存器之前,对应的布尔项为“高”。在此期间不管输入信号如何变化,对应的布尔项不再变化。写入控制寄存器会清除这个布尔项为“低”。
③ 锁存的下降沿。从输入信号出现一个下降沿,到再次写入模式匹配引擎的控制寄存器之前,对应的布尔项为“高”。在此期间不管输入信号如何变化,对应的布尔项不再变化。写入控制寄存器会清除这个布尔项为“低”。
④ 锁存的边沿。这是上面2个条件的结合,输入信号的上升沿或下降沿,都会使对应的布尔项为“高”。同样,写入控制寄存器会清除这个布尔项为“低”。
⑤ 高电平。当输入信号为高电平时,对应的布尔项为“高”。注意这个条件没有经过锁存,即当输入信号变低时,对应的布尔项也变为“低”。
⑥ 低电平。当输入信号为低电平时,对应的布尔项为“高”。注意这个条件没有经过锁存,即当输入信号变高时,对应的布尔项也变为“低”。这个条件相当于布尔“非”运算。
⑦ 恒为“低”。这种情况与输入信号无关,对应的布尔项始终为“低”。一般是按照应用逻辑。用于设置闲置的布尔项。
⑧ 实时的边沿事件。当检测到输入信号的上升沿或下降沿时,对应的布尔项为“高”,在一个时钟周期之后,对应的布尔项会自动变为“低”。下次再次检测到上升沿或下降沿时,重复这个“高-低”的过程。这个条件与上面的②③④不同,没有经过锁存,只持续一个时钟周期。

图1. 布尔项(slice)的构成逻辑
19.png


3.3. 模式匹配引擎的实现
如果我们把8个布尔项按顺序排列,在每两个布尔项之间就可以指定“与”或“或”操作。下图给出了布尔项与操作(符)之间的关系。
图2. 布尔项与操作(符)直接的关系
31.png

看前面3.1节的布尔表达式的例子,下面是把它们映射到内部布尔项的示意图。
图3. 布尔算式的内部实现示意图1
32.png

图4. 布尔算式的内部实现示意图2
33.png

整个模式匹配引擎是由多级的“与-或”门的级联实现,下图是完整内部实现示意图,有经验的读者可以参照用户手册加深理解。
图5. 模式匹配引擎完整示意图
34.png

3.4. 引脚组合逻辑功能的配置
解释完布尔项的概念和布尔项的选项,以及它们之间的运算关系,接下来看看与配置寄存器的对应关系。
共有三个寄存器用于设置模式匹配引擎:
PMCTRL:模式匹配引擎的控制位和结果状态位。

41.png

- SEL_PMATCH – 用于指定8个引脚中断是来自于引脚中断功能(见第1节),还是来自模式匹配功能(见第3节)。
- ENA_RXEV – 模式匹配结果为“真”时,用户可以配置该位使能向CPU核心发送RXEV事件并在引脚GPIO_INT_BMAT产生输出。
- RXEV事件用于触发WFE(Wait For Event: 等待事件)指令结束等待状态。
- PMAT[7:0] – 这里每一位对应布尔组合(i)的输出,见图5。

PMSRC:指定每个布尔项使用哪个输入引脚

42.png
- 如果要指定PINTSEL[n]对应的引脚,作为布尔项i的输入,只需要设置SRC(i)=n即可。


PMCFG:指定每个布尔项如何参与运算,同时指定结果输出结点。

43.png
- CFG[7:0] – 每一个分量用于选择对应布尔项与输入信号的关系,见3.2节描述的8种情况。
- Prod_Endpts[6:0] – 如果某一位为‘0’,表示对应的操作为‘与’,见图2。某位为‘1’表示对应的操作为‘或’,并且需要输出前面各个布尔项相与的结果到PMAT,和对应的中断请求。
- 注意,不存在Prod_Endpts7。即使有这一位,它也会始终为‘1’。

3.5. 模式匹配引擎的中断
可以用模式匹配(引脚组合逻辑)的结果产生中断,当PMATn为’1’时,如果使能了对应的PININTn中断,则这些分项(见图5中的绿色标注的信号),可以触发中断。这些中断的触发方式,在芯片内部固定设置为高电平触发,不能由软件配置。

既然是电平触发,这个中断不能由软件清除,只要这个信号为高,中断就会反复出现,直到该分项变为低。如果不希望反复进入中断,可以尝试使用“实时的边沿事件”作为组合条件(见3.2节的第8个选项)。


4、模式匹配引擎的设置

在配置模式匹配引擎之前,建议用户先按照自己的布尔表达式,填写下面的表格。
- 第一行PinInt的每个位置,填写对应PININT0~7的引脚编号。未用位置不填。
- 第二行SliceSrc的每个位置,填写每个布尔项对应的PININTn编号n,取值范围是0~7。
- 第三行SliceCfg的每个位置,填写如何配置每个布尔项,见3.2节,取值使用下述定义的常量:
#define SLICE_CONST1    0    // 恒为“高”
#define SLICE_RISE      1       // 锁存的上升沿
#define SLICE_FALL      2       // 锁存的下降沿
#define SLICE_EDGE      3       // 锁存的边沿
#define SLICE_DIRECT    4    // 高电平
#define SLICE_NOT       5         // 低电平
#define SLICE_CONST0    6    // 恒为“低”
#define SLICE_EVENT     7      // 实时的边沿事件

- 第四行SliceEndp的每个位置,填写’0’表示这个位置执行“与”运算并没有结果输出;填写非’0’的值,表示这个位置执行“或”运算并输出结果。芯片中没有SliceEndp第7个位置对应的配置位,此处内容无效。
35.png

此表格的目的是在写代码之前有一个完整的概念,这样写代码时不会产生各个寄存器内容不匹配的问题。

下面再定义几个宏,配合上述表格就可以很容易地实现对模式匹配引擎的设置。

首先参考3.4节的寄存器说明,定义一组移位操作的宏,每三位代码为一组进行移位:


#define PMPOS0(src)     (((src)&0x7)<<8)
#define PMPOS1(src)     (((src)&0x7)<<11)
#define PMPOS2(src)     (((src)&0x7)<<14)
#define PMPOS3(src)     (((src)&0x7)<<17)
#define PMPOS4(src)     (((src)&0x7)<<20)
#define PMPOS5(src)     (((src)&0x7)<<23)
#define PMPOS6(src)     (((src)&0x7)<<26)
#define PMPOS7(src)     (((src)&0x7)<<29)

接下来是涉及PMCFG的低6位的另一组移位操作的宏:

#define PMep0(ep)     (((ep)&0x1)<<0)
#define PMep1(ep)     (((ep)&0x1)<<1)
#define PMep2(ep)     (((ep)&0x1)<<2)
#define PMep3(ep)     (((ep)&0x1)<<3)
#define PMep4(ep)     (((ep)&0x1)<<4)
#define PMep5(ep)     (((ep)&0x1)<<5)
#define PMep6(ep)     (((ep)&0x1)<<6)

然后使用上述宏,定义SliceSrc如下,逐个填入上述表格第二行的所有内容,然后写入PMSRC寄存器进行初始化:

#define SliceSrc(S0, S1, S2, S3, S4, S5, S6, S7) \
    (PMPOS0(S0) | PMPOS1(S1) | PMPOS2(S2) | PMPOS3(S3) | \
     PMPOS4(S4) | PMPOS5(S5) | PMPOS6(S6) | PMPOS7(S7))

定义SliceCfg如下,逐个填入上述表格第三行的所有内容:
#define SliceCfg(C0, C1, C2, C3, C4, C5, C6, C7) \
    (PMPOS0(C0) | PMPOS1(C1) | PMPOS2(C2) | PMPOS3(C3) | \
     PMPOS4(C4) | PMPOS5(C5) | PMPOS6(C6) | PMPOS7(C7))

定义ProdEndp如下,逐个填入上述表格第四行的所有内容,再和SliceCfg宏的结果相’或’ ,写入PMCFG寄存器进行初始化:

#define ProdEndp(P0, P1, P2, P3, P4, P5, P6) \
    (PMep0(P0) | PMep1(P1) | PMep2(P2) | PMep3(P3) | \
     PMep4(P4) | PMep5(P5) | PMep6(P6))

下面再用实例说明初始化设置的过程。


5、模式匹配引擎的使用实例

5.1. 异或的实现

假定有一个电梯控制器,它只有两个按钮,一个“向上”,一个“向下”,如果分别单独按下任一个按钮,电梯会按指定方向运行,如果两个按钮同时按下,则电梯不会运行。这就是“异或”操作逻辑。
下面的代码用USERKEY和ISPKEY,分别代表“向上”和“向下”按钮,用模式匹配引擎实现这个“异或”操作逻辑。
整个的布尔表达式是:


USERKEY * NOT(WAKEKEY) + NOT(USERKEY) * WAKEKEY

按照这个表达式填写前述表格如下:

46.png

参照前面的介绍,解读这个表格就很容易了:
- 第一行:USERKEY对应PININT0;ISPKEY对应PININT1。
- 第二行:PININT0对应布尔项0、2;PININT1对应布尔项1、3。
- 第三行:布尔项0、3的输入为“高电平”有效;布尔项1、2的输入为“低电平”有效。布尔项4~7的输入恒为“低”,在最终结果中不产生影响。
- 第四行:PMAT[1]得到布尔项0、1相与的结果;PMAT[3]得到布尔项2、3相与的结果。
下面的初始化代码实现了上述表格的内容。

“异或”模式匹配初始化函数
46.png

上述代码段的14、15行是对模式匹配寄存器的初始化,这里用到了上一节定义的宏。

本例程有两种操作模式:轮询和中断模式。在轮询模式中,软件循环地检测PMCTRL寄存器的PMAT域,当检测到布尔输出项1或3为’1’时,分别点亮LED1或LED3,否则熄灭LED。下面的PM_Polling()函数会被主循环调用,执行循环检测。

循环检测PMAT状态函数
47.png

中断模式下,需要安排两个中断处理程序PININT1_IRQHandler()和PININT3_IRQHandler(),分别接收布尔输出项1和3的中断。


中断处理函数
51.png

最后是主函数:
模式匹配初始化函数
52.png

在中断模式下,当没有中断时,主函数的第15行会不断地熄灭LED。但当有某个按钮按下时,由于模式匹配的中断是电平中断,一旦产生中断的条件存在,中断处理函数就会不断地被调用,CPU将不能执行主函数中的指令。这样当仅按下一个按钮时,使用者就会看见对应的LED常亮,直到松开按钮。松开按钮后,产生中断的条件消失,CPU才能返回主函数,LED被熄灭。
在模式匹配初始化函数PM_Init()的第26行,模式匹配的结果输出信号GPIO_INT_BMAT被映射到对应LED0的引脚,通过LED0的状态可以直观地看到整个逻辑运算的结果。综合起来会有如下效果。
53.png

5.2. 三人表决器
三人表决器就是每人一个按钮,任意两人按下按钮则表示表决通过。
本实例使用开发板上的三个按键USERKEY、WAKEKEY和ISPKEY。首先填写初始化的表格:
54.png

所有的布尔项都使用“锁存的上升沿”作为模式匹配的输入。电路的配置是按下按键会把引脚短接到地,因此抬起按键的动作会产生上升沿,经过内部锁存,可以保证不同按键的按下时间不同,但状态不会丢失。
下面是这个三人表决器的初始化函数,可以看出这个函数内容基本和上面例子的函数一样,只是代入的参数不同:
“三人表决器”模式匹配初始化函数
55.png

以下是主函数中的主循环部分。
“三人表决器”主循环部分
56.png

在主循环下,同样有中断模式和轮询模式的区分。操作和前面的“异或”例程一致。
特殊的是,这个例程设置了一个超时处理,能够定期地清除投票状态。
“三人表决器”的超时处理
57.png

“三人表决器”中断模式的中断处理
58.png

以下是这个例程演示的效果:
59.png

表中的“投”表示按键被按下并被释放。如果只按下按键,而保持按下而不释放,模式匹配模块不会判断它为按下状态,这种情况可以理解为,投票者还在犹豫。
当LED0亮时表示不够两个人以上投票,投票没有通过;LED0熄灭表示投票通过。
在使用这个例程的中断模式时,由于各个中断的优先级一致,同时由于电平中断的缘故,如果一旦进入了某个中断,有可能另一个布尔项的中断不能被响应,结果LED1/3/5中有些时候不能被同时点亮。这种现象,不会出现在轮询模式下。读者由此可以体会到轮询与中断的区别。

回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /4 下一条

Archiver|手机版|小黑屋|恩智浦技术社区

GMT+8, 2024-4-21 00:10 , Processed in 0.117058 second(s), 20 queries , MemCache On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表