STM32自学笔记(寄存器)——AFIO EXTI外部中断(1)

一、硬件

开发板为正点原子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

注释掉清除中断位的标志位, 按下按键后, 中断位不会被清除, 中断一直在触发

 取消注释后, 再次运行按下按键可以看到标志位被清除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值