一、硬件
开发板为正点原子stm32f103rct6mini板
LED:LED0->PA8, LED1->PD2, 阳极均连接VCC3.3v, 低电平导通
KEY: KEY0->PC5, KEY1->PA15, 按下接通GND
二、基础知识
外部中断的大致流程:
EXTI Lines and GPIO Mapping:
中断向量表:
中断号:
EXTI9_5 23
EXTI15_10 40
NVIC register map:
手册(PM0056)这里NVIC_IPR20(0x320)地址错了,已提交ST工程师,正确地址为0x350
这部分是ARM公司写的CMSIS库函数,本例不使用
三、代码实现
文件架构
1、RCC
stm32f103_rcc.h
#ifndef __STM32F103_RCC_H
#define __STM32F103_RCC_H
#ifdef __cplusplus
extern "C"
{
#endif
#define RCC_BASE (0x40021000)
#define RCC_APB2ENR_OFFSET 0x18
#define RCC_APB2ENR (*(volatile unsigned int *)(RCC_BASE + RCC_APB2ENR_OFFSET))
#define RCC_APB2ENR_IOPAEN (1 << 2)
#define RCC_APB2ENR_IOPCEN (1 << 4)
#define RCC_APB2ENR_IOPDEN (1 << 5)
#define RCC_APB2ENR_AFIOEN (1 << 0)
#ifdef __cplusplus
}
#endif
#endif /* __STM32F103_RCC_H */
rcc_config.c
#include "rcc_config.h"
#include "stm32f103_rcc.h"
void RCC_Init(void)
{
// Enable the clock for GPIOA, GPIOD, and GPIOC
RCC_APB2ENR |= RCC_APB2ENR_IOPAEN; // Enable GPIOA clock
RCC_APB2ENR |= RCC_APB2ENR_IOPDEN; // Enable GPIOD clock
RCC_APB2ENR |= RCC_APB2ENR_IOPCEN; // Enable GPIOC clock
RCC_APB2ENR |= RCC_APB2ENR_AFIOEN; // Enable AFIO clock
}
2、GPIO
stm32f103_gpio.h
#ifndef __STM32F103_GPIO_H
#define __STM32F103_GPIO_H
#ifdef __cplusplus
extern "C"
{
#endif
#define GPIOA_BASE (0x40010800)
#define GPIOC_BASE (0x40011000)
#define GPIOD_BASE (0x40011400)
#define GPIO_CRL_OFFSET 0x00
#define GPIO_CRH_OFFSET 0x04
#define GPIO_IDR_OFFSET 0x08
#define GPIO_BSRR_OFFSET 0x10
#define GPIOA_CRH (*(volatile unsigned int *)(GPIOA_BASE + GPIO_CRH_OFFSET))
#define GPIOA_BSRR (*(volatile unsigned int *)(GPIOA_BASE + GPIO_BSRR_OFFSET))
#define GPIOA_IDR (*(volatile unsigned int *)(GPIOA_BASE + GPIO_IDR_OFFSET))
#define GPIOC_CRL (*(volatile unsigned int *)(GPIOC_BASE + GPIO_CRL_OFFSET))
#define GPIOC_BSRR (*(volatile unsigned int *)(GPIOC_BASE + GPIO_BSRR_OFFSET))
#define GPIOC_IDR (*(volatile unsigned int *)(GPIOC_BASE + GPIO_IDR_OFFSET))
#define GPIOD_BSRR (*(volatile unsigned int *)(GPIOD_BASE + GPIO_BSRR_OFFSET))
#define GPIOD_CRL (*(volatile unsigned int *)(GPIOD_BASE + GPIO_CRL_OFFSET))
#define GPIO_MODE_OUTPUT_2MHZ_PP 0x02
#define GPIO_MODE_OUTOUT_2MHZ_OD 0x06
#define GPIO_MODE_INPUT 0x08
#define GPIO_PIN_SET(pin) (1 << pin)
#define GPIO_PIN_RESET(pin) (1 << (pin + 16))
#define GPIOA_PIN_Read(pin) (GPIOA_IDR & (1 << pin))
#define GPIOC_PIN_Read(pin) (GPIOC_IDR & (1 << pin))
#define PA8_PIN 8 // LED0
#define PA15_PIN 15 // KEY1
#define PC5_PIN 5 // KEY0
#define PD2_PIN 2 // LED1
#ifdef __cplusplus
}
#endif
#endif /* __STM32F103_GPIO_H */
gpio_config.c
#include "gpio_config.h"
#include "stm32f103_gpio.h"
void GPIO_Init(void)
{
// Configure PA8(LED0) as Push-Pull Output with 2 MHz speed
GPIOA_CRH &= ~(0xF << (PA8_PIN - 8) * 4); // Clear the 4 bits for PA8 configuration
GPIOA_CRH |= GPIO_MODE_OUTPUT_2MHZ_PP << (PA8_PIN - 8) * 4; // Set PA8 as 2 MHz Push-Pull output
// Configure PD2(LED1) as Push-Pull Output with 2 MHz speed
GPIOD_CRL &= ~(0xF << PD2_PIN * 4); // Clear the 4 bits for PD2 configuration
GPIOD_CRL |= GPIO_MODE_OUTPUT_2MHZ_PP << PD2_PIN * 4; // Set PD2 as 2 MHz Push-Pull output
// Configure PA15(KEY1) as Input mode with pull-up
GPIOA_CRH &= ~(0xF << (PA15_PIN - 8) * 4); // Clear the 4 bits for PA15 configuration
GPIOA_CRH |= (GPIO_MODE_INPUT << (PA15_PIN - 8) * 4); // Set PA15 as input mode
GPIOA_BSRR |= GPIO_PIN_SET(PA15_PIN); // Enable pull-up on PA15
// Configure PC5(KEY0) as Input mode with pull-up
GPIOC_CRL &= ~(0xF << (PC5_PIN * 4)); // Clear the 4 bits for PC5 configuration
GPIOC_CRL |= (GPIO_MODE_INPUT << (PC5_PIN * 4)); // Set PC5 as input mode
GPIOC_BSRR |= GPIO_PIN_SET(PC5_PIN); // Enable pull-up on PC5
}
3、AFIO
stm32f103_afio.h
#ifndef __STM32F103_AFIO_H
#define __STM32F103_AFIO_H
#ifdef __cplusplus
extern "C"
{
#endif
#define AFIO_BASE (0x40010000)
#define AFIO_EXTICR2_OFFSET 0x0C
#define AFIO_EXTICR4_OFFSET 0x14
#define AFIO_EXTICR2 (*(volatile unsigned int *)(AFIO_BASE + AFIO_EXTICR2_OFFSET))
#define AFIO_EXTICR4 (*(volatile unsigned int *)(AFIO_BASE + AFIO_EXTICR4_OFFSET))
#define AFIO_EXTI_PA 0x00
#define AFIO_EXTI_PC 0x02
#define AFIO_EXTI15 (15 - (15 / 4) * 4) * 4
#define AFIO_EXTI5 (5 - (5 / 4) * 4) * 4
#define AFIO_EXTI_PA_LINE15 (AFIO_EXTI_PA << AFIO_EXTI15)
#define AFIO_EXTI_PC_LINE5 (AFIO_EXTI_PC << AFIO_EXTI5)
#ifdef __cplusplus
}
#endif
#endif /* __STM32F103_AFIO_H */
AFIO_EXTI(X) = (X - ((X / 4) *4))*4
afio_config.c
#include "afio_config.h"
#include "stm32f103_afio.h"
void AFIO_Init(void)
{
// Configure EXTI15 to use PA15
AFIO_EXTICR4 &= ~(0xF << AFIO_EXTI15);
AFIO_EXTICR4 |= AFIO_EXTI_PA_LINE15;
// Configure EXTI5 to use PC5
AFIO_EXTICR2 &= ~(0xF << AFIO_EXTI5);
AFIO_EXTICR2 |= AFIO_EXTI_PC_LINE5;
}
4、EXTI
stm32f103_exti.h
#ifndef __STM32F103_EXTI_H
#define __STM32F103_EXTI_H
#ifdef __cplusplus
extern "C"
{
#endif
#define EXTI_BASE (0x40010400)
#define EXTI_IMR_OFFSET 0x00
#define EXTI_EMR_OFFSET 0x04
#define EXTI_RTSR_OFFSET 0x08
#define EXTI_FTSR_OFFSET 0x0C
#define EXTI_SWIER_OFFSET 0x10
#define EXTI_PR_OFFSET 0x14
#define EXTI_IMR (*(volatile unsigned int *)(EXTI_BASE + EXTI_IMR_OFFSET))
#define EXTI_EMR (*(volatile unsigned int *)(EXTI_BASE + EXTI_EMR_OFFSET))
#define EXTI_RTSR (*(volatile unsigned int *)(EXTI_BASE + EXTI_RTSR_OFFSET))
#define EXTI_FTSR (*(volatile unsigned int *)(EXTI_BASE + EXTI_FTSR_OFFSET))
#define EXTI_SWIER (*(volatile unsigned int *)(EXTI_BASE + EXTI_SWIER_OFFSET))
#define EXTI_PR (*(volatile unsigned int *)(EXTI_BASE + EXTI_PR_OFFSET))
#define EXTI_LINE_5 (1 << 5)
#define EXTI_LINE_15 (1 << 15)
#ifdef __cplusplus
}
#endif
#endif /* __STM32F103_EXTI_H */
exti_config.c
#include "exti_config.h"
#include "stm32f103_exti.h"
void EXTI_Init(void)
{
// Configure EXTI5 (PC5) for falling edge trigger
EXTI_FTSR |= EXTI_LINE_5;
EXTI_IMR |= EXTI_LINE_5;
// Configure EXTI15 (PA15) for falling edge trigger
EXTI_FTSR |= EXTI_LINE_15;
EXTI_IMR |= EXTI_LINE_15;
}
5、NVIC
stm32f103_nvic.h
#ifndef __STM32F103_NVIC_H
#define __STM32F103_NVIC_H
#ifdef __cplusplus
extern "C"
{
#endif
#define NVIC_BASE (0xE000E100)
#define NVIC_ISER0_OFFSET 0x000
#define NVIC_ISER1_OFFSET 0x004
#define NVIC_ICER0_OFFSET 0x080
#define NVIC_ISPR0_OFFSET 0x100
#define NVIC_ICPR0_OFFSET 0x180
#define NVIC_IPR0_OFFSET 0x300
#define NVIC_IPR5_OFFSET 0x314
#define NVIC_IPR10_OFFSET 0x328
#define NVIC_ISER0 (*(volatile unsigned int *)(NVIC_BASE + NVIC_ISER0_OFFSET))
#define NVIC_ISER1 (*(volatile unsigned int *)(NVIC_BASE + NVIC_ISER1_OFFSET))
#define NVIC_ICER0 (*(volatile unsigned int *)(NVIC_BASE + NVIC_ICER0_OFFSET))
#define NVIC_ISPR0 (*(volatile unsigned int *)(NVIC_BASE + NVIC_ISPR0_OFFSET))
#define NVIC_ICPR0 (*(volatile unsigned int *)(NVIC_BASE + NVIC_ICPR0_OFFSET))
#define NVIC_IPR0 (*(volatile unsigned int *)(NVIC_BASE + NVIC_IPR0_OFFSET))
#define NVIC_IPR5 (*(volatile unsigned int *)(NVIC_BASE + NVIC_IPR5_OFFSET))
#define NVIC_IPR10 (*(volatile unsigned int *)(NVIC_BASE + NVIC_IPR10_OFFSET))
#define NVIC_EXTI9_5_IRQ 23
#define NVIC_EXTI15_10_IRQ 40
#ifdef __cplusplus
}
#endif
#endif /* __STM32F103_NVIC_H */
IPR[m], 其中m = 中断号 / 4
nvic_config.c
#include "stm32f103_nvic.h"
void NVIC_Init(void)
{
// Enable EXTI9_5 interrupt (for EXTI5 - PC5)
NVIC_ISER0 |= (1 << NVIC_EXTI9_5_IRQ);
// Enable EXTI15_10 interrupt (for EXTI15 - PA15)
NVIC_ISER1 |= (1 << (NVIC_EXTI15_10_IRQ - 32 * 1));
// Set priority for EXTI9_5 interrupt (lower value means higher priority)
NVIC_IPR5 |= (0x20 << ((NVIC_EXTI9_5_IRQ % 4) * 8));
// Set priority for EXTI15_10 interrupt (lower value means higher priority)
NVIC_IPR10 |= (0x20 << ((NVIC_EXTI15_10_IRQ % 4) * 8));
}
1个字节偏移量为1, 一个32位寄存器分成4个8位寄存器
IPR[m] = IP[4m + 3] | IP[4m + 2] | IP[4m + 1] | IP[4m]
m = 中断号 / 4
offset = 中断号 % 4
四、调试与验证
main.c
#include "LED.h"
#include "afio_config.h"
#include "exti_config.h"
#include "gpio_config.h"
#include "nvic_config.h"
#include "rcc_config.h"
#include "stm32f103_exti.h"
// Define a macro to enable interrupts
#define __enable_irq() __asm volatile("cpsie i" : : : "memory")
// Interrupt handler for EXTI lines 5-9
void __attribute__((interrupt("IRQ"))) EXTI9_5_IRQHandler(void)
{
if (EXTI_PR & EXTI_LINE_5) // Check if EXTI5 triggered
{
LED0_ON();
LED1_OFF();
EXTI_PR = EXTI_LINE_5; // Clear the interrupt flag
}
}
// Interrupt handler for EXTI lines 10-15
void __attribute__((interrupt("IRQ"))) EXTI15_10_IRQHandler(void)
{
if (EXTI_PR & EXTI_LINE_15) // Check if EXTI15 triggered
{
LED1_ON();
LED0_OFF();
EXTI_PR = EXTI_LINE_15; // Clear the interrupt flag
}
}
// Main function
int main(void)
{
// Initialize
RCC_Init();
GPIO_Init();
AFIO_Init();
NVIC_Init();
EXTI_Init();
LEDALL_OFF();
// Enable global interrupts
__enable_irq();
while (1)
{
}
return 0;
}
RCC
GPIO
PA8
PA15
PD2
PC5
AFIO
EXTI
NVIC
注释掉清除中断位的标志位, 按下按键后, 中断位不会被清除, 中断一直在触发
取消注释后, 再次运行按下按键可以看到标志位被清除