EXTI外部中断原理与配置

参考资料
开发板配套资料
《STM32Fx开发指南-HAL库版本》-第x章 外部中断实验
STM32Fxxx官方资料:
《STM32Fxx中文参考手册》- 中断和事件

笔记基于正点原子官方视频
视频连接https://www.bilibili.com/video/BV1Wx411d7wT?p=71&spm_id_from=333.1007.top_right_bar_window_history.content.click
如有侵权,联系删除

一、IO口外部中断原理概述

1.STM32控制器支持的外部中断/事件请求:

2.IO口外部中断

① STM32的每个IO都可以作为外部中断输入。
每个外部中断线可以独立的配置触发方式(上升沿下降沿或者双边沿触发),触发/屏蔽,专用的状态位。
③ STM32供IO使用的中断线只有16个,但是STM32F系列的IO口多达上百个,STM32F103ZGT6(112个IO口),那么中断线怎么跟IO口对应呢?

3.GPIO和中断线映射关系

在位置:H:\2技术书籍\STM32F429开发指南中书《STM32F4xx中文参考手册》8.2.4小节及以后几小节有相关寄存器的详细介绍。


其中:
SYSCFG 外部中断配置寄存器 1 的低16位配置的是 EXTI0 至 EXTI3
SYSCFG 外部中断配置寄存器 2 的低16位配置的是 EXTI4 至 EXTI7
SYSCFG 外部中断配置寄存器 3 的低16位配置的是 EXTI8 至 EXTI11
SYSCFG 外部中断配置寄存器 4 的低16位配置的是 EXTI12 至 EXTI15

例如:
在 EXTI0 对应的四个位填入 0000,则配置的是 PA[0] 引脚
在 EXTI2 对应的四个位填入 0000,则配置的是 PA[2] 引脚
在 EXTI6 对应的四个位填入 0011,则配置的是 PD[6] 引脚
在 EXTI12 对应的四个位填入 0110,则配置的是 PG[12] 引脚
在 EXTI15 对应的四个位填入 0101,则配置的是 PF[15] 引脚

4.16个中断线就分配16个中断服务函数?

  • IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数。

  • 从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数外部中断线10~15分配一个中断向量,共用一个中断服务函数。

5.中断服务函数列表:

 EXTI0_IRQHandler EXTI1_IRQHandlerEXTI2_IRQHandler EXTI3_IRQHandler EXTI4_IRQHandler EXTI9_5_IRQHandler EXTI15_10_IRQHandler

二、IO口外部中断HAL库配置方法

1.外部中断操作使用到的函数分布文件

  • stm32fxxx_hal_gpio.c
    位置:工程模板 - HALLIB - stm32fxxx_hal_gpio.c
  • stm32fxxx_hal_gpio.h
    位置:工程模板 - HALLIB - stm32fxxx_hal_gpio.c - stm32fxxx_hal_gpio.h

2.外部中断配置

  • 外部中断的中断线映射配置触发方式都是在GPIO初始化函数中完成:
 GPIO_InitTypeDef GPIO_Initure;GPIO_Initure.Pin=GPIO_PIN_0;                              //PA0GPIO_Initure.Mode=GPIO_MODE_IT_RISING;   //上升沿触发GPIO_Initure.Pull=GPIO_PULLDOWN;HAL_GPIO_Init(GPIOA,&GPIO_Initure);

