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
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);
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);
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);
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);
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);
- 读取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);}
- 排查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寿命有以下几种方法:
- 不固定数据存放的地址,而是用一个固定的基地址加上EEPROM内的一个单元的内容(即偏移地址)作为真正的地址;若发现存储单元已坏(写入和读出的内容不同),则偏移地址加1,重新写入。此方法有一弊端:当某一个EEPROM单元被写坏再用下一个单元时,后面到所有数据都会被覆盖。
- 从第一个存储单元开始存储数据N次,然后转到下一个单元再存N次,依次类推,当最后一个单元存放N次之后,再转到第一个单元重新开始。即不重复读写(擦写)某几个存储单元,尽量用到EEPROM上的所有存储单元,防止某几个存储单元反复擦写导致损坏。
- 对于一些需要上电初始化的数据,在每次写入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响应中断后,转去执行的相应处理程序
- 中断的特点:
- 同步工作:中断是CPU 和接口之间的信息传递方式之一,它使CPU 与外设同步工作,较好地解决了快速CPU 与慢速外设之间的匹配问题;
- 异常处理:针对难以预料的异常情况,如掉电、存储出错、运算溢出等,可以通过中断系统由故障源向CPU 发出中断请求,再由CPU 转到相应的故障处理程序进行处理;
- 实时处理: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 初始化的同时配置好,中途不能对其进行修改,以免造成中断混乱;
- 抢占优先级和响应优先级的区别:
- 抢占 = 打断别人,高优先级的抢占优先级可以打断正在进行的低抢占优先级中断(值越小说明级越高);
- 响应 = 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
- 若需要挂起/解挂中断,查看中断当前激活状态,分别调用相关函数;
应用举例:
- 假设MCU 设置中断优先级组为2,然后对以下三个中断进行配置:
- 中断4(FLASH中断 )抢占优先级为2,响应优先级为1
- 中断5(RCC中断)抢占优先级为3,响应优先级为0
- 中断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.h
和stm32f10x_exti.c
文件中;
EXTI(External interrupt/event controller - 外部中断/事件控制器):是 STM32 实现 “外部信号触发硬件响应” 的核心外设,既能触发 CPU 中断(如按键检测、传感器电平变化),也能直接联动其他外设(如触发 ADC 采样、定时器启动),无需 CPU 干预,大幅提升系统效率。
它管理着中断控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可实现对每个中断/事件线进行单独配置,可单独配置为中断或者事件,以及触发事件的属性。
- EXTI 的功能:
- 接收外部信号(GPIO 引脚、部分外设内部信号);
- 按配置的 “触发条件”(上升沿、下降沿、双边沿)检测信号变化;
- 输出两种结果:
- 中断请求:发送到 NVIC(嵌套向量中断控制器),最终触发 CPU 执行中断服务函数(ISR);
- 事件请求:直接以事件脉冲方式输出到其他外设(如 ADC、TIM、DMA),触发硬件联动(无 CPU 参与,“零延迟”);
- EXTI功能框图:其中标黄的 20/ 代表着有20条相同的线路
- GPIO 与 EXTI_Line 的映射:STM32 的每个 GPIO 都可以作为外部中断的中断输入口,比如STM32F103 的中断控制器支持 19 个外部中断/事件请求。Line 是GPIO 的专用通道,两者实行固定映射规则,即所有 GPIO 端口的 Pin0 都映射到 Line0,Pin1 映射到 Line1,…,Pin15 映射到 Line15;
以STM32F103 的19 个外部中断为例:
- 线 0~15:对应外部 GPIO 口的输入中断。
- 线 16:连接到 PVD 输出。
- 线 17:连接到 RTC 闹钟事件。
- 线 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 的典型应用场景:
-
按键检测(中断方式):传统轮询方式会占用 CPU 资源,用 EXTI 中断可实现 “按键按下时才触发 CPU 处理”,大幅降低功耗;
- 配置:GPIO 上拉输入(避免悬空误触发),EXTI 下降沿触发(按键按下时电平从高变低),ISR 中添加软件消抖,处理按键逻辑(如切换 LED 状态)。
-
传感器数据采集(硬件触发):如红外传感器、光敏电阻等,当检测到信号变化时(如红外遮挡),通过 EXTI 触发 ADC 采样,无需 CPU 干预;
- 配置:EXTI 事件模式(EMR 使能),触发 ADC 外部采样,采样完成后通过 DMA 将数据传输到内存,CPU 仅在 DMA 传输完成后读取数据。
-
外设联动(无 CPU 干预):如定时器(TIM)的启动触发:用 EXTI 事件触发 TIM 的计数器启动,实现 “外部信号精准同步”(如电机控制中的位置信号触发 PWM 输出);
- 配置:EXTI 事件模式,TIM 的触发源选择 “EXTI 事件”,当 EXTI 检测到触发信号时,TIM 自动启动计数,无需 CPU 发送启动指令。
-
低功耗唤醒(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个寄存器:
SCB_AIRCR
:32 位寄存器,有效位为第8到10位,用于设置5种中断优先级分组;IP
:240个8位寄存器,每个中断使用一个寄存器来确定优先级,每个8位寄存器有效位为第4到7位,用于设置抢占优先级与响应优先级;
如STM32F10x系列一共60个可屏蔽中断,那么它就只使用了IP[59]~IP[0l;
ISER
:8个32位寄存器,每个位控制一个中断的使能,写1
使能,写0
无效;
如STM32F10x系列一共60个可屏蔽中断,所以它只使用了其中的ISER[0]和ISER[1]; ISER[0]的bito~ bit31分别对应中断0~ 31、ISER[1]的bit0~ bit27对应中断32~ 59;
ICER
:8个32位寄存器,每个位控制一个中断的失能,写1
失能,写0
无效;
如STM32F10x系列一共60个可屏蔽中断,所以它只使用了其中的ICER[0]和ICER[1]; ICER[0]的bito~ bit31分别对应中断0~ 31、ICER[1]的bit0~ bit27对应中断32~ 59;
ISPR
:8个32位寄存器,每个位控制一个中断的挂起,写1
失能,写0
无效;
如STM32F10x系列一共60个可屏蔽中断,所以它只使用了其中的ISPR[0]和ISPR[1]; ISPR[0]的bito~ bit31分别对应中断0~ 31、ISPR[1]的bit0~ bit27对应中断32~ 59;
ICPR
:8个32位寄存器,每个位控制一个中断的解挂,写1
失能,写0
无效;
如STM32F10x系列一共60个可屏蔽中断,所以它只使用了其中的ICPR[0]和ICPR[1]; ICPR[0]的bito~ bit31分别对应中断0~ 31、ICPR[1]的bit0~ bit27对应中断32~ 59;
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:
- 输入配置为浮空输入
- 若涉及到端口复用问题,还需要打开相应的端口复用时钟
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 其他:
- 基于CORTEX-M3内核的硬件因素,清除中断标志不会马上生效,需要一段时间,如果你的中断服务程序时间很短,就会出现中断重复进入的异常;这种情况,可以在程序中增加去抖动和延时功能;