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 << 2)) & ~(1U << 3)
,但这会导致程序与调试器的连接松动。
我正在使用带有以下 MCU GCC 编译器设置的 STM32CubeIDE:
提前致谢,如果引用不正确,请原谅我是嵌入式系统的新手。
【问题讨论】:
【参考方案1】:我发现了问题并意识到我为 GPIOIEN 处理了错误的位值。而不是使用:#define GPIOIEN (1U << 8)
查看 GPIOIEN 的位地址,我错误地使用:#define GPIOIEN (1U << 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 << 2)) & ~(1U << 3)
。这避免了中间(可能无效)状态。 (PS:我个人宁愿写GPIOI_MODE_R = (GPIOI_MODE_R & ~0xC) | 0x4;
先清除,然后设置所有MODER1
位,但效果当然是一样的)。
@wovano,感谢您的评论,我将更新我这边的项目,并确保尽可能结合二进制操作以避免将来出现潜在的无效状态。另外,我只是想检查一下您何时说您将亲自使用GPIOI_MODE_R = (GPIOI_MODE_R & ~0xC) | 0x4
,这是因为这是正确的标准和正确的实施方式吗?我刚刚开始学习,我想确保我生成的代码符合正确的标准。再次感谢您非常感谢的建议。
只是为了让代码更好理解。不是对(看似)任意位进行 2 次操作,而是更明确地对寄存器的第 2 位和第 3 位进行操作。为此创建一个“掩码”变量(或定义)是很常见的。看看 STM32F7 HAL 是如何做到的:stm32f7xx_hal_gpio.c 的第 231-235 行(您还会注意到他们使用临时变量来避免对寄存器进行 2 次写入)。掩码在 stm32f746xx.h 中定义。
PS:如果你使用了那个头文件,它就会避免你的错字。我不确定您是否自己编写所有内容,因为您想学习和理解它,但总的来说,我建议您使用这些库,因为它们已经为您完成了很多工作:-)
谢谢,@wovano 我最初是自己编写所有内容,以便更好地了解所有内容的工作原理。不过,我现在确实有一个基本的了解,并且我已经开始正确使用大厅图书馆了。再次感谢你的建议。 :-)以上是关于GPIO 输出寄存器位值未更新的主要内容,如果未能解决你的问题,请参考以下文章
STM32GPIO配置为输出和输入捕获怎么配置(HAL库)?