在上面代码配置GPIO_Initure.Mode时,里面模式的子代码里就有关于触发方式的代码。
在位置:工程模板 - HARDWARE - exit.c 文件中有关于触发方式的代码,如下图:

  • 和串口中断一样,HAL库同样提供了外部中断通用处理函数HAL_GPIO_EXTI_IRQHandler,我们在外部中断服务函数中会调用该函数处理中断。
 //中断服务函数void EXTI0_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);       //调用中断处理公用函数}void EXTI2_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);      //调用中断处理公用函数}
  • HAL_GPIO_EXTI_IRQHandler函数内部通过判断中断来源引脚,最终调用外部中断回调函数HAL_GPIO_EXTI_Callback来处理中断。
 void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin){if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET){ __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);HAL_GPIO_EXTI_Callback(GPIO_Pin);}}
  • 用户最终编写中断处理回调函数来编写中断处理逻辑。
 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){switch(GPIO_Pin){case GPIO_PIN_0://控制逻辑break;case GPIO_PIN_2://控制逻辑break;}}

3.外部中断的一般配置步骤

① 使能IO口时钟。
② 初始化IO口,设置触发方式:HAL_GPIO_Init();
③ 设置中断优先级,并使能中断通道。
④ 编写中断服务函数:函数中调用外部中断通用处理函数HAL_GPIO_EXTI_IRQHandler。
⑥ 编写外部中断回调函数:HAL_GPIO_EXTI_Callback;

4.外部中断实验硬件连接


KEY0->PH3 上拉输入,下降沿触发
KEY1->PH2 上拉输入,下降沿触发
KEY2->PC13 上拉输入,下降沿触发
WK_UP->PA0 下拉输入,上升沿触发

三、IO口外部中断实验

实现的功能:
按键KEY0按下: 同时控制LED0和LED1翻转。
按键KEY1按下: LED1状态翻转。
按键KEY2按下: LED0翻转。
按键WK_UP按下:控制LED0和LED1互斥点亮。

这里使用之前的按键输入实验作为模板进行外部中断实验

1.在HARDWARE文件夹下面创建exti.c和exti.h

在HARDWARE文件夹下面创建exti.c和exti.h,并添加到工程中

2.搭建exti.h头文件

1)搭建exti.h框架

3.搭建exti.c头文件

1)搭建exti.c文件框架

2)编写中断服务函数框架

因为使用到了PH3, PH2 , PC13,PA0四个引脚,所以要分别对这四个引脚编写中断服务函数

中断服务列表

 EXTI0_IRQHandler EXTI1_IRQHandlerEXTI2_IRQHandler EXTI3_IRQHandler EXTI4_IRQHandler EXTI9_5_IRQHandler EXTI15_10_IRQHandler

可见用到了EXTI0_IRQHandler、EXTI2_IRQHandler、EXTI3_IRQHandler、EXTI15_10_IRQHandler 四个句柄函数,
同样在位置:工程文件 - CORE - starup_stm32f429xx.s中也可以看到配置好的句柄函数

所以在exti.c文件中搭建中断服务函数框架

3)编写外部中断回调函数

在位置:工程文件 - HALLIB - stm32f4xx_hal.c - stm32f4xx_hal_gpio.h 文件中

至此exti.c文件框架搭建完毕
#include “exti.h”

void EXTI_Init(void)
{

}

void EXTI0_IRQHandler(void)
{

}

void EXTI2_IRQHandler(void)
{

}

void EXTI3_IRQHandler(void)
{

}

void EXTI15_10_IRQHandler(void)
{

}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{

}

4.使能IO口时钟、设置触发方式

按键模式配置
KEY0->PH3 上拉输入,下降沿触发
KEY1->PH2 上拉输入,下降沿触发
KEY2->PC13 上拉输入,下降沿触发
WK_UP->PA0 下拉输入,上升沿触发

因为用到了PA、PC、PH三个IO口,所以要对这三个口进行初始化,然后对IO口模式进行配置,具体使能方式前文按键配置有详细说明。
代码如下

     GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟__HAL_RCC_GPIOC_CLK_ENABLE();               //开启GPIOC时钟__HAL_RCC_GPIOH_CLK_ENABLE();               //开启GPIOH时钟GPIO_Initure.Pin=GPIO_PIN_0;                //PA0GPIO_Initure.Mode=GPIO_MODE_IT_RISING;      //上升沿触发GPIO_Initure.Pull=GPIO_PULLDOWN;          //下拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;            //高速HAL_GPIO_Init(GPIOA,&GPIO_Initure);         GPIO_Initure.Pin=GPIO_PIN_13;               //PC13GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发GPIO_Initure.Pull=GPIO_PULLUP;              //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;            //高速HAL_GPIO_Init(GPIOC,&GPIO_Initure);         GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3;     //PH2,3GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发GPIO_Initure.Pull=GPIO_PULLUP;             //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;            //高速HAL_GPIO_Init(GPIOH,&GPIO_Initure);

5.设置中断优先级、使能中断通道

