1. 输入模式
1.1 四种输入模式:
GPIO共有四种输入模式:
- 输入浮空
- 输入上拉
- 输入下拉
- 模拟输入
1.2 相关寄存器
- GPIOx_MODER:端口模式寄存器
- GPIOx_
2. 按键输入检测
对于按键输入检测,首先需要确定按键的状态。
- 对于按下后为高电平的按键,则平时为低电平,设置端口为下拉输入,检测高电平。
- 对于按下后为低电平的按键,则平时为高电平,设置端口为上拉输入,检测低电平。
在STM32F407 Discovery板载资源中,按键的形式为第一种,因此需要设置为输入下拉(PuPd_DOWN)
3. 相关操作
-
库函数读取IO输入电平
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
-
寄存器读取IO输入电平
GPIOx_IDR:端口输入寄存器GPIOx->IDR&GPIO_Pin_x
-
位带操作
Bit-Banding简称位带。支持位带操作后,可以使用普通的加载存储指令来对单一的bit进行读写操作。
在STM32中不能直接操作寄存器的某一个Bit位。例如单独控制PA端口输出数据寄存器中的ODR的位15:0只能以字(16位)的形式进行操作。
为了能直接操作ODR这类的Bit位,在Cortex内核中开辟了一块地址区域:位带别名 ,可以将ODR这类Bit位(位带区)映射到(位带别名区),只需要操作映射后的位带别名区的地址就可以实现操作ODR的Bit位了。- 位带区:支持位带操作的地址区域
- 位带别名:对别名地址的访问会最终作用到位带区上
- 位带区->位带别名区计算公式:
位带操作的主要目的:通过Bit位地址(A)计算得到位带别名区地址(AliasAddr)。- SRAM计算方式:
AliasAddr = 0 x 22000000 + ( ( A‐ 0 x 20000000 ) × 8 + n ) × 4 = 0 x 42000000 + ( A − 0 x 20000000 ) × 32 + n × 4 \text{AliasAddr}= 0\text{x}22000000 + ((\text{A}‐0\text{x}20000000)\times8+n)\times4=0\text{x}42000000+( \text{A}- 0\text{x}20000000)\times32 + n\times4 AliasAddr=0x22000000+((A‐0x20000000)×8+n)×4=0x42000000+(A−0x20000000)×32+n×4 - 外设:
AliasAddr = 0 x 42000000 + ( ( A‐ 0 x 40000000 ) × 8 + n ) × 4 = 0 x 42000000 + ( A − 0 x 40000000 ) × 32 + n × 4 \text{AliasAddr}= 0\text{x}42000000 + ((\text{A}‐0\text{x}40000000)\times8+n)\times4= 0\text{x}42000000+( \text{A}- 0\text{x}40000000)\times32 + n\times4 AliasAddr=0x42000000+((A‐0x40000000)×8+n)×4=0x42000000+(A−0x40000000)×32+n×4
- SRAM计算方式:
由此可以对IO进行
- 读取:PXin(n) ⇒ \Rightarrow ⇒读取端口X的第n位输入,返回端口输入的值
- 写入:PXout(n)=M ⇒ \Rightarrow ⇒对端口X的第n位进行写入值M
//计算映射地址 #define BITBAND(addr,bitnum) ((addr&0xF00000000)+0x20000000+((addr&0xFFFFF)<<5)+(bitnum<<2)) //访问映射后的地址 #define MEM_ADDR(addr) *((volatile unsigned long*)(addr)) ' //由addr和bitnum计算出映射后的地址,然后访问 #define BIT_ADDR(addr,bitnum) MEM_ADDR(BITBAND(addr,bitnum)) // IO口地址映射 #define GPIOA_ODR_Addr (GPIOA_BASE+20) //0x40020014 #define GPIOB_ODR_Addr (GPIOB_BASE+20) //0x40020414 #define GPIOC_ODR_Addr (GPIOC_BASE+20) //0x40020814 #define GPIOD_ODR_Addr (GPIOD_BASE+20) //0x40020C14 #define GPIOE_ODR_Addr (GPIOE_BASE+20) //0x40021014 #define GPIOF_ODR_Addr (GPIOF_BASE+20) //0x40021414 #define GPIOG_ODR_Addr (GPIOG_BASE+20) //0x40021814 #define GPIOH_ODR_Addr (GPIOH_BASE+20) //0x40021C14 #define GPIOI_ODR_Addr (GPIOI_BASE+20) //0x40022014 #define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010 #define GPIOB_IDR_Addr (GPIOB_BASE+16) //0x40020410 #define GPIOC_IDR_Addr (GPIOC_BASE+16) //0x40020810 #define GPIOD_IDR_Addr (GPIOD_BASE+16) //0x40020C10 #define GPIOE_IDR_Addr (GPIOE_BASE+16) //0x40021010 #define GPIOF_IDR_Addr (GPIOF_BASE+16) //0x40021410 #define GPIOG_IDR_Addr (GPIOG_BASE+16) //0x40021810 #define GPIOH_IDR_Addr (GPIOH_BASE+16) //0x40021C10 #define GPIOI_IDR_Addr (GPIOI_BASE+16) //0x40022010 //IO口操作,只对单一的IO口! //确保n的值小于16! #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出 #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出 #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入 #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出 #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入 #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出 #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入 #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出 #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入 #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出 #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入 #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出 #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入 #define PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //输出 #define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //输入 #define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //输出 #define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //输入
4. 实验验证
使用STM32F407Discovery套件,对端口输入和输出进行实验验证。首先需要初始化外设,分别是LED和Key。其中LED为PD13
// 挂载GPIOD的时钟
RCC->AHB1ENR|=1<<3;
// 配置GPIOD
// 普通输出模式
GPIOD->MODER&=~(3<<(13*2));
GPIOD->MODER|=(1<<(13*2)); //PD13
// 高速输出
GPIOD->OSPEEDR|=(3<<(13*2));
// 推挽输出
GPIOD->OTYPER|=(0<<13);
// 输出上拉
GPIOD->PUPDR|=(1<<(13*2));
Key为PA0
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIOInitStructure;
GPIOInitStructure.GPIO_Mode=GPIO_Mode_IN;
GPIOInitStructure.GPIO_Pin=GPIO_Pin_0;//Pin0
GPIOInitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;
GPIOInitStructure.GPIO_Speed=GPIO_Speed_100MHz;
GPIO_Init(GPIOA,&GPIOInitStructure);
编写按键扫描函数,使用库函数进行端口读入操作
uint8_t KeyScan(void)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)!=0)// 有按键按下
{
delay_ms(20); // 延时消抖
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)!=0)
{
return 1;
}
return 0;
}
return 0;
}
主函数
int main(void)
{
LED_Init();
Key_Init();
while(1)
{
if(KeyScan())// 有按键按下
{
GPIOD->BSRRL|=(1<<13);
}
else
{
GPIOD->BSRRH|=(1<<13);
}
delay_ms(200);// 为了让按键按下后LED点亮的效果更明显
}
}