当前位置: 首页 > news >正文

STM32理论 —— 存储、中断

文章目录

  • 1. 存储
    • 1.1 EEPROM芯片 - AT24Cxx
      • 1.1.1 写操作
      • 1.1.2 读操作
    • 1.2 代码应用例程
    • 1.3 延长EEPROM寿命
    • 1.4 关于存储器读写地址
  • 2. 中断
    • 2.1 STM32 的中断
    • 2.1 嵌套向量中断控制器 NVIC
      • 2.1.1 中断优先级分组(中断管理方法)
      • 2.1.2 NVIC 参数结构体
    • 2.2 外部中断/事件控制器 EXTI
    • 2.3 中断服务函数
    • 2.4 STM32 中断优先级寄存器配置及其参考代码
    • 2.5 其他:

1. 存储


1.1 EEPROM芯片 - AT24Cxx

AT24Cxx常用的IIC通讯的EEPROM 器件;
数据存储根据地址高位在前低位在后的原则;

如使用芯片为AT24C02,最低地址位为0,最高地址位为255,地址为255的数据为0x77,地址为254的数据为0x86,则从地址255 连续读取两个字节时,读到的数据为0x7786;

❗❗❗注意在对芯片进行写入时,为避免数据错位覆盖,应提前规划好写入地址与写入的数据字节长度;

1.1.1 写操作

字节写操作

在该模式下,主器件发送IIC起始信号(通知从器件开始工作)和从器件地址信息(选择与哪个从器件进行通信)给从器件,从器件回应主器件以应答信号后,主器件发送CAT24WC01/02/04/08/16的字节地址(EEPROM内存储单元的地址),从器件回应主器件以应答信号后,主器件发送要写入的数据到被寻址的存储单元,CAT24WC01/02/04/08/16回应主器件以应答信号后,主器件发送IIC停止信号(通知从器件停止工作)给从器件。

在这里插入图片描述

页写操作

页写操作同理于字节写操作,只是写入一个字节后不产生停止信号,主器件被允许发送P个额外的字节(CAT24WC01:P=7,CAT24WC02/04/08/16:P=15,即使用写页操作下,CAT24WC0一次可写8个字节,CAT24WC02/04/08/16一次可写16个字节)。

注意,若在发送停止信号前主器件发送的字节超过P+1个,地址计数器将自动翻转,先前写入的数据将被覆盖。

在这里插入图片描述

1.1.2 读操作

字节读操作

在该模式下,主器件发送IIC起始信号(通知从器件开始工作)和从器件地址信息(选择与哪个从器件进行通信)给从器件,从器件回应主器件以应答信号后,主器件发送CAT24WC01/02/04/08/16的字节地址(EEPROM内存储单元的地址),CAT24WC01/02/04/08/16回应主器件以应答信号后,从器件向主器件返回所要求的一个字节数据,此后主器件不发送应答,但产生一个停止信号。

在这里插入图片描述

页读操作

页读操作就是在字节读操作读完一个字节后,主器件不发送停止信号给从器件,而是发送一个应答信号表示要求进一步读取下一个字节信号,直到主器件向从器件发送停止信号。

注意,若主器件读取的字节超过E个,地址计数器将自动翻转,计数器将翻转到零并继续输出字节数据。(24WC01,E=127;24WC02,E=255;24WC04,E=511;24WC08,E=1023;24WC16,E=2047;)

1.2 代码应用例程