其中共调用了4个中断,所以要配置4条中断线
在位置:工程文件 - HALLIB - stm32f4xx_hal.c - stm32f429xx.h 文件中有中断线函数的头文件名

配置在 HAL_NVIC_SetPriority(EXTI0_IRQn,2,0); 和 HAL_NVIC_EnableIRQ(EXTI0_IRQn); 中

    //中断线0-PA0HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);       //抢占优先级为2,子优先级为0HAL_NVIC_EnableIRQ(EXTI0_IRQn);             //使能中断线0//中断线2-PH2HAL_NVIC_SetPriority(EXTI2_IRQn,2,1);       //抢占优先级为2,子优先级为1HAL_NVIC_EnableIRQ(EXTI2_IRQn);             //使能中断线2//中断线3-PH3HAL_NVIC_SetPriority(EXTI3_IRQn,2,2);       //抢占优先级为2,子优先级为2HAL_NVIC_EnableIRQ(EXTI3_IRQn);             //使能中断线2//中断线13-PC13HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3);   //抢占优先级为2,子优先级为3HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);         //使能中断线13

至此,外部中断初始化函数编写完毕,代码如下

void EXTI_Init(void)             //外部中断初始化
{GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟__HAL_RCC_GPIOC_CLK_ENABLE();               //开启GPIOC时钟__HAL_RCC_GPIOH_CLK_ENABLE();               //开启GPIOH时钟GPIO_Initure.Pin=GPIO_PIN_0;                //PA0GPIO_Initure.Mode=GPIO_MODE_IT_RISING;      //上升沿触发GPIO_Initure.Pull=GPIO_PULLDOWN;         //下拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;            //高速HAL_GPIO_Init(GPIOA,&GPIO_Initure);         GPIO_Initure.Pin=GPIO_PIN_13;               //PC13GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发GPIO_Initure.Pull=GPIO_PULLUP;              //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;            //高速HAL_GPIO_Init(GPIOC,&GPIO_Initure);         GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3;     //PH2,3GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发GPIO_Initure.Pull=GPIO_PULLUP;             //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;            //高速HAL_GPIO_Init(GPIOH,&GPIO_Initure);//中断线0-PA0HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);       //抢占优先级为2,子优先级为0HAL_NVIC_EnableIRQ(EXTI0_IRQn);             //使能中断线0//中断线2-PH2HAL_NVIC_SetPriority(EXTI2_IRQn,2,1);       //抢占优先级为2,子优先级为1HAL_NVIC_EnableIRQ(EXTI2_IRQn);             //使能中断线2//中断线3-PH3HAL_NVIC_SetPriority(EXTI3_IRQn,2,2);       //抢占优先级为2,子优先级为2HAL_NVIC_EnableIRQ(EXTI3_IRQn);             //使能中断线2//中断线13-PC13HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3);   //抢占优先级为2,子优先级为3HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);         //使能中断线13
}

6.编写中断服务器函数

上文说到,中断服务函数格式如下:

在位置:工程文件 - HALLIB - stm32f4xx_hal.c - stm32f4xx_hal_gpio.h 文件中也有其定义句柄函数名

把该句柄函数放在中断服务器函数中,代码如下

void EXTI0_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);      //调用中断处理公用函数
}void EXTI2_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);      //调用中断处理公用函数
}void EXTI3_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);      //调用中断处理公用函数
}void EXTI15_10_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);     //调用中断处理公用函数
}

7.编写外部中断回调函数

因为在HAL库中,有关于位带操作的定义,所以可以直接使用LED0、LED1、KEY0、KEY1、KEY2、WK_UP变量名来编写函数

编写好按键控制逻辑,代码如下

#include "delay.h"
#include "led.h"
#include "key.h"//中断服务程序中需要做的事情
//在HAL库中所有的外部中断服务函数都会调用此函数
//GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{delay_ms(100);      //消抖switch(GPIO_Pin){case GPIO_PIN_0:if(WK_UP==1) {LED1=!LED1;//控制LED0,LED1互斥点亮LED0=!LED1;}break;case GPIO_PIN_2:if(KEY1==0)  //LED1翻转{LED1=!LED1;    }break;case GPIO_PIN_3:if(KEY0==0)  //同时控制LED0,LED1翻转 {LED0=!LED0;LED1=!LED1;}break;case GPIO_PIN_13:if(KEY2==0)  {LED0=!LED0;//控制LED0翻转}break;}
}

