一、写在前面:

之前上过嵌入式的课程,也曾用rt1052/64把别人的程序编编改改,但对于单片机的理解仅停留在胎教的阶段。此次利用老师给的stm32f4(探索者)开发板,对单片机进行一个全面的学习。

   该文章为学习笔记,内容主要来自《Cortex M3与M4权威指南》、《STM32F4xx中文参考手册》、《STM32F4开发指南-寄存器版本_V1.2》、《STM32F4开发指南-库函数版本_V1.2》、正点原子的教学视频及网络。环境选择Keil uVision5。

        由于我编程和模电水平确实较差,望大家多批评指正了

二、GPIO基本情况

1.概述

   GPIO(General-purpose input/output),通用型输入输出的简称。既然一个引脚可以用于输入、输出或其他特殊功能,那么一定有寄存器用来选择这些功能。对于输入,一定可以通过读取某个寄存器来确定引脚电位的高低;对于输出,一定可以通过写入某个寄存器来让这个引脚输出高电位或者低电位;而对于其他特殊功能,则有另外的寄存器来控制它们。

2.引脚说明

     ①一共有7组IO口:GPIOA—GPIOG

        ②每组IO口有16个IO:GPIOA_0—GPIOA_15

        ③一共7*16=112个IO

        ④所有IO口都可以作为中断输入

3.GPIO工作方式

 图1、2  5V容忍I/O端口位的基本结构

(1)4种输入模式

  输入浮空(input floating)

逻辑器件与引脚即不接高电平,也不接低电平,电压不确定

   ②输入上拉(input pull-up)

将不确定的信号通过一个电阻嵌位在高电平,上拉电阻同时可起到限流作用,IO口的常态为高电平

 ③输入下拉(input pull-down)

把电压拉低到GND,IO口的常态为低电平

   ④模拟功能(analog)

关闭施密特触发器,将电压信号传送到片上外设模块,不接上下拉电阻

(2)4种输出模式

  ①带上拉或下拉的开漏输出(output open-drain) 

相当于三极管的集电极

                开漏输出只可以输出强低电平 ,高电平靠外部电阻拉高

                利用外部电路的驱动能力,减少IC(integrated circuit)内部的驱动。驱动电流从外部的VCC流出,IC内部仅需很小的栅极驱动电流

        ②带上拉或下拉的开漏复用功能(alternate function open-drain)

                可同时当作普通GPIO及内部外设(片上外设)控制器的引脚来使用

                I/O引脚通过一个复用器连接到板载外设,该复用器一次仅允许一个外设的复用功能(AF)连接到I/O引脚,确保共用同一个I/O引脚的外设之间不会发生冲突

        ③带上拉或下拉的推挽输出(output push-pull)

                推挽输出可输出强高低电平,连接数字器件。

                推挽结构一般指两个三极管分别受两互补信号的控制,总是一个导通一个截止

                推挽电路是两个参数一样的三极管以推挽形式置于电路中,每次只有一个导通,导通功耗小、效率高。输出即可向负载灌电流,也可从负载抽取电流。因此可提高电路的负载能力以及开关速度。

        ④带上拉或下拉的推挽复用功能(alternate function push-pull)

               同②

        关于推挽输出和开漏输出可用图3来概括:


6ebd493549d90459cb6e7&690

  图3  左为推挽、右为开漏

(3)4种最大输出速度

  2MHz / 25MHz / 50MHz 100MHz

(4)主要特性

受控 I/O 多达 16 个

        输出状态:推挽或开漏 + 上拉/下拉

        从输出数据寄存器 (GPIOx_ODR) 或外设(复用功能输出)输出数据

        可为每个 I/O 选择不同的速度

        输入状态:浮空、上拉/下拉、模拟

        将数据输入到输入数据寄存器 (GPIOx_IDR) 或外设(复用功能输入)

        置位和复位寄存器 (GPIOx_BSRR),对 GPIOx_ODR 具有按位写权限

        锁定机制 (GPIOx_LCKR),可冻结 I/O 配置

        模拟功能

        复用功能输入/输出选择寄存器(一个 I/O 最多可具有 16 个复用功能)

        快速翻转(toggle),每次翻转最快只需要两个时钟周期

        引脚复用非常灵活,允许将 I/O 引脚用作 GPIO 或多种外设功能中的一种

4.GPIO相关配置寄存器

每组IO口含下面10个寄存器,即10个寄存器可以控制一组GPIO的16个IO口。

        均为32位

        每个通用 I/O 端口包括:

                4 个 32 位配置寄存器(GPIOx_MODER、GPIOx_OTYPER、GPIOx_OSPEEDR 和GPIOx_PUPDR)

                2 个 32 位数据寄存器(GPIOx_IDR 和GPIOx_ODR)

                1 个 32 位置位/复位寄存器 (GPIOx_BSRR)

                1 个 32 位锁定寄存器 (GPIOx_LCKR)

                2 个 32 位复用功能选择寄存器(GPIOx_AFRH 和 GPIOx_AFRL)

三、GPIO的那一堆寄存器

1.端口模式寄存器 (GPIOx_MODER)

GPIO port mode register

MODER寄存器每2位控制一个IO。32个位控制一组IO的16个IO

        00:输入(复位状态)

        01:通用输出状态

        10:复用功能模式

        11:模拟模式

2.端口输出类型寄存器(GPIOx_OTYPER)

GPIO port output type register

位31-16保留,必须保持复位值。

        位15-0 端口x配置位,每位控制一个IO  0:输出推挽(复位状态)1:输出开漏

3.端口输出速度寄存器(GPIOx_OSPEEDR)