代码备份位置:百度网盘 - 知识资源 - 电子工程 - 单片机 - AT24Cxx 源代码.rar

  1. AT24CXX_WriteOneByte();AT24CXX_ReadOneByte();函数的应用例程,在AT24CXX 指定地址读写1个字节数据(最大值为0xFF)
		u8 write_data = 255;// 要写入的数据 u16 start_address = 0x0000; // 起始地址  u8 i2c_Channel = 0x01; // IIC通道或设备地址 u8 read_data = 0x00;delay_ms(1000);AT24CXX_WriteOneByte(start_address,write_data,i2c_Channel);delay_ms(1000);read_data = AT24CXX_ReadOneByte(start_address,i2c_Channel);delay_ms(1000);// 验证读取的数据是否与写入的数据一致  if (read_data == write_data) {  printf("Data read and written are the same.\n");  } else {  printf("Data read and written are different!\n");  } Uart4_Printf("address 0x%04X: 0x%02X\n", start_address, read_data);
  1. AT24CXX_WriteLenByte();AT24CXX_ReadLenByte();函数的应用例程,在AT24CXX 指定地址读写长度为Len 个字节的数据(最大值为0xFFFF FFFF)
		u32 write_data = 0xFFFFFFFF;// 要写入的数据 u16 start_address = 0x0000; // 起始地址  u8 i2c_Channel = 0x01; // IIC通道或设备地址 u32 read_data = 0x00;delay_ms(1000);AT24CXX_WriteLenByte(start_address,write_data,4,i2c_Channel);delay_ms(1000);read_data = AT24CXX_ReadLenByte(start_address,4,i2c_Channel);delay_ms(1000);// 验证读取的数据是否与写入的数据一致  // 验证数据  if (read_data == write_data) {  Uart4_Printf("Data written and read are same.\n");  } else {  Uart4_Printf("Data written and read are different.\n");  } Uart4_Printf("Written: 0x%08X, Read: 0x%08X\n", write_data, read_data);
  1. AT24CXX_Write();AT24CXX_Read();函数的应用例程,功能与上面AT24CXX_WriteLenByte() 相同,只是传入参数改为指针类型,多用于数组
		int i = 0;u8 data_to_write[] = {0x01, 0x02, 0x03, 0x04}; // 要写入的数据  u16 start_address = 0x0000; // 起始地址u8 read_buffer[sizeof(data_to_write)]; // 读取数据的缓冲区u8 i2cChannel = 0x01; // IIC通道或设备地址 AT24CXX_Write(start_address, data_to_write,sizeof(data_to_write),i2cChannel);	delay_ms(1000);delay_ms(1000);AT24CXX_Read(start_address,read_buffer,sizeof(read_buffer),i2cChannel);delay_ms(1000);delay_ms(1000);// 验证读取的数据是否与写入的数据一致  if (memcmp(data_to_write, read_buffer, sizeof(data_to_write)) == 0) {  Uart4_Printf("Data read and written are the same.\n");  } else {  Uart4_Printf("Data read and written are different!\n");  }  // 输出读取的数据 Uart4_Printf("Read data: ");  for (i = 0; i < sizeof(read_buffer); i++) {  Uart4_Printf("0x%02X ", read_buffer[i]);  }  Uart4_Printf("\n");delay_ms(1000);delay_ms(1000);
  1. AT24CXX_WriteFloat();AT24CXX_ReadFloat();函数的应用例程,在AT24CXX 指定地址开始读写float 型数据
		float dataToWrite = 3.14159f; // 要写入的数据 u16 start_address = 0x0000; // 起始地址  u8 bufferWrite[4];  u8 bufferRead[4];u8 i2c_Channel = 0x01; // IIC通道或设备地址 float dataRead;// 将float转换为字节数组  memcpy(bufferWrite, &dataToWrite, 4);delay_ms(1000);AT24CXX_WriteFloat(start_address, bufferWrite, i2c_Channel);  delay_ms(1000);AT24CXX_ReadFloat(start_address, bufferRead,i2c_Channel); delay_ms(1000);// 将字节数组转换回float并验证  memcpy(&dataRead, bufferRead, 4); // 验证数据是否一致  if(dataRead == dataToWrite)  {  Uart4_Printf("The read float is the same as the written float.\n");  }  else  {  Uart4_Printf("The read float is NOT the same as the written float.\n");   }Uart4_Printf("Original: %f, Read: %f\n", dataToWrite, dataRead); 
  1. AT24CXX_Write_double();AT24CXX_Read_double();函数的应用例程,在AT24CXX 指定地址开始读写double 型数据
		double dataToWrite = 3.14159265358979323846; // 要写入的数据 u16 start_address = 0x0000; // 起始地址  u8 i2c_Channel = 0x01; // IIC通道或设备地址 double dataRead = 0.0;delay_ms(1000);AT24CXX_Write_double(start_address, dataToWrite, i2c_Channel);  delay_ms(1000);dataRead = AT24CXX_Read_double(start_address, i2c_Channel); delay_ms(1000);// 比较两个double值是否相等(由于浮点数的精度问题,不能直接比较)  if (dataRead == dataToWrite) {  Uart4_Printf("Data written and read are same.\n");  } else {  Uart4_Printf("Data written and read are different.\n");  }Uart4_Printf("Written: %f, Read: %f\n", dataToWrite, dataRead);
  1. 读取EEPROM 内所有数据
		// 读取EEPROM 内所有数据u8 i=0;u16 addr = 0; // 地址从0开始u8 i2c_Channel = 0x01; // IIC通道或设备地址 u8 dataRead;for(i=0;i<=EE_TYPE;i++) // EE_TYPE 为当前硬件AT24Cxx 的容量{Uart4_Printf("\r\n");delay_ms(10);dataRead = AT24CXX_ReadOneByte(addr+i,i2c_Channel);Uart4_Printf("addr%d - %d",i,dataRead);}
  1. 排查EEPROM 的读写功能
		u16 i=0;u16 addr = 0;u8 i2c_Channel = 0x01; // IIC通道或设备地址 u8 dataRead;// 全写入1for(i=0;i<=EE_TYPE;i++) // EE_TYPE 为当前硬件AT24Cxx 的容量{AT24CXX_WriteOneByte(addr+i,0xff,i2c_Channel);if(i==EE_TYPE){break;}}
