是否可以通过 C 变量而不是使用 GPIO_Pin_N 值来读取 and/or 写入端口?
Is it possible to read and/or write ports via a C variable instead of using GPIO_Pin_N values?
我能找到的所有示例代码都像这样访问端口:
GPIO_SetBits(GPIOE, GPIO_Pin_9 | GPIO_Pin_13);
起初看起来不错,直到我尝试通过索引 (0-7) 引用 LED(我的端口 E)。 switch
或 LUT 是一种解决方案,但我都不喜欢。是否可以声明e。 G。 uint8_t
并映射到某个端口的具体pin脚范围?
GPIO_Pin_X
常量的值定义为 16 位值中的位位置,引脚 0 为最低有效位,引脚 15 为最高有效位:
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
[...]
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
(这就是为什么您可以将它们组合在一起写入多个引脚的原因!)
如果您需要动态索引引脚,您可以使用如下宏:
#define GPIO_Pin(x) ((uint16_t) 1 << (x))
标准 Cortex-M3/M4 内存映射允许 CPU 具有所谓的 "bit-band" 区域别名,其中写入位带别名中的每个字执行自动读取-修改-写入以更改相应区域中的目标位。
以我必须提供的STM32F411 (Cortex-M4F)手册为例,显示外围区域0x40000000-0x400FFFFF
被区域0x42000000-0x43FFFFFF
中的位带别名覆盖。因此,例如在该设备上(如果我的数学没问题的话)来自 0x42420280-0x42420300
的每个单词对应于 0x40021014
处的 GPIO 端口 E 数据寄存器 GPIOE_ODR 中的一个位,因此:
volatile int *leds = (void *)0x42420280;
leds[x] = 1; /* only bit 0 of the bit-band word actually holds data */
使硬件执行与此等效的整洁抽象的操作:
volatile int *leds = (void *)0x40021014;
int val = *leds;
val |= (1 << x);
*leds = val;
如果您有合适的设备,不想一次更新多个位,并且在某些情况下不介意额外的开销与对常规寄存器的单次访问(例如编写 set/clear 寄存器而不是数据),这是一个非常巧妙的技巧。
总线是固定宽度的,您不能用软件更改该硬件,也不,您不能用软件将外围设备中固定大小的寄存器分解成更小的部分。您可以让它出现在软件中来做这样的事情,但最终由高级代码生成的真实代码仍然会根据总线或外围寄存器大小的事务进行读取-修改-写入或任何需要或支持的操作。
试图让事情变得更小,甚至使变量字节大小与寄存器的本机大小最终会产生更多或效率低下的代码。所以试着去理解为什么你会想要做这样的事情。
我最近玩的一个 stm32 有一个 clear/set 寄存器,写一个 1 可以清除或设置引脚的输出状态。在逻辑上,它只是清除或设置控制该端口输出的触发器,在逻辑上,它们 can/do 将控制的后端实现为更小的部分,但是从软件中你不能直接访问未对齐的那些,不正确大小的转移。有些公司要求您在软件中执行读取-修改-写入。因此,使用具有这些功能的 stm32(其他品牌也可能具有类似功能,有时使用地址,有时像这样,但在单独的寄存器中,一个用于设置一个用于清除)实际上使您的代码尽可能简单。
非常欢迎您为 gpio 引脚组合创建任意数量的定义,这样在您的主代码中您只需要一个助记符而不是多个 orred 在一起,如果您愿意的话。
如果可以让编译器可靠地生成正确大小的 str 和正确的组合(如果单个定义中的位可以简单地将代码转换为一 = b;情况。
我从你模糊的问题中猜测,LUT 是否是最佳解决方案。
我能找到的所有示例代码都像这样访问端口:
GPIO_SetBits(GPIOE, GPIO_Pin_9 | GPIO_Pin_13);
起初看起来不错,直到我尝试通过索引 (0-7) 引用 LED(我的端口 E)。 switch
或 LUT 是一种解决方案,但我都不喜欢。是否可以声明e。 G。 uint8_t
并映射到某个端口的具体pin脚范围?
GPIO_Pin_X
常量的值定义为 16 位值中的位位置,引脚 0 为最低有效位,引脚 15 为最高有效位:
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
[...]
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
(这就是为什么您可以将它们组合在一起写入多个引脚的原因!)
如果您需要动态索引引脚,您可以使用如下宏:
#define GPIO_Pin(x) ((uint16_t) 1 << (x))
标准 Cortex-M3/M4 内存映射允许 CPU 具有所谓的 "bit-band" 区域别名,其中写入位带别名中的每个字执行自动读取-修改-写入以更改相应区域中的目标位。
以我必须提供的STM32F411 (Cortex-M4F)手册为例,显示外围区域0x40000000-0x400FFFFF
被区域0x42000000-0x43FFFFFF
中的位带别名覆盖。因此,例如在该设备上(如果我的数学没问题的话)来自 0x42420280-0x42420300
的每个单词对应于 0x40021014
处的 GPIO 端口 E 数据寄存器 GPIOE_ODR 中的一个位,因此:
volatile int *leds = (void *)0x42420280;
leds[x] = 1; /* only bit 0 of the bit-band word actually holds data */
使硬件执行与此等效的整洁抽象的操作:
volatile int *leds = (void *)0x40021014;
int val = *leds;
val |= (1 << x);
*leds = val;
如果您有合适的设备,不想一次更新多个位,并且在某些情况下不介意额外的开销与对常规寄存器的单次访问(例如编写 set/clear 寄存器而不是数据),这是一个非常巧妙的技巧。
总线是固定宽度的,您不能用软件更改该硬件,也不,您不能用软件将外围设备中固定大小的寄存器分解成更小的部分。您可以让它出现在软件中来做这样的事情,但最终由高级代码生成的真实代码仍然会根据总线或外围寄存器大小的事务进行读取-修改-写入或任何需要或支持的操作。
试图让事情变得更小,甚至使变量字节大小与寄存器的本机大小最终会产生更多或效率低下的代码。所以试着去理解为什么你会想要做这样的事情。
我最近玩的一个 stm32 有一个 clear/set 寄存器,写一个 1 可以清除或设置引脚的输出状态。在逻辑上,它只是清除或设置控制该端口输出的触发器,在逻辑上,它们 can/do 将控制的后端实现为更小的部分,但是从软件中你不能直接访问未对齐的那些,不正确大小的转移。有些公司要求您在软件中执行读取-修改-写入。因此,使用具有这些功能的 stm32(其他品牌也可能具有类似功能,有时使用地址,有时像这样,但在单独的寄存器中,一个用于设置一个用于清除)实际上使您的代码尽可能简单。
非常欢迎您为 gpio 引脚组合创建任意数量的定义,这样在您的主代码中您只需要一个助记符而不是多个 orred 在一起,如果您愿意的话。
如果可以让编译器可靠地生成正确大小的 str 和正确的组合(如果单个定义中的位可以简单地将代码转换为一 = b;情况。
我从你模糊的问题中猜测,LUT 是否是最佳解决方案。