STM32操作外设(点亮LED灯)的两种方式

准备工作:

  • 硬件gec6818开发板、搭载stm32f407zet6芯片
  • keil项目模板,准备好官方库函数
  • 官方提供的《STM32f407数据手册》、《STM32F4xx中文参考手册》
  • 《gec6818开发板原理图》

一、使用ST公司官方提供的库函数

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

PF9意为GPIO外设下F端口第9个引脚(引脚序号为0~15,共16个),根据官方给出的示例代码可以很容易地写出:

/********************************************************************************
* @file GPIO/GPIO_IOToggle/main.c
* @author MCD Application Team
* @version V1.4.0
* @date 2025/4/27
* @brief 使开发板上的LED0灯亮
******************************************************************************/ // ST公司提供的库函数
#include "stm32f4xx.h" // 定义初始化对象
GPIO_InitTypeDef GPIO_InitStructure; int main()
{
/* 打开外设时钟 */
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; // 设置速度为100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // 设置上拉还是下拉,即引脚不给输出信号时的默认电平,上拉为高电平,下拉为低电平,nopull为浮空 /* 初始化*/
GPIO_Init(GPIOF, &GPIO_InitStructure); while(1)
{
// 设置PF8为高电平
// 为什么直接取GPIOF下的BSRRL就能找到对应的比特位呢?(第二节)
GPIOF->BSRRL = GPIO_Pin_9; // GPIOx_BSRR 为置位/复位寄存器(32位),高16位用作复位(BSRRH),低16位用作置位(BSRRL) //置位的第二种写法
GPIO_SetBits(GPIOF, GPIO_Pin_8);
}
}

问:ST公司提供的库函数使用起来非常方便简洁,但是底层是靠什么逻辑呢?

即答:底层靠预先封装好的寄存器地址寻址

所以,如果操作上完全不使用库函数也是可以直接做到点亮LED0的,只要能找到正确的寄存器地址并赋值。

二、直接使用寄存器地址操作寄存器

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

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

  • 外设基地址为0x4000_0000

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

所以:

  • AHB1下的GPIOF的基地址为0x4002_1400

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

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

  • 端口模式:0x00
  • 端口输出类型:0x04
  • 端口输出速度:0x08
  • 端口上拉/下拉:0x0C
  • 端口置位/复位:0x18

问:现在知道寄存器的基地址和偏移地址了,只要用指针取地址下的值就可以操作寄存器了,但是现在要写什么数据进寄存器才能得到我们想要的结果呢?

端口下的每一个寄存器有32位,每2位对应一个引脚配置,例如文档所述的端口模式寄存器:

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外设时钟使能寄存器的地址和要设置的电平:

  • 基地址0x4002_3800,偏移地址0x30,5 = 1

所有数据查询完毕,接下来终于可以着手写代码了:

/********************************************************************************
* @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 << 5; /* 设置引脚模式为输出模式 */
GPIOF_MODER &= ~(1 << 9 ); /* 设置引脚类型为推挽模式 */
GPIOF_OTYPER |= 1 << 2*9 ;
GPIOF_OTYPER |= 1 << (2*9 + 1); /* 设置输出速度为100MHz */
GPIOF_OSPEEDR &= ~(1 << 2*9 );
GPIOF_OSPEEDR |= 1 << (2*9 + 1); /* 设置上拉/下拉为浮空 */
GPIOF_PUPDR &= ~(1 << 2*9 );
GPIOF_PUPDR &= ~(1 << (2*9 + 1)); while(1)
{
// 设置PF8为高电平
GPIOx_BSRR &= ~(1 << 9 );
GPIOx_BSRR |= 1 << 25;
}
}

以上代码中没有包含任何库文件,即可完成对LED0灯的点亮!!

三、官方库函数底层代码

看过两种方式点亮LED0后,再回头看官方的库函数的底层代码,本质上也是通过封装寄存器的地址来点亮LED的:

// stm32f4xx.h

/**
* @brief General Purpose I/O
*/
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */
__IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
} GPIO_TypeDef;
...
#define PERIPH_BASE ((uint32_t)0x40000000) /* 外设基地址 */
...
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000) /* AHB1总线基地址 */
...
#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400) /* GPIOF端口基地址 */
...
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) /* 端口接口体指针 */

看过以上库函数的封装以后,就可以解释一开头的那句 GPIOF->BSRRL = GPIO_Pin_9;为什么可以直接对引脚置位了。

四、总结

