首页 > 解决方案 > STM32F103 处理嵌套中断的优先级

问题描述

我可以在 STM32F10x 库中找到与处理嵌套中断的优先级相关的 NVIC_Init 问题。我们知道任何优先级值等于或高于 BASEPRI(在我们的例子中为 11)的中断都可以调用 FromISR() FreeRTOS API 函数。

FreeRTOS 使用 15 级(最低优先级)。

#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY    15

换句话说,FreeRTOS 允许我们以 15-11 的优先级从 ISR 调用 API 功能(请参阅 xQueueSendToBackFromISR)。当我们初始化 NVIC 时,我们使用级别 #11

#define    WRTU2_DMA1_SPI2_IRQ_PRIORITY     (configLIBRARY_KERNEL_INTERRUPT_PRIORITY-4*)
    /* DMA1 Channel4 interrupt setting */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = WRTU2_DMA1_SPI2_IRQ_PRIORITY;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = WRTU2_DMA1_SPI2_IRQ_PRIORITY;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

所以,我们应该没问题。但问题是存在的。我决定检查 NVIC_Init。

根据 STM32 数据表中的信息,优先级寄存器为0xe000e40e(NVIC 通道 14 属于DMA1_Channel4中断)。

在初始化 NVIC 后,我可以从该寄存器中读取 0x00 。这意味着 NVIC 通道 #14 在系统中具有最高优先级。

它会导致所有问题。

我添加了最简单的修复 NVIC->IP[DMA1_Channel4_IRQn] = 0xF0; 并且系统不再失败。这样,我们当前的问题就解决了。

当然,我试着分析一下 NVIC_Init 中发生了什么

**
  * @brief  Initializes the NVIC peripheral according to the specified
  *         parameters in the NVIC_InitStruct.
  * @param  NVIC_InitStruct: pointer to a NVIC_InitTypeDef structure that contains
  *         the configuration information for the specified NVIC peripheral.
  * @retval None
  */
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[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;

    /* Enable the Selected IRQ Channels --------------------------------------*/
    NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
      (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  }
  else
  {
    /* Disable the Selected IRQ Channels -------------------------------------*/
    NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
      (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  }
}

因此,我在我的应用程序中添加了类似的测试代码,以查看它如何转换所有值

uint32_t NVIC_IRQChannelPreemptionPriority=0xFF,NVIC_IRQChannelSubPriority=0xFF;
int32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
tprintf("\n\rSCB->AIRCR=0x%08x",SCB->AIRCR);

 tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
 tprintf("\n\rtmppriority=0x%08x",tmppriority);
tmppre = (0x4 - tmppriority);
tmpsub = tmpsub >> tmppriority;

 tprintf("\n\rtmppre=0x%08x",tmppre);
 tprintf("\n\rtmpsub=0x%08x",tmpsub);


tmppriority = (uint32_t)NVIC_IRQChannelPreemptionPriority << tmppre;
tmppriority |=  NVIC_IRQChannelSubPriority & tmpsub;
tmppriority = tmppriority << 0x04;
tprintf("\n\rtmppriority=0x%08x",tmppriority);    

有一个日志

SCB->AIRCR=0xfa050000
tmppriority=0x00000007
tmppre=0xfffffffd
tmpsub=0x00000000
tmppriority=0x00000000

请注意,即使我为两个输入参数指定 0xFF,它也会返回 0x00。

我对这种行为感到非常惊讶。这是一个库函数。

人们使用它很多年。所以,我真的很困惑,我可以在那个函数中找到问题。

可能与应用中断和复位控制寄存器(SCB_AIRCR)有关 地址偏移:0x0C 复位值:0xFA05 0000 所需权限:Privileged AIRCR 提供异常模型的优先级分组控制、数据访问的字节序状态和系统的复位控制.

请注意,在库资源中我可以看到#define AIRCR_VECTKEY_MASK ((uint32_t)0x05FA0000)

所以,看起来我们在 16 位半字节中有某种 BIG 与 LITTLE ENDIAN 字节顺序。

你对这个问题有什么建议或知识吗?

标签: cinterruptstm32freertos

解决方案


我发现了问题,问题是我应该使用 NVIC_SetPriorityGrouping(3); 对于 cortex-m3 为 3。

然后它提供 4 位抢占式优先级和 0 位用于子优先级。最后,当我像这样初始化时,它按我的预期工作。

  /* DMA1 Channel4 interrupt setting */
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 11;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

推荐阅读