前言
时不可以苟遇,道不可以虚行。
一、GPIO 基本结构和工作方式
- IO口引脚
- stm32的大部分引脚除了当GPIO使用外,还可以复用为外设功能引脚(比如串口)
1、GPIO 的工作方式
- 四种输入模式:
- 输入浮空 (
_IN_FLOATING_
)- 输入上拉 (
In Pull Up:IPU
)- 输入下拉 (
In Pull Down:IPD
)- 模拟输入 (
AIN:Analog In)
- 四种输出模式:
- 开漏输出(带上拉或者下拉):
_Out_OD_(Out Open Drain)
- 开漏复用功能(带上拉或者下拉):
AF_OD
- 推挽式输出(带上拉或者下拉):
Out_PP(Out Push Pull)
,点灯- 推挽式复用功能(带上拉或者下拉):
AF_PP
- 四种最大速度:
- 2 MHz
- 25 MHz
- 50 MHz
- 100 MHz
- 推挽输出:可以输出强高低电平,连接数字器件
- 开漏输出:只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻,适合做电流型的驱动,其吸收电流的能力相对强(一般在 20 MA 以内)
二、GPIO寄存器说明
GPIO 相关配置寄存器:
3、STM32F4xx GPIO 引脚说明
- 端口复用
-
STM32F4 的大部分端口都具有复用功能。
所谓复用,就是一些端口不仅仅可以做为通用的 IO 口,还可以复用为一些外设引脚,比如:PA9、PA10 可以复用为 STM32F4 的串口 1 引脚。
-
作用:最大限度的利用端口资源。
- 所有 IO 口都可以作为中断输入
四、GPIO 库函数介绍
库函数代码:
main.c
:
#include "stm32f4xx.h"
#include "LED.h"
#include "delay.h"
/** 库函数版本 **/
int main(void)
{
delay_init(168);
LED_Init();
while(1)
{
GPIO_SetBits(GPIOF,GPIO_Pin_9);
GPIO_ResetBits(GPIOF,GPIO_Pin_10);
delay_ms(500);
GPIO_SetBits(GPIOF,GPIO_Pin_10);
GPIO_ResetBits(GPIOF,GPIO_Pin_9);
delay_ms(500);
}
}
LED.c
:
#include "LED.h"
/** 库函数版本 **/
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOF, &GPIO_InitStructure);
GPIO_SetBits(GPIOF, GPIO_Pin_9 | GPIO_Pin_10);
}
LED.h
:
#ifndef _LED_H_
#define _LED_H_
#include "stm32f4xx.h"
void LED_Init(void);
#endif
五、寄存器配置
寄存器代码:
main.c
:
#include "stm32f4xx.h"
#include "LED.h"
#include "delay.h"
/** 寄存器版本 **/
int main(void)
{
delay_init(168);
LED_Init();
while(1)
{
GPIOF->ODR &= 1 << 9; //PF9:0
GPIOF->ODR &= 1 << 10; //PF10:0
delay_ms(500);
GPIOF->ODR |= 1 << 9; //PF9:1
GPIOF->ODR |= 1 << 10; //PF10:1
delay_ms(500);
}
}
LED.c
:
#include "LED.h"
/** 寄存器版本 **/
void LED_Init(void)
{
RCC->AHB1ENR |= 1 << 5; // 1 左移 5 位,将寄存器的第五位设置为1,其他位不变
//PF9
// 11 = 3,左移18(2*9)位,然后再取反,将MODER寄存器的18、19位清零,其他位不变
GPIOF->MODER &= ~(3 << 2*9);
// 01(通用输出功能) = 1,左移18(2*9)位,将MODER寄存器的19、18位置为01,其他位不变
GPIOF->MODER |= 1 << (2*9);
// 11 = 3,左移18(2*9)位,然后再取反,将OSPEEDR寄存器的18、19位清零,其他位不变
GPIOF->OSPEEDR &= ~(3 << 2*9);
// 10(50MHz:快速) = 2,左移18(2*9)位,将OSPEEDR寄存器的19、18位置为10,其他位不变
GPIOF->OSPEEDR |= 2 << (2*9);
GPIOF->OTYPER &= ~(1 << 9);
GPIOF->OTYPER |= 0 << 9;
// 11 = 3,左移18(2*9)位,然后再取反,将PUPDR寄存器的18、19位清零,其他位不变
GPIOF->PUPDR &= ~(3 << 2*9);
// 01(上拉) = 1,左移18(2*9)位,将PUPDR寄存器的19、18位置为01,其他位不变
GPIOF->PUPDR |= 1 << (2*9);
//输出高低电平:使用ODR寄存器
GPIOF->ODR |= 1 << 9; //1
//GPIOF->ODR &= 1 << 9; //0
//PF10
GPIOF->MODER &= ~(3 << 2*10);
GPIOF->MODER |= 1 << (2*10);
GPIOF->OSPEEDR &= ~(3 << 2*10);
GPIOF->OSPEEDR |= 2 << (2*10);
GPIOF->OTYPER &= ~(1 << 10);
GPIOF->OTYPER |= 0 << 10;
GPIOF->PUPDR &= ~(3 << 2*10);
GPIOF->PUPDR |= 1 << (2*10);
GPIOF->ODR |= 1 << 10; //1
}
LED.h
:
#ifndef _LED_H_
#define _LED_H_
#include "stm32f4xx.h"
void LED_Init(void);
#endif
六、位操作
1、位操作基本原理
位带操作简单的说,就是 把每个比特膨胀为一个 32 位的字,当访问这些字的时候就达到了访问比特的目的,比如说 GPIO 的 ODR 寄存器有 32 个位,那么可以映射到 32 个地址上,我们去访问这 32 个地址就达到访问 32 个比特的目的。这样我们往某个地址写 1 就达到往对应比特位写 1 的目的,同样往某个地址写 0 就达到往对应的比特位写 0 的目的。
映射关系
- 位带区:支持位带操作的地址区
- 位带别名:对别名地址的访问最终作用到位带区的访问上
支持位带操作的两个内存区的范围是:
0x2000_0000 ~ 0x200F_FFFF
(SRAM 区中的最低 1 MB)0x4000_0000 ~ 0x400F_FFFF
(片上外设区中最低 1 MB)
对于 SRAM 位带区的某个比特,记它所在字节地址为 A,位序号为 n(0 <= n <= 7),则该比特在别名区的地址为:
对于 片上外设 位带区的某个比特,记它所在字节地址为 A,位序号为 n(0 <= n <= 7),则该比特在别名区的地址为:
位带操作代码:
main.c
:
#include "stm32f4xx.h"
#include "LED.h"
#include "delay.h"
#include "sys.h"
/** 位带操作版本 **/
int main(void)
{
delay_init(168);
LED_Init();
while(1)
{
PFout(9) = 1;
PFout(10) = 1;
delay_ms(500);
PFout(9) = 0;
PFout(10) = 0;
delay_ms(500);
}
}