这篇文章是 STM32F10x系列 GPIO 外设的寄存器定义和操作函数。头文件stm32f10x.h 定义了 GPIO 和 RCC 外设的寄存器结构体、基地址映射、引脚编号枚举以及工作模式/速度配置类型。stm32f10x_gpio.h 声明了三个关键函数:GPIO_SetBits/ResetBits 用于控制引脚输出电平,GPIO_Init 函数实现引脚模式初始化,通过分析 GPIO_Mode 参数判断输入/输出模式,并配置 CRL/CRH 寄存器设置具体引脚的工作模式和速度。整个设计采用寄存器直接映射方式,通过结构体指针访问外设寄存器,实现了对 STM32 GPIO 外设的底层硬件控制。
stm32f10x.h
c
#ifndef __STM32F10X_H__
#define __STM32F10X_H__
//volatile表⽰容易变得变量,防⽌编译器优化,
#define __IO volatile
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
// GPIO寄存器结构体定义
typedef struct
{
__IO uint32_t CRL; //端⼝配置低寄存器,偏移地址0X00
__IO uint32_t CRH; //端⼝配置⾼寄存器,地址偏移0X04
__IO uint32_t IDR; //端⼝数据输⼊寄存器,地址偏移0X08
__IO uint32_t ODR; //端⼝数据输出寄存器,地址偏移0X0C
__IO uint32_t BSRR; //端⼝位设置、清除寄存器,地址偏移0X10
__IO uint32_t BRR; //端⼝位清除寄存器,地址偏移0X14
__IO uint32_t LCKR; //端⼝配置锁定寄存器,地址偏移0X18
} GPIO_TypeDef;
typedef struct
{
__IO uint32_t CR;
__IO uint32_t CFGR;
__IO uint32_t CIR;
__IO uint32_t APB2RSTR;
__IO uint32_t APB1RSTR;
__IO uint32_t AHBENR;
__IO uint32_t APB2ENR;
__IO uint32_t APB1ENR;
__IO uint32_t BDCR;
__IO uint32_t CSR;
} RCC_TypeDef;
/*⽚上外设基地址*/
#define PERIPH_BASE ((unsigned int)0x40000000)
/*APB2总线基地址*/
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
/*AHB总线基地址*/
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
/*RCC外设基地址*/
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18)
/*GPIO外设基地址*/
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)
/* GPIOC寄存器地址*/
#define GPIOC_CRL *(unsigned int*)(GPIOC_BASE+0x00)
#define GPIOC_CRH *(unsigned int*)(GPIOC_BASE+0x04)
#define GPIOC_IDR *(unsigned int*)(GPIOC_BASE+0x08)
#define GPIOC_ODR *(unsigned int*)(GPIOC_BASE+0x0C)
#define GPIOC_BSRR *(unsigned int*)(GPIOC_BASE+0x10)
#define GPIOC_BRR *(unsigned int*)(GPIOC_BASE+0x14)
#define GPIOC_LCKR *(unsigned int*)(GPIOC_BASE+0x18)
#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)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
// RCC 外设声明
#define RCC ((RCC_TypeDef *) RCC_BASE)
#define GPIO_Pin_0 (uint16_t)0x0001)
#define GPIO_Pin_1 ((uint16_t)0x0002)
#define GPIO_Pin_2 ((uint16_t)0x0004)
#define GPIO_Pin_3 ((uint16_t)0x0008)
#define GPIO_Pin_4 ((uint16_t)0x0010)
#define GPIO_Pin_5 ((uint16_t)0x0020)
#define GPIO_Pin_6 ((uint16_t)0x0040)
#define GPIO_Pin_7 ((uint16_t)0x0080)
#define GPIO_Pin_8 ((uint16_t)0x0100)
#define GPIO_Pin_9 ((uint16_t)0x0200)
#define GPIO_Pin_10 ((uint16_t)0x0400)
#define GPIO_Pin_11 ((uint16_t)0x0800)
#define GPIO_Pin_12 ((uint16_t)0x1000)
#define GPIO_Pin_13 ((uint16_t)0x2000)
#define GPIO_Pin_14 ((uint16_t)0x4000)
#define GPIO_Pin_15 ((uint16_t)0x8000)
typedef enum
{
GPIO_Speed_10MHz = 1, // 10MHZ (01)b
GPIO_Speed_2MHz, // 2MHZ (10)b
GPIO_Speed_50MHz // 50MHZ (11)b
} GPIOSpeed_TypeDef;
typedef enum
{
GPIO_Mode_AIN = 0x0,
GPIO_Mode_IN_FLOATING = 0x04,
GPIO_Mode_IPD = 0x28,
GPIO_Mode_IPU = 0x48,
GPIO_Mode_Out_OD = 0x14,
GPIO_Mode_Out_PP = 0x10,
GPIO_Mode_AF_OD = 0x1C,
GPIO_Mode_AF_PP = 0x18
} GPIOMode_TypeDef;
typedef struct
{
uint16_t GPIO_Pin;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
} GPIO_InitTypeDef;
#endif
stm32f10x_gpio.h
c
#ifndef __STM32F10X_GPIO__
#define __STM32F10X_GPIO__
#include "stm32f10x.h"
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
#endif
c
#include "stm32f10x_gpio.h"
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
GPIOx->BSRR = GPIO_Pin;
}
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
GPIOx->BRR = GPIO_Pin;
}
/**
*函数功能:初始化引脚模式
*参数说明: GPIOx,该参数为 GPIO_TypeDef 类型的指针,指向 GPIO 端⼝的地址
* GPIO_InitTypeDef:GPIO_InitTypeDef 结构体指针,指向初始化变量
*/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t currentmode =0x00,currentpin = 0x00,pinpos = 0x00,pos = 0x00;
uint32_t tmpreg = 0x00, pinmask = 0x00;
/*---------------- GPIO 模式配置 -------------------*/
// 把输⼊参数 GPIO_Mode 的低四位暂存在 currentmode
currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
// bit4是1表⽰输出,bit4是0则是输⼊
// 判断bit4是1还是0,即⾸选判断是输⼊还是输出模式
if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
{
// 输出模式则要设置输出速度
currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
}
/*-----GPIO CRL 寄存器配置 CRL 寄存器控制着低 8 位 IO- ----*/
// 配置端⼝低 8 位,即 Pin0~Pin7
if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
{
// 先备份 CRL 寄存器的值
tmpreg = GPIOx->CRL;
// 循环,从 Pin0 开始配对,找出具体的 Pin
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
//pos的值为1左移pinpos位
pos = ((uint32_t)0x01) << pinpos;
//令 pos 与输⼊参数 GPIO_PIN 作位与运算
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
//若 currentpin=pos,则找到使⽤的引脚
if (currentpin == pos)
{
//pinpos 的值左移两位(乘以 4),因为寄存器中 4 个位配置⼀个引脚
pos = pinpos << 2;
//把控制这个引脚的 4 个寄存器位清零,其它寄存器位不变
pinmask = ((uint32_t)0x0F) << pos;
tmpreg &= ~pinmask;
// 向寄存器写⼊将要配置的引脚的模式
tmpreg |= (currentmode << pos);
// 判断是否为下拉输⼊模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
{
// 下拉输⼊模式,引脚默认置 0,对 BRR 寄存器写 1 对引脚置 0
GPIOx->BRR = (((uint32_t)0x01) << pinpos);
}
else
{
// 判断是否为上拉输⼊模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
// 上拉输⼊模式,引脚默认值为 1,对 BSRR 寄存器写 1 对引脚置 1
GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
}
}
}
}
// 把前⾯处理后的暂存值写⼊到 CRL 寄存器之中
GPIOx->CRL = tmpreg;
}
/*--------GPIO CRH 寄存器配置 CRH 寄存器控制着⾼ 8 位 IO- -----*/
// 配置端⼝⾼ 8 位,即 Pin8~Pin15
if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
{
// 先备份 CRH 寄存器的值
tmpreg = GPIOx->CRH;
// 循环,从 Pin8 开始配对,找出具体的 Pin
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
pos = (((uint32_t)0x01) << (pinpos + 0x08));
// pos 与输⼊参数 GPIO_PIN 作位与运算
currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
//若 currentpin=pos,则找到使⽤的引脚
if (currentpin == pos)
{
//pinpos 的值左移两位(乘以 4),因为寄存器中 4 个位配置⼀个引脚
pos = pinpos << 2;
//把控制这个引脚的 4 个寄存器位清零,其它寄存器位不变
pinmask = ((uint32_t)0x0F) << pos;
tmpreg &= ~pinmask;
// 向寄存器写⼊将要配置的引脚的模式
tmpreg |= (currentmode << pos);
// 判断是否为下拉输⼊模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
{
// 下拉输⼊模式,引脚默认置 0,对 BRR 寄存器写 1 可对引脚置 0
GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
// 判断是否为上拉输⼊模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
// 上拉输⼊模式,引脚默认值为 1,对 BSRR 寄存器写 1 可对引脚置 1
GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
}
}
GPIOx->CRH = tmpreg;
}
}

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!