在编写stm32程序时,对寄存器进行操着需要知道每个外设的基地址,标准库的stm32f10x.h文件里也有各种外设的基地址。
比如GPIO:在定义输出数据寄存器地址GPIOA_ODR_Addr时,在GPIOA_BASE(GPIO端口A的基址地址)地址基础上偏移
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
在stm32f10x.h文件里面有定义GPIOA_BASE,是APB2PERIPH_BASE(APB2(Advanced Peripheral Bus 2)总线的基址地址,高速总线)偏移0x0800
APB2PERIPH_BASE是在PERIPH_BASE地址基础上偏移0x10000
PERIPH_BASE地址就是表示内存地址空间的起始地址
/**********************************************************/
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
/**********************************************************/
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
/**********************************************************/
#define PERIPH_BASE ((uint32_t)0x40000000)
//通用IO口地址映射表
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;
这段代码定义了一个名为GPIO_TypeDef
的结构体类型,这是一种通用输入输出端口(GPIO)的类型定义。
"__IO"前缀相当于代表了C 语言中的关键字“volatile”,在 C 语言中该关键字用于表示变量是易变的,要求编译器不要优化。因为GPIO_TypeDef这个结构体里面都对应着寄存器,经常由外设或者STM32芯片状态修改,即使CPU不去改变这些寄存器内的值,这些寄存器内的值也会发生变化,当使用这些变量时,就要求CPU重新去访问地址读取数据,若没有“__IO”前缀,就会从CPU某个缓存区读取没及时更新的旧的数据。
下面是结构体中每个成员的含义:
-
CRL
(Configuration Low Register)- 低字节配置寄存器,用于配置端口低8位引脚的模式、速度和上拉/下拉选择。 -
CRH
(Configuration High Register)- 高字节配置寄存器,用于配置端口高8位引脚的模式、速度和上拉/下拉选择。 -
IDR
(Input Data Register)- 输入数据寄存器,用于存储输入引脚的状态。 -
ODR
(Output Data Register)- 输出数据寄存器,用于存储输出引脚的状态。 -
BSRR
(Bit Set/Reset Register)- 位设置/复位寄存器,用于单独设置或复位引脚的状态。 -
BRR
(Bit Reset Register)- 位复位寄存器,用于复位引脚的状态。 -
LCKR
(Lock Register)- 锁寄存器,用于锁定GPIO的配置寄存器,防止意外修改。
在使用库函数时,里面经常会看到下面这些:
#define I2C1 ((I2C_TypeDef *) I2C1_BASE)
#define EXTI ((EXTI_TypeDef *) EXTI_BASE)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define ADC1 ((ADC_TypeDef *) ADC1_BASE)
#define TIM1 ((TIM_TypeDef *) TIM1_BASE)
#define SPI1 ((SPI_TypeDef *) SPI1_BASE)
#define USART1 ((USART_TypeDef *) USART1_BASE)
比如#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)为例,定义GPIO_TypeDef结构体指针,结构体里面成员的类型和分布顺序是和寄存器里面一一对应的(结构体里面成员是32位的(即四个字节),下表GPIO寄存器里面也是每一个偏移4个字节)
然后就可以通过该结构体指针就可以直接操作
用操作寄存器写一个配置GPIO的示例程序:(知道就行)
#include "stm32f10x.h"
void GPIOA_PinConfig(uint32_t GPIO_Pin)
{
// 1. 使能GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. 获取GPIOA对应的寄存器地址
GPIO_TypeDef *GPIOx = GPIOA; // 指向GPIOA寄存器的指针
// 3. 配置引脚为复用推挽模式(AF_PP)
GPIOx->CRH &= ~(0x0F << (4 * GPIO_Pin)); // 清除模式位
GPIOx->CRH |= (GPIO_Mode_Out_PP << (4 * GPIO_Pin)); // 设置模式位
// 4. 配置输出速度
GPIOx->CRH &= ~(0x03 << (2 * GPIO_Pin)); // 清除速度位
GPIOx->CRH |= (GPIO_Speed_50MHz << (2 * GPIO_Pin)); // 设置输出速度
}
int main(void)
{
// 系统初始化
SystemInit();
// 配置GPIOA的第0号引脚
GPIOA_PinConfig(GPIO_Pin_0);
// 主循环
while (1)
{
}
}
-
使能GPIOA的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
这行代码是用来使能STM32的时钟控制模块(RCC)中GPIOA端口的时钟。在STM32中,GPIO端口是映射到APB2总线上的,所以使用RCC_APB2PeriphClockCmd
函数来控制其时钟。ENABLE
是一个参数,表示使能时钟。 -
获取GPIOA对应的寄存器地址
GPIO_TypeDef *GPIOx = GPIOA; // 指向GPIOA寄存器的指针
这里定义了一个指向GPIOA
端口寄存器的指针。GPIO_TypeDef
是STM32的寄存器定义结构体,用于存放端口的寄存器地址。GPIOx
是一个类型为GPIO_TypeDef
的指针变量,通过这个指针可以访问到GPIOA的所有寄存器。 -
配置引脚为复用推挽模式(AF_PP)
GPIOx->CRH &= ~(0x0F << (4 * GPIO_Pin)); // 清除模式位
GPIOx->CRH |= (GPIO_Mode_Out_PP << (4 * GPIO_Pin)); // 设置模式位
这两行代码用于配置GPIOA的某一个引脚的模式。CRH
是GPIOA端口的高字节寄存器,用于控制第11到第15引脚的配置。GPIO_Pin
是一个宏,代表你要配置的引脚编号。0x0F
是一个十六进制数,左移四位后用来清除模式位。GPIO_Mode_Out_PP
是一个宏,代表复用推挽输出模式。这两行代码的组合效果就是清除原来的模式位并设置新的模式位,将引脚配置为复用推挽模式。 -
配置输出速度
GPIOx->CRH &= ~(0x03 << (2 * GPIO_Pin)); // 清除速度位
GPIOx->CRH |= (GPIO_Speed_50MHz << (2 * GPIO_Pin)); // 设置输出速度
这里设置GPIOA引脚的输出速度。CRH
寄存器的高四位用于设置输出速度。GPIO_Speed_50MHz
是一个宏,表示输出速度为50MHz。通过清除原来的速度位并设置新的速度位,将引脚的输出速度配置为50MHz。
用标准库函数写一个配置GPIO的示例程序:
#include "stm32f10x.h"
void GPIOA_PinConfig(void)
{
// 1. 首先,我们需要使能GPIOA的时钟,这样相关的寄存器才能被访问
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. 配置GPIOA的第0号引脚为复用推挽模式(AF_PP),用于输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 选择PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 复用推挽模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 输出速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA的第0号引脚
}
int main(void)
{
// 系统初始化
SystemInit();
// 配置GPIOA的第0号引脚
GPIOA_PinConfig();
// 主循环
while (1)
{
}
}
标准库的GPIO库函数:
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
void GPIO_AFIODeInit(void);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
1.void GPIO_DeInit(GPIO_TypeDef* GPIOx);
将GPIOx外设寄存器初始化为其默认重置值。
// 假设此时你需要重置GPIOB到默认状态
GPIO_DeInit(GPIOB);
2.void GPIO_AFIODeInit(void);
取消初始化备用函数(重新映射、事件控制和EXTI配置)注册到它们的默认重置值。
通俗讲:将AFIO模块的配置恢复到出厂默认设置。包括将所有复用功能配置重置为GPIO模式,确保没有其他外设占用这些端口。在系统复位或初始化时调用这个函数,可以确保所有的GPIO端口都是按照预期的方式配置的。
3.void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
根据指定初始化GPIOx外设GPIO_InitStruct中的*参数。
void
表示函数没有返回值。
GPIO_Init
是函数名。
GPIO_TypeDef* GPIOx
是函数的第一个参数,它是一个指向 GPIO_TypeDef
类型的指针,这个类型定义了GPIO端口的相关结构和特性。GPIOx
通常用来指代不同的GPIO端口,比如 GPIOA
, GPIOB
, GPIOC
等。
GPIO_InitTypeDef* GPIO_InitStruct
是函数的第二个参数,它是一个指向 GPIO_InitTypeDef
类型的指针,这个类型定义了GPIO端口的初始化结构体,包含了端口的各种配置选项,如模式、速度、输出类型、输出速度、上下拉配置等。
这个函数的作用是根据 GPIO_InitStruct
结构体中指定的参数来配置 GPIOx
端口的相关特性。例如,你可以设置端口为输入模式,并配置内部上下拉电阻;或者设置端口为输出模式,并配置输出速度和类型。
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
4.void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct)
用默认值填充每个GPIO_InitStruct成员。
将配置好的GPIO_InitStructure结构体成员恢复默认。
5.uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
读取指定的输入端口引脚。
// 读取GPIOx的Pin0的状态
uint8_t pin_state = GPIO_ReadInputDataBit(GPIOx, GPIO_Pin_0);
/*返回(uint8_t)Bit_SET或者(uint8_t)Bit_RESET;*/
6.uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
读取指定的GPIO输入数据端口。
while (1)
{
// 读取GPIOx的所有引脚的状态
uint16_t input_data = GPIO_ReadInputData(GPIOx);
// 检查每个引脚的状态
for (int i = 0; i < 16; i++) { // 假设GPIOA有16个引脚
if ((input_data & (1 << i)) != 0) {
// 如果第i个引脚是高电平
printf("Pin %d is HIGH\n", i);
} else {
// 如果第i个引脚是低电平
printf("Pin %d is LOW\n", i);
}
}
}
先定义了要使用的 GPIO 端口。然后在主循环中,使用 GPIO_ReadInputData
函数来读取 GPIOA 端口的所有引脚的状态。我们将读取的数据循环检查每个引脚的状态,并通过printf函数输出是高电平还是低电平。
7.uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
读取指定的输出数据端口位。
8.uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
读取指定的GPIO输出数据端口。
9.void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);将GPIOx的GPIO_Pin_x脚置高电平。
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);将GPIOx的GPIO_Pin_x脚置低电平。
10.void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);设置或清除所选数据端口位。
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);将数据写入指定的GPIO数据端口。
// 切换GPIOA的Pin0和Pin1的状态
GPIO_WriteBit(GPIOA, GPIO_PIN0, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_PIN1, Bit_SET);
// 设置GPIOA的所有引脚为高电平
uint16_t all_pins = 0xFFFF; // 16位全为1,表示所有引脚
GPIO_Write(GPIOA, all_pins);
11.void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
锁定GPIO引脚配置寄存器。
12.void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
选择用作事件输出的GPIO引脚。
void GPIO_EventOutputCmd(FunctionalState NewState);
启用或禁用事件输出。
#include "stm32f10x.h"
// 假设我们使用GPIOA的Pin0作为事件输出源
#define GPIO_PortSource GPIOA
#define GPIO_PinSource GPIO_Pin_0
void EXTI0_IRQHandler(void) // EXTI0中断处理函数
{
// 检查是否是GPIOA的Pin0产生的中断
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 清除中断标志
EXTI_ClearITPendingBit(EXTI_Line0);
// 这里添加中断处理代码
printf("GPIOA Pin0 interrupt occurred!\n");
}
}
int main(void)
{
// 系统初始化代码(略)
// 配置GPIOA的Pin0作为事件输出源!!!!!!!!!!!
GPIO_EventOutputConfig(GPIO_PortSource, GPIO_PinSource);
// 启用GPIOA的Pin0的事件输出功能
GPIO_EventOutputCmd(GPIO_PortSource, GPIO_PinSource, ENABLE);
// 配置GPIOA的Pin0为上拉输入,并配置为中断模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // 输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 上拉
GPIO_Init(GPIO_PortSource, &GPIO_InitStructure);
// 配置EXTI线为中断模式
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
EXTI_Init(&EXTI_InitStructure);
// 使能中断!!!!!!!!!!!
NVIC_EnableIRQ(EXTI0_IRQn);
// 主循环
while (1)
{
// 可以在这里添加其他代码
}
}
13.void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
更改指定引脚的映射。
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
重映射后记得打开复用时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); /* 打开GPIO时钟 */
14.void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
选择用作EXTI线的GPIO引脚。
#include "stm32f10x.h" // STM32F10x 系列的标准库头文件
// 假设我们使用GPIOB的Pin0作为外部中断源
#define GPIO_PortSource GPIOB
#define GPIO_PinSource GPIO_Pin_0
void EXTI0_IRQHandler(void) // EXTI0中断处理函数
{
// 检查是否是GPIOB的Pin0产生的中断
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 清除中断标志
EXTI_ClearITPendingBit(EXTI_Line0);
// 这里添加中断处理代码
printf("GPIOB Pin0 interrupt occurred!\n");
}
}
int main(void)
{
// 系统初始化代码(略)
// 配置GPIOB的Pin0为外部中断源
GPIO_EXTILineConfig(GPIO_PortSource, GPIO_PinSource);
// 配置EXTI线为中断模式
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
EXTI_Init(&EXTI_InitStructure);
// 使能中断
NVIC_EnableIRQ(EXTI0_IRQn);
// 主循环
while (1)
{
// 可以在这里添加其他代码
}
}
15.void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);文章来源:https://www.toymoban.com/news/detail-848645.html
选择以太网媒体接口。文章来源地址https://www.toymoban.com/news/detail-848645.html
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< 选择Pin0 */ //(00000000 00000001)b
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< 选择Pin1 */ //(00000000 00000010)b
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< 选择Pin2 */ //(00000000 00000100)b
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< 选择Pin3 */ //(00000000 00001000)b
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< 选择Pin4 */ //(00000000 00010000)b
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< 选择Pin5 */ //(00000000 00100000)b
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< 选择Pin6 */ //(00000000 01000000)b
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< 选择Pin7 */ //(00000000 10000000)b
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< 选择Pin8 */ //(00000001 00000000)b
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< 选择Pin9 */ //(00000010 00000000)b
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< 选择Pin10 */ //(00000100 00000000)b
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< 选择Pin11 */ //(00001000 00000000)b
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< 选择Pin12 */ //(00010000 00000000)b
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< 选择Pin13 */ //(00100000 00000000)b
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< 选择Pin14 */ //(01000000 00000000)b
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< 选择Pin15 */ //(10000000 00000000)b
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< 选择全部引脚*/ //(11111111 11111111)b
到了这里,关于STM32外设GPIO(学习笔记)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!