0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

LPC800前生今世-第八章 引脚中断和引脚组合逻辑 (Pin Interrupt & Pin Pattern)

恩智浦MCU加油站 来源:恩智浦MCU加油站 作者:恩智浦MCU加油站 2023-12-14 16:20 次阅读

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

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

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

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

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

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

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

例如:
      LPC_SYSCON->PINTSEL[0] = 20// 指定PIO0_20为引脚中断0的输入源
      LPC_SYSCON->PINTSEL[3] = 20// 指定PIO0_20为引脚中断3的输入源
      LPC_SYSCON->PINTSEL[6] = 33// 指定PIO1_1为引脚中断6的输入源

1.1.1 指定中断触发源和触发方式

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

下面这个表格是按照要求的触发方式,标示出应该如何设置寄存器控制位。

wKgZomV6u-iAMLnVAAAe0vODBJU730.png 表2.触发方式的配置

表2中可以看出,SIENR和CIENR都是只写寄存器,一个用于设置IENR寄存器位,另一个用于清除IENR寄存器位;这两个寄存器的目的是为了在修改IENR寄存器时的“读-修改-写”的操作,只需写操作,即可改变所有需要设置的位或需要清除的位。

同样,SIENF和CIENF也都是只写寄存器,用于设置IENF寄存器位。

由于独立的IENR和IENF寄存器,用户可以配置同一个信号的上升沿和下降沿都产生中断。在中断处理中,可以通过读出RISE和FALL寄存器判断是哪个边沿产生的中断。在RISE和/或FALL寄存器中写’1’可以清除中断状态,也可以在IST寄存器中写’1’ 清除中断状态。

1.1.2 电平触发方式的使用

因为通过软件本身,在MCU内部不能清除电平触发所产生的中断,软件必须执行某种操作,让外部电路改变信号线上的电平,才能使MCU不再产生中断,所以使用电平中断时要小心处理。

可以通过在IST寄存器中写’1’的方式,改变触发的电平,从而间接地清除中断状态。例如当高电平触发中断后,在IST寄存器中写’1’,控制器将变为有低电平时产生中断,同时清除中断状态。

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

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

1.2.1复位所有引脚中断寄存器(PinInt_Reset)

PinInt_Reset()函数的功能就是清除所有未处理的引脚中断标志,同时关闭所有的引脚中断。

代码片段1.复位所有引脚中断函数
01  void PinInt_Reset()
02  {
03      LPC_PIN_INT->ISEL = 0;      // 边沿触发
04      LPC_PIN_INT->IENR = 0;      // 关闭上升沿或电平中断
05      LPC_PIN_INT->IENF = 0;      // 关闭下降沿中断
06      LPC_PIN_INT->RISE = 0xFF;   // 清除上升沿检测标志
07      LPC_PIN_INT->FALL = 0xFF;   // 清除下降沿检测标志
08  }

1.2.2使能引脚的中断

共有4个函数分别使能引脚的中断为上升沿触发、下降沿触发、高电平触发和低电平触发,这些函数的功能是按照给定的输入参数,使能对应的引脚中断,它的输入参数是需要设置的引脚中断的位域值。

输入参数的第0位为’1’表示需要设置PININT0,输入参数的第1位为’1’表示需要设置PININT1,依次类推直至第7位。

注意,不要把PININTn和引脚编号混淆,使用以下函数之前,需要指定PININTn和引脚的关系,由LPC_SYSCON->PINTSEL[0]定义,见1.1节。

下面分别是这4个函数的代码:

代码片段2.函数PinInt_Enable_Rising()
01  void PinInt_Enable_Rising(uint32_t pins_mask)
02  {
03      uint32_t Pint_mode;
04      Pint_mode = LPC_PIN_INT->ISEL;
05      if (Pint_mode & pins_mask) {
06          Pint_mode &= ~pins_mask;
07          LPC_PIN_INT->ISEL = Pint_mode;  // 边沿触发
08      }
09      LPC_PIN_INT->SIENR = pins_mask;     // 使能上升沿中断
10  }

