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

    文章

    16024

    浏览量

    343719
  • 恩智浦
    +关注

    关注

    14

    文章

    5716

    浏览量

    100785
  • 引脚
    +关注

    关注

    16

    文章

    1045

    浏览量

    48976
  • 组合逻辑
    +关注

    关注

    0

    文章

    45

    浏览量

    9993
  • LPC800
    +关注

    关注

    1

    文章

    17

    浏览量

    22426

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

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

收藏 人收藏

    评论

    相关推荐

    恩智浦NXP LPC800系列微控制器

    LPC800系列MCU是采用ARM®CortexTM M0+内核的32位低功耗微处理器,运行频率高达30MHz。LPC800采用低引脚封装,最高支持16KB片内Flash存储器和4KB片内SRAM
    发表于 02-20 11:22

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

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

    自动控制原理—北京航天航空大学精品课件(八章全)

    第一:拉氏变换第二:数学模型第三:时域分析法第四:根轨迹法第五:频率法第六:系统校正
    发表于 10-11 13:58

    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

    51单片机第八章素材

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

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

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

    lpc800技术手册

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

    LPC800 dip8封装各项参数指标介绍

    LPC800是NXP推出可以替代8/16位单片机的产品,LPC800简单易用,LPC800以8位MCU的价格可以达到32位的性能备受好评;可调用片内的外设驱动程序,LPC800采用
    发表于 10-25 15:00 6539次阅读
    <b class='flag-5'>LPC800</b> dip8封装各项参数指标介绍

    LPC800引脚图与引脚功能说明

    单片机引脚的连接非常重要,如果单片机的管脚都通过一个CPLD连接后,在连接到应用,是不是就可以不用担心PCB画板了,只需要更加应用更改CPLD内部连接就可以了,而LPC800引脚则可以实现。 大家
    发表于 10-25 16:21 3139次阅读
    <b class='flag-5'>LPC800</b><b class='flag-5'>引脚</b>图与<b class='flag-5'>引脚</b>功能说明

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

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

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

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

    低功耗、引脚少、省空间:LPC800系列MCU,来全面了解一下!

    恩智浦官网精彩导览 LPC800系列MCU 在嵌入式开发中,如果你希望获得一个低功耗、节省空间、低引脚数的32-bit微控制器解决方案,那么一定要来全面了解一下恩智浦的LPC800系列MCU
    的头像 发表于 02-11 13:25 915次阅读

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

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

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

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