STM32F1xx官方资料:
& @7 E6 v& d0 K' C
《STM32中文参考手册V10》-第8章通用和复用功能IO(GPIO和AFIO) ' W# R4 P* A$ k9 _1 h) \( t
" D& q) N6 k- D端口复用功能端口复用的定义STM32有许多的内置外设(如串口、ADC、DCA等等),这些外设的外部引脚都是和GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫复用。详细的可以参考《STM32F103ZET6数据手册》p30的内容,表格的倒数第二栏就表示端口复用功能。 # a5 m; L+ Y1 q+ X8 Z
比如说,STM32的串口1的引脚对应的I/O位PA9、PA10。而PA9、PA10默认的功能都是GPIO,所以说当PA9、PA10引脚作为串口1使用的时候就是端口复用。
9 p ^' i$ @+ s* D; f- S; t6 v1 d( n8 b: O+ Q: [' t: `: ]: M. z f7 z
0 C, J" @' B P4 [0 @! t
那么,什么时候端口是默认功能,什么时候端口是复用功能呢? " z4 A4 z# `/ j
STM32时钟系统的配置除了初始化的时候在system_stm32f10x.c中的SystemInit函数中外,其他的配置主要在stm32f10x_rcc.c文件中, 所以GPIO等等外设的时钟使能函数都是在此文件中。同时我们通过函数名可以得到规律:GPIOA-GPIOC是挂载在APB2下面,TIM2-TIM4是挂载在APB1下面,DMA是挂载在AHB下面。所以调用函数的名称是需要根据这个来确定的。 p" Q I5 F& m: p( b' t
端口复用初始化过程( c7 G( p7 W9 e) |. j
接下来看一下端口复用初始化过程的步骤,拿串口1为例: 1、GPIO端口时钟使能。要使用到端口复用,首先是要使能端口的时钟了; - <font size="3">RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);</font>
复制代码
8 H- B3 b' X1 ^, t2、复用的外设时钟使能。比如要将PA9、PA10引脚复用成串口,必须也要使能串口时钟; - <font size="3">RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);</font>
复制代码 4 ?, |/ U) ~; F7 y) ^; l' W
3、端口模式配置。在I/O复用位内置外设功能引脚的时候,必须设置GPIO端口的模式。至于在复用功能下,GPIO的模式怎么设置,可以查看手册《STM32中文参考手册》p110的内容。这里拿USART1为例,进行配置,要配置全双工的串口1,TX引脚需要推挽复用输出,RX引脚需要浮空输入或者上拉输入; 0 E$ T9 X2 V+ C( h6 h
5 O( I0 L9 t- G% `* F' |( f6 k
% e" y$ e' {4 Q& t
总而言之,使用复用功能的时候至少要使能2时钟:GPIO时钟使能、复用的外设时钟使能。同时还要初始化GPIO以及复用外设功能(端口模式配置)。
4 n6 F. R) ~1 f7 j4 t: G3 ~& Z端口重映射端口重映射的定义为了使不同的器件封装的外设I/O功能数量达到最优,可以把一些复用功能重新映射到其他的引脚上。STM32中有许多的内置外设的输入、输出引脚都具有重映射(Remap)的功能。
4 q0 H& L: O$ V. I9 j& q G, l
我们知道,每个内置外设都有若干个输入、输出引脚,一般这些引脚的输出端口都是固定不变的,为了更好地安排引脚的走向和功能,在STM32中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的端口之外,还可以通过设定重映射寄存器的方式把这个外设的引脚映射到其他的端口。
: u- O$ a" x$ N3 G: {1 p
简单讲,就是把引脚的外设功能映射到其他的引脚上,但不是可以随便映射的,具体的对应关系参考《STM32F103ZET6数据手册》p30的内容,表格的最后一栏就表示端口重映射功能。
9 R5 x, }: d/ R
这里同样用串口1为例来说明。 
+ D$ J2 `$ B6 _$ G# ~
- <font size="3">
" l% l4 w6 [: F% b- `5 i) ] - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9//复用推挽输出: M {# s8 ]$ ~4 t& E" l" K) D
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
6 f2 D4 T. b+ }( E f - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出" v6 ^4 r- z' b7 r8 m
- GPIO_Init(GPIOA, &GPIO_InitStructure);% W* p) [3 @* @* e+ u
-
$ F. v3 C: R. e: @1 ^2 w - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 PA.10 浮空输入& [# k9 \' @) b
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
! w1 n1 s7 K- M |7 ? - GPIO_Init(GPIOA, &GPIO_InitStructure); </font>
复制代码
0 N$ a: k8 S* \3 d" W/ b2 [' k
可以看出,我们可以将串口1重映射到PB6、PB7引脚上。 * N( I# Y* Y7 n3 J2 ~7 ~; z
端口重映射初始化过程$ [ A$ M4 F* u
接下来看一下端口重映射初始化过程的步骤,拿串口1为例,除了之前使能复用功能的2个时钟之外,还需要使能AFIO功能时钟,然后调用重映射函数: + g" H+ P* s3 |2 R
1、GPIO端口时钟使能。要使用到端口复用,首先是要使能端口的时钟了; - <font size="3">RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);</font>
复制代码 - z! C M) \8 }2 T; |
2、复用的外设时钟使能。比如要将PB6、PB7引脚复用成串口,必须也要使能串口时钟; - <font size="3">RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);</font>
复制代码
! z4 s; n$ H2 i3、使能AFIO时钟。重映射必须使能AFIO时钟; - <font size="3">RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);</font>
复制代码
+ v" Q( Z1 r# n( B! ~, a) ?: F E4、开启重映射; - <font size="3">GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);</font>
复制代码
- O/ k; W1 @& J2 {% x6 g这样,就将串口1的TX和RX引脚映射到PB6、PB7引脚上面了。至于哪些功能可以重映射,除了查看中文参考手册之外,还可以从GPIO_PinRemapConfig函数入手查看第一个入口参数的取值范围的值。stm32f10x_gpio.h中定义了一些宏定义的标识符: - <font size="3">
, P4 R# _" {4 q" Z/ C7 Z - #define GPIO_Remap_SPI1 ((uint32_t)0x00000001) /*!< SPI1 Alternate Function mapping */
) V4 B ^( W6 g A! [& I - #define GPIO_Remap_I2C1 ((uint32_t)0x00000002) /*!< I2C1 Alternate Function mapping *// ?# H% ~7 T% R" R5 Z* K
- #define GPIO_Remap_USART1 ((uint32_t)0x00000004) /*!< USART1 Alternate Function mapping */) ~# f, u& \4 ]6 z
- #define GPIO_Remap_USART2 ((uint32_t)0x00000008) /*!< USART2 Alternate Function mapping */
. D* D( v3 K4 b2 Y3 N - #define GPIO_PartialRemap_USART3 ((uint32_t)0x00140010) /*!< USART3 Partial Alternate Function mapping */) B8 i; z' X2 B$ {& j
- #define GPIO_FullRemap_USART3 ((uint32_t)0x00140030) /*!< USART3 Full Alternate Function mapping */% t8 I3 A' s7 o! U
- #define GPIO_PartialRemap_TIM1 ((uint32_t)0x00160040) /*!< TIM1 Partial Alternate Function mapping */
8 J* H- }& \5 f! g% ?' @; Y1 W. ^6 C% C - #define GPIO_FullRemap_TIM1 ((uint32_t)0x001600C0) /*!< TIM1 Full Alternate Function mapping */7 `) e6 |" ?6 ~4 p% l) M
- #define GPIO_PartialRemap1_TIM2 ((uint32_t)0x00180100) /*!< TIM2 Partial1 Alternate Function mapping */2 {1 e+ w8 n; g
- #define GPIO_PartialRemap2_TIM2 ((uint32_t)0x00180200) /*!< TIM2 Partial2 Alternate Function mapping */: ~7 m( z- v. S
- #define GPIO_FullRemap_TIM2 ((uint32_t)0x00180300) /*!< TIM2 Full Alternate Function mapping */
8 ?- y9 s: ^# v9 h2 O4 K - #define GPIO_PartialRemap_TIM3 ((uint32_t)0x001A0800) /*!< TIM3 Partial Alternate Function mapping */! Q. @; C0 w. K, v
- #define GPIO_FullRemap_TIM3 ((uint32_t)0x001A0C00) /*!< TIM3 Full Alternate Function mapping */</font>
复制代码8 [) p- U6 K; i9 |& P8 m; e
可以看出,USART1只有一种重映射,而USART3存在部分重映射和完全重映射。所谓部分重映射就是部分引脚和默认的是一样的,完全重映射就是所有引脚都映射到了新的引脚。可以查看《STM32中文参考手册》p119的内容查看部分重映射和完全重映射的内容。而在之前最后开启重映射的函数中,根据第一个参数,来确定是部分重映射还是全部重映射。
, v* J1 U; I; `1 V* kAFIO辅助功能时钟+ o0 t" L* ~+ Z* a* k3 k
之前在端口重映射的时候,讲到要使能AFIO辅助功能时钟。那么什么时候需要开启(使能)呢?
: ?; I8 M3 m" I! L6 t* Q
对寄存器AFIO_MAPR、AFIO_EXTICRx和AFIO_EVCR进行读写操作前,应当首先开启AFIO时钟。 1 _+ m! C& U( k/ N8 a6 n% D3 u6 X
- AFIO_MAPR:配置复用功能重映射
* }0 \" _. f% {( P. L3 A4 i
$ t+ L0 S$ U5 M6 p# t S0 s& _, f; \' b; L }# X* K; o
- AFIO_EXTICRx:配置外部中断线映射
3 B& {3 s5 D* d* g# L: `) j 7 e$ C4 i6 u9 H, k+ s
+ H) ?# m. ]" [: I" {& f, B0 ~& y! `
- AFIO_EVCR: 配置事件输出3 w: ^- {8 u+ u4 |' ^4 ~
1 D" y# }" G2 ]% a
2 i( @! A* s _8 Z: w; c* k- C$ _! P5 X* [! j& [. L5 P$ |+ \
9 k4 |, \' r- q- P9 l; r1 Z
. u/ v1 ~8 J* M+ K7 |6 L4 c: l4 E
|