HC32F4A0 上有两个最容易踩坑点:
-
GPIO_FUNC
引脚复用功能号,把普通 IO 切换为外设引脚(SPI/USART/PWM 等)。
-
FCG
外设时钟门控(Function Clock Gating),外设必须打开 FCG 时钟,模块才工作;不打开,寄存器读写无效,相当于 STM32 的 RCC 时钟使能。
一、GPIO_FUNC 引脚复用功能
1、API 函数
/*初始化IO电气属性(上下拉、驱动能力、输入输出)*/
GPIO_Init(port,pin,&stcGpioInit);
/*设置引脚复用功能,FUNC_xx数字来自芯片手册【引脚复用功能表】*/
GPIO_SetFunc(GPIO_PORT_E, GPIO_PIN_03, GPIO_FUNC_46);
GPIO_FUNC_0:普通 GPIO 功能(默认)
GPIO_FUNC_1 ~ GPIO_FUNC_63:各种外设复用功能,每个引脚支持多个不同外设映射。
FUNC 数字不能凭记忆写!必须查阅数据手册《复用功能表》。例如 SPI6:
- PE3 SCK → FUNC46
- PE4 MOSI → FUNC47
- PE5 MISO → FUNC48
- PE6 NSS → FUNC49
写错 FUNC 号:外设完全没有波形输出,寄存器配置全部正常,硬件无输出。
2、GPIO_SetFunc 参数说明
void GPIO_SetFunc(uint8_t u8Port, uint16_t u16Pin, uint8_t u8Func, en_subfunc_t enSubFunc = PIN_SUBFUNC_DISABLE);
u8Func:就是 GPIO_FUNC_XX;
enSubFunc:子功能,绝大多数场景填 PIN_SUBFUNC_DISABLE 关闭子功能。
3、典型错误场景
- 只调用
GPIO_Init(),忘记调用 GPIO_SetFunc:引脚仍然是普通 GPIO,外设完全不工作。
- FUNC 号抄错:例如把 SPI6 的 PE3 写成 FUNC45,不是 46,没有 SCK 时钟输出。
- 一个引脚同时配置两个外设 FUNC:硬件不支持,一个引脚同一时刻只能选一个主 FUNC。
4、查询 FUNC 号的正确位置
数据手册 DS_HC32F4A0Series,表格 Alternate function multiplexing(引脚复用功能)。
表格:行 = 引脚,列 = FUNC 编号,单元格写外设名称,找到外设对应的 FUNC 数字。
示例片段(手册表格):
| Pin |
FUNC0 |
FUNC7 |
FUNC46 |
FUNC47 |
| PE3 |
GPIO |
‑ |
SPI6_SCK |
‑ |
PE3 用作 SPI6_SCK,FUNC=46。
二、FCG 外设时钟门控 FCG0/FCG1/FCG2/FCG3
HC32F4A0 外设时钟分为 4 组 FCG0~FCG3,每组有大量外设时钟位。函数:
//FCG0组时钟开关
FCG_Fcg0PeriphClockCmd(mask, ENABLE/DISABLE);
//FCG1组时钟开关
FCG_Fcg1PeriphClockCmd(mask, ENABLE/DISABLE);
//FCG2组时钟开关
FCG_Fcg2PeriphClockCmd(mask, ENABLE/DISABLE);
//FCG3组时钟开关
FCG_Fcg3PeriphClockCmd(mask, ENABLE/DISABLE);
宏命名规则:FCGx_PERIPH_外设名,例:FCG0_PERIPH_DMA1、FCG1_PERIPH_SPI6、FCG3_PERIPH_USART1。
FCG 分组总览
| 分组 |
典型外设 |
| FCG0 |
DMA1/DMA2、AOS、DCU、FMAC(数据运算协处理器) |
| FCG1 |
SPI1‑SPI6、ETH、QSPI、SDIOC、CAN、I2S |
| FCG2 |
GPIO 端口、TMR0/TMR2/TMR4/TMR6/TMRA、CRC、CMP、OTS |
| FCG3 |
USART1~USART10、I2C1‑I2C6 |
重要高频外设归类速查:
- DMA+AOS → FCG0
- SPI → FCG1
- GPIO、TMR6、TMRA(PWM) → FCG2
- USART 串口、I2C → FCG3
//DMA1 + AOS 时钟打开
FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_DMA1 | FCG0_PERIPH_AOS, ENABLE);
//SPI6时钟打开
FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_SPI6, ENABLE);
//TMR6_1定时器时钟打开
FCG_Fcg2PeriphClockCmd(FCG2_PERIPH_TMR6_1, ENABLE);
//USART1串口时钟打开
FCG_Fcg3PeriphClockCmd(FCG3_PERIPH_USART1, ENABLE);
//GPIO端口时钟
FCG_Fcg2PeriphClockCmd(FCG2_PERIPH_GPIO, ENABLE);
FCG 使用核心要点
- 任何片上外设,使用前必须打开对应的 FCG 时钟;关闭 FCG,外设停止工作,寄存器读写出错。
- SysTick 是 Cortex‑M4 内核定时器,不受 FCG 控制,不需要开 FCG 时钟。
- 写保护:FCG 寄存器属于受
LL_PERIPH_WE 写保护的寄存器;main 函数一开始必须 WE 解锁,否则 FCG_FcgXPeriphClockCmd 设置时钟无效!
LL_PERIPH_WE(LL_PERIPH_FCG);
FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_SPI6, ENABLE);
LL_PERIPH_WP(LL_PERIPH_FCG);
忘记 WE 解锁,调用时钟使能函数,但是时钟根本没有打开,外设完全不工作。
踩坑现象(忘记开 FCG)
- 函数返回 LL_OK,没有报错;但是外设无输出、不计数、不收发。
- 调试看外设寄存器全部读出来是 0。
三、GPIO_FUNC + FCG 完整配置范式(模板,以 SPI6 主机为例)
/*1、解锁写保护,操作FCG*/
LL_PERIPH_WE(LL_PERIPH_FCG);
/*2、打开SPI6外设时钟 FCG1组*/
FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_SPI6, ENABLE);
LL_PERIPH_WP(LL_PERIPH_FCG);
/*3、GPIO电气初始化(驱动能力、输入输出)*/
stc_gpio_init_t stcGpioInit;
GPIO_StructInit(&stcGpioInit);
stcGpioInit.u16PinDrv = PIN_HIGH_DRV;
GPIO_Init(GPIO_PORT_E, GPIO_PIN_03|GPIO_PIN_04|GPIO_PIN_05|GPIO_PIN_06, &stcGpioInit);
/*4、设置引脚复用功能,FUNC号来自手册复用表!*/
GPIO_SetFunc(GPIO_PORT_E, GPIO_PIN_03, GPIO_FUNC_46); //SCK
GPIO_SetFunc(GPIO_PORT_E, GPIO_PIN_04, GPIO_FUNC_47); //MOSI
GPIO_SetFunc(GPIO_PORT_E, GPIO_PIN_05, GPIO_FUNC_48); //MISO
GPIO_SetFunc(GPIO_PORT_E, GPIO_PIN_06, GPIO_FUNC_49); //NSS
/*5、然后调用SPI_Init初始化外设寄存器*/
SPI_Init(CM_SPI6, &stcSpiInitCfg);
SPI_Cmd(CM_SPI6, ENABLE);
流程顺序总结:WE 解锁 → 打开 FCG 外设时钟 → GPIO_Init 电气属性 → GPIO_SetFunc 设置复用 FUNC → 外设初始化 → 使能外设
四、STM32 对比记忆
| 项目 |
HC32F4A0 |
STM32 |
| 外设时钟 |
FCG_FcgXPeriphClockCmd |
__HAL_RCC_XXX_CLK_ENABLE() |
| 引脚复用 |
GPIO_SetFunc(port,pin,FUNC_xx) |
GPIO_InitTypeDef.Alternate |
| 写保护 |
FCG 寄存器受 LL_PERIPH_WE 解锁 |
无写保护,直接配置 RCC |
五、排错调试清单(外设不工作按顺序排查)
LL_PERIPH_WE 是否解锁 FCG;
- 是否调用
FCG_FcgXPeriphClockCmd 打开对应外设时钟;
- GPIO 是否执行
GPIO_Init 初始化 IO 电气属性;
- 是否执行
GPIO_SetFunc,FUNC 功能号是否和手册完全一致;
- 外设结构体初始化,外设
XXX_Cmd(ENABLE) 是否打开外设使能。
类似问题也可以到云栈社区搜索或讨论。