至此,exti.c文件编写完毕,代码如下:

#include "exti.h"
#include "delay.h"
#include "led.h"
#include "key.h"void EXTI_Init(void)              //外部中断初始化
{GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOA_CLK_ENABLE();               //开启GPIOA时钟__HAL_RCC_GPIOC_CLK_ENABLE();               //开启GPIOC时钟__HAL_RCC_GPIOH_CLK_ENABLE();               //开启GPIOH时钟GPIO_Initure.Pin=GPIO_PIN_0;                //PA0GPIO_Initure.Mode=GPIO_MODE_IT_RISING;      //上升沿触发GPIO_Initure.Pull=GPIO_PULLDOWN;         //下拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;            //高速HAL_GPIO_Init(GPIOA,&GPIO_Initure);         GPIO_Initure.Pin=GPIO_PIN_13;               //PC13GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发GPIO_Initure.Pull=GPIO_PULLUP;              //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;            //高速HAL_GPIO_Init(GPIOC,&GPIO_Initure);         GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3;     //PH2,3GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发GPIO_Initure.Pull=GPIO_PULLUP;             //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;            //高速HAL_GPIO_Init(GPIOH,&GPIO_Initure);//中断线0-PA0HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);       //抢占优先级为2,子优先级为0HAL_NVIC_EnableIRQ(EXTI0_IRQn);             //使能中断线0//中断线2-PH2HAL_NVIC_SetPriority(EXTI2_IRQn,2,1);       //抢占优先级为2,子优先级为1HAL_NVIC_EnableIRQ(EXTI2_IRQn);             //使能中断线2//中断线3-PH3HAL_NVIC_SetPriority(EXTI3_IRQn,2,2);       //抢占优先级为2,子优先级为2HAL_NVIC_EnableIRQ(EXTI3_IRQn);             //使能中断线2//中断线13-PC13HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,3);   //抢占优先级为2,子优先级为3HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);         //使能中断线13
}void EXTI0_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);      //调用中断处理公用函数
}void EXTI2_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);      //调用中断处理公用函数
}void EXTI3_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);      //调用中断处理公用函数
}void EXTI15_10_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);     //调用中断处理公用函数
}//中断服务程序中需要做的事情
//在HAL库中所有的外部中断服务函数都会调用此函数
//GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{delay_ms(100);      //消抖switch(GPIO_Pin){case GPIO_PIN_0:if(WK_UP==1) {LED1=!LED1;//控制LED0,LED1互斥点亮LED0=!LED1;}break;case GPIO_PIN_2:if(KEY1==0)  //LED1翻转{LED1=!LED1;    }break;case GPIO_PIN_3:if(KEY0==0)  //同时控制LED0,LED1翻转 {LED0=!LED0;LED1=!LED1;}break;case GPIO_PIN_13:if(KEY2==0)  {LED0=!LED0;//控制LED0翻转}break;}
}

8.编写main.c函数

  • 引入#include “exti.h” 头文件
  • 把初始化按键函数 KEY_Init(); 更改为EXTI_Init();

至此,main.c函数编写完毕,代码如下

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "exti.h"int main(void)
{HAL_Init();                     //初始化HAL库   Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhzdelay_init(180);                //初始化延时函数uart_init(115200);              //初始化USARTLED_Init();                     //初始化LED EXTI_Init();                    //外部中断初始化while(1){printf("OK\r\n");           //打印OK提示程序运行delay_ms(1000);             //每隔1s打印一次 }
}

9.下载到开发板

现象这里不再赘述

至此,IO口外部中断实验结束

