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,即:
放大这部分可以看到这其中包含了四条总线: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
所有数据查询完毕,接下来终于可以着手写代码了:
[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 |