打印
[信息]

【转】STM32总结

[复制链接]
2758|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

以下内容大部分是根据零死角玩转STM32这篇文档,写的非常好。这个总结只是写了基础的部分,包括STM32的简介及STM32的IO口操作、中断配置,总结在此处方便以后的回顾。

一、Cortex简介

ARM Cortex 系列是新一代的,一个为广泛的技术需求提供标准架构的处理器。与其他的ARM 处理器不同,Cortex 系列是一个完整的处理器核心,一个标准的CPU 和系统架构。

Cortex-M3的4Gbyte地址空间被分成明确定义的区域:代码区,SRAM区,外设区和系统外设区。与ARM7不同的是,Cortex-M3是一个哈佛结构的处理器,所以有多条总线,允许执行并行操作,提高其整体性能。与早期的ARM架构不同,Cortex 系列允许未对齐的数据访问。这将确保最有效地利用内部SRAM。Cortex 系列还有一个称为位带的方法,支持在两个1Mbyte 的内存区域里进行位设置和清除。这样可以有效地访问位于SRAM存储器的外设寄存器和标志,而不需要一个完整的布尔处理器。

Cortex CPU 是一个有加载和存储体系结构的RISC处理器。为了执行数据处理指令,操作数必须被加载到一个中央寄存器,数据操作必须在这些寄存器上执行,并且把结果回存到内存上。

二、STM32简介

STM32的核心是Cortex-M3 处理器。Cortex M3 处理器是一个标准化的微控制器,包括32 位CPU,总线结构,嵌套的中断单元,调试系统和标准内存布局。

M3处理器支持两种处理器的工作模式,还支持两级特权操作。
两种工作模式: 处理模式 handler mode  线程模式 thread mode。
引入两个模式的本意,在于区别普通应用程序代码和异常服务程序,包括中断服务程序代码
M3的特权分级: 特权级  用户级,可以提供一种存储器访问的保护机制,使得普通的用户程序代码不能意外地,甚至是恶意地执行涉及到要害的操作。这也是一个基本的安全模型。当处理器运行主应用程序时(线程模式),既可以使用特权级,也可以使用用户级,但是异常服务程序必须在特权模式下执行。复位后,处理器默认进入线程模式,特权级访问。在特权级下,程序可以访问所有的存储器空间(除非被MPU设置禁用),并且可以执行所有指令。(特权级进入用户级后就只能处理在用户级的状态下能做的功能,想要回到特权级只有触发异常)在非特权模式下某些指令将被禁用(如允许访问xPSR 和它的别名的MRS 和MSR指令。

三、开发工具

MDK 是一个集代码编辑,编译,链接和下载于一体的集成开发环境(KDE)。

Translate 就是翻译当下修改过的文件,说明白点就是检查下有没有语法错误,并不会去链接库文件,也不会生成可执行文件。
Build 就是编译当下修改过的文件,它包含了语法检查,链接动态库文件,生成可执行文件。
Rebuild 重新编译整个工程,跟Build 这个按钮实现的功能是一样的,但有所不同的是它编译的是整个工程的所有文件,耗时巨大。

四、STM32

在 51 单片机的程序开发中,我们直接配置 51 单片机的寄存器,控制芯片的工作方式,如中断,定时器等。配置的时候,我们常常要查阅寄存器表,看用到哪些配置位,为了配置某功能,该置1 还是置 0。这些都是很琐碎的、 机械的工作,因为 51 单片机的软件相对来说较简单,而且资源很有限,所以可以直接配置寄存器的方式来开发。 STM32 库是由 ST 公司针对 STM32 提供的函数接口,即 API(Application Program Interface),开发者可调用这些函数接口来配置 STM32 的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。

库是架设在寄存器与用户驱动层之间的代码,向下处理与寄存器直接相关的配置,向上为用户提供配置寄存器的接口。


所谓库函数,就是 STM32的库文件中为我们编写好的函数接口,我们只要调用这些库函数,就可以对 STM32 进行配置,达到控制目的。我们可以不知道库函数是如何实现的,但我们调用函数必须要知道函数的功能、 可传入的参数及其意义、和函数的返回值。


五、为什么采用库来开发?对于 STM32,因为外设资源丰富,带来的必然是寄存器的数量和复杂度的增加,这时直接配置寄存器方式的缺陷就突显出来了:
1) 开发速度慢
2) 程序可读性差
这两个缺陷直接影响了开发效率,程序维护成本,交流成本。库开发方式则正好弥补了这两个缺陷。而**采用直接配置寄存器的方式开发的程序员,会列举以下原因:
1) 更直观
2) 程序运行占用资源少
初学 STM32 的读者,普遍因为第一个原因而选择以直接配置寄存器的方法来学习。认为这种方法直观,能够了解到是配置了哪些寄存器,怎样配置寄存器。事实上,库函数的底层实现恰恰是直接配置寄存器方式的最佳例子,想深入了解芯片是如何工作的话,只要追踪到库的最底层实现就能理解,相信你会为它严谨、优美的实现方式而陶醉。