28.EXTI外部中断原理与配置相关推荐

  1. 02、【江科大自化协stm32F103c8t6】笔记之【入门32单片机及EXTI外部中断初始化参数配置】

    ----------接着上一篇笔记写 四.返回值函数 五.EXTI外部中断 1.配置RCC 2.配置GPIO 3.配置AFIO 4.配置EXTI 5.配置NVIC 中断函数 四.返回值函数 uint8 ...

  2. STM32学习笔记(三)丨中断系统丨EXTI外部中断(对射式红外传感器计次、旋转编码器计次)

    本篇文章包含的内容 一.中断系统 1.1 中断的定义 1.2 中断优先级 1.3 中断的嵌套 1.4 STM32中的中断系统 1.4.1 STM32的中断资源 1.4.2 嵌套中断向量控制器 NVIC ...

  3. 【MM32F5270开发板试用】GPIO输入+EXTI外部中断例程demo试用

    本篇文章来自极术社区与灵动组织的MM32F5270开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:Zeee 前言: 首先,感谢灵动微电子与极术社区给予宝贵的试用机会.借助本次对Plus-F ...

  4. ESP32 外部中断原理分析 GPIO外部中断实战

    ESP32 外部中断原理分析 & GPIO外部中断实战 阅读建议:   有一定Cortex-m架构.Xtensa® 32-bit LX6 架构知识基础. 软件环境 VSCODE-ESP32-I ...

  5. STM32 EXTI外部中断/事件

    一.EXTI外部中断/事件功能框图 1.中断线路:①-②-③-④-⑤ ①输入线:有19个中断/事件输入线(联网型有20个),通过寄存器将这些输入线设置为任意一个GPIO. ②边沿检测电路:选择上升沿触 ...

  6. STM32中断—EXTI外部中断

    5. 中断 本文来自于<STM32--江科大>的笔记整理. 中断系统 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处 ...

  7. STM32(五)——EXTI外部中断

    文章笔记源于--江科大自化协的视频 一. 中断系统 中断 : 在主程序运行过程中,出现特定的中断触发条件,使得CPU暂停当前正在运行的程序,而去处理中断程序,完成后,又返回原来被暂停的位置继续工作 中 ...

  8. STM32 EXTI外部中断的使用

    简介: 1.中断系统 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行 中断优先级:当有多个 ...

  9. STM32笔记——EXTI外部中断

    目录 一.概述 二.主要外设介绍 2.1 AFIO复用IO口 2.2 EXTI简介 EXTI框图: 2.3 NVIC中断优先级分组 三.EXTI外部中断整体结构 四.实验程序 4.1 对射式红外传感器 ...

最新文章

  1. python到底怎么学-学 Python 到底能帮你解决什么问题 ?
  2. 【视频课】Pytorch模型分析进阶(可视化,参数量与计算量,计算速度)
  3. 【CyberSecurityLearning 31】Linux网络信息查看与配置、日志文件的管理、备份及日志服务器的搭建
  4. Arrays.copyOf()、Arrays.copyOfRange()与System.arraycopy()用法
  5. 图像编解码:CRF(质量/码率控制)和QP
  6. 直播 | EMNLP 2020:用语义分割的思路解决不完整话语重写任务
  7. BBQ Hard dp + 组合数学 + 建模
  8. Android官方开发文档Training系列课程中文版:分享简单数据之发送简单数据给其它APP
  9. 2022年移动应用趋势洞察白皮书
  10. 盘一盘 synchronized (一)—— 从打印Java对象头说起
  11. 转!面向对象设计原则
  12. CSDN《老友记》简记
  13. PPC丢失后,手机信息如何保护?(C#)
  14. Unity Animator动画状态机 深入理解(一)
  15. ITK-SNAP自动分割应用示例:如何进行乳腺腺体脂肪体积测量
  16. APP推广技巧:APP营销推广的八种渠道你一定要了解!
  17. adblock 屏蔽网站广告 免费
  18. Python 用 os.listdir() 获取文件列表和筛选特定格式文件
  19. 手机信号上的G、E、O、3G、H、H+是什么意思?
  20. 电大计算机毕业论文任务书范文,广播电视大学毕业设计任务书表格.doc

热门文章

  1. Base64 转 File (附下载)
  2. sublime基本使用
  3. 微服务分布式一致性模式
  4. 点到直线垂足坐标的计算
  5. C语言的三大基本结构
  6. java 人脸检测_Java人脸检测库
  7. Java 中Pair的认识
  8. 什么叫Kotlin元组(Pair Triple)
  9. Sentry异常日志监控-Java接入
  10. GWAS分析中SNP解释百分比PVE | 第二篇,GLM模型中如何计算PVE?