設置 | 登錄 | 註冊

目前共有7篇帖子。

【示例程序】NVIC中斷優先級的配置

1樓 巨大八爪鱼 2017-1-15 16:30
【示例1:高優先級中斷屏蔽並清除低優先級中斷】
#include <stm32f10x.h>

#define _BV(n) (1 << (n))

uint16_t status = 0;
uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};

void delay(void)
{
    uint16_t i;
    for (i = 0; i < 20000; i++);
}

void ser_in(uint8_t data)
{
    uint8_t i;
    for (i = 0; i < 8; i++)
    {
        GPIOB->BRR = GPIO_BRR_BR9; // SCLK=PB9
        if (data & 0x80)
            GPIOB->BSRR = GPIO_BSRR_BS7; // DIO=PB7
        else
            GPIOB->BRR = GPIO_BRR_BR7;
        data <<= 1;
        GPIOB->BSRR = GPIO_BSRR_BS9;
    }
}

void par_out(void)
{
    GPIOB->BRR = GPIO_BRR_BR8; // RCLK=PB8
    GPIOB->BSRR = GPIO_BSRR_BS8;
}

// 單步數碼管掃描
void seg_scan(void)
{
    static uint8_t i = 0;
    static uint16_t num;
    
    __disable_irq(); // 關總中斷, 防止74HC595晶片時序錯亂導致數碼管閃爍
    if (i == 0)
        num = TIM1->CNT / 100;
    if (i == 5)
        num = status;
    
    ser_in(seg8[num % 10]);
    ser_in(_BV(i));
    par_out();
    delay();
    num /= 10;
    
    // 數碼管0~2顯示定時器1計數值的百~萬位, 數碼管5~7顯示status的個~百位
    // 從低位向高位掃描
    if (i == 2)
        i = 5;
    else if (i == 7)
        i = 0;
    else
        i++;
    __enable_irq(); // 開總中斷, 並立即處理剛才中斷關閉時已經置位的中斷請求
}

/*
// 這個數碼管掃描函數影響中斷處理的程度要高一些
// 功能和上面那個一樣
void seg_scan(void)
{
    uint8_t i;
    uint16_t num = TIM1->CNT / 100;
    __disable_irq();
    for (i = 0; i <= 2; i++)
    {
        ser_in(seg8[num % 10]);
        ser_in(_BV(i));
        par_out();
        delay();
        num /= 10;
    }
    
    num = status;
    for (i = 5; i <= 7; i++)
    {
        ser_in(seg8[num % 10]);
        ser_in(_BV(i));
        par_out();
        delay();
        num /= 10;
    }
    __enable_irq();
}
*/

int main(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_AFIOEN;
    GPIOB->CRH = 0x00000033;
    GPIOB->CRL = 0x30000008;
    GPIOB->BSRR = GPIO_BSRR_BS0;
    
    AFIO->EXTICR[0] = 0x01; // Px0 = PB0
    EXTI->IMR |= EXTI_IMR_MR0; // 開Px0外中斷
    EXTI->FTSR |= EXTI_FTSR_TR0; // Px0下降沿觸發
    //EXTI->RTSR |= EXTI_RTSR_TR0; // Px0上升沿觸發
    
    TIM1->ARR = 12099;
    TIM1->PSC = 48000;
    
    TIM1->DIER |= TIM_DIER_UIE;
    NVIC->ISER[0] = NVIC_ISER_SETENA_6 | NVIC_ISER_SETENA_25;
    
    TIM1->CR1 |= TIM_CR1_CMS | TIM_CR1_URS;
    TIM1->EGR |= TIM_EGR_UG;
    TIM1->CR1 &= ~TIM_CR1_URS;
    
    TIM1->CR1 |= TIM_CR1_CEN;
    
    while (1)
    {
        seg_scan();
    }
}

// 高優先級中斷(外部中斷)
void EXTI0_IRQHandler(void)
{
    status++;
    while ((GPIOB->IDR & GPIO_IDR_IDR0) == 0) // 等待按鍵釋放
    {
        seg_scan();
        if (NVIC->ICPR[0] & NVIC_ICPR_CLRPEND_25)
        {
            // 高優先級中斷清除低優先級中斷
            // 只要按鍵沒有鬆開, 定時器中斷就無法觸發
            TIM1->SR &= ~TIM_SR_UIF;
            NVIC->ICPR[0] |= NVIC_ICPR_CLRPEND_25;
        }
    }
    EXTI->PR |= EXTI_PR_PR0;
}

