目前共有7篇帖子。 内容转换:不转换▼
 
点击 回复
761 6
【示例程序】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位的,只有高四位有效,低四位始终为零。

回复帖子

内容:
用户名: 您目前是匿名发表
验证码:
(快捷键:Ctrl+Enter)
 

本帖信息

点击数:761 回复数:6
评论数: ?
作者:巨大八爪鱼
最后回复:巨大八爪鱼
最后回复时间:2017-1-15 19:35
 
©2010-2024 Arslanbar Ver2.0
除非另有声明,本站采用知识共享署名-相同方式共享 3.0 Unported许可协议进行许可。