相对于库开发的方式,直接配置寄存器方式生成的代码量的确会少一点,但因为 STM32 有充足的资源,权衡库的优势与不足,绝大部分时候,我们愿意牺牲一点资源,选择库开发。一般只有在对代码运行时间要求极苛刻的地方,才用直接配置寄存器的方式代替,如频繁调用的中断服务函数。


六、Cortex 微控制器软件接口标准

因为基于 Cortex的某系列芯片采用的内核都是相同的,区别主要为核外的片上外设(如芯片内部的模数转换外设ADC、串口UART、定时器TIM等)的差异,这些差异却导致软件在同内核,不同外设的芯片上移植困难。为了解决不同的芯片厂商生产的 Cortex微控制器软件的兼容性问题,ARM 与芯片厂商建立了 CMSIS 标准(Cortex MicroController SoftwareInterface Standard)。

CMSIS 标准中最主要的为 CMSIS 核心层,它包括了:
内核函数层:其中包含用于访问内核寄存器的名称、地址定义,主要由 ARM 公司提供。
设备外设访问层:提供了片上的核外外设的地址和中断定义,主要由芯片生产商提供。

CMSIS 为所有的Cortex 微控制器提供业界标准的驱动程序级别的接口。CMSIS 的目标只是使软件驱动程序和组件与硬件的交互变得尽量简单。这使得节省了开发时间,可以集中精力建设最好的应用程序代码。

七、位带

早期的ARM7和ARM9的CPU只能在SRAM和外设存储器位置上通过使用AND和OR指令执行位操作。这需要一个读-修改-写操作,在设置和清除位需要的周期数量和对每一个位操作所需的整体代码空间方面来说,这是很昂贵的。


为了克服这个限制,将有可能引入一个专用的位设置和清除指令,或者一个完整的布尔处理器,但是这将会增加Cortex CPU 的大小和复杂性。取而代之的是,一个叫位带的技术允许直接位操作外设区和SRAM区内存空间,而不需要其他任何特别指令的介入。Cortex存储器映射中的位寻址区域是由位带区(容量高达1M byte的实际存储器或外设寄存器)和占用内存映射32Mbyte 的位带别名区组成的。位带技术把位带区域中的一个位映射到别名区中的一个字地址。因此,通过设置和清除别名区字地址,我们可以设置和清除真正内存中的位。(不同于51单片机,这些位带对应的都是一位的地址,没有字节地址,所以不需要类似51中的sbit来区分是字节还是位地址)

计算别名地址的公式如下:

位带别名区的地址 = 位带别名区的基地址+位带字偏移

位带字偏移 = 位带基地址偏移*32+位号*4

这比一开始看起来要容易得多。对于一个实际的例子,GPIO 输出数据寄存器被设计为可以设置和清除单个IO线。端口B 输出寄存器的物理地址是0x40010C0C。在这个例子中,我们可以使用上面的公式来设置和清除这个字的第8位。

字地址 =0x40010C0C

