STM32-GPIO_寄存器版(1)

本文基于STM32F407ZGT6,适用于绝大部分M3/M4内核的STM32芯片。
———————————————————————————————

STM32每个通用 I/O 端口包括 10个32位寄存器

一、4个 32 位配置寄存器

  • 1、GPIOx_MODER——用于选择 I/O 方向(输入、输出、AF(复用)、模拟)。
  • 2、GPIOx_OTYPER——用于选择输出类型(推挽或开漏)(IO口设置为输入时不用设置此寄存器)
  • 3、GPIOx_OSPEEDR ——用于选择输出速度(IO口设置为输入时不用设置此寄存器)
  • 4、GPIOx_PUPDR——用于选择上拉/下拉

二、2 个 32 位数据寄存器

  • 1、GPIOx_IDR——用于存储输入的数据,可对其进行读/写访问,一般用于读取IO口的输入电平。

  • 2、GPIOx_ODR——用于存储待输出数据,可对其进行读/写访问,一般用于读取IO口的输出电平。

数据寄存器:保存了 GPIO 的输入电平 或 将要输出的电平。

三、1 个 32 位置位/复位寄存器: GPIOx_BSRR
置位/复位寄存器:控制输出IO口的电平(0或者1)

四、1 个 32 位锁定寄存器: GPIOx_LCKR
锁定寄存器:可以冻结 GPIO 控制寄存器,设置某锁定引脚后,就不能修改其配置。

五、2 个 32 位复用功能选择寄存器

  • 1、GPIOx_AFRH
  • 2、GPIOx_AFRL

根据应用程序的要求将某个复用功能连接到其它某个引脚。(比如把PA9 和 PA10 用作串口usart1的 Tx 和 Rx)

关于哪个引脚的具有哪些复用功能可以查看芯片的数据手册(不是中文参考手册)。

STM32的配置模式

STM32的IO口不是设置为输入就是输出,所以IO口可以设置为输入或者输出模式。
输入模式有4种:
1、上拉输入
2、下拉输入
3、浮空输入
4、模拟输入
设置为输入模式需要配置的寄存器有2个(当然你设置了其他寄存器也不会出错,只是会有点浪费单片机的资源):

  • 1、GPIOx_MODER——选择 I/O 方向输入/模拟
  • 2、GPIOx_PUPDR——用于选择上拉/下拉(如果是浮空输入/模拟输入模式不用设置该寄存器)

输出模式有4种:
1、具有上拉或下拉功能的开漏输出
2、具有上拉或下拉功能的推挽输出
3、具有上拉或下拉功能的复用功能推挽
4、具有上拉或下拉功能的复用功能开漏
设置为输出模式需要配置的寄存器有5个:

  • 1、GPIOx_MODER——选择 I/O 方向输出/AF
  • 2、GPIOx_OTYPER——选择输出类型(推挽或开漏)
  • 3、GPIOx_OSPEEDR ——选择输出速度
  • 4、GPIOx_PUPDR——选择输出上拉/下拉
  • 5、GPIOx_AFRH/GPIOx_AFRL——仅当你使用复用功能时还需要配置复用功能选择寄存器

下面用STM32的GPIOA2、GPIOA3设置为复用输出模式的USART2为例子:

 /****************** UART2模块 GPIO口配置 *************************/

    //开GPIOA  口时钟,每个GPIO都要开启时钟
    RCC->AHB1ENR |=  1 << 0;

    //配置 GPIOA2、GPIOA3 为:复用输出模式
    GPIOA->MODER &= ~(3 << 2 * 2 | 3 << 3 * 2);             //对应的两位分别清0
    GPIOA->MODER |=  (2 << 2 * 2 | 2 << 3 * 2);             //对应的两位分别设置为10

    //配置GPIOA2、GPIOA3 为:输出推挽(复用状态)
    GPIOA->OTYPER &= ~(1 << 2 | 1 << 3 );                   //对应的位分别清0

	//配置 GPIOA2、GPIOA3 速度为: 2 MHz(低速) -- 可选
	GPIOA->OSPEEDR &= ~(3 << 2 * 2 | 3 << 3 * 2);

    //配置 GPIOA2、GPIOA3 为:01:上拉  -- 可选
    //(注意不能设置为下拉,否则可能第一次通信可能会多发一个 '?' 字符)
    GPIOA->PUPDR &= ~(3 << 2 * 2 | 3 << 3 * 2);             //对应的两位分别清0
    GPIOA->PUPDR |=  (1 << 2 * 2 | 1 << 3 * 2);             //对应的两位分别设置为01

    //配置为复用引用功能
    GPIOA->AFR[0] &= ~(0Xf << 2 * 4);
    GPIOA->AFR[0] |=  (0x7 << 2 * 4);
    GPIOA->AFR[0] &= ~(0Xf << 3 * 4);
	GPIOA->AFR[0] |=  (0x7 << 3 * 4);
//*****************************end***********************************//

总结:个人觉得用寄存器来编程虽然可以提高程序运行速率,但是这样做会导致程序编写难度太大、程序可移植性和可读性不高。所以笔者建议大家都使用库函数开发STM32,但是这并不意味着我们不需要懂寄存器,理解寄存器才能更好地理解STM32的库

