GPIO 输出寄存器位值未更新

Posted

技术标签:

【中文标题】GPIO 输出寄存器位值未更新【英文标题】:GPIO Output Register Bit value not updating 【发布时间】:2021-11-03 20:21:48 【问题描述】:

我刚开始学习嵌入式系统,但在我的 stm32f746ng-discovery 板上正确设置 LED 引脚时遇到了一些麻烦。我不确定我的类型转换是否正确或引脚的地址错误,但是,我相信我已经正确处理了所有内容,并且我没有在监视窗口中看到导致我的 GPIO 输出数据寄存器的值变化相信我的代码可能有问题。

为了定义寄存器及其各自的地址,这是我采用的方法:

//Referring to STM32F746xx Memory Map and Register Boundary Addresses:
#define PERIPH_BASE             (0x40000000UL)

#define AHB1PERIPH_OFFSET       (0x00020000UL)
#define AHB1PERIPH_BASE         (PERIPH_BASE + AHB1PERIPH_OFFSET)

#define GPIOI_OFFSET            (0x2000UL)
#define GPIOI_BASE              (AHB1PERIPH_BASE + GPIOI_OFFSET)

#define RCC_OFFSET              (0x3800UL)
#define RCC_BASE                (AHB1PERIPH_BASE + RCC_OFFSET)

#define RCC_AHB1EN_R_OFFSET     (0x30UL)
#define RCC_AHB1EN_R            (*(volatile unsigned int *)(RCC_BASE + RCC_AHB1EN_R_OFFSET)) //register

#define MODE_R_OFFSET           (0x00UL)
#define GPIOI_MODE_R            (*(volatile unsigned int *)(GPIOI_BASE + MODE_R_OFFSET)) //register

#define OD_R_OFFSET             (0x14UL)
#define GPIOI_OD_R              (*(volatile unsigned int *)(GPIOI_BASE + OD_R_OFFSET)) //register

#define GPIOIEN                 (1U << 0)

#define PIN_1                   (1U << 1)
#define LED_PIN                 PIN_1

上述十六进制地址是我从 stm32f746xx 数据表的内存映射/表和 stm32f74xxx 的 RM0385 参考手册中找到的。

下面的代码是我尝试更改GPIOI_OD_R寄存器的位值的主要功能:

int main(void)

    /* 1. Enable clock access for GPIOI.*/
    /* 1.1 I use the OR operator to only change the first bit instead of the whole 32bit chain. */
    RCC_AHB1EN_R |= GPIOIEN;

    /* 2. Sets PIN_1 as output.*/
    GPIOI_MODE_R |= (1U << 2);
    GPIOI_MODE_R &=~(1U << 3);

    while(1)
    
        /* 3. Sets PIN_1 high */
        GPIOI_OD_R |= LED_PIN;

    

我遇到的问题是 GPIOI_OD_R 寄存器的位值未正确更新并设置为 00 而不是 01,这是 GPIOI Pin_1 (LED) 设置为通用目的所需的值输出模式。

以上参数是我从stm32f74xxx的RM0385参考手册中得到的,如下图所示:

但是,在运行代码时,GPIOI_MODE_R 和 GPIOI_OD_R 位的值不会改变,如下图所示:

我需要正确的寄存器值才能将我的 stm32f746ng-discovery 板上的 LED PIN 设置为高电平。

我尝试将 GPIOI_MODE_R 设置操作合并为一个操作:GPIOI_MODE_R = (GPIOI_MODE_R | (1U &lt;&lt; 2)) &amp; ~(1U &lt;&lt; 3),但这会导致程序与调试器的连接松动。

我正在使用带有以下 MCU GCC 编译器设置的 STM32CubeIDE:

提前致谢,如果引用不正确,请原谅我是嵌入式系统的新手。

【问题讨论】:

【参考方案1】:

我发现了问题并意识到我为 GPIOIEN 处理了错误的位值。而不是使用:#define GPIOIEN (1U &lt;&lt; 8) 查看 GPIOIEN 的位地址,我错误地使用:#define GPIOIEN (1U &lt;&lt; 0) 查看 GPIOAEN 的位地址。

我认为这是一个非常愚蠢的错误,但我认为这可能是许多像我这样的初学者可能会犯的错误。从我的经历和解决问题的过程中,我能给出的唯一建议是,在阅读各种电路板的参考手册和数据表时,尝试更加准确和适当地集中注意力。当谈到解决问题时,我会说与您的代码保持一致很重要,这使得在调试过程中变得更加容易,因为我遵循了这种方法,我能够跟踪我的代码的每个 srep 并比较我期望的值与我实际得到的相比。

