STM32的I/O口可以由软件配置成如下8种模式:输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、推挽式复用功能及开漏复用功能。每个I/O口由7个寄存器来控制:配置模式的端口配置寄存器CRL和CRH(模式、速度);数据寄存器IDR和ODR;置位/复位寄存器BSRR;复位寄存器BRR;锁存寄存器LCKR。
I/O口模式:
| 通用输出 | 推挽输出(Push-Pull) | 可以输出高、低电平,连接数字器件 | |
| 开漏输出(Open-Drain) | 开漏引脚不连接外部的上拉电阻时,只能输出低电平;如果需要同时具备输出高电平的功能,则需要接上拉电阻 | ||
| 复用功能输出 | 复用功能推挽输出 | 片内外设功能(I2C的SCL,SDA) | GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用) |
| 复用功能开漏输出 | 片内外设功能(TX1,MOSI,MISO,SCK,SS) | ||
| 输入 | 模拟输入 | 应用ADC模拟输入,或者低功耗下省电 | |
| 浮空输入 | 可以做KEY识别,外部按键输入 | IO的电平状态是不确定,完全由外部输入决定 | |
| 下拉输入 | IO内部下拉电阻输入 | 不确定信号->低电平 | |
| 上拉输入 | IO内部上拉电阻输入 | 不确定信号->高电平 |
1.GPIO口配置步骤
①使能PORTx(x=A~G)
APB2外设时钟使能寄存器(RCC_APB2ENR)

置1开启。清0关闭。
8-2位使能GPIO G-A
Eg:RCC->APB2ENR| = 1 << 2; //使能PORTA时钟
②配置IO口模式 低8位(CRL) 高8位(CRH)
端口配置低寄存器(GPIOx_CRL) (x=A..E)


Eg:GPIOA->CRL |= 0x00000003; //PA0推挽输出
③配置端口输入和输出电平
端口输入数据寄存器(GPIOx_IDR) (x=A..E)

端口输出数据寄存器(GPIOx_ODR) (x=A..E)

Eg: GPIOA->ODR |= 1 <<8; //PA8输出高
2.GPIO配置相关寄存器
端口位设置/清除寄存器(GPIOx_BSRR) (x=A..E)

ODR寄存器只进行置1操作,不支持写0操作。用BSRR寄存器进行清除。
端口位清除寄存器(GPIOx_BRR) (x=A..E)


端口位清除寄存器(GPIOx_BRR) (x=A..E) 具体参看数据手册
当执行正确的写序列设置了位16(LCKK)时,该寄存器用来锁定端口位的配置。位[15:0]用于锁定GPIO端口的配置。在规定的写入操作期间,不能改变LCKP[15:0]。当对相应的端口位执行了LOCK序列后,在下次系统复位之前将不能再更改端口位的配置。 每个锁定位锁定控制寄存器(CRL, CRH)中相应的4个位。
3.LED

//led.c
//LED IO口初始化
void LED_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA时钟
RCC->APB2ENR|=1<<5; //使能PORTD时钟
GPIOA->CRH&=0XFFFFFFF0;
GPIOA->CRH|=0X00000003;//PA8推挽输出
GPIOA->ODR|=1<<8; //PA8输出高
GPIOD->CRL&=0XFFFFF0FF;
GPIOD->CRL|=0X00000300;//PD2推挽输出
GPIOD->ODR|=1<<2; //PD2输出高
}
//led.h
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED端口定义
#define LED0 PAout(8) // PA8
#define LED1 PDout(2) // PD2
void LED_Init(void); //初始化
#endif
//main.c
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
LED_Init(); //初始化与LED连接的硬件接口
while(1)
{
LED0=0;
LED1=1;
delay_ms(300);
LED0=1;
LED1=0;
delay_ms(300);
}
}

4.按键KEY输入

//key.c
#include "key.h"
#include "delay.h"
//按键初始化函数
//PA0 PA15 PC5设置成输入
void KEY_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA时钟
RCC->APB2ENR|=1<<4; //使能PORTC时钟
JTAG_Set(SWD_ENABLE); //关闭JTAG 开启SWD PA15占用了JTAG一个口
GPIOA->CRL&=0XFFFFFFF0; //PA0设置成输入
GPIOA->CRL|=0X00000008;
GPIOA->CRH&=0X0FFFFFFF; //PA15设置成输入
GPIOA->CRH|=0X80000000;
GPIOA->ODR|=1<<15; //PA15上拉 PA0默认下拉
GPIOC->CRL&=0XFF0FFFFF; //PC5设置成输入
GPIOC->CRL|=0X00800000;
GPIOC->ODR|=1<<5; //PC5上拉
}
//按键处理函数
//mode:0不支持连续按;1,支持连续按
//响应优先级KEY0>KEY1>WK_UP
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)
return KEY0_PRES;
else if(KEY1==0)
return KEY1_PRES;
else if(WK_UP==1)
return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&WK_UP==0)
key_up=1;
return 0;// 无按键按下
}
//key.h
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#define KEY0_PRES 1 //KEY0按下
#define KEY1_PRES 2 //KEY1按下
#define WKUP_PRES 3 //WK_UP按下
#define KEY0 PCin(5) //PC5
#define KEY1 PAin(15) //PA15
#define WK_UP PAin(0) //PA0 WK_UP
void KEY_Init(void);
u8 KEY_Scan(u8 mode);
#endif
//main.c
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
int main(void)
{
u8 t;
Stm32_Clock_Init(9);
delay_init(72);
LED_Init();
KEY_Init();
LED0 = 0;
while(1)
{
t=KEY_Scan(0);
switch(t)
{
case KEY0_PRES:
LED0=!LED0;
break;
case KEY1_PRES:
LED1=!LED1;
break;
case WKUP_PRES:
LED0=!LED0;
LED1=!LED1;
break;
default:
delay_ms(10);
}
}
}
来源:https://www.cnblogs.com/kuotian/p/5506564.html