搜索

[技术问答] 华大H32F460的MCU的中断是怎么用的呢?看了用户手册还是没...

[复制链接]
305|10
 楼主 | 2021-1-18 11:18 | 显示全部楼层 |阅读模式
华大H32F460的MCU的中断是怎么用的呢?看了用户手册还是没看懂

使用特权

评论回复
 楼主 | 2021-1-18 11:39 | 显示全部楼层
下面具体说说我看到的手册里的内容
首先
中断控制器(INTC)的功能有
1. 选择中断事件请求作为中断输入到 NVIC,唤醒 WFI;
2. 选择中断事件请求作为事件输入,唤醒 WFE
3. 选择中断事件请求作为低功耗模式(休眠模式和停止模式)的唤醒条件;
4. 外部管脚 NMI 和 EIRQ 的中断控制功能;
5. 软件中断的中断/事件选择功能。
这5个功能,这个是简介

使用特权

评论回复
 楼主 | 2021-1-18 12:24 | 显示全部楼层
经过对比STM32的中断配置
  1. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  2.           NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
  3.           NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
  4.           NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
  5.           NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器
复制代码
  1. NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
  2.          NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
  3.          NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
复制代码

这样对比的话应该是看不出什么的,别着急 继续往内部看
STM32的配置过程
  1. void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
  2. {
  3.   uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
  4.   
  5.   /* Check the parameters */
  6.   assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
  7.   assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));  
  8.   assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
  9.    
  10.   if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
  11.   {
  12.     /* Compute the Corresponding IRQ Priority --------------------------------*/   
  13.     tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
  14.     tmppre = (0x4 - tmppriority);
  15.     tmpsub = tmpsub >> tmppriority;

  16.     tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
  17.     tmppriority |=  NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
  18.     tmppriority = tmppriority << 0x04;
  19.         
  20.     NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
  21.    
  22.     /* Enable the Selected IRQ Channels --------------------------------------*/
  23.     NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
  24.       (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  25.   }
  26.   else
  27.   {
  28.     /* Disable the Selected IRQ Channels -------------------------------------*/
  29.     NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
  30.       (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  31.   }
  32. }
复制代码

下面是华大的配置过程
  1. __STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
  2. {
  3.   if ((int32_t)(IRQn) >= 0)
  4.   {
  5.     NVIC->IP[((uint32_t)IRQn)]               = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
  6.   }
  7.   else
  8.   {
  9.     SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
  10.   }
  11. }
复制代码
  1. __STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn)
  2. {
  3.   if ((int32_t)(IRQn) >= 0)
  4.   {
  5.     NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
  6.   }
  7. }
复制代码
  1. __STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
  2. {
  3.   if ((int32_t)(IRQn) >= 0)
  4.   {
  5.     __COMPILER_BARRIER();
  6.     NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
  7.     __COMPILER_BARRIER();
  8.   }
  9. }
复制代码

使用特权

评论回复
 楼主 | 2021-1-18 12:29 | 显示全部楼层
通过上面的对比应该看出来了吧 ,首先都是要给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.21  Page 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[1:0]位,选择触发边沿或电平;设定 SMPCLK[1:0]
位,选择数字滤波器采样时钟;设定 EFEN 位,使能数字滤波器。

使用特权

评论回复
 楼主 | 2021-1-18 12:32 | 显示全部楼层
本来挺简单的中断优先级配置,却让他的用户手册搞的那么复杂,结贴了,感谢各位大神观看了

使用特权

评论回复
 楼主 | 2021-1-22 11:06 | 显示全部楼层
本帖最后由 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                                                              0

B                                             1                                                              0

C                                             1                                                              1

若内核正在执行C 的中断服务函数,则它能被抢占优先级更高的中断A 打断,

由于B和C 的抢占优先级相同,所以C 不能被B 打断。

但如果B 和C 中断是同时到达的,内核就会首先响应响应优先级别更高的B 中断。


这样的解释大家应该都清楚了吧