外设位带基地址 =0x40000000

外设位带别名基地址 = 0x42000000

位带基地址偏移 =0x40010C0C-0x40000000=10C0C

位带字偏移 = (0x10C0C*32)+(8*4)=0x2181A0

位别名地址 =0x42000000+0x2181A0=0x422181A0

现在,我们可以用下面的C代码来创建一个指针指向这个地址:

#define PortBbit8 (*((volatile unsigned long *)0x422181A0 ))然后,可以使用这个指针来设置和清除IO端口位:

PortBbit8 = 1; //led on


沙发
俺是村长他爹|  楼主 | 2017-2-11 12:25 | 只看该作者
八、STM32GPIO

想要控制 LED 灯,当然是通过控制STM32 芯片的I/O 引脚电平的高低来实现。在 STM32 芯片上,I/O 引脚可以被软件设置成各种不同的功能,如输入或输出,所以被称为 GPIO(General-purpose I/O)。而 GPIO 引脚又被分GPIOA、GPIOB„„GPIOG 不同的组,每组端口分为0~15,共 16 个不同的引脚,对于不同型号的芯片,端口的组和引脚的数量不同,具体请参考相应芯片型号的 datasheet。

于是,控制 LED的步骤就自然整理出来了:

1. GPIO 端口引脚多 --> 就要选定需要控制的特定引脚

2. GPIO 功能如此丰富 --> 配置需要的特定功能

3. 控制 LED 的亮和灭 --> 设置 GPIO 输出电压的高低
继续思考,要控制 GPIO端口,就要涉及到控制相关的寄存器。这时我们就要查一查与 GPIO 相关的寄存器了,可以通过《 STM32 参考手册》来查看。



1. 配置寄存器:选定GPIO 的特定功能,最基本的如:选择作为输入还是输出端口。

2. 数据寄存器:保存了GPIO 的输入电平或 将要输出的电平。

3. 位控制寄存器:设置某引脚的数据为 1 或 0,控制输出的电平。

4. 锁定寄存器:设置某锁定引脚后,就不能修改其配置。

一切要以官方的数据手册为依据。接下来查看各寄存器相应位的含义,进行设置即可。

例如:对 x端口的寄存器 GPIOx_BSRR 的第0(BS0)进行写1,则 x 端口的第 0 引脚被设置为 1,输出高电平,若要令第0 引脚再输出低电平,则需要向GPIOx_BSRR 的第16(BR0)写 1。

首先请大家回顾一下在 51 单片机上点亮LED 是怎样实现的。

[cpp] view plain copy


  • <span style="font-size:18px;">#include<reg52.h>  
  • int main (void)  
  • {  
  • P0=0;  
  • while(1);  
  • }  
  • </span>  


以上代码就可以点亮 P0端口与 LED 阴极相连的LED 灯了,当然,这里省略了启动代码。为什么这个 P0 =0;句子就能控制P0 端口为低电平?很多刚入门 51 单片机的同学还真解释不来,关键之处在于这个代码所包含的头文件
<reg52.h>。在这个文件下有以下的定义:
1. /* BYTE Registers */
2. sfr P0 = 0x80;
3. sfr P1 = 0x90;
.........
20. sfr IP = 0xB8;
21. sfr SCON = 0x98;
22. sfr SBUF = 0x99;
这些定义被称为地址映射。

所谓地址映射,就是将芯片上的存储器 甚至 I/O等资源与地址建立一一对应的关系。如果某地址对应着某寄存器,我们就可以运用c 语言的指针来寻址并修改这个地址上的内容,从而实现修改该寄存器的内容。

正是因为<reg52.h>头文件中有了对于各种寄存器和I/O端口的地址映射,我们才可以在 51 单片机程序中方便地使用P0=0xFF; TMOD =0xFF等赋值句子对寄存器进行配置,从而控制单片机。 Cortex-M3 的地址映射也是类似的。Cortex-M3 有 32 根地址线,所以它的寻址空间大小为 2^32 bit=4GB。 ARM 公司设计时,预先把这 4GB 的寻址空间大致地分配好了。它把地址从 0x4000 0000 至 0x5FFF FFFF( 512MB )的地址分配给片上外设。通过把片上外设的寄存器映射到这个地址区,就可以简单地以访问内存的方式,访问这些外设的寄存器,从而控制外设的工作。结果,片上外设可以使用C 语言来操作。 M3存储器映射见下图