代码片段3.函数PinInt_Enable_Falling()
01  void PinInt_Enable_Falling(uint32_t pins_mask)
02  {
03      uint32_t Pint_mode;
04      Pint_mode = LPC_PIN_INT->ISEL;
05      if (Pint_mode & pins_mask) {
06          Pint_mode &= ~pins_mask;
07          LPC_PIN_INT->ISEL = Pint_mode;  // 边沿触发
08      }
09      LPC_PIN_INT->SIENF = pins_mask;     // 使能下降沿中断
10  }

代码片段4.函数PinInt_Enable_High()

01  void PinInt_Enable_High(uint32_t pins_mask)
02  {
03      uint32_t Pint_mode;
04      Pint_mode = LPC_PIN_INT->ISEL;
05      if ((Pint_mode & pins_mask) != pins_mask) {
06          Pint_mode |= pins_mask; 
07          LPC_PIN_INT->ISEL = Pint_mode;  // 电平触发
08      }
09      LPC_PIN_INT->SIENF = pins_mask;     // 使能高电平中断
10  }

代码片段5.函数PinInt_Enable_Low ()

01  void PinInt_Enable_Low(uint32_t pins_mask)
02  {
03      uint32_t Pint_mode;
04      Pint_mode = LPC_PIN_INT->ISEL;
05      if ((Pint_mode & pins_mask) != pins_mask) {
06          Pint_mode |= pins_mask;
07          LPC_PIN_INT->ISEL = Pint_mode;  // 电平触发
08      }
09      LPC_PIN_INT->CIENF = pins_mask;     // 使能低电平中断
10  }

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

代码片段6.函数PinInt_ Disable ()

01  void PinInt_Disable(uint32_t pins_mask)
02  {
03      uint32_t Pin_mask;
04      for (Pin_mask = 1; Pin_mask < 0x100; Pin_mask <<= 1) {
05          if ((pins_mask & Pin_mask) == 0)
06              continue;      // 对未指定的位,不做任何操作
07          if (LPC_PIN_INT->ISEL & Pin_mask) { // 电平中断
08              LPC_PIN_INT->CIENR = Pin_mask;  // 关闭对应中断
09          }
10          else {  // 边沿触发
11              LPC_PIN_INT->CIENR = Pin_mask;  // 关闭上升沿中断
12              LPC_PIN_INT->CIENF = Pin_mask;  // 关闭下降沿中断
13          }
14      }
15  }

1.2.4清除对应的引脚中断标志(PinInt_Clear)

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

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

代码片段7.函数PinInt_Clear()
01  void PinInt_Clear(uint32_t pins_mask)
02  {
03      uint32_t Pin_mask;
04      for (Pin_mask = 1; Pin_mask < 0x100; Pin_mask <<= 1) {
05          if ((pins_mask & Pin_mask) == 0)
06              continue; 
07          if (!(LPC_PIN_INT->ISEL & Pin_mask))
08              LPC_PIN_INT->IST = Pin_mask; 
09      }
10  }

1.3.引脚中断的使用实例下面借用GPIO章的例程,略作修改演示引脚中断的操作。

本例程也是使用LPC824-Lite开发板,循环执行下列操作:

■通过GPIO循环点亮板上的八个红色LED

■分别点亮八个红色LED,模拟一个小虫爬行

设置USER(S4)、WAKEUP(S3)和ISP(S2)按键分别产生引脚中断0~2(PINTSEL0/1/2),对应的中断处理的意义如下:

A.按下USER键:循环体中跳过上述第2项操作,再按一次USER键恢复1-2循环。

B.按下WAKEUP键:循环体中跳过上述第1项操作,再按一次WAKEUP键恢复1-2循环。

C.按下ISP键:循环体中的操作速度变慢50%,再按一次ISP键恢复默认的速度。