//		// 全读取在串口查看
//		for(i=0;i<=EE_TYPE;i++)
//		{
//			Uart4_Printf("\r\n");
//			dataRead = AT24CXX_ReadOneByte(addr+i,i2c_Channel);
//			Uart4_Printf("addr%d - %d",i,dataRead);
//			if(i==EE_TYPE){break;}
//		}// 自动对比,有错误就报错for(i=0;i<=EE_TYPE;i++){dataRead = AT24CXX_ReadOneByte(addr+i,i2c_Channel);if (dataRead != 0xff){Uart4_Printf("Error:addr%d - %d",i,dataRead);}if(i==EE_TYPE){break;}}Uart4_Printf("\r\n---------------------------------Write complete---------------------------------------------");// 全写入0for(i=0;i<=EE_TYPE;i++){AT24CXX_WriteOneByte(addr+i,0x00,i2c_Channel);if(i==EE_TYPE){break;}}
//		// 全读取在串口查看
//		for(i=0;i<=EE_TYPE;i++)
//		{
//			Uart4_Printf("\r\n");
//			dataRead = AT24CXX_ReadOneByte(addr+i,i2c_Channel);
//			Uart4_Printf("addr%d - %d",i,dataRead);
//			if(i==EE_TYPE){break;}
//		}// 自动对比,有错误就报错for(i=0;i<=EE_TYPE;i++){dataRead = AT24CXX_ReadOneByte(addr+i,i2c_Channel);if (dataRead != 0x00){Uart4_Printf("Error:addr%d - %d",i,dataRead);}if(i==EE_TYPE){break;}}Uart4_Printf("\r\n----------------------------Read complete--------------------------------------------------");

1.3 延长EEPROM寿命

