版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、STM32 开发基础知识开发基础知识 MDK 下下 C 语言基础复习语言基础复习 STM32 系统架构系统架构 STM32 时钟系统时钟系统 端口复用和重映射端口复用和重映射 STM32 NVIC 中断管理中断管理 MDK 中寄存器地址名称映射分析中寄存器地址名称映射分析 MDK 固件库快速开发技巧固件库快速开发技巧MDK 下下 C 语言基础复习语言基础复习位操作位操作位操作在单片机开发中的一些实用技巧位操作在单片机开发中的一些实用技巧1)不改变其他位的值的状况下,对某几个位进行设值。不改变其他位的值的状况下,对某几个位进行设值。 这个场景单片机开发中经常使用,方法就是先对需要设置的位用这个场
2、景单片机开发中经常使用,方法就是先对需要设置的位用&操作符进行清零操作,然后用操作符进行清零操作,然后用| 操作符设值。比如我要改变操作符设值。比如我要改变 GPIOA 的的状态状态, 可以先对寄存器的值进行可以先对寄存器的值进行&清零操作清零操作GPIOA-CRL&=0XFFFFFF0F; /将第将第 4-7 位清位清 0然后再与需要设置的值进行然后再与需要设置的值进行| 或运算或运算GPIOA-CRL|=0X00000040; /设置相应位的值,不改变其他位的值设置相应位的值,不改变其他位的值移位操作提高代码的可读性移位操作提高代码的可读性移位操作在单片机开发中也非
3、常重要,下面让我们看看固件库移位操作在单片机开发中也非常重要,下面让我们看看固件库的的 GPIO 初始化的函数里面的一行代码初始化的函数里面的一行代码GPIOx-BSRR = (uint32_t)0 x01) BSRR =0 x0030;这样的代码就不好看也不好重用了。这样的代码就不好看也不好重用了。类似这样的代码很多类似这样的代码很多:GPIOA-ODR|=15; /PA.5 输出高输出高,不改变其他位不改变其他位这样我们一目了然,这样我们一目了然, 5 告诉我们是第告诉我们是第 5 位也就是第位也就是第 6 个端口,个端口, 1 告诉我们是设置为告诉我们是设置为 1 了。了。uint8_t
4、 / uint16_t / uint32_t /uint64_t 是什么数据类型是什么数据类型这些数据类型是这些数据类型是 C99 中定义的,具体定义在:中定义的,具体定义在:/usr/include/stdint.h ISO C99: 7.18 Integer types 取反操作使用技巧取反操作使用技巧SR 寄存器的每一位都代表一个状态,某个时刻我们希望去设置寄存器的每一位都代表一个状态,某个时刻我们希望去设置某一位的值为某一位的值为 0,同时其他位都保留为,同时其他位都保留为 1,简单的作法是直接给,简单的作法是直接给寄存器设置一个值:寄存器设置一个值:TIMx-SR=0 xFFF7;这
5、样的作法设置第这样的作法设置第 3 位为位为 0,但是这样的作法同样不好看,并且,但是这样的作法同样不好看,并且可读性很差。看看库函数代码中怎样使用的:可读性很差。看看库函数代码中怎样使用的:TIMx-SR = (uint16_t)TIM_FLAG;而而 TIM_FLAG 是通过宏定义定义的值:是通过宏定义定义的值:#define TIM_FLAG_Update (uint16_t)0 x0001)#define TIM_FLAG_CC1 (uint16_t)0 x0002)看这个应该很容易明白,可以直接从宏定义中看出看这个应该很容易明白,可以直接从宏定义中看出 TIM_FLAG_Update
6、 就是设置的第就是设置的第 0 位了,可读性非常强。位了,可读性非常强。define 宏定义宏定义define 是是 C 语言中的预处理命令,它用于宏定义,可以提高源语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供方便。常见的格式:代码的可读性,为编程提供方便。常见的格式:#define 标识符标识符 字符串字符串“标识符标识符”为所定义的宏名。为所定义的宏名。“字符串字符串”可以是常数、表达式、可以是常数、表达式、格式串等。例如:格式串等。例如:#define SYSCLK_FREQ_72MHz 72000000定义标识符定义标识符 SYSCLK_FREQ_72MHz 的
7、值为的值为 72000000。ifdef条件编译条件编译单片机程序开发过程中,经常会遇到一种情况,单片机程序开发过程中,经常会遇到一种情况, 当满足某条件当满足某条件时对一组语句进行编译,而时对一组语句进行编译,而当条件不满足时则编译另一组语句。当条件不满足时则编译另一组语句。 条件编译命令最常见的形条件编译命令最常见的形式为:式为:#ifdef 标识符标识符程序段程序段 1#else程序段程序段 2#endif它的作用是:当标识符已经被定义过它的作用是:当标识符已经被定义过(一般是用一般是用#define 命令命令定义定义) ,则对程序段,则对程序段 1 进行编译,进行编译,否则编译程序段否
8、则编译程序段 2。 其中其中#else 部分也可以没有,即:部分也可以没有,即:#ifdef程序段程序段 1#endif这个条件编译在这个条件编译在 MDK 里面是用得很多的,在里面是用得很多的,在 stm32f10 x.h 这个头文件中经常会看到这样的语句:这个头文件中经常会看到这样的语句:#ifdef STM32F10X_HD大容量芯片需要的一些变量定义大容量芯片需要的一些变量定义#end而而 STM32F10X_HD 则是我们通过则是我们通过#define 来定义的。来定义的。extern 变量申明变量申明C 语言中语言中 extern 可以置于变量或者函数前,以表示变量或者函数可以置于
9、变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。块中寻找其定义。 这里面要注意,对于这里面要注意,对于 extern 申明变量可以多申明变量可以多次,但定义只有一次。在我们的代码中你会看到看到这样的语句:次,但定义只有一次。在我们的代码中你会看到看到这样的语句:extern u16 USART_RX_STA;这个语句是申明这个语句是申明 USART_RX_STA 变量在其他文件中已经定义了,变量在其他文件中已经定义了,在这里要使用到。所以,你肯定可以找到在某个地方有变量定义在这里要
10、使用到。所以,你肯定可以找到在某个地方有变量定义的语句:的语句:u16 USART_RX_STA;的出现。下面通过一个例子说明一下使用方法。的出现。下面通过一个例子说明一下使用方法。引出引出IO口口在在 Main.c 定义的全局变量定义的全局变量 id, id 的初始化都是在的初始化都是在 Main.c 里面里面进行的。进行的。Main.c 文件文件u8 id;/定义只允许一次定义只允许一次main()id=1;printf(d%,id);/id=1test();printf(d%,id);/id=2但是我们希望在但是我们希望在 test.c 的的 changeId(void) 函数中使用变量
11、函数中使用变量 id, 这这个时候我们就需要在个时候我们就需要在 test.c里面去申明变量里面去申明变量 id 是外部定义的了,是外部定义的了,因为如果不申明,变量因为如果不申明,变量 id 的作用域是到不了的作用域是到不了 test.c 文件中。文件中。看下面看下面 test.c 中的代码:中的代码:extern u8 id;/申明变量申明变量 id 是在外部定义的,申明可以在是在外部定义的,申明可以在很很 多个文件中进行多个文件中进行void test(void) id=2;在在 test.c 中申明变量中申明变量 id 在外部定义,然后在在外部定义,然后在 test.c 中就可中就可以
12、使用变量以使用变量 id 了。了。对于对于 extern 申明函数在外部定义的应用,这里我们就不多申明函数在外部定义的应用,这里我们就不多讲解了。讲解了。typedef类型别名类型别名typedef 用于为现有类型创建一个新的名字,或称为类型别名,用来用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。简化变量的定义。typedef 在在 MDK 用得最多的就是定义结构体的类型别名和枚举类型用得最多的就是定义结构体的类型别名和枚举类型了。了。struct _GPIO_IO uint32_t CRL;_IO uint32_t CRH;定义了一个结构体定义了一个结构体 GPIO,这
13、样我们定义变量的方式为:,这样我们定义变量的方式为:struct _GPIO GPIOA; /定义结构体变量定义结构体变量 GPIOA但是这样很繁琐,但是这样很繁琐, MDK 中有很多这样的结构体变量需要定义。这里中有很多这样的结构体变量需要定义。这里我们可以为结体定义一个别名我们可以为结体定义一个别名 GPIO_TypeDef,这样我们就可以在其,这样我们就可以在其他地方通过别名他地方通过别名 GPIO_TypeDef 来定义结构体变量了。来定义结构体变量了。方法如下:方法如下:typedef struct_IO uint32_t CRL;_IO uint32_t CRH; GPIO_Typ
14、eDef;Typedef 为结构体定义一个别名为结构体定义一个别名 GPIO_TypeDef,这样我们可,这样我们可以通过以通过 GPIO_TypeDef 来定义结构体来定义结构体变量:变量:GPIO_TypeDef _GPIOA,_GPIOB;这里的这里的 GPIO_TypeDef 就跟就跟 struct _GPIO 是等同的作用了。是等同的作用了。 这样是不是方便很多?这样是不是方便很多?结构体结构体经常很多用户提到,他们对结构体使用不是很熟悉,但是经常很多用户提到,他们对结构体使用不是很熟悉,但是 MDK 中太多地方使用结构体以及结构体指针,这让他们一下中太多地方使用结构体以及结构体指针
15、,这让他们一下子摸不着头脑,学习子摸不着头脑,学习 STM32 的积极性大大降低,其实结构体的积极性大大降低,其实结构体并不是那么复杂,这里我们稍微提一下结构体的一些知识,并不是那么复杂,这里我们稍微提一下结构体的一些知识,还有一些知识我们会在下一节的还有一些知识我们会在下一节的“寄存器地址名称映射分析寄存器地址名称映射分析”中讲到一些。中讲到一些。声明结构体类型:声明结构体类型:Struct 结构体名结构体名 成员列表成员列表;变量名列表;变量名列表;例如:例如:Struct U_TYPE Int BaudRate Int WordLength;usart1,usart2;在结构体申明的时候
16、可以定义变量,也可以申明之后定义,方在结构体申明的时候可以定义变量,也可以申明之后定义,方法是:法是:Struct 结构体名字结构体名字 结构体变量列表结构体变量列表 ;例如:例如: struct U_TYPE usart1, usart2;结构体成员变量的引用方法是:结构体成员变量的引用方法是:结构体变量名字结构体变量名字.成员名成员名比如要引用比如要引用 usart1 的成员的成员 BaudRate,方法是:,方法是: usart1.BaudRate;结构体指针变量定义也是一样的,跟其他变量没有啥区别。结构体指针变量定义也是一样的,跟其他变量没有啥区别。例如:例如: struct U_TY
17、PE *usart3; /定义结构体指针变量定义结构体指针变量 usart1;结构体指针成员变量引用方法是通过结构体指针成员变量引用方法是通过“-”符号实现,比如要符号实现,比如要访问访问 usart3 结构体指针指向的结结构体指针指向的结构体的成员变量构体的成员变量 BaudRate, 方法是:方法是:Usart3-BaudRate;上面讲解了结构体和结构体指针的一些知识,其他的什么初上面讲解了结构体和结构体指针的一些知识,其他的什么初始化这里就不多讲解了。始化这里就不多讲解了。 讲到这里,有人会问,结构体到底讲到这里,有人会问,结构体到底有什么作用呢?为什么要使用结构体呢?下面我们将简单的
18、有什么作用呢?为什么要使用结构体呢?下面我们将简单的通过一个实例回答一下这个问题。通过一个实例回答一下这个问题。在我们单片机程序开发过程中,经常会遇到要初始化一个外设在我们单片机程序开发过程中,经常会遇到要初始化一个外设比如串口,它的初始化状态是由几个属性来决定的,比如串口比如串口,它的初始化状态是由几个属性来决定的,比如串口号,波特率,极性,以及模式。对于这种情况,在我们没有学号,波特率,极性,以及模式。对于这种情况,在我们没有学习结构体的时候,我们一般的方法是:习结构体的时候,我们一般的方法是:void USART_Init(u8 usartx,u32 u32 BaudRate,u8 pa
19、rity,u8 mode);这种方式是有效的同时在一定场合是可取的。但是试想,如果这种方式是有效的同时在一定场合是可取的。但是试想,如果有一天,我们希望往这个函数里面再传入一个参数,那么势必有一天,我们希望往这个函数里面再传入一个参数,那么势必我们需要修改这个函数的定义,重新加入字长这个入口参数。我们需要修改这个函数的定义,重新加入字长这个入口参数。于是我们的定义被修改为:于是我们的定义被修改为:void USART_Init (u8 usartx,u32 BaudRate, u8 parity,u8 mode,u8 wordlength );但是如果我们这个函数的入口参数是随着开发不断的增多
20、,那但是如果我们这个函数的入口参数是随着开发不断的增多,那么是不是我们就要不断的修改函数的定义呢?这是不是给我们么是不是我们就要不断的修改函数的定义呢?这是不是给我们开发带来很多的麻烦?那又怎样解决这种情况呢?开发带来很多的麻烦?那又怎样解决这种情况呢?这样如果我们使用到结构体就能解决这个问题了。我们可以这样如果我们使用到结构体就能解决这个问题了。我们可以在不改变入口参数的情况下,只需要改变结构体的成员变量,在不改变入口参数的情况下,只需要改变结构体的成员变量,就可以达到上面改变入口参数的目的。结构体就是将多个变就可以达到上面改变入口参数的目的。结构体就是将多个变量组合为一个有机的整体。上面的
21、函数量组合为一个有机的整体。上面的函数, BaudRate, wordlength, Parity, mode, wordlength 这些参数,他们对于串这些参数,他们对于串口而言,是一个有机整体,都是来设置串口参数的,所以我口而言,是一个有机整体,都是来设置串口参数的,所以我们可以将他们通过定义一个结构体来组合在一个。们可以将他们通过定义一个结构体来组合在一个。 MDK 中中是这样定义的:是这样定义的:typedef structuint32_t USART_BaudRate;uint16_t USART_WordLength;uint16_t USART_StopBits;uint16_
22、t USART_Parity;uint16_t USART_Mode;uint16_t USART_HardwareFlowControl; USART_InitTypeDef;于是,我们在初始化串口的时候入口参数就可以是于是,我们在初始化串口的时候入口参数就可以是 USART_InitTypeDef 类型的变量或者指针变量了,类型的变量或者指针变量了, MDK 中是中是这样做的:这样做的:void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);这样,任何时候,我们只需要修改结构体成员变量,往这样,
23、任何时候,我们只需要修改结构体成员变量,往结构体中间加入新的成员变量,而不需要修改函数定义就可以结构体中间加入新的成员变量,而不需要修改函数定义就可以达到修改入口参数同样的目的了。达到修改入口参数同样的目的了。 这样的好处是不用修改任何这样的好处是不用修改任何函数定义就可以达到增加变量的目的。函数定义就可以达到增加变量的目的。在以后的开发过程中,如果你的变量定义过多,如果某在以后的开发过程中,如果你的变量定义过多,如果某几个变量是用来描述某一个对象,你可以考虑将这些变量定义几个变量是用来描述某一个对象,你可以考虑将这些变量定义在结构体中,这样也许可以提高你的代码的可读性。使用结构在结构体中,这
24、样也许可以提高你的代码的可读性。使用结构体组合参数,可以提高代码的可读性,不会觉得变量定义混乱。体组合参数,可以提高代码的可读性,不会觉得变量定义混乱。STM32 系统架构系统架构STM32 主系统主要由四个驱动单元和四个被动单元构成。主系统主要由四个驱动单元和四个被动单元构成。四个驱动单元是:四个驱动单元是:内核内核 DCode 总线总线;系统总线系统总线;通用通用 DMA1;通用通用 DMA2;四被动单元是:四被动单元是:AHB 到到 APB 的桥:连接所有的的桥:连接所有的 APB 设备;设备;内部内部 FlASH 闪存;闪存;内部内部 SRAM;FSMC; ICode 总线:该总线将总
25、线:该总线将 M3 内核指令总线和闪存指令接口相内核指令总线和闪存指令接口相连,指令的预取在该总线上面完成。连,指令的预取在该总线上面完成。 DCode 总线:该总线将总线:该总线将 M3 内核的内核的 DCode 总线与闪存存储总线与闪存存储器的数据接口相连接,常量加载和调试访问在该总线上面完成。器的数据接口相连接,常量加载和调试访问在该总线上面完成。 系统总线:该总线连接系统总线:该总线连接 M3 内核的系统总线到总线矩阵,总内核的系统总线到总线矩阵,总线矩阵协调内核和线矩阵协调内核和 DMA 间访问。间访问。 DMA 总线:该总线将总线:该总线将 DMA 的的 AHB 主控接口与总线矩阵
26、相主控接口与总线矩阵相连,总线矩阵协调连,总线矩阵协调 CPU 的的DCode 和和 DMA 到到 SRAM,闪存和外闪存和外设的访问。设的访问。 总线矩阵:总线矩阵协调内核系统总线和总线矩阵:总线矩阵协调内核系统总线和 DMA 主控总线之主控总线之间的访问仲裁,仲裁利用轮换算法。间的访问仲裁,仲裁利用轮换算法。 AHB/APB 桥桥:这两个桥在这两个桥在 AHB 和和 2 个个 APB 总线间提供同总线间提供同步连接,步连接, APB1 操作速度限于操作速度限于36MHz, APB2 操作速度全速。操作速度全速。STM32 时钟系统时钟系统众所周知,时钟系统是众所周知,时钟系统是 CPU 的
27、脉搏,就像人的心跳一样。所以的脉搏,就像人的心跳一样。所以时钟系统的重要性就不言而喻了。时钟系统的重要性就不言而喻了。 STM32 的时钟系统比较复杂,的时钟系统比较复杂,不像简单的不像简单的 51 单片机一个系统时钟就可以解决一切。于是有人单片机一个系统时钟就可以解决一切。于是有人要问,采用一个系统时钟不是很简单吗?为什么要问,采用一个系统时钟不是很简单吗?为什么 STM32 要有多要有多个时钟源呢?因为首先个时钟源呢?因为首先STM32 本身非常复杂,外设非常的多,本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这么高的频率,比如看门但是并不是所有外设都需要系统时钟这么高的频率
28、,比如看门狗以及狗以及 RTC 只需要几十只需要几十 k 的时钟即可。同一个电路,时钟越快的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的的 MCU 一般都是采取多时钟源的方法来解决这些问题。一般都是采取多时钟源的方法来解决这些问题。在在 STM32 中,有五个时钟源,为中,有五个时钟源,为 HSI、 HSE、 LSI、 LSE、 PLL。从时钟频率来分可以分为高速时钟源和低速时钟源,在。从时钟频率来分可以分为高速时钟源和低速时钟源,在这这 5 个中个中 HIS, HSE 以及以及 PLL 是高速时钟
29、,是高速时钟, LSI 和和 LSE 是低是低速时钟。从来源可分为外部时钟源和内部时钟源,外部时钟源速时钟。从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时钟源,其中就是从外部通过接晶振的方式获取时钟源,其中 HSE 和和 LSE 是外部时钟源。是外部时钟源。、 HSI 是高速内部时钟,是高速内部时钟, RC 振荡器,振荡器, 频率为频率为 8MHz。、 HSE 是高速外部时钟,可接石英是高速外部时钟,可接石英 /陶瓷谐振器,或者接外部陶瓷谐振器,或者接外部时钟源,频率范围为时钟源,频率范围为4MHz16MHz。 我们的开发板接的是我们的开发板接的是 8M 的晶振
30、。的晶振。、 LSI 是低速内部时钟,是低速内部时钟, RC 振荡器,频率为振荡器,频率为 40kHz。 独立独立看门狗的时钟源只能是看门狗的时钟源只能是 LSI,同时,同时 LSI 还可以作为还可以作为 RTC 的时钟的时钟源。源。、 LSE 是低速外部时钟,接频率为是低速外部时钟,接频率为 32.768kHz 的石英晶体。的石英晶体。 这个主要是这个主要是 RTC 的时钟源。的时钟源。、 PLL 为锁相环倍频输出,其时钟输入源可选择为为锁相环倍频输出,其时钟输入源可选择为 HSI/2、 HSE 或者或者 HSE/2。倍频可选择为。倍频可选择为216 倍,但是其输出频率最大倍,但是其输出频率
31、最大不得超过不得超过 72MHz。那么这那么这 5 个时钟源是怎么给各个外设以及系统提供时钟的呢?个时钟源是怎么给各个外设以及系统提供时钟的呢?A. MCO 是是 STM32 的一个时钟输出的一个时钟输出 IO(PA8),它可以选择一个,它可以选择一个时钟信号输出,时钟信号输出, 可以选择为可以选择为 PLL 输出的输出的 2 分频、分频、 HSI、 HSE、或者系统时钟。这个时钟可以用来给外部其他系统提供时钟源。或者系统时钟。这个时钟可以用来给外部其他系统提供时钟源。B. 这里是这里是 RTC 时钟源,从图上可以看出,时钟源,从图上可以看出, RTC 的时钟源可以的时钟源可以选择选择 LSI
32、, LSE,以及,以及HSE 的的 128 分频。分频。C. 从图中可以看出从图中可以看出 C 处处 USB 的时钟是来自的时钟是来自 PLL 时钟源时钟源.STM32 中有一个全速功能的中有一个全速功能的 USB 模块,其串行接口引擎需要一个频率模块,其串行接口引擎需要一个频率为为 48MHz 的时钟源。该时钟源只能从的时钟源。该时钟源只能从 PLL 输出端获取,可以选输出端获取,可以选择为择为 1.5 分频或者分频或者 1 分频,也就是,当需要使用分频,也就是,当需要使用 USB模块时,模块时,PLL 必须使能,并且时钟频率配置为必须使能,并且时钟频率配置为 48MHz 或或 72MHz。
33、D. 处就是处就是 STM32 的系统时钟的系统时钟 SYSCLK,它是供,它是供 STM32 中绝大中绝大部分部件工作的时钟源。部分部件工作的时钟源。 系统时钟可选择为系统时钟可选择为 PLL 输出、输出、 HSI 或或者者 HSE。系统时钟最大频率为。系统时钟最大频率为 72MHz. 这里的这里的 E 处是指其他所有外设了。从时钟图上可以看出,其处是指其他所有外设了。从时钟图上可以看出,其他所有外设的时钟最终来源都是他所有外设的时钟最终来源都是 SYSCLK。 SYSCLK 通过通过 AHB 分频器分频后送给各模块使用。这些模块包括:分频器分频后送给各模块使用。这些模块包括:、 AHB 总
34、线、内核、内存和总线、内核、内存和 DMA 使用的使用的 HCLK 时钟。时钟。、通过、通过 8 分频后送给分频后送给 Cortex 的系统定时器时钟,也就是的系统定时器时钟,也就是 systick 、直接送给、直接送给 Cortex 的空闲运行时钟的空闲运行时钟 FCLK。、送给、送给 APB1 分频器。分频器。 APB1 分频器输出一路供分频器输出一路供 APB1 外设使外设使用用(PCLK1,最大频率,最大频率 36MHz),另一路送给定时器,另一路送给定时器(Timer)2、 3、 4 倍频器使用。倍频器使用。、送给、送给 APB2 分频器。分频器。 APB2 分频器分频输出一路供分频
35、器分频输出一路供 APB2 外外设使用设使用(PCLK2,最大频率,最大频率 72MHz),另一路送给定时器,另一路送给定时器(Timer)1 倍频器使用。倍频器使用。 其中需要理解的是其中需要理解的是 APB1 和和 APB2 的区别,的区别, APB1 上面连接上面连接的是低速外设,包括电源接口、备份接口、的是低速外设,包括电源接口、备份接口、 CAN、 USB、 I2C1、 I2C2、 UART2、 UART3 等等,等等, APB2 上面连接的是高速外设包上面连接的是高速外设包括括 UART1、 SPI1、 Timer1、 ADC1、 ADC2、所有普通、所有普通 IO 口口(PAPE
36、)、第二功能、第二功能 IO 口等。口等。在以上的时钟输出中,有很多是带使能控制的,例如在以上的时钟输出中,有很多是带使能控制的,例如AHB 总线总线时钟、内核时钟、各种时钟、内核时钟、各种 APB1外设、外设、 APB2 外设等等。当需要外设等等。当需要使用某模块时,记得一定要先使能对应的时钟。使用某模块时,记得一定要先使能对应的时钟。 后面我们讲解后面我们讲解实例的时候会讲解到时钟使能的方法。实例的时候会讲解到时钟使能的方法。STM32 时钟系统的配置除了初始化的时候在时钟系统的配置除了初始化的时候在 system_stm32f10 x.c 中的中的 SystemInit()函数中外,其他
37、的配置主函数中外,其他的配置主要在要在 stm32f10 x_rcc.c 文件中,里面有很多时钟设置函数,大家文件中,里面有很多时钟设置函数,大家可以打开这个文件浏览一下,基本上看看函数的名称就知道这可以打开这个文件浏览一下,基本上看看函数的名称就知道这个函数的作用。这里需要指明一下,对于系统时钟,默认情况个函数的作用。这里需要指明一下,对于系统时钟,默认情况下是在下是在 SystemInit 函数的函数的 SetSysClock()函数中间判断的,而设函数中间判断的,而设置是通过宏定义设置的。我们可以看看置是通过宏定义设置的。我们可以看看 SetSysClock()函数体:函数体:stati
38、c void SetSysClock(void)#ifdef SYSCLK_FREQ_HSESetSysClockToHSE();#elif defined SYSCLK_FREQ_24MHzSetSysClockTo24();#elif defined SYSCLK_FREQ_36MHzSetSysClockTo36();#elif defined SYSCLK_FREQ_48MHzSetSysClockTo48();#elif defined SYSCLK_FREQ_56MHzSetSysClockTo56();#elif defined SYSCLK_FREQ_72MHzSetSysCl
39、ockTo72();#endif这段代码非常简单,就是判断系统宏定义的时钟是多少,这段代码非常简单,就是判断系统宏定义的时钟是多少,然后设置相应值。我们系统默认宏定义是然后设置相应值。我们系统默认宏定义是 72MHz:#define SYSCLK_FREQ_72MHz 72000000如果你要设置为如果你要设置为 36MHz,只需要注释掉上面代码,然后,只需要注释掉上面代码,然后加入下面代码即可:加入下面代码即可:#define SYSCLK_FREQ_36MHz 36000000同时还要注意的是,当我们设置好系统时钟后,可以通过变量同时还要注意的是,当我们设置好系统时钟后,可以通过变量 Sy
40、stemCoreClock 获取系统时钟值,如果系统是获取系统时钟值,如果系统是 72M 时钟,时钟,那么那么 SystemCoreClock=72000000。这是在。这是在 system_stm32f10 x.c 文件中设置的:文件中设置的:#ifdef SYSCLK_FREQ_HSEuint32_t SystemCoreClock = SYSCLK_FREQ_HSE;#elif defined SYSCLK_FREQ_36MHzuint32_t SystemCoreClock = SYSCLK_FREQ_36MHz;#elif defined SYSCLK_FREQ_48MHzuint3
41、2_t SystemCoreClock = SYSCLK_FREQ_48MHz;#elif defined SYSCLK_FREQ_56MHzuint32_t SystemCoreClock = SYSCLK_FREQ_56MHz;#elif defined SYSCLK_FREQ_72MHzuint32_t SystemCoreClock = SYSCLK_FREQ_72MHz;#elseuint32_t SystemCoreClock = HSI_VALUE;#endif这里总结一下这里总结一下 SystemInit()函数中设置的系统时钟大小:函数中设置的系统时钟大小:SYSCLK(系统
42、时钟)(系统时钟) =72MHzAHB 总线时钟总线时钟(使用使用 SYSCLK) =72MHzAPB1 总线时钟总线时钟(PCLK1) =36MHzAPB2 总线时钟总线时钟(PCLK2) =72MHzPLL 时钟时钟 =72MHz端口复用和重映射端口复用和重映射端口复用功能端口复用功能STM32 有很多的内置外设,这些外设的外部引脚都是有很多的内置外设,这些外设的外部引脚都是与与 GPIO 复用的。也就是说,一个复用的。也就是说,一个 GPIO如果可以复用为内置如果可以复用为内置外设的功能引脚,那么当这个外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时作为内置外设使用的时候,就叫
43、做复用。候,就叫做复用。大家都知道,大家都知道, MCU 都有串口,都有串口, STM32 有好几个串口。有好几个串口。比如说比如说 STM32F103ZET6 有有 5 个串口,我们可以查手册知道,个串口,我们可以查手册知道,串口串口 1 的引脚对应的的引脚对应的 IO 为为 PA9,PA10。 PA9,PA10 默认功能是默认功能是GPIO, 所以当所以当PA9,PA10 引脚作为串口引脚作为串口 1 的的 TX,RX 引脚使用引脚使用的时候,那就是端口复用。的时候,那就是端口复用。复用端口初始化有几个步骤:复用端口初始化有几个步骤:1) GPIO 端口时钟使能。要使用到端口复用,当然要使
44、端口时钟使能。要使用到端口复用,当然要使能端口的时钟了。能端口的时钟了。RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);2) 复用的外设时钟使能。比如你要将端口复用的外设时钟使能。比如你要将端口 PA9,PA10 复复用为串口,所以要使能串口时钟。用为串口,所以要使能串口时钟。RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);3) 端口模式配置。端口模式配置。 在在 IO 复用位内置外设功能引脚的时复用位内置外设功能引脚的时候,必须设置候,必须设置 GPIO 端口的模式,至于在复
45、用功能下端口的模式,至于在复用功能下 GPIO 的模式是怎么对应的,这个可以查看手册的模式是怎么对应的,这个可以查看手册 。这里。这里我们拿我们拿 Usart1 举例:举例:从表格中可以看出,我们要配置全双工的串口从表格中可以看出,我们要配置全双工的串口 1,那么,那么 TX 管管脚需要配置为推挽复用输出,脚需要配置为推挽复用输出,RX 管脚配置为浮空输入或者带管脚配置为浮空输入或者带上拉输入。上拉输入。我们在使用复用功能的是时候,最少要使能我们在使用复用功能的是时候,最少要使能 2 个时钟:个时钟:1) GPIO 时钟使能时钟使能2) 复用的外设时钟使能复用的外设时钟使能同时要初始化同时要初
46、始化 GPIO 以及复用外设功能以及复用外设功能/USART1_TX PA.9 复用推挽输出复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; /PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; /复用推挽输复用推挽输出出GPIO_Init(GPIOA, &GPIO_InitStructure);/USART1_RX PA.10 浮空输入浮空输入GPIO_InitStructure.GPIO_
47、Pin = GPIO_Pin_10;/PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;/浮空输入浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);端口重映射端口重映射为了使不同器件封装的外设为了使不同器件封装的外设 IO 功能数量达到最优,可功能数量达到最优,可以把一些复用功能重新映射到其他一些引脚上。以把一些复用功能重新映射到其他一些引脚上。 STM32 中有中有很多内置外设的输入输出引脚都具有重映射很多内置外设的输入输出引脚都具有重映射(remap) 的功能。的功能。 我们知道每个内
48、置外设都有若干个输入输出引脚,一般这些引我们知道每个内置外设都有若干个输入输出引脚,一般这些引脚的输出端口都是固定不变的,为了让设计工程师可以更好地脚的输出端口都是固定不变的,为了让设计工程师可以更好地安排引脚的走向和功能,在安排引脚的走向和功能,在 STM32 中引入了外设引脚重映射中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的端口外,还可以通的概念,即一个外设的引脚除了具有默认的端口外,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其它的过设置重映射寄存器的方式,把这个外设的引脚映射到其它的端口。简单的讲就是把管脚的外设功能映射到另一个管脚,但端口。简单的讲就是把管
49、脚的外设功能映射到另一个管脚,但不是可以随便映射的不是可以随便映射的.上图是截取的中文参考手册中的重映射表,从表中可以上图是截取的中文参考手册中的重映射表,从表中可以看出,默认情况下,串口看出,默认情况下,串口 1 复用的时候的引脚位复用的时候的引脚位 PA9,PA10,同,同时我们可以将时我们可以将 TX 和和 RX 重新映射到管脚重新映射到管脚 PB6 和和 PB7 上面去。上面去。所以重映射我们同样要使能复用功能的时候讲解的所以重映射我们同样要使能复用功能的时候讲解的 2 个时钟外,个时钟外,还要使能还要使能 AFIO 功能时钟,然后要调用重映射函数。功能时钟,然后要调用重映射函数。 1
50、) 使能使能 GPIOB 时钟:时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);2) 使能串口使能串口 1 时钟:时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);3) 使能使能 AFIO 时钟:时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);4) 开启重映射:开启重映射:GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);这样就将串口的这样就将串口的 TX 和和
51、RX 重映射到管脚重映射到管脚 PB6 和和 PB7 上面了。上面了。有哪些功能可以重映射,大家除了查看中文参考手册之有哪些功能可以重映射,大家除了查看中文参考手册之外,还可以从外,还可以从 GPIO_PinRemapConfig 函数入手查看第一个入函数入手查看第一个入口参数的取值范围可以得知。在口参数的取值范围可以得知。在 stm32f10 x_gpio.h 文件中定义文件中定义了取值范围为下面宏定义的标识符,这里我们贴一小部分:了取值范围为下面宏定义的标识符,这里我们贴一小部分:#define GPIO_Remap_SPI1 (uint32_t)0 x00000001)#define G
52、PIO_Remap_I2C1 (uint32_t)0 x00000002)#define GPIO_Remap_USART1 (uint32_t)0 x00000004)#define GPIO_Remap_USART2 (uint32_t)0 x00000008)#define GPIO_PartialRemap_USART3 (uint32_t)0 x00140010)#define GPIO_FullRemap_USART3 (uint32_t)0 x00140030)从上面可以看出,从上面可以看出, USART1 只有一种重映射,而对于只有一种重映射,而对于 USART3, 存在部分重
53、映射和完全重映射。存在部分重映射和完全重映射。 所谓部分重映射就是所谓部分重映射就是部分管脚和默认的是一样的,而部分管脚是重新映射到其他管部分管脚和默认的是一样的,而部分管脚是重新映射到其他管脚。脚。 而完全重映射就是所有管脚都重新映射到其他管脚。而完全重映射就是所有管脚都重新映射到其他管脚。部分重映射就是部分重映射就是 PB10, PB11,PB12 重映射到重映射到 PC10,PC11,PC12 上。而上。而 PB13 和和 PB14 和没有重映射情况是一样和没有重映射情况是一样的,都是的,都是USART3_CTS 和和 USART3_RTS 对应管脚。对应管脚。 完全重映完全重映射就是将
54、这两个脚重新映射到射就是将这两个脚重新映射到 PD11 和和 PD12 上去。上去。 我们要使用我们要使用 USART3 的部分重映射,我们调用函数方法为:的部分重映射,我们调用函数方法为:GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);STM32 NVIC 中断优先级管理中断优先级管理CM3 内核支持内核支持 256 个中断,其中包含了个中断,其中包含了 16 个内核中断个内核中断和和 240 个外部中断,并且具有个外部中断,并且具有 256级的可编程中断设置。但级的可编程中断设置。但 STM32 并没有使用并没有使用 CM3 内核
55、的全部东西,而是只用了它的一内核的全部东西,而是只用了它的一部分。部分。STM32 有有 84 个中断,包括个中断,包括 16 个内核中断和个内核中断和 68 个可屏个可屏蔽中断,具有蔽中断,具有 16 级可编程的中断优先级。而我们常用的就是这级可编程的中断优先级。而我们常用的就是这 68 个可屏蔽中断,但是个可屏蔽中断,但是 STM32 的的 68 个可屏蔽中断,在个可屏蔽中断,在 STM32F103 系列上面,又只有系列上面,又只有 60 个(在个(在 107 系列才有系列才有 68 个)。个)。所以我们就只针对所以我们就只针对 STM32F103 系列这系列这 60 个可屏蔽中断进行介个
56、可屏蔽中断进行介绍。绍。在在 MDK 内,与内,与 NVIC 相关的寄存器,相关的寄存器, MDK 为其定义为其定义了如下的结构体:了如下的结构体:typedef structvu32 ISER2;u32 RESERVED030;vu32 ICER2;u32 RSERVED130;vu32 ISPR2;u32 RESERVED230;vu32 ICPR2;u32 RESERVED330;vu32 IABR2;u32 RESERVED462;vu32 IPR15; NVIC_TypeDef;STM32 的中断在这些寄存器的控制下有序的执行的。只的中断在这些寄存器的控制下有序的执行的。只有了解这些
57、中断寄存器,才能了解有了解这些中断寄存器,才能了解STM32 的中断。下面简要介的中断。下面简要介绍这几个寄存器:绍这几个寄存器:ISER2: ISER 全称是:全称是: Interrupt Set-Enable Registers,这,这是一个中断使能寄存器组。上面说了是一个中断使能寄存器组。上面说了 STM32F103 的可屏蔽中的可屏蔽中断只有断只有 60 个,这里用了个,这里用了 2 个个 32 位的寄存器,总共可以表示位的寄存器,总共可以表示 64 个中断。而个中断。而 STM32F103 只用了其中的前只用了其中的前 60 位。位。 ISER0的的 bit0bit31 分别对应中断
58、分别对应中断 031。 ISER1的的 bit027 对应中断对应中断 3259;这样总共;这样总共 60 个中断就分别对应上了。你要使能某个中个中断就分别对应上了。你要使能某个中断,必须设置相应的断,必须设置相应的 ISER 位为位为 1,使该中断被使能,使该中断被使能(这里仅仅这里仅仅是使能,还要配合中断分组、屏蔽、是使能,还要配合中断分组、屏蔽、 IO 口映射等设置才算是口映射等设置才算是一个完整的中断设置一个完整的中断设置)。ICER2:全称是:全称是: Interrupt Clear-Enable Registers,是一个中断除能,是一个中断除能寄存器组。该寄存器组与寄存器组。该寄
59、存器组与 ISER 的作用恰好相反,是用来清除某个中断的作用恰好相反,是用来清除某个中断的使能的。其对应位的功能,也和的使能的。其对应位的功能,也和 ICER 一样。这里要专门设置一个一样。这里要专门设置一个 ICER 来清除中断位,而不是向来清除中断位,而不是向 ISER 写写 0 来清除,是因为来清除,是因为 NVIC 的这的这些寄存器都是写些寄存器都是写 1 有效的,写有效的,写 0 是无效的。是无效的。ISPR2:全称是:全称是: Interrupt Set-Pending Registers,是一个中断挂起,是一个中断挂起控制寄存器组。每个位对应的中断和控制寄存器组。每个位对应的中断
60、和 ISER 是一样的。通过置是一样的。通过置 1,可以,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的是无效的.ICPR2:全称是:全称是: Interrupt Clear-Pending Registers,是一个中断解,是一个中断解挂控制寄存器组。其作用与挂控制寄存器组。其作用与 ISPR 相反,对应位也和相反,对应位也和 ISER 是一样的。是一样的。通过设置通过设置 1,可以将挂起的中断接挂。写,可以将挂起的中断接挂。写 0 无效。无效。IABR2:全称是:全称是: Interrupt Active Bit Registers,是一个中断激活标
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026国网河南省电力公司高校毕业生提前批招聘笔试参考题库浓缩500题附答案详解(突破训练)
- 国家管网集团山东分公司2026届秋季高校毕业生招聘考试备考试题(浓缩500题)带答案详解(培优)
- 2026广西北部湾投资集团有限公司高校毕业生校园招聘考试参考试题(浓缩500题)及答案详解【网校专用】
- 2026国网四川省电力校园招聘(提前批)笔试模拟试题浓缩500题附答案详解(典型题)
- 2026国网湖北省电力公司高校毕业生提前批招聘(约450人)笔试备考题库浓缩500题及完整答案详解
- 2026年黔西南州农村信用社联合社秋季校园招聘笔试备考题库(浓缩500题)及参考答案详解
- 2026国网浙江省高校毕业生提前批招聘(约450人)笔试模拟试题浓缩500题附答案详解(综合题)
- 2025国网上海市电力校园招聘(提前批)笔试模拟试题浓缩500题含答案详解(预热题)
- 2026秋季国家管网集团工程技术创新公司(国家管网集团造价管理中心)高校毕业生招聘考试备考题库(浓缩500题)及答案详解(有一套)
- 2026国网山西电力公司高校毕业生提前批招聘笔试模拟试题浓缩500题及答案详解(新)
- 新修订去极端化条例课件
- 数控机床维修知识培训课件
- 未成年人保护法规及义务教育培训资料
- 公务员考试备考行测《其他常识》练习题含答案解析
- 浙江省鄞州中学2026届化学高一上期末联考试题含解析
- 2025至2030年中国医用制氧机行业市场竞争格局及投资前景展望报告
- 2025知识产权普法知识竞赛题库附答案
- 口腔健康与全身疾病的关系
- 二精处方管理办法
- 学校运营监管方案(3篇)
- 津贴补贴管理办法
评论
0/150
提交评论