GPIO口的使用:
1.GPIO和AFIO 全系列支持
GPIO寄存器
(1)两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH);
(2)两个32位数据寄存器(GPIOx_IDR,GPIOx_ODR);
(3)一个32位置为/复位寄存器(GPIOx_BSRR);
(4)一个16位复位寄存器(GPIOx_BRR);
(5)一个32位锁存器(GPIOx_LCKR);
输入配置
当I/O端口配置为输入时:
● 输出缓冲器被禁止
● 施密特触发输入被激活
● 根据输入配置(上拉,下拉或浮动)的不同,弱上拉和下拉电阻被连接
● 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器
● 对输入数据寄存器的读访问可得到I/O状态
输出配置
当I/O端口被配置为输出时:
● 输出缓冲器被激活
─ 开漏模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将端口置于高阻状态(PMOS从不被激活)。
─ 推挽模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将激活P-MOS。
● 施密特触发输入被激活
● 弱上拉和下拉电阻被禁止
● 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器
● 在开漏模式时,对输入数据寄存器的读访问可得到I/O状态
● 在推挽式模式时,对输出数据寄存器的读访问得到最后一次写的值。
STM32中的配置寄存器在固件函数库中早已生成,因此无需再对寄存器的每个设定写定义,而是直接调用关键字。这样我们可以不再关心寄存器的具体配置(因为那已经在固件配置好了);因此直观的从配置函数中去看,更能有效的提高。
GPIO相关的库函数如下,位于在“stm32f10x_gpio.h”
GPIO相关函数如下:
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
以下将逐个说明函数功能及注释说明:
·void GPIO_DeInit(GPIO_TypeDef* GPIOx);
该函数原型在"stm32f10x_gpio.C"当中,类似C++ 的注释说明如下:
* @brief Deinitializes the GPIOx peripheral registers to their default reset values.
* @param GPIOx: where x can be (A..G) to select the GPIO peripheral.
* @retval None
其中是为不同组的IO口进行寄存器值的初始化。
初始化语句如下:
“ RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, DISABLE);”
再追根溯源到这个函数,位于“stm32f10x_rcc.C”当中
"void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)"
{
assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
RCC->APB2RSTR |= RCC_APB2Periph;
}
else
{
RCC->APB2RSTR &= ~RCC_APB2Periph;
}
}
函数注释如下:
一目了然,即配置IO口时钟状态为使能或者失效。
当然在其中此函数作为一个初学实例还是值得深究的:
assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
assert_param(IS_FUNCTIONAL_STATE(NewState));
此处两句即类似于C++中的断言函数,作为函数运行的先决条件。这里将断言函数直接说明,在后续的实例中,仍旧会有使用到的地方。
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr) ((void)0)
#endif
#endif
若满足断言值为"1"的条件,否则判定失败输出 文件名和所在行。不为"0"返回0.
再返回“assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));”此句中。“IS_RCC_APB2_PERIPH”如下定义:
·#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00))
此处使用到的是AP2 进入该函数还可以看到AP2、AP1、AP三个高速时钟族的各项定义。姑且在这里认为是判定开启对应时钟前的时钟功能验证。
·#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))
只为考虑还是的形参是否是“DISABLE”or“ENABLE” 两个状态。
“ if (NewState != DISABLE)
{
RCC->APB2RSTR |= RCC_APB2Periph;
}
else
{
RCC->APB2RSTR &= ~RCC_APB2Periph;
}
”
而APB2RSTR则即将牵扯到RCC的设置问题,我们下一节再讲。
·void GPIO_AFIODeInit(void);功能复用,重新映射事件控制。
同样调用“RCC_APB2PeriphResetCmd”。也是串口初始化判断
·void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
寄存器手册中记为:根据GPIO_InitStruct中指定参数初始化外设GPIOx寄存器
不想在此处在赘述此函数,主要通过写寄存器的值来配置GPI0x,GPIO_pin,GPIO_Mode,GPIO_speed,以及写GPIO CRL/CRH寄存器。
·void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);被上一结构体调用
GPIO_Speed 描述
GPIO_Speed_10MHz 最高输出速率10MHz
GPIO_Speed_2MHz 最高输出速率2MHz
GPIO_Speed_50MHz 最高输出速率50MHz
GPIO_Mode_AIN 模拟输入
GPIO_Mode_IN_FLOATING 浮空输入
GPIO_Mode_IPD 下拉输入
GPIO_Mode_IPU 上拉输入
GPIO_Mode_Out_OD 开漏输出
GPIO_Mode_Out_PP 推挽输出
GPIO_Mode_AF_OD 复用开漏输出
GPIO_Mode_AF_PP 复用推挽输出
·
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);,
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
读取指定管脚输入/输出,读取管脚输入/输出数据值。 一个读取的是管脚的状态,而一个读取的输入or 输出数据寄存器的值。这一点要分清
·
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
"bitval must be Bit_RESET or Bit_SET“
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
“Portval为将写入数据寄存器的值”
设定/清除指定的数据位
·void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
锁存管脚寄存器,锁存指定GPIO组指定引脚。
·void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
配置GPIO为事件输出,其后我们来解决这个疑问。
·void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
此函数决定了IO口的重新映射,实际是IO复用功能的实现,GPIO_Remap选择输入引脚,NewState的配置值如下:GPIO_Remap_SPI1 SPI1复用功能映射
GPIO_Remap_I2C1 I2C1复用功能映射
GPIO_Remap_USART1 USART1复用功能映射
GPIO_PartialRemap_USART3 USART2复用功能映射
GPIO_FullRemap_USART3 USART3复用功能完全映射
GPIO_PartialRemap_TIM1 USART3复用功能部分映射
GPIO_FullRemap_TIM1 TIM1复用功能完全映射
GPIO_PartialRemap1_TIM2 TIM2复用功能部分映射1
GPIO_PartialRemap2_TIM2 TIM2复用功能部分映射2
GPIO_FullRemap_TIM2 TIM2复用功能完全映射
GPIO_PartialRemap_TIM3 TIM3复用功能部分映射
GPIO_FullRemap_TIM3 TIM3复用功能完全映射
GPIO_Remap_TIM4 TIM4复用功能映射
GPIO_Remap1_CAN CAN复用功能映射1
GPIO_Remap2_CAN CAN复用功能映射2
GPIO_Remap_PD01 PD01复用功能映射
GPIO_Remap_SWJ_NoJTRST 除JTRST外SWJ完全使能(JTAG+SW-DP)
GPIO_Remap_SWJ_JTAGDisable JTAG-DP失能+ SW-DP使能
GPIO_Remap_SWJ_Disable SWJ完全失能(JTAG+SW-DP)
每个功能在后面小节的应用中体现。
·void GPIO_EXTILineConfig(u8 GPIO_PortSource, u8 GPIO_PinSource)
GPIO配置为外部中断,两个值分别为端口值和引脚。
·void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface)
最后一个配置以太网接口。该函数只有两行语句。此处不作介绍。