对于Flash或EEPROM,读操作对其寿命影响不大,写操作(对Nand Flash则为块擦除)次数基本决定了存储器寿命;而且写入寿命在每个位之间是独立的。延长EEPROM寿命有以下几种方法:

  1. 不固定数据存放的地址,而是用一个固定的基地址加上EEPROM内的一个单元的内容(即偏移地址)作为真正的地址;若发现存储单元已坏(写入和读出的内容不同),则偏移地址加1,重新写入。此方法有一弊端:当某一个EEPROM单元被写坏再用下一个单元时,后面到所有数据都会被覆盖。
  2. 从第一个存储单元开始存储数据N次,然后转到下一个单元再存N次,依次类推,当最后一个单元存放N次之后,再转到第一个单元重新开始。即不重复读写(擦写)某几个存储单元,尽量用到EEPROM上的所有存储单元,防止某几个存储单元反复擦写导致损坏。
  3. 对于一些需要上电初始化的数据,在每次写入EEPROM时,附带写入一个标志位。AT24CXX_WriteOneByte(DATA_FLAG_Addr,0x0A);,上电初始化EEPROM时,读取该位并进行比较,查看EEPROM是否被写入过;
temp = AT24CXX_ReadOneByte(DATA_FLAG_Addr);
if(temp==0x0A)
{// 读取EEPROM中的数据到对应变量中
}
else
{// 第一次上电,向EEPROM 写入一些数据的初始值...AT24CXX_WriteOneByte(DATA_FLAG_Addr,0x0A); // 写入标志位,表示该系统已写入过一些初始化数据
}

1.4 关于存储器读写地址

存储器写入首先不能超过存储器的最高数据位,如AT24C02是2k(256*8)即2k bit(256个字节)的存储芯片,其最高字节地址为256,最后一个位的地址为2048. 其次是每个地址要定义好用于存储多少位的数据,千万不能产生数据存储错乱,比如定义为用于存储一个字节数据的地址,却写入了两个字节数据,那么不仅在下次读该字节地址中的值产生错误,还会导致多出的一个字节数据写入到下一个字节数据地址里去,造成下一个字节数据地址的数据错乱。

#define xxx0_Addr	0 // xxx0_Addr该地址用于存储一个字节的数据
#define xxx1_Addr	8 // 那么下一个字节数据就要相隔一个字节,xxx1_Addr 该地址用于存储两个字节的数据
#define xxx1_Addr	16 // 那么下一个字节数据就要相隔两个字节,如此类推

参考:AT24C02使用详解

2. 中断


中断是指通过硬件来改变CPU 的运行方向。单片机在执行程序的过程中,外部设备向CPU 发出中断请求信号,要求CPU 暂时中断当前程序的执行而转去执行相应的处理程序,待处理程序执行完毕后,再继续执行原来被中断的程序。这种程序在执行过程中由于外界的原因而被中间打断的情况称为“中断”;

在这里插入图片描述

  • 主程序:原来正常运行的程序
  • 中断源:引起中断的原因,或者能发出中断请求的来源
  • 中断请求:中断源要求服务的请求称为中断请求(或中断申请)
  • 断点:主程序被断开的位置(或者地址)
  • 中断入口地址:中断服务程序的首地址
  • 中断服务程序:cpu响应中断后,转去执行的相应处理程序

  • 中断的特点
    1. 同步工作:中断是CPU 和接口之间的信息传递方式之一,它使CPU 与外设同步工作,较好地解决了快速CPU 与慢速外设之间的匹配问题;
    2. 异常处理:针对难以预料的异常情况,如掉电、存储出错、运算溢出等,可以通过中断系统由故障源向CPU 发出中断请求,再由CPU 转到相应的故障处理程序进行处理;
    3. 实时处理:CPU 能够及时处理应用系统的随机事件,实时性大大增加;

2.1 STM32 的中断

Cortex-M3(CM3)内核MCU 最多支持256个中断,其中包含了16 个内核中断和240个可屏蔽中断,最多具有 256 级的可编程中断设置,根据不同型号单片机,其支持的中断数量不同,具体可查看对应芯片的数据手册中的中断向量表

如::STM32F1 系列芯片只用了CM3内核的部分资源,共有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级;而STM32F103系列又只有60个可屏蔽中断,如下图:

在这里插入图片描述