stm32f10x.h 这个文件非常重要,是一个非常底层的文件。所有处理器厂商都会将对内存的操作封装成一个宏,即我们通常说的寄存器,并且把这些实现封装成一个系统文件,包含在相应的开发环境中。这样,我们在开发自己的应用程序的时候只要将这个文件包含进来就可以了。

stm32f10x.h 这个文件中重要的内容就是把 STM32 的所有寄存器进行地址映射。如同51 单片机的<reg52.h>头文件一样,stm32f10x.h像一个大表格,我们在使用的时候就是通过宏定义进行类似查表的操作,大家想像一下没有这个文件的话,我们要怎样访问 STM32的寄存器?有什么缺点?
不进行这些宏定义的缺点有:

1、地址容易写错

2、我们需要查大量的手册来确定哪个地址对应哪个寄存器

3、看起来还不好看,且容易造成编程的错误,效率低,影响开发进度。

在这里我们以外接了 LED灯的外设 GPIOC 为例,在这个文件中有这样的一系列宏定义:
1. #define GPIOC_BASE(APB2PERIPH_BASE + 0x1000)
2. #define APB2PERIPH_BASE(PERIPH_BASE + 0x10000)
3. #define PERIPH_BASE((uint32_t)0x40000000)

外设基地址
首先看到 PERIPH_BASE 这个宏,宏展开为 0x40000000,并把它强制转换为 uint32_t 的 32 位类型数据,这是因为地 STM32 的地址是32 位的,是不是觉得0x40000000 这个地址很熟?是的,这个是 Cortex-M3核分配给片上外设的从 0x40000000 至 0x5FFF FFFF 的 512MB 寻址空间中的第一个地址,我们把0x4000 0000 称为外设基地址。

总线基地址
接下来是宏 APB2PERIPH_BASE,宏展开为 PERIPH_BASE(外设基地址) 加上偏移地址0x1 0000,即指向的地址为 0x40010000。这个APB2PERIPH_BASE宏是什么地址呢? STM32 不同的外设是挂载在不同的总线上的。有AHB 总线、 APB2 总线、 APB1 总线,挂载在这些总线上的外设有特定的地址范围。

其中像 GPIO、串口1、 ADC 及部分定时器是挂载这个被称为 APB2 的总线上,挂载到 APB2 总线上的外设地址空间是从 0x4001 0000 至地址0x4001 3FFF。这里的第一个地址,也就是0x4001 0000,被称为APB2PERIPH_BASE (APB2 总线外设的基地址)。而 APB2 总线基地址相对于外设基地址的偏移量为 0x1 0000 个地址,即为APB2 相对外设基地址的偏移地址。

寄存器组基地址
最后到了宏 GPIOC_BASE,宏展开为APB2PERIPH_BASE (APB2 总线外设的基地址)加上相对 APB2 总线基地址的偏移量0x1000 得到了GPIOC 端口的寄存器组的基地址。这个所谓的寄存器组又是什么呢?它包括什么寄存器?



注意到这个说明中有一个偏移地址: 0x04,这里的偏移地址的是相对哪个地址的偏移呢?下面进行举例说明。
对于 GPIOC 组的寄存器,GPIOC 含有的端口配置高寄存器(GPIOC_CRH)寄存器地址为:GPIOC_BASE +0x04。也就是说,这个偏移地址,就是该寄存器相对所在寄存器组基地址的偏移量。

ST 公司的工程师采用了更巧妙的方式来确定这些地址。