<think>好的,我现在需要分析用户提供的函数`GPIO_Input_Pin_Data_Get`,并解释它的功能、参数、返回值以及实现逻辑。用户之前询问过如何读取GPIO电平,现在给出具体的代码,可能需要进一步的解释。 首先,我要理解这个函数的结构。函数名是GPIO_Input_Pin_Data_Get,看起来是用于获取指定GPIO端口的引脚状态。参数有两个:GPIOx和pin,分别代表GPIO模块(如GPIOA、GPIOB等)和具体的引脚号(如PIN_0到PIN_15)。返回的是引脚的状态,即高电平或低电平。 接下来看函数的实现。函数内部检查GPIOx->PID & pin是否不等于PIN_RESET。如果成立,返回PIN_SET,否则返回PIN_RESET。这里可能存在一些疑问,比如PID寄存器的含义是什么?通常,在STM32中,输入数据寄存器是IDR,而不是PID。可能用户代码中的PID是特定厂商的命名,或者是一个笔误? 然后需要验证代码的正确性。假设PID确实是输入数据寄存器,那么按位与操作可以检测特定引脚的电平。例如,如果PID寄存器的某位为1,表示高电平,否则低电平。但需要确认寄存器名称是否正确,因为标准外设库中通常使用IDR。 另外,用户提供的代码中,判断条件是(GPIOx->PID & pin) != PIN_RESET。这里的PIN_RESET应该是0,而PIN_SET是1。所以当引脚对应的位被置位时,返回1,否则0。但需要注意,pin参数是uint16_t类型,而PID是uint32_t,进行按位与时可能需要类型转换。用户代码中已经将pin转换为uint32_t,这可能没问题,但需要确认硬件寄存器的大小是否匹配。 还需要考虑不同硬件平台的差异,比如STM32的HAL库和标准外设库的区别。例如,HAL库中使用HAL_GPIO_ReadPin函数,而这里直接访问寄存器,可能是更低层的操作,或者是特定厂商的库函数。 用户可能希望了解这个函数的作用、如何调用它以及使用时需要注意的事项。同时,可能需要指出潜在的问题,比如寄存器名称是否正确,是否需要先配置引脚为输入模式,以及是否启用了时钟等配置步骤。 总结来说,需要分步骤解释函数的功能、参数、返回值、实现逻辑,并指出可能的注意事项和常见问题。同时,对比之前提供的通用方法,说明这个函数的具体应用场景和可能的硬件平台。</think>--- ### 函数解析:`GPIO_Input_Pin_Data_Get` #### 1. **功能说明** - **核心作用**:读取指定GPIO端口的某个引脚的电平状态(高电平或低电平)。 - **适用场景**:直接操作寄存器或特定硬件抽象层(HAL)的场景,常见于裸机开发或自定义驱动库。 --- #### 2. **参数说明** - **`GPIOx`** - 类型:`GPIO_Module*` - 含义:指向GPIO端口模块的指针(例如`GPIOA`, `GPIOB`等)。 - 可选值:`GPIOA`、`GPIOB`、`GPIOC`、`GPIOD`(需根据硬件支持扩展)。 - **`pin`** - 类型:`uint16_t` - 含义:目标引脚编号(例如`GPIO_PIN_0`到`GPIO_PIN_15`)。 - 可选值:`GPIO_PIN_0` ~ `GPIO_PIN_15`(需根据硬件实际引脚数调整)。 --- #### 3. **返回值** - **`PIN_SET`**(高电平):对应值为`1`。 - **`PIN_RESET`**(低电平):对应值为`0`。 --- #### 4. **代码逻辑分析** ```c uint8_t GPIO_Input_Pin_Data_Get(GPIO_Module* GPIOx, uint16_t pin) { // 判断目标引脚的电平状态 if ((GPIOx->PID & pin) != (uint32_t)PIN_RESET) { return (uint8_t)PIN_SET; // 高电平 } else { return (uint8_t)PIN_RESET; // 低电平 } } ``` - **关键操作**: 1. **`GPIOx->PID`**:访问GPIO端口的输入数据寄存器(PID)。 - **注意**:在标准STM32库中,输入数据寄存器通常命名为`IDR`(Input Data Register),此处`PID`可能是特定厂商或自定义的寄存器别名。 2. **按位与操作**:`GPIOx->PID & pin` - 作用:检查目标引脚对应的寄存器位是否为高电平。 3. **判断结果**:若结果不为`PIN_RESET`(即不为0),则返回高电平;否则返回低电平。 --- #### 5. **典型应用示例** ```c // 示例:读取GPIOA的PIN_5电平 uint8_t level = GPIO_Input_Pin_Data_Get(GPIOA, GPIO_PIN_5); if (level == PIN_SET) { // 高电平处理逻辑 } else { // 低电平处理逻辑 } ``` --- #### 6. **注意事项** 1. **寄存器名称验证** - 确认硬件文档中输入数据寄存器的实际名称(例如STM32的标准库使用`IDR`而非`PID`)。 - 若名称不符,需修改为正确的寄存器访问(如`GPIOx->IDR`)。 2. **GPIO初始化要求** - 引脚必须预先配置为**输入模式**(Input Mode),否则可能无法正确读取电平。 - 需启用GPIO端口的时钟(参考代码): ```c // 示例:启用GPIOA时钟(STM32标准库) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); ``` 3. **电平稳定性处理** - 若读取机械开关等易抖动的信号,需添加**软件消抖**(参考前文提供的`stable_read`函数)。 4. **硬件兼容性** - 不同芯片的GPIO寄存器地址和位定义可能不同,需根据具体硬件调整代码。 --- #### 7. **与标准库对比** - **STM32标准库**:通常使用`GPIO_ReadInputDataBit`函数: ```c uint8_t level = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5); ``` - **本函数的优势**:直接操作寄存器,适用于自定义库或对性能敏感的裸机开发。 - **潜在风险**:寄存器名称或地址错误可能导致未定义行为。 --- ### 总结 此函数通过直接访问GPIO输入寄存器实现电平读取,适合需要底层控制的场景。使用时需确保: 1. 寄存器名称正确性 2. GPIO已初始化为输入模式 3. 硬件平台的兼容性验证 若需移植到不同平台(如STM32、ESP32等),需根据目标硬件调整寄存器操作部分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值