中断优先级数字越小,对应中断优先级越高;


STM32 外部中断功能实现细分如下图:
在这里插入图片描述
下面进行逐一讲解:

2.1 嵌套向量中断控制器 NVIC

NVIC(嵌套向量中断控制器) 控制整个芯片中断的相关功能;

在这里插入图片描述

2.1.1 中断优先级分组(中断管理方法)

STM32F1系列芯片通过配置应用中断与复位控制寄存器Application interrupt and reset control register AIRCR[10:8] 来对MCU 的中断优先级分组进行配置,中断优先级分组的作用就是决定把IP bit[7:4]这4个位如何分配给抢占优先级和子优先级
在这里插入图片描述

  • 配置中断优先级的功能通过函数NVIC_PriorityGroupConfig() 实现,它定义在源文件misc.c中,其函数定义如下:
/*** @brief  Configures the priority grouping: pre-emption priority and subpriority.* @param  NVIC_PriorityGroup: specifies the priority grouping bits length. *   This parameter can be one of the following values:*     @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority  级别最高*                                4 bits for subpriority*     @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority*                                3 bits for subpriority*     @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority*                                2 bits for subpriority*     @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority*                                1 bits for subpriority*     @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority*                                0 bits for subpriority* @retval None*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{/* Check the parameters */assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}// 如将MCU 的中断优先级分组配置为组0,即IP bit 第4~ 7位为0 位抢占优先级,4位响应优先级:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

中断优先级分组配置应该在MCU 初始化的同时配置好,中途不能对其进行修改,以免造成中断混乱;


  • 抢占优先级和响应优先级的区别
    • 抢占 = 打断别人,高优先级的抢占优先级可以打断正在进行的低抢占优先级中断(值越小说明级越高);
    • 响应 = 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
      在这里插入图片描述
  • 若需要挂起/解挂中断,查看中断当前激活状态,分别调用相关函数;

应用举例:

  1. 假设MCU 设置中断优先级组为2,然后对以下三个中断进行配置:
  2. 中断4(FLASH中断 )抢占优先级为2,响应优先级为1
  3. 中断5(RCC中断)抢占优先级为3,响应优先级为0
  4. 中断6(EXTIO中断) 抢占优先级为2,响应优先级为0

则,3个中断的优先级顺序为: 中断6>中断4>中断5

2.1.2 NVIC 参数结构体

NVIC的所有需要配置的参数都列举在结构体NVIC_InitTypeDef中,它定义在源文件misc.c中,其结构体如下:

/** * @brief  NVIC Init Structure definition  */typedef struct
{uint8_t NVIC_IRQChannel;                    /* 中断源 !< Specifies the IRQ channel to be enabled or disabled.This parameter can be a value of @ref IRQn_Type (For the complete STM32 Devices IRQ Channels list, pleaserefer to stm32f10x.h file) */uint8_t NVIC_IRQChannelPreemptionPriority;  /* 抢占优先级 !< Specifies the pre-emption priority for the IRQ channelspecified in NVIC_IRQChannel. This parameter can be a valuebetween 0 and 15 as described in the table @ref NVIC_Priority_Table */uint8_t NVIC_IRQChannelSubPriority;         /* 子优先级(响应优先级) !< Specifies the subpriority level for the IRQ channel specifiedin NVIC_IRQChannel. This parameter can be a valuebetween 0 and 15 as described in the table @ref NVIC_Priority_Table */FunctionalState NVIC_IRQChannelCmd;         /* 中断源使能 !< Specifies whether the IRQ channel defined in NVIC_IRQChannelwill be enabled or disabled. This parameter can be set either to ENABLE or DISABLE */   
} NVIC_InitTypeDef;

2.2 外部中断/事件控制器 EXTI

关于EXTI 的代码都在固件库的 stm32f10x_exti.hstm32f10x_exti.c 文件中;

