GPIO简介: stm32f103中的GPIO电路结构:

保护电路:输入电压大于3v3,保护二极管从输入向3v3放电;输入电压小于0v,保护二极管从0v向输入放电;输入介于0v-3v3,保护二极管不放电,正常工作。
上/下拉电阻:当引脚处于悬空状态时,引脚电位极易受到外界扰动而改变。在悬空时,上/下拉电阻可以保持设置的电位。上/下拉电阻阻值较大,为弱上/下拉。
下图是施密特触发器的工作原理:

GPIO工作模式(GPIO_InitStructure.GPIO_Mode): GPIO输出速度(GPIO_InitStructure.GPIO_Speed):
可以限制gpio引脚输出时的最大翻转速度,用来保证稳定性和低功耗。
点灯(先直接上代码):

#include "stm32f10x.h"                  // Device header

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    // 开启APB2外设时钟
    
    GPIO_InitTypeDef GPIO_InitStructure;
    // GPIO_InitTypeDef类型的结构体,名为GPIO_InitStructure,这是官方推荐用名
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    // 13引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    // 模式:universal push-pull output (通用推挽输出)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    // 速度50Mhz
    
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    // 初始化GPIOC
    
// GPIO_SetBits(GPIOC, GPIO_Pin_13);   // 置高电平
    GPIO_ResetBits(GPIOC, GPIO_Pin_13); // 置低电平
    // 看你的led另一个脚接的正极还是负极来定,此处led另一脚接正
    
    while(1);
    // 主循环保持程序运行,不断执行空操作
}

操作stm32的GPIO需要三个步骤:

1. 使用RCC开启GPIO的时钟。
2. 使用GPIO_Init()函数初始化GPIO(一个结构体对象)。
3. 使用输入/输出函数控制GPIO口。

常用的stm32f10x_rcc.h中的函数:

RCC_AHBPeriphClockCmd()、RCC_APB2PeriphClockCmd()、RCC_APB1PeriphClockCmd() 等

AHB、APB2、APB1总线:

不同的外设挂载在不同的总线上。写代码时想找那个外设位于哪个总线,可以看stm32f10x_rcc.h大概1200行的位置,其中第二个参数就是外设名。
APB:Advanced Peripheral Bus(先进外设总线),用于挂载一般外设。
AHB:Advanced High Performance Bus(先进高性能总线)。
AHB总线(一般频率72Mhz)性能高于APB总线,而APB2(一般频率72Mhz)性能又高于APB1(一般频率36Mhz)总线,所以更重要的外设挂载在APB2上,但实际难以感受到速度区别。
具体如下图:

有关GPIO的操作:

可以查看stm32f10x_gpio.h的约350行以后,全是gpio操作的函数头部。具体定义查看stm32f10x_gpio.c。
常用GPIO操作:
void GPIO_Init(GPIO_TypeDef* GPIOx, 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);

丝滑小细节 —— 在右键选择 goto definition of 'GPIO_Pin' 时为什么打开member项:

对于GPIO_InitStructure.GPIO_Pin这个表达式,GPIO_Pin是GPIO_InitTypeDef结构体的一个成员。
因此,在选择“Go to Definition”时,你应该关注的是GPIO_Pin作为结构体成员的定义(member),而不是作为某个函数的参数(parameter)。

GPIO操作函数详解:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 
将指定GPIO的指定IO口设置为高电平,第一个参数为GPIOX(X:A to G),第二个参数是具体的GPIO具体的引脚的结构体。

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
(同上),但设置为低电平。

void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
其实作用就是上面两个的集合。前两个参数同上。第三个参数为设置高/低电平。第三个参数为 Bit_SET 置高电平,Bit_RESET 置低电平。

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
第一个参数同上。第二个参数为十六进制值(2字节,16位),其中0代表置低电平,1代表置高电平。其中低位为0号口,高位为15号口。
举例 GPIO_Write(GPIOA, 0x5555); 就会把GPIOA的0号口置高,……,15号口置低(二进制 0101 0101 0101 0101 = 十六进制 5555)。

轻触开关(数字量输入):

轻触开关每次按下和松开都有5-20毫秒的抖动期,期间IO脚会收到多次电平变化,必须要消抖。
软件消抖:延时。
硬件消抖法其一:开关两端并联电容。由ai计算,抖动期取最大20ms,电压取0v与3v3,上拉电阻取10k,时间系数取5倍的情况下,电容大小应该是1uf。
设置引脚B12为按键(其余代码同初始化LED):
……
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;    // 设置引脚模式为上拉(弱上拉输入)
……
GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);      // 读取B12按下的值,数据类型uint8_t