STM32 库对寄存器的封装 ST 的工程师用结构体的形式,封装了寄存器组,在 stm32f10x.h文件中,有以下代码:
1. #define GPIOA((GPIO_TypeDef *) GPIOA_BASE)
2. #define GPIOB((GPIO_TypeDef *) GPIOB_BASE)
3. #define GPIOC((GPIO_TypeDef *) GPIOC_BASE)

有了这些宏,我们就可以定位到具体的寄存器地址,在这里发现了一个陌生的类型GPIO_TypeDef,追踪它的定义,可以在 stm32f10x.h 文件中找到如下代码:
1. typedef struct
2. {
3. __IO uint32_t CRL;
4. __IO uint32_t CRH;
5. __IO uint32_t IDR;
6. __IO uint32_t ODR;
7. __IO uint32_t BSRR;
8. __IO uint32_t BRR;
9. __IO uint32_t LCKR;
10. } GPIO_TypeDef;

这个代码用 typedef 关键字声明了名为GPIO_TypeDef的结构体类型,结构体内又定义了 7 个 __IOuint32_t类型的变量。这些变量每个都为 32位,也就是每个变量占内存空间 4个字节。
在 c 语言中,结构体内变量的存储空间是连续的,也就是说假如我们定义了一个GPIO_TypeDef,这个结构体的首地址(变量 CRL 的地址)若为 0x40011000, 那么结构体中第二个变量(CRH)的地址即为0x40011000 +0x04 ,加上的这个0x04,正是代表 4 个字节地址的偏移量。这个 0x04 偏移量,正是 GPIOx_CRH 寄存器相对于所在寄存器组的偏移地址。同理, GPIO_TypeDef结构体内其它变量的偏移量,也和相应的寄存器偏移地址相符。于是,只要我们匹配了结构体的首地址,就可以确定各寄存器的具体地址了。

以后我们写代码的时候,如果要修改 GPIO的寄存器,就可以用以下的方式来实现。

1. GPIO_TypeDef * GPIOx; //定义一个GPIO_TypeDef型结构体指针GPIOx
2. GPIOx = GPIOA; //把指针地址设置为宏GPIOA地址
3. GPIOx->CRL = 0xffffffff; //通过指针访问并修改GPIOA_CRL 寄存器
通过类似的方式,我们就可以给具体的寄存器写上适当的参数,控制 STM32 了。


使用特权

评论回复
板凳
俺是村长他爹|  楼主 | 2017-2-11 12:25 | 只看该作者
九、STM32 的时钟系统

STM32 芯片为了实现低功耗,设计了一个功能完善但却非常复杂的时钟系统。普通的MCU,一般只要配置好 GPIO 的寄存器,就可以使用了,但 STM32还有一个步骤,就是开启外设时钟。

为什么 STM32 的时钟系统如此复杂,有倍频、分频及一系列的外设时钟的开关。需要倍频是考虑到电磁兼容性,如外部直接提供一个 72MHz 的晶振,太高的振荡频率可能会给制作电路板带来一定的难度。分频是因为STM32 既有高速外设又有低速外设,各种外设的工作频率不尽相同,如同pc 机上的南北桥,把高速的和低速的设备分开来管理。最后,每个外设都配备了外设时钟的开关,当我们不使用某个外设时,可以把这个外设时钟关闭,从而降低STM32的整体功耗。所以,当我们使用外设时,一定要记得开启外设的时钟。

十、一些库函数解析初始化库函数——GPIO_Init()



1. typedefstruct
2. {
3. uint16_t GPIO_Pin;/*指定将要进行配置的GPIO 引脚*/
4. GPIOSpeed_TypeDefGPIO_Speed; /*指定GPIO 引脚可输出的最高频率*/
5. GPIOMode_TypeDef GPIO_Mode;/*指定 GPIO引脚将要配置成的工作状态*/
6. }GPIO_InitTypeDef;

1. #define GPIO_Pin_0 ((uint16_t)0x0001)/*!< Pin 0 selected */
2. #define GPIO_Pin_1 ((uint16_t)0x0002)/*!< Pin 1 selected */
3. #define GPIO_Pin_2 ((uint16_t)0x0004)/*!< Pin 2 selected */
4. #define GPIO_Pin_3 ((uint16_t)0x0008)/*!< Pin 3 selected */