EXTI(External interrupt/event controller - 外部中断/事件控制器):是 STM32 实现 “外部信号触发硬件响应” 的核心外设,既能触发 CPU 中断(如按键检测、传感器电平变化),也能直接联动其他外设(如触发 ADC 采样、定时器启动),无需 CPU 干预,大幅提升系统效率。

它管理着中断控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可实现对每个中断/事件线进行单独配置,可单独配置为中断或者事件,以及触发事件的属性。

  • EXTI 的功能
  1. 接收外部信号(GPIO 引脚、部分外设内部信号);
  2. 按配置的 “触发条件”(上升沿、下降沿、双边沿)检测信号变化;
  3. 输出两种结果:
    • 中断请求:发送到 NVIC(嵌套向量中断控制器),最终触发 CPU 执行中断服务函数(ISR);
    • 事件请求:直接以事件脉冲方式输出到其他外设(如 ADC、TIM、DMA),触发硬件联动(无 CPU 参与,“零延迟”);
  • EXTI功能框图:其中标黄的 20/ 代表着有20条相同的线路
    EXTI功能框图

  • GPIO 与 EXTI_Line 的映射:STM32 的每个 GPIO 都可以作为外部中断的中断输入口,比如STM32F103 的中断控制器支持 19 个外部中断/事件请求。Line 是GPIO 的专用通道,两者实行固定映射规则,即所有 GPIO 端口的 Pin0 都映射到 Line0,Pin1 映射到 Line1,…,Pin15 映射到 Line15;

以STM32F103 的19 个外部中断为例:

  1. 线 0~15:对应外部 GPIO 口的输入中断。
  2. 线 16:连接到 PVD 输出。
  3. 线 17:连接到 RTC 闹钟事件。
  4. 线 18:连接到 USB 唤醒事件。

即stm32f103中外部GPIO 口的输入中断有16条线,但STM32 的GPIO却远远不止16个,其分配方式如下图:
在这里插入图片描述
以线 0 为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0 . 而中断线每次只能连接到 1个 GPIO 口上,这就需要通过配置来决定对应的中断线配置到哪个 GPIO 上;

  • 多GPIO 共享Line 的问题:同一 Line(如 Line0)可绑定多个 GPIO Pin(如 GPIOA_Pin0、GPIOB_Pin0),但不能同时使能多个;若需使用多个 GPIO Pin 触发中断,建议选择不同的 Line(如 GPIOA_Pin0→Line0,GPIOB_Pin1→Line1),或通过 “软件轮询” 在 ISR 中判断是哪个 Pin 触发(需读取 GPIO 电平);

  • STM32 EXTI 中断向量表
    在这里插入图片描述
    从上图可知,IO 口外部中断在中断向量表中只分配了7个中断向量也就是只能使用7个中断服务函数;从表中可以看出,外部中断线5~ 9分配一个中断向量,共用一个服务函数;外部中断线10~ 15分配一个中断向量,共用一个中断服务函数;

  • EXTI 的典型应用场景
  1. 按键检测(中断方式):传统轮询方式会占用 CPU 资源,用 EXTI 中断可实现 “按键按下时才触发 CPU 处理”,大幅降低功耗;

    • 配置:GPIO 上拉输入(避免悬空误触发),EXTI 下降沿触发(按键按下时电平从高变低),ISR 中添加软件消抖,处理按键逻辑(如切换 LED 状态)。
  2. 传感器数据采集(硬件触发):如红外传感器、光敏电阻等,当检测到信号变化时(如红外遮挡),通过 EXTI 触发 ADC 采样,无需 CPU 干预;

    • 配置:EXTI 事件模式(EMR 使能),触发 ADC 外部采样,采样完成后通过 DMA 将数据传输到内存,CPU 仅在 DMA 传输完成后读取数据。
  3. 外设联动(无 CPU 干预):如定时器(TIM)的启动触发:用 EXTI 事件触发 TIM 的计数器启动,实现 “外部信号精准同步”(如电机控制中的位置信号触发 PWM 输出);

    • 配置:EXTI 事件模式,TIM 的触发源选择 “EXTI 事件”,当 EXTI 检测到触发信号时,TIM 自动启动计数,无需 CPU 发送启动指令。
  4. 低功耗唤醒(STOP 模式唤醒):STM32 进入 STOP 模式(低功耗模式)后,CPU 停止工作,仅部分外设(如 EXTI)保持运行,可通过 EXTI 中断唤醒 CPU;

    • 配置:选择支持低功耗唤醒的 GPIO Pin(如 PA0),EXTI 配置为上升沿触发,NVIC 使能中断,CPU 进入 STOP 模式后,当 PA0 检测到上升沿,EXTI 触发中断,唤醒 CPU 并恢复运行。

