华大H32F460的MCU的中断是怎么用的呢?看了用户手册还是没...
华大H32F460的MCU的中断是怎么用的呢?看了用户手册还是没看懂下面具体说说我看到的手册里的内容
首先
中断控制器(INTC)的功能有
1. 选择中断事件请求作为中断输入到 NVIC,唤醒 WFI;
2. 选择中断事件请求作为事件输入,唤醒 WFE
3. 选择中断事件请求作为低功耗模式(休眠模式和停止模式)的唤醒条件;
4. 外部管脚 NMI 和 EIRQ 的中断控制功能;
5. 软件中断的中断/事件选择功能。
这5个功能,这个是简介 经过对比STM32的中断配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
这样对比的话应该是看不出什么的,别着急 继续往内部看
STM32的配置过程void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
{
uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));
assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
{
/* Compute the Corresponding IRQ Priority --------------------------------*/
tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
tmppre = (0x4 - tmppriority);
tmpsub = tmpsub >> tmppriority;
tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
tmppriority |=NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
tmppriority = tmppriority << 0x04;
NVIC->IP = tmppriority;
/* Enable the Selected IRQ Channels --------------------------------------*/
NVIC->ISER =
(uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
}
else
{
/* Disable the Selected IRQ Channels -------------------------------------*/
NVIC->ICER =
(uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
}
}
下面是华大的配置过程__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
else
{
SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
}
__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
}
}
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
__COMPILER_BARRIER();
NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
__COMPILER_BARRIER();
}
} 通过上面的对比应该看出来了吧 ,首先都是要给NVIC->IP赋值 从STM32那应该能猜出是NVIC_InitStruct->NVIC_IRQChannel 这个是选择的IRQ通道号
其实第二步直接使能就行了 因为在STM32里有一个步骤是如果不使能会清除标志位,但是在华大里他没有这么用,而是在每次使能前清除相应的标志位
这样反过来看华大的用户手册里 他是这么写的
1. 需要使用 NMI 管脚时,先将 INT_NMICR.NFEN 位清“0”,禁止数字滤波器;设定
INT_NMICR 寄存器的 NMITRG 位,选择 NMI 触发边沿;设定 SMPCLK 位选择
数字滤波器的采样时钟;设定 NFEN 位,使能数字滤波器。
2. 使用其他不可屏蔽中断事件请求时,请配置相应的功能。
3. 对 INT_NMICFR 各寄存器位写“1”,清除 INT_NMIFR 标志寄存器位,防止误动
作。
4. 通过设定 INT_NMIENR 选择寄存器来使能不可屏蔽中断事件。
注意:
– 一旦INT_NMIENR相应位被设定为“1”后,将不能被更改,除非用RESET 来复位。
ICG 设定只针对外部 NMI 管脚,通过配置 ICG 寄存器 ICG1.NMIICGENA 位,使能
HC32F460 系列用户手册 Rev1.21Page 283 of 1141
ICG 设定。设定 ICG1.NMITRG 选择 NMI 触发边沿;设定 ICG1.SMPCLK 位选择数字
滤波器采样时钟;设定 ICG1.NFEN 位,使能数字滤波器;设定 ICG1.NMIENR 位,使
能 NMI 管脚中断。ICG 设定后,寄存器设定无效。ICG1 的寄存器说明,请参考初始
化配置(ICG)章节。
10.4.2外部管脚中断事件请求
需要使用外部管脚中断事件请求时,请按照如下流程设定:
1. 清除 INT_EIRQCRm.EFEN 位(m=0~15),禁止数字滤波器。
2. 设定 INT_EIRQCRm 的 IRQTRG位,选择触发边沿或电平;设定 SMPCLK
位,选择数字滤波器采样时钟;设定 EFEN 位,使能数字滤波器。
本来挺简单的中断优先级配置,却让他的用户手册搞的那么复杂,结贴了,感谢各位大神观看了 本帖最后由 binoo7 于 2021-1-22 23:10 编辑
#申请原创#
现在又经过了几天的学习,再来说一下中断的事儿吧
首先HC32F460用的是Cortex-M4的内核,这个内核有16个异常,也就是ARM的M4自带的叫做异常
还有多达 256 级的可编程优先级,并且支持 128级抢占,这个就是芯片厂商负责的部分了,例如HC32F460 定义了144个中断向量
因为用的是Cortex-M4的内核,支持中断优先级分组,分组的方式见图片
根据HC32F460的例程给出的定义
#define __NVIC_PRIO_BITS 4 /*!< HC32F46X uses 4 Bits for the Priority Levels */
他们用了前4位来配置优先级分组
但是这个分组不知道能不能配置,因为例程里设置的是一个固定的分组组0也就是所有的中断都在同一个抢占优先级,而大家都在剩余的亚优先级内,举个例子比如
以下转自野火教程
中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。
抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被中断B 打断,执行完中断服务函数B 再继续执行中断服务函数A)中断向量 抢占优先级 亚优先级(响应优先级)A 0 0B 1 0C 1 1 若内核正在执行C 的中断服务函数,则它能被抢占优先级更高的中断A 打断,由于B和C 的抢占优先级相同,所以C 不能被B 打断。但如果B 和C 中断是同时到达的,内核就会首先响应响应优先级别更高的B 中断。
这样的解释大家应该都清楚了吧
SCB->AIRCR的配置,可以配置中断优先级分组,HC32F460的配置是这样的<font color="rgb(77,77,77)"><font face="-apple-system,"><font size="3">__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */
reg_value=SCB->AIRCR; /* read old register configuration */
reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */
reg_value=(reg_value |
((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos)); /* Insert write key and priority group */
SCB->AIRCR =reg_value;
}
</font></font></font>
但是这个函数并没有用到,而是在这个函数里配置了/**
\brief System Reset
\details Initiates a system reset request to reset the MCU.
*/
__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void)
{
__DSB(); /* Ensure all outstanding memory accesses included
buffered write are completed before reset */
SCB->AIRCR= (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */
__DSB(); /* Ensure completion of memory access */
for(;;) /* wait until reset */
{
__NOP();
}
}
解释一下华大用的这个配置函数,他将SCB->AIRCR寄存器&上了0x700,在权威指南里对于SCB->AIRCR优先级分组在复位后所有的数据都会是0,也就说在每次复位后,SCB->AIRCR寄存器&0x700=0,也就是将中断优先级分组配置成了组4
第04组: 所有4 位用来配置抢占优先级。
是不是大家有疑惑了,他才有16个抢占中断优先级,他的中断向量表不是有144个啊,这也不够用啊
别担心,这144个中断向量不是说每一个向量都要占一个中断优先级,可以几个中断向量占同一个中断优先级,谁先来,就先响应谁,等这个中断结束了,再依次响应其他优先级低的中断,如果优先级高的中断来了,就会响应高优先级的中断,这样就形成了中断的嵌套,这么说的话大家就清楚了吧。
可以根据自己的需要来修改#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */是7还是6/5/4/3 这样的话就可以得到响应的中断优先级分组了,还有可以调用
__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */
reg_value=SCB->AIRCR; /* read old register configuration */
reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */
reg_value=(reg_value |
((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos)); /* Insert write key and priority group */
SCB->AIRCR =reg_value;
}
这个函数来改变中断优先级。
在权威指南里还说了这样一句话大家要注意:
其实在绝大多数情况下,优先级的分组都要预先经过计算论证,并且在开机初始化时一次性地设置好,以后就再也不动它了。
只有在绝对需要且绝对有把握时,才小心地更改,并且要经过尽可能充分的测试。
另外,优先级组所在的寄存器 AIRCR也基本上是“一次成型”,只是需要手工产生复位时才写里面相应的位。
学习了,哈哈,楼主研究的很透彻 flycamelaaa 发表于 2021-1-22 14:16
学习了,哈哈,楼主研究的很透彻
现在流行,给点个赞再走啊 今天再补充一点,关于中断的 /* Set USART RX error IRQ */
stcIrqRegiCfg.enIRQn = Int001_IRQn;
这次我用串口中断来举例子
stcIrqRegiCfg.enIRQn = Int001_IRQn;
这个是要配置的这里IrqHandler[pstcIrqRegiConf->enIRQn] = pstcIrqRegiConf->pfnCallback;
这里的stcIrqRegiCfg.enIRQn = Int001_IRQn在通过配置文件后就相当于上面蓝色的部分,
IrqHandler[]一看以为是个数组,其实不然,他是一个指针,数组类型的函数指针,为什么这么说呢
func_ptr_t IrqHandler[IRQ_NUM_MAX] = {NULL};
而func_ptr_t是什么呢 查看原型能发现
typedef void (*func_ptr_t)(void);
看这个定义眼熟吗? 如果还没看明白的话看下面这个函数的定义
void delay (void)
{
//
}
是不是一样啊?
我们再来分析这句话的意思
IrqHandler[pstcIrqRegiConf->enIRQn] = pstcIrqRegiConf->pfnCallback;
将pfnCallback这个回调函数的函数指针赋值给IrqHandler[pstcIrqRegiConf->enIRQn] 这个指针
也就是说如果调用IrqHandler这个函数的话,就调用了pfnCallback这个回调函数,
有一点很关键pstcIrqRegiConf->enIRQn大家一定要记住了,不同的回调函数不能用同一个中断向量
例如两个串口的接收中断,就不能都设置成Int001_IRQn记住了啊,
而中断优先级是怎么配置的呢?因为给的库函数里没有配置中断优先级,默认是优先级4,也就是全部都是抢占优先级,没有子优先级
不过为了保险起见,还是建议大家都配置一下中断优先级分组,然后再配置中断优先级
真正在这里起作用的是NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
NVIC_SetPriority()就是库函数给的设置中断优先级的函数,直接调用这个函数就能配置优先级
stcIrqRegiCfg.enIRQn 是
__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
else
{
SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
}
真正配置的是NVIC->IP
IP[IRQn]:全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。
IP 寄存器组由 240 个 8bit的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。
而HC32F460只用到了其中的DCD IRQ143_Handler ; IRQ143_Handler 也就是144个。IP~IP分别对应中断 143~0。
而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。
这 4 位,又分为抢占优先级和响应优先级。
抢占优先级在前,响应优先级在后。
而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。
DDL_IRQ_PRIORITY_DEFAULT就是对应的抢占优先级和子优先级,所以记住了stcIrqRegiCfg.enIRQn千万不要设置成一样的,否则会导致只有最后一个起作用,其他的都不起作用!!!
楼主这记录学习的过程是个非常好的习惯 都是Cortex-M4的内核,华大的库整的太复杂...... 果断关注楼主 我是一直没弄明白,我配置的IO用来做中断,单起一个工程跑没有问题,加到别的工程里,就一直中断,一直响应,没按键也响应,实在找不到问题,能不能懂的帮看看。
#include "hc32_ddl.h"
#define LED1_PORT PortD
#define LED1_Pin Pin02
#define LED2_PORT PortB
#define LED2_Pin Pin03
#define LED3_PORT PortB
#define LED3_Pin Pin04
#define LED4_PORT PortB
#define LED4_Pin Pin05
#define KEY1_PORT PortD
#define KEY1_Pin Pin02
#define KEY2_PORT PortC
#define KEY2_Pin Pin12
#define KEY3_PORT PortA
#define KEY3_Pin Pin06
typedef struct{
uint8_t key1:1;
uint8_t key2:1;
uint8_t key3:1;
uint8_t key4:1;
uint8_t key5:1;
uint8_t key6:1;
uint8_t key7:1;
uint8_t key8:1;
}Key_IO_bit_t;
typedef union{
Key_IO_bit_t keys;
uint8_t status;
}KEY_status_t;
void User_Gpio_Init(void)
{
stc_port_init_t Port_CFG;
MEM_ZERO_STRUCT(Port_CFG);
Port_CFG.enPinMode = Pin_Mode_Out;
PORT_Init(LED1_PORT, LED1_Pin, &Port_CFG);
PORT_Init(LED2_PORT, LED2_Pin, &Port_CFG);
PORT_Init(LED3_PORT, LED3_Pin, &Port_CFG);
PORT_Init(LED4_PORT, LED4_Pin, &Port_CFG);
}
static void key_1_callback(void){
static int i = 0;
KEY_status_t keyGroup = {0};
if(i == 65534){
i = 0;
}
if(Set == EXINT_Irq**Get(ExtiCh01)){
do{
keyGroup.keys.key1 =PORT_GetBit(KEY1_PORT, KEY1_Pin);
keyGroup.keys.key2 =PORT_GetBit(KEY2_PORT, KEY2_Pin);
keyGroup.keys.key3 =PORT_GetBit(KEY3_PORT, KEY3_Pin);
if(keyGroup.status != 7) break;
}while(1);
Ddl_Delay1ms(40);
if((keyGroup.keys.key1 == Reset) && (Reset == PORT_GetBit(KEY1_PORT, KEY1_Pin))){
if(i%2== 1){
PORT_ResetBits(LED1_PORT,LED1_Pin);
}else{
PORT_SetBits(LED1_PORT,LED1_Pin);
}
//logDebug("key1Press! cnt:%d\n",i);
i++;
}else if((keyGroup.keys.key2 == Reset) && (Reset == PORT_GetBit(KEY2_PORT, KEY2_Pin))){
if(i%2== 1){
PORT_ResetBits(LED2_PORT,LED2_Pin);
}else{
PORT_SetBits(LED2_PORT,LED2_Pin);
}
//logDebug("key2Press! cnt:%d\n",i);
i++;
}else if((keyGroup.keys.key3 == Reset) && (Reset == PORT_GetBit(KEY3_PORT, KEY3_Pin))){
if(i%2== 1){
PORT_ResetBits(LED3_PORT,LED3_Pin);
}else{
PORT_SetBits(LED3_PORT,LED3_Pin);
}
// logDebug("key3Press! cnt:%d\n",i);
i++;
}
/* clear int request flag */
EXINT_Irq**Clr(ExtiCh01);
}
}
void Init_intruppt()
{
stc_exint_config_t stcExtiConfig; /* 外部中断配置结构体 */
stc_irq_regi_conf_t stcIrqRegiConf; /*irq配置结构体 */
stc_port_init_t stcPortInit; /*管脚配置结构体 */
/* configuration structure initialization */
MEM_ZERO_STRUCT(stcExtiConfig);
MEM_ZERO_STRUCT(stcIrqRegiConf);
MEM_ZERO_STRUCT(stcPortInit);
/* Set PD06 as External Int Ch.6 input */
stcPortInit.enExInt = Enable;
PORT_Init(KEY1_PORT, KEY1_Pin, &stcPortInit);
PORT_Init(KEY2_PORT, KEY2_Pin, &stcPortInit);
PORT_Init(KEY3_PORT, KEY3_Pin, &stcPortInit);
stcExtiConfig.enExitCh = ExtiCh01;
/* 过滤器配置 */
stcExtiConfig.enFilterEn = Enable;
stcExtiConfig.enFltClk = Pclk3Div8;
/* 低电平触发(触发模式) */
stcExtiConfig.enExtiLvl = ExIntLowLevel;
EXINT_Init(&stcExtiConfig);
/* 选择外部中断通道 Ch.1 */
stcIrqRegiConf.enIntSrc = INT_PORT_EIRQ1;
/*注册外部中断 to Vect.No.000 */
stcIrqRegiConf.enIRQn = Int000_IRQn;
/* 绑定回调函数 */
stcIrqRegiConf.pfnCallback = &key_1_callback;
enIrqRegistration(&stcIrqRegiConf);
/* Clear Pending */
NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
/* 配置优先级 */
NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_15);
/* 使能 NVIC */
NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}
int32_t main()
{
User_Gpio_Init();
Init_intruppt();
while(1)
{
;;;
}
}
zengxing3624 发表于 2021-10-29 09:01
我是一直没弄明白,我配置的IO用来做中断,单起一个工程跑没有问题,加到别的工程里,就一直中断,一直响应 ...
#define KEY1_PORT PortD
#define KEY1_Pin Pin02
#define KEY2_PORT PortC
#define KEY2_Pin Pin12
#define KEY3_PORT PortA
#define KEY3_Pin Pin06
你所定义的IO就没有一个是Pin01的,但是你中断源却选择的是Pin01的中断,你能跑正常就奇怪了。
页:
[1]