文档库 最新最全的文档下载
当前位置:文档库 › STM32关于GPIO与AFIO的总结20130709

STM32关于GPIO与AFIO的总结20130709

STM32关于GPIO与AFIO的总结20130709
STM32关于GPIO与AFIO的总结20130709

GPIO功能描述

每个GPI/O 端口有两个32位配置寄存器(GPIOx_CRL ,GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR 和GPIOx_ODR),一个32位置位/ 复位寄存器(GPIOx_BSRR) ,一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR) 。

根据数据手册中列出的每个I/O 端口的特定硬件特征,GPIO 端口的每个位可以由软件分别配置成多种模式。

─输入浮空

─输入上拉

─输入下拉

─模拟输入

─开漏输出

─推挽式输出

─推挽式复用功能

─开漏复用功能

每个I/O 端口位可以自由编程,然而I/0端口寄存器必须按32位字被访问( 不允许半字或字节访问) 。GPIOx_BSRR和GPIOx_BRR寄存器允许对任何GPIO寄存器的读/ 更改的独立访问;这样,在读和更改访问之间产生IRQ 时不会发生危险。

推挽与开漏

推挽输出:可以输出高,低电平,连接数字器件; 推挽结构一般是指两个三极管分别受两

互补信号的控制,总是在一个三极管导通的时候另一个截止.

开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内).

单独的位设置或位清除

当对GPIOx_ODR 的个别位编程时,软件不需要禁止中断:在单次APB2写操作里,可以只更改一个或多个位。

这是通过对“置位/ 复位寄存器”(GPIOx_BSRR ,复位是GPIOx_BRR) 中想要更改的位写’1’来实现的。没被选择的位将不被更改。

外部中断/唤醒线

所有端口都有外部中断能力。为了使用外部中断线,端口必须配置成输入模式。

复用功能(AF)

使用默认复用功能前必须对端口位配置寄存器编程。

●对于复用的输入功能,端口必须配置成输入模式(浮空、上拉或下拉)且输入引脚必须由外部驱动

注意:也可以通过软件来模拟复用功能输入引脚,这种模拟可以通过对GPIO控制器编程来实现。此时,端口应当被设置为复用功能输出模式。显然,这时相应的引脚不再由外部驱动,而是通过GPIO控制器由软件来驱动。

●对于复用输出功能,端口必须配置成复用功能输出模式(推挽或开漏)。

●对于双向复用功能,端口位必须配置复用功能输出模式(推挽或开漏)。这时,输入驱动器被配置成浮空输入模式。

如果把端口配置成复用输出功能,则引脚和输出寄存器断开,并和片上外设的输出信号连接。

如果软件把一个GPIO脚配置成复用输出功能,但是外设没有被激活,它的输出将不确定。

输入配置

当I/O 端口配置为输入时:

●输出缓冲器被禁止

●施密特触发输入被激活

●根据输入配置(上拉,下拉或浮动)的不同,弱上拉和下拉电阻被连接

●出现在I/O 脚上的数据在每个APB2时钟被采样到输入数据寄存器

●对输入数据寄存器的读访问可得到I/O 状态

输出配置

当I/O 端口被配置为输出时:

●输出缓冲器被激活

─开漏模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将端口置于高阻状态(P-MOS从不被激活)。

─推挽模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将激活P-MOS。

●施密特触发输入被激活

●弱上拉和下拉电阻被禁止

●出现在I/O 脚上的数据在每个APB2时钟被采样到输入数据寄存器

●在开漏模式时,对输入数据寄存器的读访问可得到I/O 状态

●在推挽式模式时,对输出数据寄存器的读访问得到最后一次写的值。

复用功能配置

当I/O 端口被配置为复用功能时:

●在开漏或推挽式配置中,输出缓冲器被打开

●内置外设的信号驱动输出缓冲器(复用功能输出)

●施密特触发输入被激活

●弱上拉和下拉电阻被禁止

●在每个APB2时钟周期,出现在I/O 脚上的数据被采样到输入数据寄存器

●开漏模式时,读输入数据寄存器时可得到I/O 口状态

