巨大八爪鱼
武林盟主 二十一级
|
【示例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; }
|
巨大八爪鱼
武林盟主 二十一级
|
【注意】 开关总中断的函数__enable_irq()和__disable_irq()不需要包含任何头文件(包括stm32f10x.h),直接就能使用!注意开头的两个下划线。
|
巨大八爪鱼
武林盟主 二十一级
|
【抢占优先级和响应优先级】 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,这是库函数中的表示方法。
|
巨大八爪鱼
武林盟主 二十一级
|
注意,在写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官方手册中有关中断的章节相当分散。
|
巨大八爪鱼
武林盟主 二十一级
|
在该程序的主函数里添加: 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。
|
巨大八爪鱼
武林盟主 二十一级
|
中断编号如果记不住,还可以用常量表示,如: NVIC->IP[EXTI0_IRQn] = 0x30; // 001.10000 NVIC->IP[TIM1_UP_IRQn] = 0x20; // 001.00000
|
巨大八爪鱼
武林盟主 二十一级
|
系统外设(如SysTick)的中断优先级是在SCB->SHP中设置的,具体请参阅编程手册的4.4.8 System handler priority registers (SHPRx)的内容。同样也是每个数组元素是8位的,只有高四位有效,低四位始终为零。
|