|
【程序】STM32外部中断的使用(以PB4为例) |
一派护法 十九级 |
#include <stm32f10x.h> #define _BV(n) (1 << (n)) uint8_t num = 0; uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90}; void delay(void) { uint32_t i; for (i = 0; i < 2000; i++); } // 数码管显示部分 void SerIn(uint8_t data) { uint8_t i; for (i = 0; i < 8; i++) { GPIOC->BRR = _BV(15); if (data & 0x80) GPIOC->BSRR = _BV(13); else GPIOC->BRR = _BV(13); GPIOC->BSRR = _BV(15); data <<= 1; } } void ParOut(void) { GPIOC->BRR = _BV(14); GPIOC->BSRR = _BV(14); } int main(void) { RCC->APB2ENR = 0x19; // 开启PB和PC以及AFIO的时钟 GPIOB->CRL = 0x00280000; // PB4设为带上下拉的输入, PB5设为2MHz推挽输出 GPIOB->ODR = 0x10; // PB4选择上拉模式, PB5输出0 GPIOC->CRH = 0x33300000; // PC13~15设为50MHz推挽输出 // External interrupt/event controller EXTI->IMR = _BV(4); // 开启PX4中断 (Interrupt mask register) EXTI->FTSR = _BV(4); // PX4在下降沿触发中断 (Falling trigger selection register) AFIO->EXTICR[1] = 0x01; // 指定PX4中的PX为PB SerIn(seg8[num]); // 段选 SerIn(_BV(0)); // 位选 ParOut(); while (1) { if (EXTI->PR & _BV(4)) // 检测中断标志位 (Pending register) { num++; if (num > 9) num = 0; SerIn(seg8[num]); SerIn(_BV(0)); ParOut(); // 等待按键PB4释放 while ((GPIOB->IDR & _BV(4)) == 0) delay(); EXTI->PR |= _BV(4); // 通过写1来清除中断标志位 } } }
|
一派护法 十九级 |
【步骤】 1.开启AFIO的时钟,也就是将RCC->APB2ENR寄存器的最低位设为1 2.把相应端口设为输入 3.通过EXTI->IMR寄存器开中断 4.通过EXTI->FTSR寄存器设置中断触发方式 5.通过AFIO->EXTICR[n]寄存器决定PX中X的值。PX0~3在该[0]号寄存器中设置,PX4~7在[1]号寄存器中设置,以此类推
|
一派护法 十九级 |
对于第4步,EXTI_RTSR寄存器设置的触发方式为上升沿触发,EXTI_FTSR为下降沿触发
|
一派护法 十九级 |
1楼的程序是通过查询的方式来处理中断。下面的程序则是通过中断服务函数来处理: #include <stm32f10x.h> #define _BV(n) (1 << (n)) uint8_t num = 0; uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90}; void delay(void) { uint32_t i; for (i = 0; i < 2000; i++); } // 数码管显示部分 void SerIn(uint8_t data) { uint8_t i; for (i = 0; i < 8; i++) { GPIOC->BRR = _BV(15); if (data & 0x80) GPIOC->BSRR = _BV(13); else GPIOC->BRR = _BV(13); GPIOC->BSRR = _BV(15); data <<= 1; } } void ParOut(void) { GPIOC->BRR = _BV(14); GPIOC->BSRR = _BV(14); } // 中断服务函数 void EXTI4_IRQHandler(void) { num++; if (num > 9) num = 0; SerIn(seg8[num]); SerIn(_BV(0)); ParOut(); // 等待按键PB4释放 while ((GPIOB->IDR & _BV(4)) == 0) delay(); // 清除中断标志 //NVIC->ICPR[0] = _BV(10); // 无效, 因为该中断正在执行 EXTI->PR |= _BV(4); // 只能通过这种方式清除中断 } int main(void) { RCC->APB2ENR = 0x19; // 开启PB和PC以及AFIO的时钟 GPIOB->CRL = 0x00280000; // PB4设为带上下拉的输入, PB5设为2MHz推挽输出 GPIOB->ODR = 0x10; // PB4选择上拉模式, PB5输出0 GPIOC->CRH = 0x33300000; // PC13~15设为50MHz推挽输出 // External interrupt/event controller EXTI->IMR = _BV(4); // 开启PX4中断 (Interrupt mask register) EXTI->FTSR = _BV(4); // PX4在下降沿触发中断 (Falling trigger selection register) AFIO->EXTICR[1] = 0x01; // 指定PX4中的PX为PB NVIC->ISER[0] = _BV(10); // 允许执行中断函数 SerIn(seg8[num]); // 段选 SerIn(_BV(0)); // 位选 ParOut(); while (1); }
|
一派护法 十九级 |
首先,对于PX4中断,所使用的线路是EXTI4,也就是EXTI Line4 interrupt。该线路的位置(Position)是10。这在用户手册中是可以查到的:
|
一派护法 十九级 |
要想中断函数得到执行,就必须配置NVIC->ISER[n]寄存器(Interrupt set-enable registers)。 表中的Position的值的范围是0~59,而每个ISER寄存器只有32位,所以分了好几个寄存器来设置:  [0]号寄存器专门设置0~31号中断 [1]号寄存器专门设置32~63号中断 因此配置第10号中断的代码就是: NVIC->ISER[0] = _BV(10); 写1是开启,写0无效。要想关闭只有对NVIC->ICER[n]寄存器(Interrupt clear-enable registers)写1才行。 中断服务函数的名称必须为EXTI4_IRQHandler,不能自行修改。在该函数中必须通过对EXTI->PR寄存器(Pending register)写1来清除中断标志,防止该函数反复执行。
|
一派护法 十九级 |
IRQHandler的全称是Interrupt Request Handler,也就是中断请求处理器。
|