GPIO port output speed register

   每2位控制一个IO口。32位控制一组IO口的16个IO

        00:2MHz(低速)

        01:25MHz(中速)

        10:50MHz(快速)

        11:30pF时为100MHz(高速)【15pF时为80MHz(高速)】        皮法 (pF)

4.端口上拉/下拉寄存器(GPIOx_PUPDR)

GPIO port pull-up/pull-down register

00:无上拉或下拉

        01:上拉

        10:下拉

        11:保留

5.端口输入数据寄存器(GPIOx_IDR)

GPIO port input data register

31:16保留,必须保持复位值

        15:0这些位为只读形式,只能在字模式下访问。它们包含相应I/O端口的输入值

6.端口输出数据寄存器(GPIOx_ODR)

GPIO port output data register

  31:16保留,必须保持复位值

        15:0  通过写入该寄存器,可分别对ODR位进行置位(1)和复位(0)

7.端口置位/复位寄存器(GPIOx_BSRR)

GPIO port bit set/reset register

31:16BRy:用于端口复位

                这些位为只写形式,只能在字、半字或字节模式下访问。读取这些位可返回值0x0000

                0:不会对相应的ODRx位执行任何操作

                1:对相应ODRx位进行复位

        15:0  BSy:用于端口置位

                这些位为只写形式,只能在字、半字或字节模式下访问。读取这些位可返回值0x0000

                0:不会对相应的ODRx位执行任何操作

                1:对相应ODRx位进行置位

        如果同时对BSx和BRx置位,则BSx的优先级更高

8.GPIO端口配置锁定寄存器 (GPIOx_LCKR) (x = A..I)

GPIO port configuration lock register

9.GPIO 复用功能低位寄存器 (GPIOx_AFRL) (x = A..I)

GPIO alternate function low register

   图4  引脚0到7所用复用器

31:0ADRLy:端口x位y的复用功能选择(Alternate function selection for port x bit y) (y = 0..7) 这些位通过软件写入,用于配置复用功能 I/O。AFRLy 选择:

0000: AF0
        0001: AF1
        0010: AF2
        0011: AF3
        0100: AF4
        0101: AF5
        0110: AF6
        0111: AF7
        1000: AF8
        1001: AF9
        1010: AF10
        1011: AF11
        1100: AF12
        1101: AF13
        1110: AF14
        1111: AF15

10.GPIO 复用功能高位寄存器 (GPIOx_AFRH) (x = A..I)

GPIO alternate function high register

  图5  引脚8到15所用复用器

功能同复用功能低位寄存器。

四、GPIO小实验:跑马灯

注:任何方式操作IO口,都必须先使能相应的IO口时钟

                go to defination的快捷键为F12

1.硬件

     GPIO:推挽输出(上拉)

2.库函数版(library function)

(1)重要函数

        1个初始化函数:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

                作用:初始化一个或者多个IO口(同一组)的工作模式,输出类型,速度以及上下拉方式。也就是一组IO口的4个配置寄存器。

                (GPIOx->MODER, GPIOx->OSPEEDR,GPIOx->OTYPER,GPIOx->PUPDR)

  2个读取输入电平函数:

 uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

                作用:读取某个GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。

                uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

                作用:读取某组GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。
 2个读取输出电平函数:

  uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

                作用:读取某个GPIO的输出电平。实际操作的是GPIO_ODR寄存器。

                uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

                作用:读取某组GPIO的输出电平。实际操作的是GPIO_ODR寄存器。

     4个设置输出电平函数:

     void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

                作用:设置某个IO口输出为高电平(1)。实际操作BSRRL寄存器

                void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

                作用:设置某个IO口输出为低电平(0)。实际操作的BSRRH寄存器。

                void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);

                void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

                这两个函数不常用,也是用来设置IO口输出电平。

 使能IO口时钟

 RCC_AHB1PeriphClockCmd();

(2)led.c

#include "led.h"
#include "stm32f4xx.h"
void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE); //IO口时钟使能
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10; //对结构体进行赋值
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    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); //置位
}

3.寄存器版(register)

(1)RCC AHB1 外设时钟使能寄存器 (RCC_AHB1ENR)

 RCC(Reset Clock Controller)

        RCC AHB1 peripheral clock enable register

(2)led.c

#include "stm32f4xx.h"
 
void LED_Init(void)
{
    RCC->AHB1ENR|= 1<<5; //IO口时钟使能
    //PF9
    GPIOF->MODER &= ~(3<<2*9); //通用输出
    GPIOF->MODER |= 1<<(2*9);
    GPIOF->OSPEEDR &= ~(3<<2*9); //50MHz
    GPIOF->OSPEEDR |= 2<<(2*9);
    GPIOF->OTYPER &= ~(1<<9); //推挽
    GPIOF->OTYPER |= (0<<9);
    GPIOF->PUPDR &= ~(3<<2*9); //上拉
    GPIOF->PUPDR |= 1<<(2*9);
    GPIOF->ODR |= 1<<9; //高电平
    //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;
}

4.位操作版(Bit-band operations):

(1)位带操作

 位带别名区(Bit Band alias address)把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

(2)步骤

  使能IO口时钟,调用RCC_AHB1PeriphClockCmd();

        初始化IO口模式

        位带操作

(3)led.c

#include "stm32f4xx.h"
#include "led.h"
#include "delay.h"
 
int main(void)
{
    delay_init(168); //初始化延时函数
    LED_Init();
    while(1)
    {
        PFout(9) = 1; //PFout()为IO口操作宏定义
        PFout(10) = 1;
        delay_ms(500);
 
        PFout(9) = 0;
        PFout(10) = 0;
        delay_ms(500);
    }    
}

本文结束