我附在下面的最终代码解决方案:

//Referring to STM32F746xx Memory Map and Register Boundary Addresses:
#define PERIPH_BASE             (0x40000000UL)

#define AHB1PERIPH_OFFSET       (0x00020000UL)
#define AHB1PERIPH_BASE         (PERIPH_BASE + AHB1PERIPH_OFFSET)

#define GPIOI_OFFSET            (0x2000UL)
#define GPIOI_BASE              (AHB1PERIPH_BASE + GPIOI_OFFSET)

#define RCC_OFFSET              (0x3800UL)
#define RCC_BASE                (AHB1PERIPH_BASE + RCC_OFFSET)

#define RCC_AHB1EN_R_OFFSET     (0x30UL)
#define RCC_AHB1EN_R            (*(volatile unsigned int *)(RCC_BASE + RCC_AHB1EN_R_OFFSET))

#define MODE_R_OFFSET           (0x00UL)
#define GPIOI_MODE_R            (*(volatile unsigned int *)(GPIOI_BASE + MODE_R_OFFSET))

#define OD_R_OFFSET             (0x14UL)
#define GPIOI_OD_R              (*(volatile unsigned int *)(GPIOI_BASE + OD_R_OFFSET))

#define GPIOIEN                 (1U << 8) // updated from (1U << 0)

#define PIN_1                   (1U << 1)
#define LED_PIN                 PIN_1

int main(void)

    /* 1. Enable clock access for GPIOI.*/
    /* 1.1 I use the OR operator to only change the first bit instead of the whole 32bit chain. */
    RCC_AHB1EN_R |= GPIOIEN;

    /* 2. Sets PIN_1 as output.*/
    GPIOI_MODE_R |= (1U << 2);
    GPIOI_MODE_R &=~(1U << 3);

    while(1)
    
        /* 3. Sets PIN_1 high */
        GPIOI_OD_R |= LED_PIN;

    

【讨论】:

PS:在对您的问题的评论中已经提到过,但最好将GPIOI_MODE_R 上的两个二进制操作结合在一个语句中,例如GPIOI_MODE_R = (GPIOI_MODE_R | (1U &lt;&lt; 2)) &amp; ~(1U &lt;&lt; 3)。这避免了中间(可能无效)状态。 (PS:我个人宁愿写GPIOI_MODE_R = (GPIOI_MODE_R &amp; ~0xC) | 0x4;先清除,然后设置所有MODER1位,但效果当然是一样的)。 @wovano,感谢您的评论,我将更新我这边的项目,并确保尽可能结合二进制操作以避免将来出现潜在的无效状态。另外,我只是想检查一下您何时说您将亲自使用GPIOI_MODE_R = (GPIOI_MODE_R &amp; ~0xC) | 0x4,这是因为这是正确的标准和正确的实施方式吗?我刚刚开始学习,我想确保我生成的代码符合正确的标准。再次感谢您非常感谢的建议。 只是为了让代码更好理解。不是对(看似)任意位进行 2 次操作,而是更明确地对寄存器的第 2 位和第 3 位进行操作。为此创建一个“掩码”变量(或定义)是很常见的。看看 STM32F7 HAL 是如何做到的:stm32f7xx_hal_gpio.c 的第 231-235 行(您还会注意到他们使用临时变量来避免对寄存器进行 2 次写入)。掩码在 stm32f746xx.h 中定义。 PS:如果你使用了那个头文件,它就会避免你的错字。我不确定您是否自己编写所有内容,因为您想学习和理解它,但总的来说,我建议您使用这些库,因为它们已经为您完成了很多工作:-) 谢谢,@wovano 我最初是自己编写所有内容,以便更好地了解所有内容的工作原理。不过,我现在确实有一个基本的了解,并且我已经开始正确使用大厅图书馆了。再次感谢你的建议。 :-)

以上是关于GPIO 输出寄存器位值未更新的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式GPIO接口及操作

STM32GPIO配置为输出和输入捕获怎么配置(HAL库)?

stm32寄存器版学习笔记01 GPIO口的配置(LED按键)

STM32输入上拉下拉 寄存器怎么设置实现

GPIO引脚的输入输出模式

Z80汇编:如何将带符号的8位值添加到16位寄存器?