2.3 中断服务函数

中断被成功出发后,代码就会执行中断服务函数中的代码。

每个中断都有其固定的中断服务函数名,只有在这个函数名下编写中断服务函数才是有效的。所有中断服务函数都可在stm32f10x_it.c 的中断向量表中查找。其中EXTI线0到EXTI线4线都是单独的中断函数名、EXTI线5到EXTI线9共用一个中断函数名、EXTI线10线到EXTI线15线共用一个中断函数名。

// 例程
void EXTI0_IRQHandler(void)
{if(EXTI_GetITStatus(KEY1_EXTI_LINE)!=RESET) // 读取中断是否执行{LED1_TOGGLE;   //LED1的亮灭状态反转}EXTI_ClearITPendingBit(KEY1_EXTI_LINE); //清除中断标志位
}

在 《STM32中文参考手册 V10》 - 第九章的表55 其它STM32F10xxx产品(小容量、中容量和大容量)的向量表中可查看所有的中断通道;
在这里插入图片描述

2.4 STM32 中断优先级寄存器配置及其参考代码

STM32 中断优先级配置一共设计以下7个寄存器:
在这里插入图片描述

  1. SCB_AIRCR:32 位寄存器,有效位为第8到10位,用于设置5种中断优先级分组;
  2. IP240个8位寄存器,每个中断使用一个寄存器来确定优先级,每个8位寄存器有效位为第4到7位,用于设置抢占优先级与响应优先级;