D.如果上述2项操作都被跳过,则连续闪烁所有的LED灯。在例程中引入3个变量,分别向主循环传递3个按键的不同状态:

wKgZomV6u-iATmn8AABR3WYCJck041.png

下面的PINT_Init()函数用于指定哪个按键,作为哪个中断的触发源,同时指定由按键的上升沿触发中断,最后使能对应的中断并设置中断优先级。

首先需要定义中断的编号:wKgZomV6u-iAZQ4fAAA4R1L1pY0903.png

代码片段8.引脚中断初始化函数

01  void PINT_Init(void)
02  {
03      LPC_GPIO_PORT->DIRCLR0 = PIN_KEYS_MASK; 
04      
05      LPC_SYSCON->PINTSEL[INT_USERKEY] = PIN_USERKEY;
06      LPC_SYSCON->PINTSEL[INT_WAKEKEY] = PIN_WAKEKEY; 
07      LPC_SYSCON->PINTSEL[INT_ISPKEY] = PIN_ISPKEY;
08      
09      PinInt_Reset();
10      PinInt_Enable_Rising((1<
11                         | (1<
12                         | (1<
13      
14      NVIC_EnableIRQ(PININT0_IRQn); 
15      NVIC_EnableIRQ(PININT1_IRQn); 
16      NVIC_EnableIRQ(PININT2_IRQn);
17    
18      NVIC_SetPriority(PININT0_IRQn, 3);
19      NVIC_SetPriority(PININT1_IRQn, 3); 
20      NVIC_SetPriority(PININT2_IRQn, 3);
21  }

下面是本例程的主函数片断,和中断处理程序的代码,略去了上面描述的2项操作的具体实现,读者可以参考GPIO章节中的代码。

代码片段9.引脚中断主函数片断
01      PINT_Init();
02      skip_step1 = skip_step2 = 0; 
03      leds_speed = 100;
04      while (1) {
05          if (skip_step1 == 0) {
06              LPC_GPIO_PORT->SET0 = PIN_LEDS_MASK; // 熄灭所有灯
07  
08          }
09  
10          if (skip_step2 == 0) {
11              LPC_GPIO_PORT->SET0 = PIN_LEDS_MASK; // 熄灭所有灯
12  
13              }
14          }
15          
16          if (skip_step1 && skip_step2) {
17              LPC_GPIO_PORT->NOT0 = PIN_LEDS_MASK; // 翻转所有灯
18              Wait1ms(leds_speed);
19          }
20      } // 结束while(1)循环

代码片段10.引脚中断的中断处理函数
01  void PININT0_IRQHandler(void)
02  {
03      if (LPC_PIN_INT->RISE & (1<
04          skip_step1 = !skip_step1;
05      PinInt_Clear(1 << INT_USERKEY); 
06  }
07  
08  void PININT1_IRQHandler(void)
09  {
10      if (LPC_PIN_INT->RISE & (1<
11          skip_step2 = !skip_step2;
12      PinInt_Clear(1<
13  }
14  
15  void PININT2_IRQHandler(void)
16  {
17      if (LPC_PIN_INT->RISE & (1<
18          if (leds_speed == DEFAULT_SPEED) 
19              leds_speed = DEFAULT_SPEED + DEFAULT_SPEED/2; 
20          else
21              leds_speed = DEFAULT_SPEED;
22      PinInt_Clear(1<
23  }

上述三个中断处理程序的最后一个语句,都是调用PinInt_Clear ()函数清除对应的中断标志。这个清除标志的语句都是不受是否有上升沿中断影响,即使进入该中断处理函数时发生的事件不是上升沿,也会执行清除标志操作,这样安排是为防止有遗漏的中断,而造成反复进入中断处理程序而死机。 1.4 模式匹配引擎功能和使用

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

前面介绍的引脚中断功能,每个中断只能由来自某个单个引脚的状态变化而产生;而通过模式匹配引擎,根据多个引脚的组合逻辑运算结果,可以产生对应的中断。例如可以实现一个键盘,键盘的每个按键可以单独产生中断,用于判断哪个键被按下,也可以使用组合逻辑功能,当某个特定的按键组合被按下,才能产生中断,这样可以更加方便地检测诸如Ctrl-C、Ctrl-V这样的组合功能。

1.4.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次,不能由模式匹配引擎实现。

1.4.2布尔项的实现

在模式匹配引擎中,布尔变量的每一次出现以一个布尔项来实现,在用户手册中“布尔项”以slice表示。

内部实现中,只有8个布尔项(slice),因此所有变量出现次数的总和不能多于8个。

对每一个布尔项,用户可以按照输入信号的不同变化,选择多达8种不同的条件。这些条件分别是:

1.恒为“高”。这种情况与输入信号无关,对应的布尔项始终为“高”。一般是按照应用逻辑,用于设置闲置的布尔项。

2.锁存的上升沿。从输入信号出现一个上升沿,到再次写入模式匹配引擎的控制寄存器之前,对应的布尔项为“高”。在此期间不管输入信号如何变化,对应的布尔项不再变化。写入控制寄存器会清除这个布尔项为“低”。

3.锁存的下降沿。从输入信号出现一个下降沿,到再次写入模式匹配引擎的控制寄存器之前,对应的布尔项为“高”。在此期间不管输入信号如何变化,对应的布尔项不再变化。写入控制寄存器会清除这个布尔项为“低”。

4.锁存的边沿。这是上面2个条件的结合,输入信号的上升沿或下降沿,都会使对应的布尔项为“高”。同样,写入控制寄存器会清除这个布尔项为“低”。

5.高电平。当输入信号为高电平时,对应的布尔项为“高”。注意这个条件没有经过锁存,即当输入信号变低时,对应的布尔项也变为“低”。

6.低电平。当输入信号为低电平时,对应的布尔项为“高”。注意这个条件没有经过锁存,即当输入信号变高时,对应的布尔项也变为“低”。这个条件相当于布尔“非”运算。

7.恒为“低”。这种情况与输入信号无关,对应的布尔项始终为“低”。一般是按照应用逻辑。用于设置闲置的布尔项。

8.实时的边沿事件。当检测到输入信号的上升沿或下降沿时,对应的布尔项为“高”,在一个时钟周期之后,对应的布尔项会自动变为“低”。下次再次检测到上升沿或下降沿时,重复这个“高-低”的过程。这个条件与上面的2、3、4不同,没有经过锁存,只持续一个时钟周期。

wKgZomV6u-iAXQpmAADzVzerm_A195.png                 图2.布尔项(slice)的构成逻辑

1.4.3 模式匹配引擎的实现

如果我们把8个布尔项按顺序排列,在每两个布尔项之间就可以指定“与”或“或”操作。下图给出了布尔项与操作(符)之间的关系。

wKgZomV6u-mAXGnmAACOwxj1nwQ482.png              图3.布尔项与操作(符)直接的关系

参看1.4.1布尔表达式的例子,下面是把它们映射到内部布尔项的示意图。

wKgZomV6u-mAeIWIAACbe35HhSI639.png              图4.布尔算式的内部实现示意图1

wKgZomV6u-mADzvkAAClt-Yd0nk422.png                图5.布尔算式的内部实现示意图2

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

wKgZomV6u-mAQCnpAAGwGH7c7FE174.png                 图6.模式匹配引擎完整示意图

1.4.4引脚组合逻辑功能的配置

解释完布尔项的概念和布尔项的选项,以及它们之间的运算关系,接下来看看与配置寄存器的对应关系。

共有三个寄存器用于设置模式匹配引擎:

▲PMCTRL:模式匹配引擎的控制位和结果状态位。

wKgZomV6u-mAeSGYAACHlf_F2Ic425.png■ SEL_PMATCH – 用于指定8个引脚中断是来自于引脚中断功能(见9.1节)还是来自模式匹配功能(见9.4节)。

■ENA_RXEV – 模式匹配结果为“真”时,用户可以配置该位使能向CPU核心发送RXEV事件并在引脚GPIO_INT_BMAT产生输出。
RXEV事件用于触发WFE(Wait For Event: 等待事件)指令结束等待状态。

■PMAT[7:0] – 这里每一位对应布尔组合(i)的输出,见图37。

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

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

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

wKgZomV6u-qAWSbBAABlhrQ6mac907.png■CFG[7:0] – 每一个分量用于选择对应布尔项与输入信号的关系,见1.4.2描述的8种情况。

■Prod_Endpts[6:0] – 如果某一位为‘0’,表示对应的操作为‘与’,见图3。某位为‘1’表示对应的操作为‘或’,并且需要输出前面各个布尔项相与的结果到PMAT[i],和对应的中断请求。
注意,不存在Prod_Endpts7。即使有这一位,它也会始终为‘1’。

1.4.5 模式匹配引擎的中断

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

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

1.5.模式匹配引擎的设置在配置模式匹配引擎之前,建议用户先按照自己的布尔表达式,填写下面的表格。

■第一行PinInt的每个位置,填写对应PININT0~7的引脚编号。未用位置不填。

■第二行SliceSrc的每个位置,填写每个布尔项对应的PININTn编号n,取值范围是0~7。

■第三行SliceCfg的每个位置,填写如何配置每个布尔项,见1.4.2节,取值使用下述定义的常量:

wKgZomV6u-qAWBmcAAAZJdXp7Cg602.png

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

wKgZomV6u-qAVSdVAAAM9o21BKM277.png

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

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

首先参考1.4.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寄存器进行初始化:

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

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

1.6.模式匹配引擎的使用实例1.6.1异或的实现

假定有一个电梯控制器,它只有两个按钮,一个“向上”,一个“向下”,如果分别单独按下任一个按钮,电梯会按指定方向运行,如果两个按钮同时按下,则电梯不会运行。这就是“异或”操作逻辑。

下面的代码用USERKEY和ISPKEY,分别代表“向上”和“向下”按钮,用模式匹配引擎实现这个“异或”操作逻辑。

整个的布尔表达式是:

USERKEY * NOT(ISPKEY) + NOT(USERKEY) * ISPKEY

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

wKgZomV6u-qAKv-bAAA0sPEJsqM657.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相与的结果。

下面的初始化代码实现了上述表格的内容:

代码片段11.“异或”模式匹配初始化函数
01  void PM_Init(void)
02  {
03      LPC_SYSCON->PINTSEL[0] = PIN_USERKEY; 
04      LPC_SYSCON->PINTSEL[1] = PIN_ISPKEY;
05  
06  #if !PM_POLLING
07      NVIC_EnableIRQ(PININT1_IRQn); 
08      NVIC_EnableIRQ(PININT3_IRQn); 
09    
10      NVIC_SetPriority(PININT1_IRQn, 3);
11      NVIC_SetPriority(PININT3_IRQn, 3); 
12  #endif  // PM_POLLING
13      
14      LPC_PIN_INT->PMSRC = SliceSrc(0, 1, 1, 0, 0, 0, 0, 0);
15      LPC_PIN_INT->PMCFG = SliceCfg(SLICE_DIRECT,
16                                    SLICE_NOT,
17                                    SLICE_DIRECT,
18                                    SLICE_NOT,
19                                    SLICE_CONST0, 
20                                    SLICE_CONST0, 
21                                    SLICE_CONST0,
22                                    SLICE_CONST0) | 
23                           ProdEndp(0, 1, 0, 1, 0, 0, 0); 
24      LPC_PIN_INT->PMCTRL = 0x03; 
25      
26      ConfigSWM(GPIO_INT_BMAT, PIN_LED0);
27  }

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

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

代码片段12.循环检测PMAT状态函数
01  void PM_Polling(void)
02  {
03      uint32_t  PMAT = LPC_PIN_INT->PMCTRL>>24; 
04    
05      if (PMAT & (1<<1))
06          LPC_GPIO_PORT->CLR0 = 1<
07      else if (PMAT & (1<<3))
08          LPC_GPIO_PORT->CLR0 = 1<
09      else
10          LPC_GPIO_PORT->SET0 = 1<
11  }

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

代码片段13.中断处理函数

01  #if ! PM_POLLING
02  void PININT1_IRQHandler(void) 
03  {
04      LPC_GPIO_PORT->CLR0 = 1<
05  }
06  
07  void PININT3_IRQHandler(void)
08  {
09      LPC_GPIO_PORT->CLR0 = 1<
10  }
11  #endif  // PM_POLLING

最后是主函数。代码片段14.模式匹配初始化函数

01  int main()
02  {
03      GPIO_Init();
04      
05      LPC_GPIO_PORT->DIRSET0 = PIN_LEDS_MASK;     // 设置LED对应的引脚为输出
06      LPC_GPIO_PORT->SET0 = PIN_LEDS_MASK;        // 熄灭所有LED灯
07      
08      PINT_Init();  // 初始化引脚中断模块
09      PM_Init();    // 初始化模式匹配模块,见代码片段21
10        
11      while(1) {
12  #if PM_POLLING
13          PM_Polling();
14  #else
15          LPC_GPIO_PORT->SET0 = 1<
16  #endif
17          LPC_PIN_INT->RISE = 0xFF;
18          LPC_PIN_INT->FALL = 0xFF; 
19      }
20  }

中断模式下,当没有中断时,主函数的第15行会不断地熄灭LED。但当有某个按钮按下时,由于模式匹配的中断是电平中断,一旦产生中断的条件存在,代码片段23的中断处理函数就会不断地被调用,CPU将不能执行主函数中的指令。这样当仅按下一个按钮时,使用者就会看见对应的LED常亮,直到松开按钮。松开按钮后,产生中断的条件消失,CPU才能返回主函数,LED被熄灭。

在代码片段21的模式匹配初始化函数的第26行,模式匹配的结果输出信号GPIO_INT_BMAT被映射到对应LED0的引脚,通过LED0的状态可以直观地看到整个逻辑运算的结果。综合起来会有如下效果:wKgZomV6u-qAYCCWAAARjscYv7Q970.png1.6.2三人表决器

三人表决器就是每人一个按钮,任意两人按下按钮则表示表决通过。

本实例使用开发板上的三个按键USERKEY、WAKEKEY和ISPKEY。首先填写初始化的表格:wKgZomV6u-qALH9nAAAx5Afjl4w682.png

所有的布尔项都使用“锁存的上升沿”作为模式匹配的输入。电路的配置是按下按键会把引脚短接到地,因此抬起按键的动作会产生上升沿,经过内部锁存,可以保证不同按键的按下时间不同,但状态不会丢失。

下面是这个三人表决器的初始化函数,可以看出这个函数内容基本和上面例子的函数一样,只是代入的参数不同:

代码片段15.“三人表决器”模式匹配初始化函数

01  void PM_Init(void)
02  {
03      LPC_SYSCON->PINTSEL[0] = PIN_USERKEY;
04      LPC_SYSCON->PINTSEL[1] = PIN_WAKEKEY;
05      LPC_SYSCON->PINTSEL[2] = PIN_ISPKEY;
06  
07  #if !PM_POLLING
08      NVIC_EnableIRQ(PININT1_IRQn); 
09      NVIC_EnableIRQ(PININT3_IRQn); 
10      NVIC_EnableIRQ(PININT5_IRQn);
11    
12      NVIC_SetPriority(PININT1_IRQn, 3);
13      NVIC_SetPriority(PININT3_IRQn, 3); 
14      NVIC_SetPriority(PININT5_IRQn, 3);
15  #endif  // PM_POLLING
16      
17      LPC_PIN_INT->PMSRC = SliceSrc(0, 1, 1, 2, 2, 0, 0, 0);
18      LPC_PIN_INT->PMCFG = SliceCfg(SLICE_RISE,
19                                    SLICE_RISE,
20                                    SLICE_RISE,
21                                    SLICE_RISE,
22                                    SLICE_RISE, 
23                                    SLICE_RISE, 
24                                    SLICE_CONST0,
25                                    SLICE_CONST0) | 
26                           ProdEndp(0, 1, 0, 1, 0, 1, 0); 
27      LPC_PIN_INT->PMCTRL = 0x03; 
29      
30      ConfigSWM(GPIO_INT_BMAT, PIN_LED0);
31  }

以下是主函数中的主循环部分。

代码片段16.“三人表决器”主循环部分

01      while(1) {
02          TimeOut_Ctrl();
03  #if PM_POLLING
04          PM_Polling();
05  #else
06          LPC_GPIO_PORT->SET0 = 1<
07  #endif
08      }

在主循环下,同样有中断模式和轮询模式的区分。操作和前面的“异或”例程一致。

特殊的是,这个例程设置了一个超时处理,能够定期地清除投票状态。

代码片段17.“三人表决器”的超时处理
01  void TimeOut_Ctrl()
02  {
03      if (uwTick > 10) { 
04          LPC_GPIO_PORT->NOT0 = 1<
05          uwTick = 0;
06              
07          LPC_PIN_INT->PMCTRL = 0x00; 
08          LPC_PIN_INT->PMCTRL = 0x03;
09      }
10  }

代码片段18.“三人表决器”中断模式的中断处理

01  void PININT1_IRQHandler(void)
02  {
03      LPC_GPIO_PORT->CLR0 = 1<
04      TimeOut_Ctrl();
05  }

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

wKgZomV6u-uAez6VAABPb90G64g848.png

表中的“投”表示按键被按下并被释放。如果只按下按键,而保持按下而不释放,模式匹配模块不会判断它为按下状态,这种情况可以理解为,投票者还在犹豫。

当LED0亮时表示不够两个人以上投票,投票没有通过;LED0熄灭表示投票通过。

在使用这个例程的中断模式时,由于各个中断的优先级一致,同时由于电平中断的缘故,如果一旦进入了某个中断,有可能另一个布尔项的中断不能被响应,结果LED1/3/5中有些时候不能被同时点亮。这种现象,不会出现在轮询模式下。读者由此可以体会到轮询与中断的区别。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mcu
    mcu
    +关注

    关注

    146

    文章

    17141

    浏览量

    351101
  • 恩智浦
    +关注

    关注

    14

    文章

    5857

    浏览量

    107408
  • 引脚
    +关注

    关注

    16

    文章

    1195

    浏览量

    50447
  • 组合逻辑
    +关注

    关注

    0

    文章

    47

    浏览量

    10040
  • LPC800
    +关注

    关注

    1

    文章

    17

    浏览量

    22453

原文标题:LPC800前生今世-第八章 引脚中断和引脚组合逻辑 (Pin Interrupt & Pin Pattern)

文章出处:【微信号:NXP_SMART_HARDWARE,微信公众号:恩智浦MCU加油站】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    《测控电路》习题完整参考答案(第八章

    《测控电路》习题完整参考答案(第八章
    发表于 05-07 11:39

    LPC800系列LPC824介绍

    LPC800 系列是 NXP 推出的以 ARM Cortex-M0+ 为核心的 32 位 MCU,为基本微控制器应用提供了一系列低功耗、节省空间、低引脚数的选项。同时,LPC800系列MCU包含
    发表于 09-23 09:05

    波形的产生与变换电路 第八章PPT

    波形的产生与变换电路 第八章
    发表于 04-20 09:33 19次下载
    波形的产生与变换电路 <b class='flag-5'>第八章</b>PPT

    信号发生电路基础 第八章

    信号发生电路基础 第八章 8.1正弦波振荡电路1、振荡电路:是一种不需要外接输入信号就能将直流能源转换成具有一定频率、一定幅度和一定波形的交流能量
    发表于 04-20 13:58 49次下载

    第八章 线性离散控制系统分析

    第八章 线性离散控制系统分析 数字控制系统是一种以数字计算机为控制器去控制具有连续工作状态的被控对象的闭环控制系统。
    发表于 05-27 15:50 0次下载

    51单片机第八章素材

    单片机第八章素材练习,主要讲解单片机的初步应用只是,配合protues使用,加强对单片机有关知识的理解。
    发表于 11-16 18:53 1次下载

    电子技术基础模拟部分第五版_第八章习题答案.pdf

    电子技术基础模拟部分第五版_第八章习题答案.pdf,作业题解答!
    发表于 04-11 17:44 0次下载

    《测控电路》习题完整参考答案(第八章

    《测控电路》习题完整参考答案(第八章
    发表于 02-14 17:02 0次下载

    lpc800技术手册

    智浦重新设计了LPC800系列外设,尽可能精简产品,响应速度更快且更加高效。相比传统的8位MCU,其优势显而易见的。 定时器在不增加复杂性的情况下引入了全新灵活度,包括开关矩阵在内的LPC800革命性功能使设计师只需通过配置工具中的一行代码或一次点击,就能将片内外设分配到
    发表于 10-24 20:04 13次下载
    <b class='flag-5'>lpc800</b>技术手册

    电路《电路原理》邱关源---第八章 相量法

    电路《电路原理》邱关源---第八章 相量法
    发表于 01-18 11:37 0次下载

    IC工艺和版图设计第八章Latch-up和GuardRing设计

    IC工艺和版图设计第八章Latch-up和GuardRing设计
    发表于 02-10 18:11 0次下载

    第八章 外部中断

    第一节原理解读打算写一下中断,又忍不住想说一下中断的概念,去书上翻一翻,或者自己在搜索引擎上搜一下,都可以找到一大堆,包括本文写的这个外部中断也不例外。如果要写光是中断就可以单独写一篇
    的头像 发表于 01-25 09:30 1413次阅读
    <b class='flag-5'>第八章</b> 外部<b class='flag-5'>中断</b>

    LPC800前生今世 第二-系列总览

    LPC800系列总览 前面一已经简要地介绍了LPC800的基本特性和系列中各产品之间的对比。本章将通过框图的形式,给出更详细的配置信息,并引导读者从框图中捕捉到有用的信息。 LPC
    的头像 发表于 09-21 11:55 581次阅读

    LPC800前生今世 第六-开关矩阵(SWM: Switch Matrix)

    总体概述 开关矩阵顾名思义就是一个由多个,甚至多组开关组成的阵列,这个阵列的目的就是把片内外设的输入输出信号线,按照用户的意向,通过编程连接到特定的外部引脚上。 开关矩 阵可以为LPC800的使用者
    的头像 发表于 10-26 09:25 2355次阅读
    <b class='flag-5'>LPC800</b><b class='flag-5'>前生</b><b class='flag-5'>今世</b> 第六<b class='flag-5'>章</b>-开关矩阵(SWM: Switch Matrix)

    LPC800 前生今世-第七 输入输出控制器(GPIO)

    一款 MCU ,最重要的是要有输入输出引脚引脚的数量要够丰富,功能要够齐全,还要有灵活的输入输出控制方式。 在 LPC800 系列中提供了很多封装和输入输出引脚数量的选项: 表1
    的头像 发表于 11-02 08:55 727次阅读
    <b class='flag-5'>LPC800</b> <b class='flag-5'>前生</b><b class='flag-5'>今世</b>-第七<b class='flag-5'>章</b> 输入输出控制器(GPIO)