●在推挽模式时,读输出数据寄存器时可得到最后一次写的值。

一组复用功能I/O 寄存器允许用户把一些复用功能重新映象到不同的引脚。

模拟输入配置

当I/O 端口被配置为模拟输入配置时:

●输出缓冲器被禁止;

●禁止施密特触发输入,实现了每个模拟I/O 引脚上的零消耗。施密特触发输出值被强置为’0’;

●弱上拉和下拉电阻被禁止;

●读取输入数据寄存器时数值为’0’。

复用功能I/O 和调试配置(AFIO)

为了优化64脚或100 脚封装的外设数目,可以把一些复用功能重新映射到其他引脚上。设置复用重映射和调试I/O 配置寄存器(AFIO_MAPR) 实现引脚的重新映射。这时,复用功能不再映射到它们的原始分配上。

复用表参看手册

GPIO代码分析

外设库

typedef struct

{

uint16_t GPIO_Pin;

GPIOSpeed_TypeDef GPIO_Speed;

GPIOMode_TypeDef GPIO_Mode;

}GPIO_InitTypeDef;

typedef struct

{

__IO uint32_t CRL;

__IO uint32_t CRH;

__IO uint32_t IDR;

__IO uint32_t ODR;

__IO uint32_t BSRR;

__IO uint32_t BRR;

__IO uint32_t LCKR;

} GPIO_TypeDef;

typedef struct

{

__IO uint32_t EVCR;

__IO uint32_t MAPR;

__IO uint32_t EXTICR[4];

uint32_t RESERVED0;

__IO uint32_t MAPR2;

} AFIO_TypeDef;

主要函数分析

void GPIO_DeInit(GPIO_TypeDef* GPIOx) 恢复默认设置

{

/* Check the parameters */

assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

如果需要初始化GPIOA,则先enbale A端口,然后关闭A端口

if (GPIOx == GPIOA)

{

RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, ENABLE);

RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, DISABLE);

}

}

同理,先启用,然后关闭

void GPIO_AFIODeInit(void) 恢复默认设置

{

RCC_APB2PeriphResetCmd(RCC_APB2Periph_AFIO, ENABLE);

RCC_APB2PeriphResetCmd(RCC_APB2Periph_AFIO, DISABLE);

}

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

{

uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00; uint32_t tmpreg = 0x00, pinmask = 0x00;

/* Check the parameters */

assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));

assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));

/*---------------------------- GPIO Mode Configuration -----------------------*/ currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F); typedef struct

{

uint16_t GPIO_Pin;

GPIOSpeed_TypeDef GPIO_Speed;

GPIOMode_TypeDef GPIO_Mode;

}GPIO_InitTypeDef;

typedef enum

{ GPIO_Mode_AIN = 0x0,

GPIO_Mode_IN_FLOATING = 0x04,

GPIO_Mode_IPD = 0x28,

GPIO_Mode_IPU = 0x48,

GPIO_Mode_Out_OD = 0x14, 输出都是1X

GPIO_Mode_Out_PP = 0x10,

GPIO_Mode_AF_OD = 0x1C,

GPIO_Mode_AF_PP = 0x18

}GPIOMode_TypeDef;

判断是不是1X,如果是则是输出类型。

if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)

{

/* Check the parameters */

assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));

/* Output mode */

输出模式下,只要把速度加上就可以了,2,3位上已经表示了CNFX的类型

currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;

}

/*---------------------------- GPIO CRL Configuration ------------------------*/

/* Configure the eight low port pins */ 把引脚数取出来,然后移位,然后再配置

if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)