SCB->AIRCR的配置,可以配置中断优先级分组,HC32F460的配置是这样的

  1. <font color="rgb(77,77,77)"><font face="-apple-system,"><font size="3">__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
  2. {
  3.   uint32_t reg_value;
  4.   uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);             /* only values 0..7 are used          */

  5.   reg_value  =  SCB->AIRCR;                                                   /* read old register configuration    */
  6.   reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change               */
  7.   reg_value  =  (reg_value                                   |
  8.                 ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
  9.                 (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos)  );              /* Insert write key and priority group */
  10.   SCB->AIRCR =  reg_value;
  11. }
  12. </font></font></font>
复制代码




但是这个函数并没有用到,而是在这个函数里配置了
  1. /**
  2.   \brief   System Reset
  3.   \details Initiates a system reset request to reset the MCU.
  4. */
  5. __NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void)
  6. {
  7.   __DSB();                                                          /* Ensure all outstanding memory accesses included
  8.                                                                        buffered write are completed before reset */
  9.   SCB->AIRCR  = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos)    |
  10.                            (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
  11.                             SCB_AIRCR_SYSRESETREQ_Msk    );         /* Keep priority group unchanged */
  12.   __DSB();                                                          /* Ensure completion of memory access */

  13.   for(;;)                                                           /* wait until reset */
  14.   {
  15.     __NOP();
  16.   }
  17. }
复制代码




解释一下华大用的这个配置函数,他将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 这样的话就可以得到响应的中断优先级分组了,还有可以调用




  1. __STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
  2. {
  3.   uint32_t reg_value;
  4.   uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);             /* only values 0..7 are used          */

  5.   reg_value  =  SCB->AIRCR;                                                   /* read old register configuration    */
  6.   reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change               */
  7.   reg_value  =  (reg_value                                   |
  8.                 ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
  9.                 (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos)  );              /* Insert write key and priority group */
  10.   SCB->AIRCR =  reg_value;
  11. }
复制代码







这个函数来改变中断优先级  。
在权威指南里还说了这样一句话大家要注意:
其实在绝大多数情况下,优先级的分组都要预先经过计算论证,并且在开机初始化时一次性地设置好,以后就再也不动它了。
只有在绝对需要且绝对有把握时,才小心地更改,并且要经过尽可能充分的测试。

另外,优先级组所在的寄存器 AIRCR也基本上是“一次成型”,只是需要手工产生复位时才写里面相应的位。


异常

异常

优先级分组

优先级分组

使用特权

评论回复
| 2021-1-22 14:16 | 显示全部楼层
学习了,哈哈,楼主研究的很透彻

使用特权

评论回复
 楼主 | 2021-1-22 15:19 | 显示全部楼层
flycamelaaa 发表于 2021-1-22 14:16
学习了,哈哈,楼主研究的很透彻

现在流行,给点个赞再走啊

使用特权

评论回复
 楼主 | 2021-2-25 00:00 | 显示全部楼层
今天再补充一点,关于中断的
  1. /* Set USART RX error IRQ */
  2.     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 是
  1. __STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
  2. {
  3.   if ((int32_t)(IRQn) >= 0)
  4.   {
  5.     NVIC->IP[((uint32_t)IRQn)]               = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
  6.   }
  7.   else
  8.   {
  9.     SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
  10.   }
  11. }
复制代码


真正配置的是NVIC->IP[IRQn]
IP[IRQn]:全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。
IP 寄存器组由 240 个 8bit的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。

而HC32F460只用到了其中的DCD     IRQ143_Handler        ; IRQ143_Handler 也就是144个。IP[143]~IP[0]分别对应中断 143~0。
而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。
这 4 位,又分为抢占优先级和响应优先级。
抢占优先级在前,响应优先级在后。
而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定

DDL_IRQ_PRIORITY_DEFAULT就是对应的抢占优先级和子优先级,所以记住了stcIrqRegiCfg.enIRQn千万不要设置成一样的,否则会导致只有最后一个起作用,其他的都不起作用!!!




使用特权

评论回复
| 2021-4-8 23:03 | 显示全部楼层
楼主这记录学习的过程是个非常好的习惯

使用特权

评论回复
| 2021-4-11 23:13 | 显示全部楼层
都是Cortex-M4的内核,华大的库整的太复杂......

使用特权

评论回复
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 我要提问 投诉建议 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

在线客服 快速回复 返回顶部 返回列表