STM32操作GPIO外设(点亮LED灯)的两种方式——使用官方库函数或直接操作寄存器
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灯)的两种方式——使用官方库函数或直接操作寄存器的更多相关文章
- Visual Studio 2022 开发 STM32 单片机 - 环境搭建点亮LED灯
安装VS2022社区版软件 选择基础的功能就好 安装VisualGDB软件(CSDN资源) 按照提示一步一步安装就好 VisualGDB激活软件(CSDN资源) 将如下软件放在VisualGDB的安装 ...
- [IOT] - 使用 .Net Core 操作 GPIO 引脚点亮 LED 灯泡
1. 在 VS 2019 中创建 .Net Core 控制台应用程序,使用 Nuget 安装程序包: System.Device.GpioIot.Device.Bindings 2. 更新 Main ...
- POI操作Excel详细解释,HSSF和XSSF两种方式
HSSF道路: package com.tools.poi.lesson1; import java.io.FileInputStream; import java.io.FileNotFoundEx ...
- POI操作Excel详解,HSSF和XSSF两种方式
package com.tools.poi.lesson1; import java.io.FileInputStream; import java.io.FileNotFoundException; ...
- 第7章 使用寄存器点亮LED灯
第7章 使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...
- 第7章 使用寄存器点亮LED灯—零死角玩转STM32-F429系列
第7章 使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...
- C语言版——点亮LED灯,深入到栈
在上一篇进行了汇编语言的编写之后,我们采用C语言来编写程序,毕竟C语言才是我们使用最多的语言. 仅仅是点亮LED灯显然太过于简单,我们需要分析最后的反汇编,了解函数调用栈,深入C语言骨髓去分析代码,并 ...
- 初探RT-Thread系统在GD32E103x芯片上的使用,点亮LED灯
初探RT-Thread系统在GD32E103x芯片上的使用,点亮LED灯 前言 随着中美贸易战的加剧,很多公司越来越重视使用国产技术的重要性.使用国产技术,一方面可规避国外对技术的封锁造成产品核心 ...
- Raspberry PI 系列 —— 裸机点亮LED灯
Raspberry PI 系列 -- 裸机点亮LED灯 背景 近期刚买了Raspberry PI B+,配置执行了官方提供的Raspbian系统,折腾了一周Linux系统,感觉没啥意思,于是就试着想了 ...
- stm32F103C8T6通过写寄存器点亮LED灯
因为我写寄存器的操作不太熟练,所以最近腾出时间学习了一下怎么写寄存器,现在把我的经验贴出来,如有不足请指正 我使用的板子是stm32F103C8T6(也就是最常用的板子),现在要通过写GPIO的寄存器 ...
随机推荐
- python式思辨
设A为条件,B.C为完全相反的结论. KFK:if〈A〉,〈B〉 GLXY:if〈A〉,〈C〉 我不知道为什么会出现这样的结果,也许是我的版本太久没有更新了吧
- Nityacke's 分块(未补全)
P2801 教主的魔法 区间加区间查询一个数排名. 对于每个块,维护其有序序列.修改时散块暴力重构,整块打tag. 查询是简单的.时间复杂度 \(O(n\log B+\dfrac{qn}{B}\log ...
- IDEA测试Flink程序
一.导入项目,添加jar包 右键项目=>open module setting 选择Flink1.9的lib目录下flink-dist_2.11-1.9.2.jar包(对应运行版本) 保存即可. ...
- Json序列化对象后,使用Lombok后属性xAxis大写变成小写xaxis解决方法
一.问题描述 最近在开发后端接口时,发现返回结构中,一个字段大小写转化有问题. lombok版本:1.18.14 VO对象如下,字段为echarts趋势图组件.预期返回xAxis,实际返回xaxis ...
- 每次下载idea都必装的十个插件!
IDEA必备插件 Alibaba Java Coding Guidelines 功能: 阿里巴巴Java开发规范插件,用于代码规范检查. 特点: 基于阿里巴巴Java开发手册,提供实时代码规范检查,帮 ...
- [ARC148C] Lights Out on Tree 题解
在考场遇到了这道题,感觉很有意思. 当时直接想到的就是虚树,可惜打挂了. 后来改对了,写篇题解纪念一下. 首先看到 \(\sum M_i\le 2\times 10^5\),很容易想到虚树的数据范围. ...
- ESP32-S3-WROOM-1-N16R8
ESP32-S3-WROOM-1-N16R8 基于立创实战派S3 参考链接:实战派开发板S3介绍 | 立创开发板技术文档中心 硬件和代码,(大部分图片)基于立创文档,在此基础上进行个人的学习记录和理解 ...
- LINUX 服务器安装nginx redis jdk等步聚
1.安装指令步聚 sudo yum update 更新linux系统 yum install -y nginx 安装nginx systemctl enable nginx 设置开机启动nginx s ...
- 【BUUCTF】HardSQL
[BUUCTF]HardSQL 题目来源 收录于:BUUCTF 极客大挑战 2019 题目描述 一道纯粹的SQL注入题 尝试进行注入,发现对以下字符进行了过滤: = %20(空格) %09 %0a % ...
- git码云安装及使用菜鸟教程
1.下载Windows本地码云 https://mirrors.huaweicloud.com/git-for-windows/(华为镜像下载),选择合适的版本下载,此处下载速度要快些 2.登录码云官 ...