2、STM32 GPIO外设详解

1、概述

  • GPIO外设是MCU(微控制处理器)最基本,最通用的端口,用于与外部设备进行交互,可通过指令配置多种工作模式,实现通信和数据采集等功能。

  • 通过对引脚(PIN)高低电平的控制,输出0/1,以及检测引脚电平状态来控制芯片功能

    • 电平一般拟采用TTL电平信号,大于2.4V就是高电平输出1.低于0.4V就是低电平输出0

    • 在这里插入图片描述

      • 电源引脚
        • VCC、VDD、VSS、VDDA、VSSA、VREF+等属于电源引脚
      • 晶振引脚 ,
        • PC14、PC15、PH0、PH1就属于晶振引脚,也可以作为其他的功能使用
      • 复位引脚
        • SET(置位) RESET(复位)
      • BOOT引脚:
        • BOOT0是专用引脚,BOOT1就属于功能引脚 (设置芯片的自举模式)
      • GPIO引脚
        • 芯片一共有144引脚,但是GPIO引脚有114个
      • 下载引脚
        • PA13、PA14、PB3、PB4等都属于下载引脚(JTAG、SWD)
    • 一般IO口引脚用(PIN)P开头,分为多个端口,PA ~ PH,每个端口有16个引脚,PA0 ~ PA15

    • 注意———芯片引脚有多个功能,但一般复位之后为GPIO功能

    • 接口介绍和描述(数据手册)

      • 在这里插入图片描述

        [!NOTE]

        PIN_Number 是指哪个型号的芯片有该引脚,通过引脚类型判断引脚属于哪一种并使用对应的代码操作

2、GPIO外设使用

那么该如何使用这些GPIO外设呢?

  • 根据官方提供STM32F40x/41x/427/437/429/439/401/411xE DSP and Standard Peripherals Library 文件(后续会上传)
    在这里插入图片描述

    找到如何使用这些外设库函数)[How to use the Library]点击进去查看,Peripheral initialization and configuration即可

  • 在这里插入图片描述

  • int main()
    {
    	//定义结构变量 
    	GPIO_InitTypeDef  GPIO_InitStructure;
    	//打开时钟
      	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
      	进行相关函数配置
      	//指定引脚开启
    	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_9;
    	//指定引脚输出模式
    	GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_OUT;
    	//指定引脚输出类型
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    	  //指定引脚输出速度
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    	//指定上拉/下拉电阻
    	GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_NOPULL;
    	//初始化
    	GPIO_Init(GPIOF,&GPIO_InitStructure);
      while(1)
    	{
    		/* Reset PF9 */
        GPIOF->BSRRH = GPIO_Pin_9 ;
    	}
    }
    
  1. 查看原理图,找到对应引脚
  2. 分析原理图以及数据手册,查看怎么控制该引脚
    • 了解一些常用物理器件,方便计算以及分析(电阻,电容)
    • 高电平导通还是低电平导通控制
  3. 进行程序设计,根据st公司提供的函数库(标准外设库+HAL库)的帮助手册进行学习 (Peripheral initialization and configuration)
  4. 具体步骤:
    • 定义外设结构体
    • 打开外设时钟
      • 目的:因为STM32属于低功耗的MCU,而为了降低功耗,所以STM32的MCU在复位之后默认会关闭绝大多数的外设的时钟,所以用户想要使用MCU内部的某个外设,就必须打开该外设的时钟(clock)。
      • 底层原理 :寄存器是由触发器构成,触发器必须要有时钟新信号参与
    • 但是应用函数接口时系统运行效率较低,所以需要了解底层原理,提高CPU运行效率

3、底层原理解释

  • 想要控制硬件,则需要控制硬件的寄存器
    • 寄存器则是用于暂时存储一组二进制数据
    • 32bit的寄存器由32个触发器组成,可以暂时存储32bit的数据
      • 触发器为时序逻辑电路,可存储0/1
      • 触发信号:电平触发,脉冲触发,边沿触发
      • 时钟相当于外设的开关,不打开时钟信号,寄存器则不能进行数据所存
        • 时钟信号则是由晶振产生
          在这里插入图片描述
  • STM32芯片有很多外设接口,但每个外设接口的功能和性能有所不能,ST公司为了降低功耗,则将不同性能的外设挂载到不同性能的总线上
    • AHB高级性能总线 168MHZ
    • APB1 高级外设总线 42MHZ
    • APB2 高级外设总线 84MHZ
    • 总线频率则影响 数据传输速率,系统响应时间,功耗与散热,系统稳定性等方面
    • 总线和外设需要匹配