// 低優先級中斷(定時器溢出中斷)
void TIM1_UP_IRQHandler(void)
{
    TIM1->SR &= ~TIM_SR_UIF;
    status += 10;
}
2樓 巨大八爪鱼 2017-1-15 16:33
【注意】
開關總中斷的函數__enable_irq()和__disable_irq()不需要包含任何頭文件(包括stm32f10x.h),直接就能使用!注意開頭的兩個下劃線。
3樓 巨大八爪鱼 2017-1-15 18:41
【搶佔優先級和響應優先級】
NVIC_IPRx寄存器用於設置中斷的優先級,其默認值為0,也就是所有的優先級都相同,響應順序由中斷編號決定,編號越低越先響應。
NVIC->IP[]是一個數組,每4個數組元素代表一個IPR寄存器。比如IPR0為IP[0]至IP[3]。每一個數組元素決定一個中斷的優先級,因此每個寄存器決定4個中斷的優先級。
不過在STM32F10x單片機中,每個數組元素的值XXXXXXXX只有高四位有效,低四位始終為0。
(詳見Programming manual中的4.3.7 Interrupt priority registers)

這個值上還有一個小數點,小數點的位置由SCB->AIRCR寄存器中的PRIGROUP位(第10~8位)決定,其取值範圍為0~7,默認為0,也就是小數點位於倒數第二位右邊:XXXXXXX.Y。如果為7,則位於最左邊:.YYYYYYYY。具體為:
0   XXXXXXX.Y  (默認)
1   XXXXXX.YY
2   XXXXX.YYY
3   XXXX.YYYY   (NVIC_PriorityGroup_4)
4   XXX.YYYYY   (NVIC_PriorityGroup_3)
5   XX.YYYYYY   (NVIC_PriorityGroup_2)
6   X.YYYYYYY   (NVIC_PriorityGroup_1)
7  .YYYYYYYY    (NVIC_PriorityGroup_0)
小數點左邊的X表示搶佔優先級,右邊的Y表示響應優先級。因為低4位恆為0,所以0~3實際是一個模式:NVIC_PriorityGroup_4,其中4表示X的位數為4,這是庫函數中的表示方法。
4樓 巨大八爪鱼 2017-1-15 18:52
注意,在寫SCB->AIRCR寄存器的時候,高16位必須為05fa,否則寫入會失敗。讀的時候是0xfa05,所以不能用|=,&=~運算符來賦值,而只能是SCB->AIRCR=0x5faxxxx。
搶佔優先級高的可以搶佔搶佔優先級低的中斷。如果搶佔優先級相同,則不能搶佔,而是等當前中斷執行完畢後,再按照響應優先級來決定先執行哪個中斷。如果響應優先級也相同,那麼就直接根據中斷號判斷,先執行低編號的中斷。
(詳見Programming manual中的:4.4.5 Application interrupt and reset control register (SCB_AIRCR),以及Binary point(二進制小數點)部分,2.3.6 Interrupt priority grouping)
參考連結:http://blog.sina.com.cn/s/blog_7e5ec6df01013mdv.html

另外,編程手冊中還有2.3.4 Vector table,以及4.4.4 Vector table offset register (SCB_VTOR)兩節與中斷向量表也有關係,不過和中斷優先級沒有關係。總之,STM32官方手冊中有關中斷的章節相當分散。
5樓 巨大八爪鱼 2017-1-15 19:03
在該程序的主函數裏添加:
NVIC->IP[6] = 0x30; //  0011000.0
NVIC->IP[25] = 0x20; // 0010000.0
因為默認的小數點位置是在倒數第二位後面,所以外部中斷(中斷號為6)的搶佔優先級變成了0011000=24,而定時器溢出中斷(中斷號為25)則變成了0010000=16,因為16<24,所以定時器溢出中斷的優先級現在高於外部中斷,一直按着按鍵不放不能阻止定時器溢出中斷的執行,因此數碼管左邊的數會加10。
這主要是因為定時器中斷在外中斷還在執行seg_scan()函數裏面的語句時觸發的可能性更大,ICPR來不及清除中斷標誌位,中斷就已經開始執行了。

如果再添加SCB->AIRCR = 0x5fa0400;,PRIGROUP=100=4,也就是前三位(7-4=3)為搶佔優先級:
NVIC->IP[6] = 0x30; //   001.10000 (搶佔優先級為1, 響應優先級為16)
NVIC->IP[25] = 0x20; // 001.00000 (搶佔優先級為1, 響應優先級為0)
這樣搶佔優先級就相同了,25號中斷不能搶佔6號中斷,於是效果又回到了最初的樣子:只要按着按鍵不放,定時器中斷就不能觸發,數碼管左邊不加10。
6樓 巨大八爪鱼 2017-1-15 19:28
中斷編號如果記不住,還可以用常量表示,如:
NVIC->IP[EXTI0_IRQn] = 0x30; //  001.10000
NVIC->IP[TIM1_UP_IRQn] = 0x20; // 001.00000
7樓 巨大八爪鱼 2017-1-15 19:35
系統外設(如SysTick)的中斷優先級是在SCB->SHP中設置的,具體請參閱編程手冊的4.4.8 System handler priority registers (SHPRx)的內容。同樣也是每個數組元素是8位的,只有高四位有效,低四位始終為零。

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。