如STM32F10x系列一共60个可屏蔽中断,那么它就只使用了IP[59]~IP[0l;

在这里插入图片描述

  1. ISER:8个32位寄存器,每个位控制一个中断的使能,写1使能,写0无效;

如STM32F10x系列一共60个可屏蔽中断,所以它只使用了其中的ISER[0]和ISER[1]; ISER[0]的bito~ bit31分别对应中断0~ 31、ISER[1]的bit0~ bit27对应中断32~ 59;

  1. ICER:8个32位寄存器,每个位控制一个中断的失能,写1失能,写0无效;

如STM32F10x系列一共60个可屏蔽中断,所以它只使用了其中的ICER[0]和ICER[1]; ICER[0]的bito~ bit31分别对应中断0~ 31、ICER[1]的bit0~ bit27对应中断32~ 59;

  1. ISPR:8个32位寄存器,每个位控制一个中断的挂起,写1失能,写0无效;

如STM32F10x系列一共60个可屏蔽中断,所以它只使用了其中的ISPR[0]和ISPR[1]; ISPR[0]的bito~ bit31分别对应中断0~ 31、ISPR[1]的bit0~ bit27对应中断32~ 59;

  1. ICPR:8个32位寄存器,每个位控制一个中断的解挂,写1失能,写0无效;

如STM32F10x系列一共60个可屏蔽中断,所以它只使用了其中的ICPR[0]和ICPR[1]; ICPR[0]的bito~ bit31分别对应中断0~ 31、ICPR[1]的bit0~ bit27对应中断32~ 59;

  1. IABR:8个32位寄存器,只读寄存器,每个位指示一个中断的激活状态,读1表示中断正在执行;

如STM32F10x系列一共60个可屏蔽中断,所以它只使用了其中的IABR[0]和IABR[1]; IABR[0]的bito~ bit31分别对应中断0~ 31、IABR[1]的bit0~ bit27对应中断32~ 59;


  • 参考代码:以配置中断5作为外部中断为例,GPIO 口选择PE9:
  1. 输入配置为浮空输入
  2. 若涉及到端口复用问题,还需要打开相应的端口复用时钟
void EXTI_Configuration(void) // 配置外部中断/事件
{EXTI_InitTypeDef EXTI_InitStructure;  // 定义外部中断参数结构体GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource9); // 外部中断线配置,PE9EXTI_InitStructure.EXTI_Line=EXTI_Line9; // 外部中断线EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 外部中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能外部中断EXTI_Init(&EXTI_InitStructure); //初始化外部中断线参数
}void NVIC_Configuration(void) // 配置NVIC
{NVIC_InitTypeDef NVIC_InitStructure;  // 定义中断配置参数结构体NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);          // 设置中断组为0NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;       // 设置中断来源(中断通道) ,外部中断线5NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;  // 设置抢占优先级为 0NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;         // 设置子优先级为3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           // 使能中断NVIC_Init(&NVIC_InitStructure); //初始化NVIC参数
}void EXTI_init(void) //外部中断初始化
{NVIC_Configuration();EXTI_Configuration();
}void EXTI9_5_IRQHandler(void)   // 外部中断5 中断服务函数,记得中断服务函数的函数名是内核规定的,不是自定义的!
{if(EXTI_GetITStatus(EXTI_Line9) != RESET) // 注意这里不要写错NVIC的 EXTI9_5_IRQn !!sprintfU4("外部中断5已被触发\r\n");EXTI_ClearITPendingBit(EXTI_Line9); //清除 LINE9 上的中断标志位
}	

2.5 其他:

  1. 基于CORTEX-M3内核的硬件因素,清除中断标志不会马上生效,需要一段时间,如果你的中断服务程序时间很短,就会出现中断重复进入的异常;这种情况,可以在程序中增加去抖动和延时功能;
http://www.dtcms.com/a/491773.html

相关文章:

  • 如何选择做网站软件制作
  • 营销型网站建设广州搭建 网站 模版
  • 在NumPy中合并两个一维数组的多种方法
  • 如何求「加减 value 任意次后的最大 MEX」同余类求解
  • 『 数据库 』MySQL复习 - MySQL表CRUD操作全解析
  • 规范门户网站建设虚拟主机可以干什么
  • 完整博客教程:使用Lamini和Hugging Face进行大语言模型微调
  • HandlerThread是什么?为什么它会存在?
  • LeetCode 面试经典 150_栈_有效的括号(52_20_C++_简单)(栈+哈希表)
  • Wine 10.15 发布!Linux 跑 Windows 应用更丝滑了
  • 国外家居设计网站温州建设信息网站
  • PT2000 Dev Studio产生的微码解析一-微核简介
  • UVa 10228 A Star not a Tree?
  • 管理系统网站开发赚钱小程序
  • 五、安装配置hive
  • 自己给公司做网站5g天天奭5g天天运动网站代码
  • Kafka Queue: 完成 alterShareGroupOffsets Api
  • 网站开发流程 原型设计深圳世茂前海中心
  • Secret 与 ConfigMap配置资源管理
  • 泛微 企业网站建设计划网站开发后台框架
  • 做网站怎么添加图片企业营业执照查询系统入口
  • 大气污染扩散Calpuff模型应用
  • 【LeetCode热题100(44/100)】二叉树的右视图
  • 打工人日报#202510016
  • 青岛公司建设网站添加友情链接的技巧
  • 10.2.3 TrinityCore 网络模块封装
  • JS逆向-安全辅助项目接口联动JSRpc进阶调用BP插件autoDecode(下)
  • 试客网站程序源码南京网站制作步骤
  • 外贸网站用什么空间好福州网站建设优化
  • RoboIntern,一款自动化办公小助手