4、寄存器控制编码

  • 优点 :可以提高程序运行效率,节约内存
  • 查找ST公司提供的中文参考手册
    • 目的:了解当前外设有哪些寄存器,以及寄存器使用流程
    • 如何分析寄存器
      • 端口模式寄存器GPIOx_MODEA 可知每个端口都有一个

在这里插入图片描述

图中每两个编号代表一个PIN口 PINA9则对应为18和19口,对其进行置位和复位操作

在这里插入图片描述

  • 推挽输出

    • 推挽输出是一种常见的输出结构,通常用于数字电路中。它由两个晶体管组成,一个负责拉高电平(P型晶体管),另一个负责拉低电平(N型晶体管)。推挽输出的特点是可以直接驱动高电平和低电平,输出阻抗较低,能够提供较强的驱动能力。
    • 推挽输出的优点包括: 能够直接输出高电平和低电平,无需外部上拉或下拉电阻。 输出阻抗低,驱动能力强,适合驱动负载较大的电路。
    • 响应速度快,适合高频应用。 推挽输出的缺点包括:
      • 不能直接与其他推挽输出连接,否则可能导致短路。 在需要开漏或开集输出的场合,推挽输出不适用。 开漏输出 开漏输出(或开集输出)是一种输出结构,通常用于需要多个设备共享同一信号线的场合。开漏输出只包含一个晶体管,通常为N型晶体管,负责拉低电平。开漏输出需要外部上拉电阻来提供高电平。
  • 开漏输出的优点包括:

    • 多个开漏输出可以连接在一起,通过“线与”逻辑实现多设备共享信号线。 适合用于I2C等总线协议,允许多个设备在同一总线上通信。
    • 输出电平可以通过外部上拉电阻调整,适应不同的电压需求。 开漏输出的缺点包括:
      - 需要外部上拉电阻,增加了电路复杂性。 输出高电平的驱动能力较弱,依赖于上拉电阻。 响应速度较慢,不适合高频应用。 推挽与开漏的比较
    • 驱动能力:推挽输出驱动能力强,开漏输出驱动能力较弱。 电路复杂性:推挽输出无需外部元件,开漏输出需要外部上拉电阻。
  • 应用场景:推挽输出适合单独驱动负载,开漏输出适合多设备共享信号线。 响应速度:推挽输出响应速度快,开漏输出响应速度较慢。 应用示例

  • 推挽输出常用于单片机GPIO引脚、LED驱动等场合。开漏输出常用于I2C、SMBus等总线协议,以及需要多设备共享信号线的场合。

在这里插入图片描述
在这里插入图片描述

  • 上拉和下拉电阻可以确保在引脚未连接外部设备时,引脚处于已知的电平状态,避免悬空状态导致的不可预测行为。
  • 上拉寄存器
    • 上拉寄存器用于在GPIO引脚上启用内部上拉电阻。当上拉电阻启用时,引脚在没有外部驱动的情况下会被拉至高电平(通常为VCC)。上拉电阻的典型值在几十千欧姆范围内。
  • 下拉寄存器
    • 下拉寄存器用于在GPIO引脚上启用内部下拉电阻。当下拉电阻启用时,引脚在没有外部驱动的情况下会被拉至低电平(通常为GND)。下拉电阻的典型值也在几十千欧姆范围内。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 寄存器地址:基地址➕偏移地址
  • 复位值:芯片复位之后寄存器默认值
  • 寄存器是32bit,每个寄存器分为16组,2bit为一组,一个端口下有16个引脚,每个引脚有4 中选择模式
  • 根据引脚编号找到寄存器比特位对其进行操作
    置1:xxx |=(1<<2y)
    置0:xxx &=~(1<<2y)

关键:怎么找到外设物理地址(根据中文参考手册)

  • 第四章内存映射表
    找到外设应黑色物理区

