找回密码
 立即注册
首页 业界区 业界 STM32操作GPIO外设(点亮LED灯)的两种方式——使用官方 ...

STM32操作GPIO外设(点亮LED灯)的两种方式——使用官方库函数或直接操作寄存器

步雪卉 前天 22:29
STM32操作外设(点亮LED灯)的两种方式

准备工作:


  • 硬件gec6818开发板、搭载stm32f407zet6芯片
  • keil项目模板,准备好官方库函数

    • 1.png


  • 官方提供的《STM32f407数据手册》、《STM32F4xx中文参考手册》
  • 《gec6818开发板原理图》
一、使用ST公司官方提供的库函数

首先获取LED0所使用的芯片引脚,由原理图可以查得LED灯使用的芯片引脚为PF9
2.png

PF9意为GPIO外设下F端口第9个引脚(引脚序号为0~15,共16个),根据官方给出的示例代码可以很容易地写出:
  1. /********************************************************************************
  2. * @file    GPIO/GPIO_IOToggle/main.c
  3. * @author  MCD Application Team
  4. * @version V1.4.0
  5. * @date    2025/4/27
  6. * @brief   使开发板上的LED0灯亮
  7. ******************************************************************************/
  8. // ST公司提供的库函数
  9. #include "stm32f4xx.h"
  10. // 定义初始化对象
  11. GPIO_InitTypeDef  GPIO_InitStructure;
  12. int main()
  13. {
  14.         /* 打开外设时钟 */
  15.         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
  16.         /* 配置初始化对象 */
  17.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // 设置要操作的引脚编号
  18.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  // 设置引脚模式为输出模式
  19.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  // 设置引脚类型为推挽模式
  20.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 设置速度为100MHz
  21.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  // 设置上拉还是下拉,即引脚不给输出信号时的默认电平,上拉为高电平,下拉为低电平,nopull为浮空
  22.    
  23.     /* 初始化*/
  24.         GPIO_Init(GPIOF, &GPIO_InitStructure);
  25.        
  26.         while(1)
  27.         {
  28.                 // 设置PF8为高电平
  29.         // 为什么直接取GPIOF下的BSRRL就能找到对应的比特位呢?(第二节)
  30.         GPIOF->BSRRL = GPIO_Pin_9;  // GPIOx_BSRR 为置位/复位寄存器(32位),高16位用作复位(BSRRH),低16位用作置位(BSRRL)
  31.         
  32.         //置位的第二种写法
  33.         GPIO_SetBits(GPIOF, GPIO_Pin_8);
  34.         }
  35. }
复制代码
问:ST公司提供的库函数使用起来非常方便简洁,但是底层是靠什么逻辑呢?
即答:底层靠预先封装好的寄存器地址寻址
所以,如果操作上完全不使用库函数也是可以直接做到点亮LED0的,只要能找到正确的寄存器地址并赋值。
二、直接使用寄存器地址操作寄存器

首先,地址映射规则由芯片厂商固化,我们可以通过《STM32f407数据手册》先看看STM32F407ZET6整体的编址结构:
3.png

找到第4章——Memory mapping,我们可以发现整体编址范围从0x0000_00000xFFFF_FFFF(共4G),分配给外设的部分为0x4000_00000x5FFF_FFFF,即:

  • 外设基地址为0x4000_0000
放大这部分可以看到这其中包含了四条总线:AHB2、AHB1、APB2、APB1,查询图后方的表格可知GPIOF位于AHB1总线上,且所属地址为0x4002_1400~0x4002_17FF,:
4.png

所以:

  • AHB1下的GPIOF的基地址为0x4002_1400
再往下深入的话,数据手册就派不上用场了,接着使用《STM32F4xx中文参考手册》查看具体的寄存器地址,找到7.4章节,其中可以找到每个端口下的寄存器的偏移地址:
5.png

逐个查询可知,本次需要配置的几个寄存器的偏移地址如下:

  • 端口模式:0x00
  • 端口输出类型:0x04
  • 端口输出速度:0x08
  • 端口上拉/下拉:0x0C
  • 端口置位/复位:0x18
问:现在知道寄存器的基地址和偏移地址了,只要用指针取地址下的值就可以操作寄存器了,但是现在要写什么数据进寄存器才能得到我们想要的结果呢?
端口下的每一个寄存器有32位,每2位对应一个引脚配置,例如文档所述的端口模式寄存器:
6.png

MODERy中的y即每个端口下的引脚序号(0~15),2个bit可以设置4种状态,因此我们想要设置9号引脚的端口模式为通用输出模式的话,只需设置端口模式寄存器的MODER9(18和19位)为01即可。然后总结一下我们需要设置的参数:
(等号左边为位号,右边为电平值)

  • 端口模式:2*9 : 2*9+1 = 0 : 1
  • 端口输出类型:9 = 0
  • 端口输出速度:2*9 : 2*9+1 = 1 : 1
  • 端口上拉/下拉:2*9 : 2*9+1 = 0 : 0
  • 端口置位/复位:9 :  25 = 0 : 1
除了以上GPIO寄存器,接下来还有最重要的一个RCC寄存器需要设置,用来打开端口的时钟,这样才能成功配置端口。同样的查询步骤可以得到RCC AHB1外设时钟使能寄存器的地址和要设置的电平:
7.png


  • 基地址0x4002_3800,偏移地址0x30,5 = 1
所有数据查询完毕,接下来终于可以着手写代码了:
[code]/********************************************************************************* @file    GPIO/GPIO_IOToggle/main.c * @author  MCD Application Team* @version V1.4.0* @date    2025/4/27* @brief   用直接操作寄存器的方式使开发板上的LED0灯亮******************************************************************************/#define RCC_AHB1Periph_GPIOF         (*(volatile unsigned int*)(0x40023800 + 0x30))                        // RCC AHB1外设时钟使能寄存器#define GPIOF_MODER                         (*(volatile unsigned int*)(0x40021400 + 0x00))                        // 端口模式#define GPIOF_OTYPER                         (*(volatile unsigned int*)(0x40021400 + 0x04))                        // 端口输出类型#define GPIOF_OSPEEDR                         (*(volatile unsigned int*)(0x40021400 + 0x08))                        // 端口输出速度#define GPIOF_PUPDR                         (*(volatile unsigned int*)(0x40021400 + 0x0C))                        // 端口上拉/下拉#define GPIOx_BSRR                                 (*(volatile unsigned int*)(0x40021400 + 0x18))                        // 端口置位/复位        int main(){        /* 打开外设时钟 */        RCC_AHB1Periph_GPIOF |= 1
您需要登录后才可以回帖 登录 | 立即注册