点亮LED灯,应该说是“麻雀虽小五脏俱全”,整个过程也蕴含了完整的开发流程和知识体系。总的来说,使用官方提高的库函数确实极大地提高了开发效率,但是却非常的抽象;通过阅读手册,更加深入地理解硬件却能更好地掌控代码与硬件之间的直接联系,这个过程着实能让人学到不少东西。

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

  1. Visual Studio 2022 开发 STM32 单片机 - 环境搭建点亮LED灯

    安装VS2022社区版软件 选择基础的功能就好 安装VisualGDB软件(CSDN资源) 按照提示一步一步安装就好 VisualGDB激活软件(CSDN资源) 将如下软件放在VisualGDB的安装 ...

  2. [IOT] - 使用 .Net Core 操作 GPIO 引脚点亮 LED 灯泡

    1. 在 VS 2019 中创建 .Net Core 控制台应用程序,使用 Nuget 安装程序包: System.Device.GpioIot.Device.Bindings 2. 更新 Main ...

  3. POI操作Excel详细解释,HSSF和XSSF两种方式

    HSSF道路: package com.tools.poi.lesson1; import java.io.FileInputStream; import java.io.FileNotFoundEx ...

  4. POI操作Excel详解,HSSF和XSSF两种方式

    package com.tools.poi.lesson1; import java.io.FileInputStream; import java.io.FileNotFoundException; ...

  5. 第7章 使用寄存器点亮LED灯

    第7章     使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...

  6. 第7章 使用寄存器点亮LED灯—零死角玩转STM32-F429系列

    第7章     使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...

  7. C语言版——点亮LED灯,深入到栈

    在上一篇进行了汇编语言的编写之后,我们采用C语言来编写程序,毕竟C语言才是我们使用最多的语言. 仅仅是点亮LED灯显然太过于简单,我们需要分析最后的反汇编,了解函数调用栈,深入C语言骨髓去分析代码,并 ...

  8. 初探RT-Thread系统在GD32E103x芯片上的使用,点亮LED灯

    初探RT-Thread系统在GD32E103x芯片上的使用,点亮LED灯 前言 ​ 随着中美贸易战的加剧,很多公司越来越重视使用国产技术的重要性.使用国产技术,一方面可规避国外对技术的封锁造成产品核心 ...

  9. Raspberry PI 系列 —— 裸机点亮LED灯

    Raspberry PI 系列 -- 裸机点亮LED灯 背景 近期刚买了Raspberry PI B+,配置执行了官方提供的Raspbian系统,折腾了一周Linux系统,感觉没啥意思,于是就试着想了 ...

  10. stm32F103C8T6通过写寄存器点亮LED灯

    因为我写寄存器的操作不太熟练,所以最近腾出时间学习了一下怎么写寄存器,现在把我的经验贴出来,如有不足请指正 我使用的板子是stm32F103C8T6(也就是最常用的板子),现在要通过写GPIO的寄存器 ...

随机推荐

  1. docker搭建rabbitmq镜像集群

    Rabbitmq普通集群模式,是将交换机.绑定.队列的元数据复制到集群里的任何一个节点,但队列内容只存在于特定的节点中,客户端通过连接集群中任意一个节点,即可以生产和消费集群中的任何队列内容(因为每个 ...

  2. flutter3-trip仿携程酒店预订|Flutter3.27+Getx预约旅游酒店App程序

    基于Flutter3.x+Dart3+GetX跨平台仿携程/飞猪旅行酒店客房预订查询app系统. flutter3_trip原创2025新版flutter3.27.1+dart3.6+getx+flu ...

  3. Linux - top相关的快捷键

    q:退出top命令窗口(quit). k:按照进程ID终止(kill)一个进程.例如,你可以输入k,然后输入进程的PID来终止它. r:重新设置进程的优先级.输入r后,你可以输入新的优先级值. f:进 ...

  4. python基础-函数(lambda表达式、函数作参数、内置函数、推导式)和pip

    函数进阶 今日概要: 函数名就是一个变量(扩展) 匿名函数(lambda表达式) 重点内置函数--python内置函数 推导式(一行代码生成数据) 1. 函数名就是变量 def func(): pas ...

  5. 【ABAQUS脚本】后处理快速出图

    效果图: # -*- coding: utf-8 -*- # Do not delete the following import lines from abaqus import * from ab ...

  6. 【Matlab】判断点和多面体位置关系的两种方法实现

    分别是向量判别法(算法来自他人论文).体积判别法(code 是我从网上找的). 方法一: 向量判别法 方法来自一会议论文:<判断点与多面体空间位置关系的一个新算法_石露>2008年,知网. ...

  7. Cordova基本使用(一)

    简述 Apache Cordova是一个开源的移动开发框架.允许你用标准的web技术-HTML5,CSS3和JavaScript做跨平台开发. 应用在每个平台的具体执行被封装了起来,并依靠符合标准的A ...

  8. docker配置Nvidia环境,使用GPU

    前言 需要 nvdia driver 安装好,请参考 Ubuntu Nvidia driver驱动安装及卸载 docker 安装 配置 apt 阿里云的镜像源 sudo curl -fsSL http ...

  9. vue watch监听路由变化

    vue watch监听路由变化 // 监听 this.$route.path // watch监听非DOM元素的改变 watch:{ '$route.path':function(to,from){ ...

  10. 云服务器下如何部署Flask项目详细操作步骤

    参考网上各种方案,再结合之前学过的Django部署方案,最后确定Flask总体部署是基于:centos7+nginx+uwsgi+python3+Flask之上做的. 本地windows开发测试好了我 ...