其中GPIO外设对应地址为0x4000_0000到0x6000_000地址,同时根据手册找到总线下对应的端口
在这里插入图片描述
通过端口物理地址后,结合上图中的偏移地址进而定位到初始化参数
通过对端口相关位进行置位/复位控制

  • 将映射区分为多个区,从中找到总线(BUS)的地址,计算出偏移地址
    • 对总线进行映射为 外设端口地址和边界地址
  • 通过基地址算出端口偏移地址,根据偏移地址+基地址算出端口下寄存器的地址

5、寄存器编码详解

  • ST公式定义好的函数接口

    • int main()
      {	//定义结构体类型
      	GPIO_InitTypeDef  GPIO_InitStructure;
      	
      	//打开外设时钟
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
      	
      	//配置引脚相应模式
      	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_9;
      	GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_OUT;
      	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
      	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
      	GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_NOPULL;
      	//初始化端口
      	GPIO_Init(GPIOF,&GPIO_InitStructure);
        while(1)
      	{
      	//引脚置位
          GPIOF->BSRRH = GPIO_Pin_9 ;
      
      	}
      }
      
  • 结构体类型内核源码

    • //通过对结构体进行重命名为GPIO_InitTypeDef,以及对内部成员进行定义,此处使用为宏定义GPIOMode_TypeDef等
      typedef struct
      {
        uint32_t GPIO_Pin;              /* 使用哪个引脚,该参数为引脚的地址,此处进行了宏定义 */
      
        GPIOMode_TypeDef GPIO_Mode;     /*使用引脚模式,推挽/开漏*/
      
        GPIOSpeed_TypeDef GPIO_Speed;   /*指定引脚速度*/
      
        GPIOOType_TypeDef GPIO_OType;   /*指定引脚输出类型 */
      
        GPIOPuPd_TypeDef GPIO_PuPd;     /*选择上拉/下拉电阻*/
      }GPIO_InitTypeDef;
      
    • GPIO_Pin引脚

      • 指定引脚地址,16个引脚对应16bit,每个bit位对应一个引脚

      • #define GPIO_Pin_0                 ((uint16_t)0x0001)  /* Pin 0 selected */ 
        #define GPIO_Pin_1                 ((uint16_t)0x0002)  /* Pin 1 selected */
        #define GPIO_Pin_2                 ((uint16_t)0x0004)  /* Pin 2 selected */
        #define GPIO_Pin_3                 ((uint16_t)0x0008)  /* Pin 3 selected */
        #define GPIO_Pin_4                 ((uint16_t)0x0010)  /* Pin 4 selected */
        #define GPIO_Pin_5                 ((uint16_t)0x0020)  /* Pin 5 selected */
        #define GPIO_Pin_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
        #define GPIO_Pin_7                 ((uint16_t)0x0080)  /* Pin 7 selected */
        #define GPIO_Pin_8                 ((uint16_t)0x0100)  /* Pin 8 selected */
        #define GPIO_Pin_9                 ((uint16_t)0x0200)  /* Pin 9 selected */
        #define GPIO_Pin_10                ((uint16_t)0x0400)  /* Pin 10 selected */
        #define GPIO_Pin_11                ((uint16_t)0x0800)  /* Pin 11 selected */
        #define GPIO_Pin_12                ((uint16_t)0x1000)  /* Pin 12 selected */
        #define GPIO_Pin_13                ((uint16_t)0x2000)  /* Pin 13 selected */
        #define GPIO_Pin_14                ((uint16_t)0x4000)  /* Pin 14 selected */
        #define GPIO_Pin_15                ((uint16_t)0x8000)  /* Pin 15 selected */
        #define GPIO_Pin_All               ((uint16_t)0xFFFF)  /* All pins selected */
        
    • GPIOMode_TypeDe源码

      • typedef enum
        { 
          GPIO_Low_Speed     = 0x00, /*!< Low speed    */
          GPIO_Medium_Speed  = 0x01, /*!< Medium speed */
          GPIO_Fast_Speed    = 0x02, /*!< Fast speed   */
          GPIO_High_Speed    = 0x03  /*!< High speed   */
        }GPIOSpeed_TypeDef;
        
    • GPIOMode_TypeDef

      • typedef enum
        { 
          GPIO_Mode_IN   = 0x00, /*!< GPIO Input Mode */
          GPIO_Mode_OUT  = 0x01, /*!< GPIO Output Mode */
          GPIO_Mode_AF   = 0x02, /*!< GPIO Alternate function Mode */
          GPIO_Mode_AN   = 0x03  /*!< GPIO Analog Mode */
        }GPIOMode_TypeDef;
        
    • 以此类推可知,每个寄存器模式都是通过地址来控制

    • 各端口地址如下

      • #define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
        #define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
        #define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
        #define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
        #define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
        #define GPIOF               ((GPIO_TypeDef *) GPIOF_BASE)
        
    • GPIOF_BASE地址

      • 端口地址

      • #define GPIOA_BASE            (AHB1PERIPH_BASE + 0x0000)
        #define GPIOB_BASE            (AHB1PERIPH_BASE + 0x0400)
        #define GPIOC_BASE            (AHB1PERIPH_BASE + 0x0800)
        #define GPIOD_BASE            (AHB1PERIPH_BASE + 0x0C00)
        #define GPIOE_BASE            (AHB1PERIPH_BASE + 0x1000)
        #define GPIOF_BASE            (AHB1PERIPH_BASE + 0x1400)
        
    • AHB1PERIPH_BASE

      • 总线地址

      • #define APB2PERIPH_BASE       (PERIPH_BASE + 0x00010000)
        #define AHB1PERIPH_BASE       (PERIPH_BASE + 0x00020000)
        #define AHB2PERIPH_BASE       (PERIPH_BASE + 0x10000000)
        
    • PERIPH_BASE

      • 在单片机内存区域,外设接口的基本物理地址

      • #define PERIPH_BASE    ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region  */
        
    • 所有映射地址都是通过,基地址 + 偏移地址 构成

  • 相应寄存器地址编写代码

      #include "stm32f4xx.h"  //必须添加
      //1.
      //外设基础地址
      #define Base_addr 			((volatile unsigned int*)0x40000000)
      //高级高性能总线地址
      #define AHB_addr 			((volatile unsigned int *)(Base_addr + 0x00020000))
      //GPIOF端口映射地址
      #define GPIO_addr 			((volatile unsigned int *)(AHB_addr + 0x1400))
      //GPIO_Pin_9引脚地址
      #define GPIO_Pin_9_addr 	((volatile unsigned int *)(GPIO_addr + 0x0200))
      //GPIOF_MODE地址
      #define GPIOF_MODE_addr		(*(volatile unsigned int *)( GPIO_addr +0x00))
      #define GPIO_OType_addr 	(*(volatile unsigned int *)( GPIO_addr +0x04))
      #define GPIO_Speed_addr 	(*(volatile unsigned int *)( GPIO_addr +0x08))
      #define GPIO_PuPd_addr 		(*(volatile unsigned int *)( GPIO_addr +0x0C))
      #define RCC_AHB1Periph_GPIOF_addr  (*((uint32_t *)0x00000020))
      
      //程序入口
      
      int main()
              {	//打开时钟
      			RCC_AHB1Periph_GPIOF_addr |=(1<<5) ;
      			//设置接口类型 PIN9 01输出模式 
      			GPIOF_MODE_addr |=(1<<(2*9 +1)) ;
      			GPIOF_MODE_addr &= ~(1<<2*9);
      			//设置开漏
      			GPIO_OType_addr |=(1<<9);
      			//设置输出速度
      			GPIO_Speed_addr |=(1<<(2*9 +1)) ;
      			GPIO_Speed_addr &= ~(1<<2*9);
      			//设置上拉电阻
      			GPIO_PuPd_addr |=(1<<(2*9 +1)) ;
      			GPIO_PuPd_addr &= ~(1<<2*9);
      		while (10){
      			//输出值置位
      			*GPIO_Pin_9_addr  |=(1<<9);
      			
      		};
    

6、函数库编码

  • 利用函数库提高开发效率

    • 利用寄存器对硬件进行控制,需要计算出每个寄存器的物理地址,并把寄存器的地址转换为指针,然后对指针进行操作
    • ST公司将地址已经将相应地址以及函数写好,使用时包含相关头文件即可
      • include “STM32xx .h”
      • 寄存器必须用volatile修饰,使用寄存器中最新的值
        • volatile 防止编译器对变量进行优化,CPU每次读取需要从地址读取,用时间换空间
        • ((*volatile unsigned int ) GPIOF ))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值