这些宏的值,就是允许我们给结构体成员 GPIO_Pin 赋的值,如我们给 GPIO_Pin 赋值为宏GPIO_Pin_0,表示我们选择了GPIO 端口的第 0 个引脚,在后面会通过一个函数把这些宏的值进行处理,设置相应的寄存器,实现我们
对 GPIO 端口的配置。

1. typedefenum
2. {
3. GPIO_Speed_10MHz = 1, //枚举常量,值为1,代表输出速率最高为10MHz
4. GPIO_Speed_2MHz, //对不赋值的枚举变量,自动加1,此常量值为2
5. GPIO_Speed_50MHz //常量值为3
6. }GPIOSpeed_TypeDef;
这是一个枚举类型,定义了三个枚举常量,这些常量可用于标识 GPIO 引脚可以配置成的各个最高速度。所以我们在为结构体中的 GPIO_Speed赋值的时候,就可以直接用这些含义清晰的枚举标识符了。

1. typedef enum
2. { GPIO_Mode_AIN = 0x0, //模拟输入模式
3. GPIO_Mode_IN_FLOATING = 0x04, //浮空输入模式
4. GPIO_Mode_IPD = 0x28, //下拉输入模式
5. GPIO_Mode_IPU = 0x48, //上拉输入模式
6. GPIO_Mode_Out_OD = 0x14, //开漏输出模式
7. GPIO_Mode_Out_PP = 0x10, //通用推挽输出模式
8. GPIO_Mode_AF_OD = 0x1C, //复用功能开漏输出
9. GPIO_Mode_AF_PP = 0x18 //复用功能推挽输出
10. }GPIOMode_TypeDef;

于是,我们可以总结 GPIO_InitTypeDef类型结构体的作用,整个结构体包含GPIO_Pin、GPIO_Speed、GPIO_Mode三个成员,我们对这三个成员赋予不同的数值可以对 GPIO 端口进行不同的配置,而这些可配置的数值,已经由 ST 的库文件封装成见名知义的枚举常量。这使我们编写代码变得非常简便。

当然,你也可以手动查数据手册一位位的赋值,但这样效率很低,而且不宜交流与查错。

十一、STM32 的中断和异常

Cortex 内核具有强大的异常响应系统,它把能够打断当前代码执行流程的事件分为异常(exception)和中断(interrupt),并把它们用一个表管理起来,编号为0~15 的称为内核异常,而 16 以上的则称为外部中断(外,相对内核而言),这个表就称为中断向量表。
而 STM32 对这个表重新进行了编排,把编号从-3 至 6 的中断向量定义为系统异常, 编号为负的内核异常不能被设置优先级,如复位(Reset)、不可屏蔽中断(NMI)、硬错误(Hardfault)。从编号7 开始的为外部中断,这些中断的优先级都是可以自行设置的。

NVIC中断控制器

STM32 的中断如此之多,配置起来并不容易,因此,我们需要一个强大而方便的中断控制器NVIC (Nested VectoredInterrupt Controller)。 NVIC 是属于 Cortex 内核的器件,不可屏蔽中断 (NMI)和外部中断都由它来处理,而 SYSTICK 不是由 NVIC 来控制的。


抢占优先级和响应优先级 STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。
抢占,是指打断其它中断的属性,即因为具有这个属性,会出现嵌套中断 (在执行中断服务函数 A 的过程中被中断 B 打断,执行完中断服务函数 B 再继续执行中断服务函数 A),抢占属性由NVIC_IRQChannelPreemptionPriority的参数配置。

而响应属性则应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达,则先处理响应优先级高的中断,响应属性由NVIC_IRQChannelSubPriority的参数配置。

