[STM32F1] GPIO 引脚数组

[复制链接]
151|21
zhouyou99 发表于 2026-8-18 09:11 | 显示全部楼层 |阅读模式
我想要定义一个 GPIO 引脚数组,格式类似 `{{PIN1_GPIO_PORT, PIN1_PIN}, ……}`。

之后在函数里使用这个数组,实现对引脚的动态操作。

我该如何声明这个数组,又该如何配合`HAL_GPIO_WritePin`使用?

主要用来切换选择不同的 SPI 从设备。

const GpioPin_t spi_cs_pins[] =
{
    {GPIOA, GPIO_PIN_0},
    {GPIOB, GPIO_PIN_1},
    {GPIOC, GPIO_PIN_2},
};



duo点 发表于 2026-8-18 15:11 | 显示全部楼层
你给出的GpioPin_t结构体类型没定义,直接用会报错。需要先用typedef struct { GPIO_TypeDef *port; uint16_t pin; } GpioPin_t;定义好类型,数组声明才能通过编译。
classroom 发表于 2026-8-18 17:23 | 显示全部楼层
数组里的GPIOA、GPIOB这些宏,本质上是GPIO_TypeDef结构体指针。声明数组时如果加const修饰,数组会存放在Flash里,不占RAM,对STM32F1这种RAM小的芯片很友好。
cr315 发表于 2026-8-18 19:48 | 显示全部楼层
HAL_GPIO_WritePin函数的第一个参数是GPIO_TypeDef *GPIOx,你数组里存的GPIOA正好匹配;第二个参数是uint16_t GPIO_Pin,也匹配。所以遍历数组时直接传spi_cs_pins.port和spi_cs_pins.pin就行。
flycamelaaa 发表于 2026-8-18 21:12 | 显示全部楼层
定义数组时如果没指定数组大小,编译器会自动根据初始化元素数量确定。建议加个宏#define SPI_CS_COUNT (sizeof(spi_cs_pins)/sizeof(spi_cs_pins[0])),方便循环遍历。
jcky001 发表于 2026-8-18 22:52 | 显示全部楼层
这种数组方式切换CS脚,别忘了在初始化阶段把所有CS脚都配成GPIO_MODE_OUTPUT_PP并拉高。如果漏配某个引脚的时钟和模式,HAL_GPIO_WritePin写进去也没实际效果。
onlycook 发表于 2026-8-19 11:13 | 显示全部楼层
所有CS引脚必须属于同一个GPIO组才能用位带操作优化,但你的数组里分散在PA、PB、PC,所以只能用HAL_GPIO_WritePin逐脚操作,没法做并行批量切换。
powerantone 发表于 2026-8-19 13:40 | 显示全部楼层
如果SPI从设备数量固定,数组长度固定,用这个方案没问题。但如果从设备数量在运行时变化,建议把数组末尾加一个哨兵元素如{NULL, 0},循环时检测到哨兵就停止。
probedog 发表于 2026-8-19 15:09 | 显示全部楼层
HAL_GPIO_WritePin内部会判断引脚号是否合法,但不会检查端口指针是否为NULL。如果你从数组取出的port是NULL,调用函数会直接进入HardFault,调用前要确保元素都有效。
solty 发表于 2026-8-19 17:25 | 显示全部楼层
数组声明放在头文件里时,别用static修饰,否则每个包含该头文件的.c文件都会生成一份副本,导致多个CS数组副本,浪费Flash。应该用extern声明,在唯一的.c文件里定义。
spicy 发表于 2026-8-19 20:14 | 显示全部楼层
用这个数组切换CS时,推荐先拉高当前CS,再拉低目标CS,中间加几个NOP或短延时,避免SPI总线上两个设备同时被选中的瞬间冲突。
stormwind123 发表于 2026-8-19 22:32 | 显示全部楼层
你的数组只存了端口和引脚,如果后续需要每个引脚输出电平极性不一样,数组里还得加一个active_level字段,否则没法统一处理。
七毛钱 发表于 2026-8-20 12:30 | 显示全部楼层
STM32F1的GPIO端口基址在stm32f1xx_hal.h里定义的是GPIOA_BASE等数值,GPIOA本身是个指向该基址的指针宏。数组里直接存GPIOA存的是地址值,而不是端口编号,这点和8位机习惯不一样。
今天会画卧蚕吗 发表于 2026-8-20 15:20 | 显示全部楼层
使用这种数组结构时,HAL_GPIO_WritePin每次调用都会重新计算寄存器地址,比直接写GPIOA->BRR或GPIOA->BSRR慢不少。如果CS切换频率很高,建议改成内联函数或者直接用寄存器操作。
内政奇才 发表于 2026-8-20 18:48 | 显示全部楼层
你提到主要用来切换SPI从设备,那除了CS数组,建议同时定义一个current_cs_index变量记录当前选中哪个从设备。切换时先检查当前索引是否和目标索引相同,相同就不重复操作,避免无谓的引脚翻转。
才没有脸红 发表于 2026-8-20 21:40 | 显示全部楼层
如果哪天需要把CS引脚换成其他功能,数组里只存端口引脚没法标记功能变化。建议在结构体里加一个mode字段,初始化时统一配置,这样遍历数组就能一次性完成所有引脚的初始化配置。
SophiaOP 发表于 2026-8-19 11:39 | 显示全部楼层
初始化阶段确实要配时钟和模式,用HAL库的GPIO_Init函数,拉高后用数组遍历控制,小心时序。
班杰明 发表于 2026-8-20 12:06 | 显示全部楼层
注意时序,用GPIO_Init设置好时钟和模式后再用数组控制。记得检查数组元素有效性,别让NULL指针引发HardFault。
时光迷宫 发表于 2026-8-21 13:04 | 显示全部楼层
记得检查时钟和模式配置,别漏了GPIOA、GPIOB、GPIOC的时钟使能和模式设置,还有,SPI从设备数量变化时,哨兵元素记得加上。
梦境漫游者 发表于 2026-8-21 20:13 | 显示全部楼层
记得在主循环里用宏SPI_CS_COUNT来遍历,避免下标越界,同时确保数组中每个元素的端口和引脚配置都正确,别忘了先设置GPIO时钟和模式。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

192

主题

192

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部
0