STM32-端口复用和重映射

1 端口复用功能

1.1 端口复用的定义

        STM32有许多的内置外设(如串口、ADC、DCA等等),这些外设的外部引脚都是和GPIO复用的。也就是说,一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫复用。详细的可以参考《STM32F103ZET6数据手册》p30的内容,表格的倒数第二栏就表示端口复用功能。
        比如说,STM32的串口1的引脚对应的I/O位PA9、PA10。而PA9、PA10默认的功能都是GPIO,所以说当PA9、PA10引脚作为串口1使用的时候就是端口复用。

        那么,什么时候端口是默认功能,什么时候端口是复用功能呢?
        STM32时钟系统的配置除了初始化的时候在system_stm32f10x.c中的SystemInit函数之外,其他的配置主要在stm32f10x_rcc.c文件中, 所以GPIO等等外设的时钟使能函数都是在此文件中。同时我们通过函数名可以得到规律:GPIOA-GPIOC是挂载在APB2下面,TIM2-TIM4是挂载在APB1下面,DMA是挂载在AHB下面。所以调用函数的名称是需要根据这个来确定的。

        使用默认复用功能前必须对端口位配置寄存器编程。
        1)对于复用的输入功能,端口可以配置成:
                ①:输入模式(浮空、上拉或下拉)(模拟输入不走复用这条线前面内部结构篇幅中提到过)
                ②:复用功能输出模式:输入驱动器被配置成浮空输入模式
        2)对于复用输出功能,端口必须配置成复用功能输出模式(推挽或开漏)。
        3)对于双向复用功能,端口位必须配置复用功能输出模式(推挽或开漏)。这时,输入驱动器被配置成浮空输入模式。
        如果把一端口配置成复用输出功能,将使引脚和输出寄存器断开,并和片上外设的输出信号连接。如果软件把一个 GPIO 脚配置成复用输出功能,但是外设没有被激活,它的输出将不确定。

1.2 端口复用初始化过程

        接下来看一下端口复用初始化过程的步骤,拿串口1为例:

                1)GPIO端口时钟使能。要使用到端口复用,首先是要使能端口的时钟了。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

                2)复用的外设时钟使能。比如要将PA9、PA10引脚复用成串口,必须也要使能串口时钟。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

                3)端口模式配置。在I/O复用位内置外设功能引脚的时候,必须设置GPIO端口的模式。至于在复用功能下,GPIO的模式怎么设置,可以查看手册《STM32中文参考手册》p110的内容。这里拿USART1为例,进行配置,要配置全双工的串口1,TX引脚需要推挽复用输出,RX引脚需要浮空输入或者上拉输入。

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9//复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
  
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 PA.10 浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);  

         总而言之,使用复用功能的时候至少要使能2时钟:GPIO时钟使能、复用的外设时钟使能。同时还要初始化GPIO以及复用外设功能(端口模式配置)。

2 端口重映射

2.1 端口重映射的定义

        为了使不同的器件封装的外设I/O功能数量达到最优,可以把一些复用功能重新映射到其他的引脚上。STM32中有许多的内置外设的输入、输出引脚都具有重映射(Remap)的功能。我们知道,每个内置外设都有若干个输入、输出引脚,一般这些引脚的输出端口都是固定不变的,为了更好地安排引脚的走向和功能,在STM32中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的端口之外,还可以通过设定重映射寄存器的方式把这个外设的引脚映射到其他的端口。

        下面我将以例子来讲解部分重映射完全重映射:部分重映射就是 PB10,PB11,PB12 重映射到 PC10,PC11,PC12 上。而 PB13 和 PB14 和没有重映射情况是一样的,都是 USART3_CTS 和 USART3_RTS 对应管脚。完全重映射就是将这两个脚重新映射到 PD11 和 PD12 上去。

         简单讲,就是把引脚的外设功能映射到其他的引脚上,但不是可以随便映射的,具体的对应关系参考《STM32F103ZET6数据手册》p30的内容,表格的最后一栏就表示端口重映射功能。这里同样用串口1为例来说明。

        上图是截取的中文参考手册中的重映射表,从表中可以看出,默认情况下,串口 1 复用的时候的引脚位 PA9,PA10,我们可以将串口1重映射到PB6、PB7引脚上

 2.2 端口重映射初始化过程

        接下来看一下端口重映射初始化过程的步骤,拿串口1为例,除了之前使能复用功能的2个时钟之外,还需要使能AFIO功能时钟,然后调用重映射函数:

                1)GPIO端口时钟使能。要使用到端口复用,首先是要使能端口的时钟了。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

                2)复用的外设时钟使能。比如要将PB6、PB7引脚复用成串口,必须也要使能串口时钟。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

                3)使能AFIO时钟。重映射必须使能AFIO时钟。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

                4)开启重映射。

GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);

        这样,就将串口1的TXRX引脚映射到PB6、PB7引脚上面了。至于哪些功能可以重映射,除了查看中文参考手册之外,还可以从GPIO_PinRemapConfig函数入手查看第一个入口参数的取值范围的值。stm32f10x_gpio.h中定义了一些宏定义的标识符:

#define GPIO_Remap_SPI1             ((uint32_t)0x00000001)  /*!< SPI1 Alternate Function mapping */
#define GPIO_Remap_I2C1             ((uint32_t)0x00000002)  /*!< I2C1 Alternate Function mapping */
#define GPIO_Remap_USART1           ((uint32_t)0x00000004)  /*!< USART1 Alternate Function mapping */
#define GPIO_Remap_USART2           ((uint32_t)0x00000008)  /*!< USART2 Alternate Function mapping */
#define GPIO_PartialRemap_USART3    ((uint32_t)0x00140010)  /*!< USART3 Partial Alternate Function mapping */
#define GPIO_FullRemap_USART3       ((uint32_t)0x00140030)  /*!< USART3 Full Alternate Function mapping */
#define GPIO_PartialRemap_TIM1      ((uint32_t)0x00160040)  /*!< TIM1 Partial Alternate Function mapping */
#define GPIO_FullRemap_TIM1         ((uint32_t)0x001600C0)  /*!< TIM1 Full Alternate Function mapping */
#define GPIO_PartialRemap1_TIM2     ((uint32_t)0x00180100)  /*!< TIM2 Partial1 Alternate Function mapping */
#define GPIO_PartialRemap2_TIM2     ((uint32_t)0x00180200)  /*!< TIM2 Partial2 Alternate Function mapping */
#define GPIO_FullRemap_TIM2         ((uint32_t)0x00180300)  /*!< TIM2 Full Alternate Function mapping */
#define GPIO_PartialRemap_TIM3      ((uint32_t)0x001A0800)  /*!< TIM3 Partial Alternate Function mapping */
#define GPIO_FullRemap_TIM3         ((uint32_t)0x001A0C00)  /*!< TIM3 Full Alternate Function mapping */

        可以看出,USART1只有一种重映射,而USART3存在部分重映射和完全重映射。所谓部分重映射就是部分引脚和默认的是一样的,完全重映射就是所有引脚都映射到了新的引脚。可以查看《STM32中文参考手册》p119的内容查看部分重映射和完全重映射的内容。而在之前最后开启重映射的函数中,根据第一个参数,来确定是部分重映射还是全部重映射。

3 AFIO辅助功能时钟

        之前在端口重映射的时候,讲到要使能AFIO辅助功能时钟。那么什么时候需要开启(使能)呢?刚接触STM32F103,在尝试编写“按键中断”和“PWM呼吸灯”程序的时候,发现例程都用到了管脚复用AFIO:

//打开管脚复用AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

        学习“232USART串口通信”程序时,当我非常自信的写下上面这句代码后,发现例程里面却没有这句话,很让人摸不着头脑……查了很多资料,加上自己的理解,发现AFIO的使用还是有点内容值得总结一下的。“什么时候需要AFIO”从前文已经知道了,但仍让我困惑的是,同样是用到AFIO,为什么有的例程不需要(比如USART例程)打开AFIO,而有的例程(比如PWM呼吸灯和按键中断例程)却需要这句话打开AFIO。

        我总结的规律是:
        1.假如是用到挂载在APB2下的内置外设,比如下图中的ADC1/2,TIM1,SPI1,USART1,也算是管脚复用,但不需要打开AFIO语句,而是直接打开相应内置外设就好了。

        2.假如是用到挂载在APB1下的内置外设,比如下图中的SPI2,USART2/3,I2C1/2,也算是管脚复用,也不用打开AFIO语句,而是直接打开相应内置外设就行了。

         3.什么时候用到打开AFIO呢?
                1)首先要有管脚复用功能AFIO;
                2)其次被复用的管脚一定是挂载在APB2上的,因为AFIO就是在APB2上;
                3)最后就是内置外设一定是上述APB2表中没有的,因为APB2有的话,直接打开就好了,也用不到打开AFIO;

        根据以上条件,打开AFIO的只有一种情况,那就是:挂载在APB1下的内置外设,经过重映射功能,把管脚映射到APB2上!实际上,一旦使用重映射功能,只能映射到APB2上,因为APB2表中第二个框子里面包括了GPIOA~E,几乎所有的管脚。

        后来又发现了一种新的方法判断何时启动RCC_APB2Periph_AFIO的Clock,只有使用了AFIO的事件控制寄存器、AFIO的重映射功能以及外部中断(EXTI)控制寄存器才需要开启AFIO的时钟。注意,STM32参考手册从来没说过使用IO的复用功能就一定要开启AFIO时钟,这是个误区。
        对寄存器AFIO_MAPR、AFIO_EXTICRxAFIO_EVCR进行读写操作前,此时才应该开启AFIO时钟。
                1)AFIO_MAPR:配置复用功能重映射
                2)AFIO_EXTICRx:配置外部中断线映射
                3)AFIO_EVCR:   配置事件输出

        还有一个问题,在STM32中,USART2和TIM2是共用相同IO的,你如何决定这几个IO到底是做USART2还是做TIM2呢?如果你要同时使用USART2和TIM2,该怎么办?
        STM32中,USART2的CTS、RTS、TX、RX和TIM2的CH1~CH4都可重映射在PA0~PA3上面,具体要如何选择这两个功能呢?如果要使用USART2,就开启USART2的外设时钟,如果要使用TIM2,就开启TIM2的外设时钟,如果你同时开启,我也不知道会怎么样,你可以自己试试,我觉得应该是两个都不能用。如果你一定要在PA0~PA3上使用这两个功能,只能是时分复用。正确的同时使用USART2和TIM2方法,应该是使用上面提到的AFIO_Remap寄存器,将其中的一个重映射到其他IO上去,当然这样其他IO的本身的复用功能你就不能开启了。
回到前面的问题,如果你不做Remap,你根本不可能同时在PA0~PA3上同时使用这两个外设,现在你的方案有两种
        1)保留USART2在PA口上,将TIM2完全重映射(Full Remap)到其他IO上,或者,如果你不用RTS和CTS的硬件流控制,PA0(CTS)和PA1(RTS)上对应的CH1和CH2是可以不用重映射的,只需要将Bit9:8改成10,也就是将CH3和CH4部分重映射到PB10和PB11上去,而CH1和CH2继续保留在PA0和PA1上。但这种情况下,PB10和PB11上的I2C2和USART3就不能用了。
        2)保留TIM2在PA口,将USART2重映射到PD口上,很明显,当TIM2完全不重映射时,USART2必须的信号线只有Clock不受影响,这种情况下也没有部分映射可言了,必须将USART2完全重映射到PD3~PD7上,同样的,这种情况下FSMC就不能用了。

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值