NVIC 的优先级组
在配置优先级的时候,还要注意一个很重要的问题,中断种类的数量。 NVIC 只可以配置16 种 中断向量的优先级,也就是说,抢占优先级和响应优先级的数量由一个4 位的数字来决定, 把这个 4 位数字的位数分配成抢占优先级部分和响应优先级部分。有 5 组分配方式:
第 0 组: 所有 4 位用来配置抢占优先级,即 NVIC 配置的 24 =16种中断向量都是只有抢占属性,没有响应属性。
第 1 组:最高 1 位用来配置抢占优先级,低 3 位用来配置响应优先级。表示有 21=2种级别的抢占优先级(0级, 1 级),有 23=8种响应优先级,即在 16种中断向量之中,有8 种中断,其抢占优先级都为 0 级,而它们的响应优先级
分别为 0~7,其余 8 种中断向量的抢占优先级则都为 1 级,响应优先级别分别为 0~7。

第 2 组: 2 位用来配置抢占优先级, 2 位用来配置响应优先级。即 22=4种抢占优先级, 22=4种响应优先级。
第 3 组:高 3 位用来配置抢占优先级,最低 1 位用来配置响应优先级。即有 8 种抢占优先级, 2 种响应 2 优先级。
第 4 组:所有 4 位用来配置响应优先级。即 16 种中断向量具有都不相同的响应优先级。

STM2 单片机的所有 I/O 端口都可以配置为 EXTI 中断模式,用来捕捉外部信号,可以配置为下降沿中断,上升沿中断和上升下降沿中断这三种模式。

EXTI 外部中断

STM32 的所有 GPIO 都引入到 EXTI 外部中断线上,使得所有的 GPIO 都能作为外部中断的输入源。 GPIO 与 EXTI 的连接方式见图

其他口类推。

PAx~PGx端口的中断事件都连接到了EXTIx,即同一时刻EXTx 只能相应一个端口的事件触发,不能够同一时间响应所有GPIO 端口的事件,但可以分时复用。它可以配置为上升沿触发,下降沿触发或双边沿触发。

一般配置一个 I/O为 EXTI中断的步骤, 主要为:
1. 使能 EXTIx 线的时钟和第二功能AFIO 时钟
2. 配置 EXTIx 线的中断优先级(NVIC 初始化配置)
3. 配置 EXTI 中断线I/O(EXTI 初始化配置)

调用 GPIO_EXTILineConfig()函数把GPIOE,Pin5 设置为EXTI 输入线。
4. 选定要配置为 EXTI的 I/O口线和 I/O口的工作模式
5. EXTI 中断线工作模式配置

AFIO (alternate-function I/O),指 GPIO 端口的复用功能, GPIO 除了用作普通的输入输出(主功能),还可以作为片上外设的复用输入输出,如串口, ADC,这些就是复用功能。大多数 GPIO 都有一个默认复用功能,有的GPIO 还有重映射功能, 重映射功能是指把原来属于A 引脚的默认复用功能,转移到了 B 引脚进行使用,前提是 B引脚具有这个重映射功能。当把 GPIO 用作 EXTI 外部中断或使用重映射功能的时候,必须开启 AFIO时钟,而在使用默认复用功能的时候,就不必开启AFIO 时钟了。

十二、中断处理

Cortex 向量表的地址范围从底部开始。然而,向量表不是从零开始而是从地址0x00000004 开始,因为前四个字节被用来存储堆栈指针的起始地址。

在软件中,向量表的维护通常是通过在启动时把中断服务程序地址加载到内存基地址来实现(在启动代码中)。产生中断的时候会跳到对应的地址找到中断入口地址,执行相应的中断处理函数。


STM32有两个堆栈指针:主堆栈与进程堆栈。这种系统代码和应用程序代码进行分区的方式使得应用程序代码中的错误不会导致RTOS 崩溃。


使用特权

评论回复
地板
heisexingqisi| | 2017-2-12 21:24 | 只看该作者
两种工作模式: 处理模式 handler mode  线程模式 thread mode。
线程模式很少看到。

使用特权

评论回复
5
_执子之手| | 2017-2-13 10:59 | 只看该作者

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

66

主题

168

帖子

2

粉丝