{

tmpreg = GPIOx->CRL;

for (pinpos = 0x00; pinpos < 0x08; pinpos++)

{

pos = ((uint32_t)0x01) << pinpos;

/* Get the port pins position */

currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;

if (currentpin == pos)

{

pos = pinpos << 2;

/* Clear the corresponding low control register bits */

pinmask = ((uint32_t)0x0F) << pos;

tmpreg &= ~pinmask;

/* Write the mode configuration in the corresponding bits */

tmpreg |= (currentmode << pos);

/* Reset the corresponding ODR bit */

对于上下拉的输入,需要对PXODR进行控制,下拉为0,上拉为1.

IPD和IPU设为2X和4X,因为设为1X与输出相同了,但是也不能是0X,0X的话,IPU 与IPD都是08,就无法区分了。因此采用了目前的机制

if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)

{

GPIOx->BRR = (((uint32_t)0x01) << pinpos);

}

else

{

/* Set the corresponding ODR bit */

if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)

{

GPIOx->BSRR = (((uint32_t)0x01) << pinpos);

}

}

}

}

GPIOx->CRL = tmpreg;

}

/*---------------------------- GPIO CRH Configuration ------------------------*/ /* Configure the eight high port pins */

if (GPIO_InitStruct->GPIO_Pin > 0x00FF)

{

tmpreg = GPIOx->CRH;

for (pinpos = 0x00; pinpos < 0x08; pinpos++)

{

pos = (((uint32_t)0x01) << (pinpos + 0x08));

/* Get the port pins position */

currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);

if (currentpin == pos)

{

pos = pinpos << 2;

/* Clear the corresponding high control register bits */

pinmask = ((uint32_t)0x0F) << pos;

tmpreg &= ~pinmask;

/* Write the mode configuration in the corresponding bits */

tmpreg |= (currentmode << pos);

/* Reset the corresponding ODR bit */

if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)

{

GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));

}

/* Set the corresponding ODR bit */

if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)

{

GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));

}

}

}

GPIOx->CRH = tmpreg;

}

}

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState)

{

uint32_t tmp = 0x00, tmp1 = 0x00, tmpreg = 0x00, tmpmask = 0x00;

/* Check the parameters */

assert_param(IS_GPIO_REMAP(GPIO_Remap));

assert_param(IS_FUNCTIONAL_STATE(NewState));

看看ramap的地址是多少,

if((GPIO_Remap & 0x80000000) == 0x80000000)

{

tmpreg = AFIO->MAPR2;

}

else

{

tmpreg = AFIO->MAPR;

}

tmpmask = (GPIO_Remap & DBGAFR_POSITION_MASK) >> 0x10;

tmp = GPIO_Remap & LSB_MASK;

if ((GPIO_Remap & (DBGAFR_LOCATION_MASK | DBGAFR_NUMBITS_MASK)) == (DBGAFR_LOCATION_MASK | DBGAFR_NUMBITS_MASK))

{

tmpreg &= DBGAFR_SWJCFG_MASK;

AFIO->MAPR &= DBGAFR_SWJCFG_MASK;

}

else if ((GPIO_Remap & DBGAFR_NUMBITS_MASK) == DBGAFR_NUMBITS_MASK) {

tmp1 = ((uint32_t)0x03) << tmpmask;

tmpreg &= ~tmp1;

tmpreg |= ~DBGAFR_SWJCFG_MASK;

}

else

{

tmpreg &= ~(tmp << ((GPIO_Remap >> 0x15)*0x10));

tmpreg |= ~DBGAFR_SWJCFG_MASK;

}

if (NewState != DISABLE)

{

tmpreg |= (tmp << ((GPIO_Remap >> 0x15)*0x10));

}

Tmpreg中保存的是最终要写进寄存器的值

if((GPIO_Remap & 0x80000000) == 0x80000000)

{

AFIO->MAPR2 = tmpreg;

}

else

{

AFIO->MAPR = tmpreg;

}

}

GPIO应用实例

void LED_Config(void){

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD , ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //设置pin5

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //口线翻转速度为50MHz GPIO_Init(GPIOB, &GPIO_InitStructure); PB的5引脚进行设置

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_3; //LED2, LED3 V7 V8

GPIO_Init(GPIOD, &GPIO_InitStructure); PD的6,3引脚也设置为50MHZ与推挽输出

}

例:

/* I2C1_SCL on PB.08, I2C1_SDA on PB.09 */

GPIO_PinRemapConfig(GPIO_Remap_I2C1, ENABLE); 这样就可以启用I2C1的引脚定义为PB8,PB9

库函数

相关文档