配置 GPIO 外设
本教程将引导您完成 GPIO(通用输入输出)外设的芯片模板配置。GPIO 是所有 MCU 外设中最基础、使用最频繁的模块,掌握 GPIO 配置方法是学习芯片模板开发的起点。
概述
GPIO UI 配置文件用于定义图形化配置界面中的 GPIO 外设参数和操作逻辑。通过创建基础配置文件和特定版本配置文件,可以为不同系列的芯片提供差异化的 GPIO 配置功能。
文件结构
ips/
└── GPIO/
├── ui/
│ ├── gpio_base.cjs # GPIO 基础配置文件(通用)
│ └── {series}.cjs # 特定系列配置文件(差异化)
└── code/
└── {version}/
├── pre.cjs # 初始化脚本(入口)
├── std.c.ejs # Standard 固件代码模板
├── hal.c.ejs # HAL 固件代码模板
└── ll.c.ejs # LL 固件代码模板
配置文件结构
GPIO UI 配置文件导出一个对象,包含以下三个主要部分:
1. project — 外设项目配置
project: {
generate_code: { value: true, fixed: false }, // 是否生成代码
rank: { value: 2, fixed: true }, // 外设排名(显示顺序)
do_not_generate_function_call: { value: false },// 是否不生成函数调用
function_name: { value: 'Studio_GPIO_Init' }, // 初始化函数名称
firmware: { // 固件类型选项
value: 'std',
options: [
{ label: 'Standard', value: 'std' },
{ label: 'HAL', value: 'hal' },
{ label: 'LL', value: 'll' }
]
},
file_name: 'gpio', // 生成文件名
ip_type: 'GPIO' // 外设类型
}
2. parameters — 外设参数配置
定义 GPIO 配置界面中的可配置参数:
mode — GPIO 模式
| 值 | 标签 | 说明 |
|---|---|---|
AIN | analog input | 模拟输入 |
IN_FLOATING | floating input | 浮空输入 |
IPD | pull-down input | 下拉输入 |
IPU | pull-up input | 上拉输入 |
OUT_OD | output open-drain | 开漏输出 |
OUT_PP | output push-pull | 推挽输出 |
AF_OD | alternate open-drain | 复用开漏输出 |
AF_PP | alternate push-pull | 复用推挽输出 |
IT_Rising | IT Rising Edge | 上升沿中断 |
IT_Falling | IT Falling Edge | 下降沿中断 |
IT_Both | IT Both Edge | 双边沿中断 |
EVT_Rising | Event Rising Edge | 上升沿事件 |
EVT_Falling | Event Falling Edge | 下降沿事件 |
EVT_Both | Event Both Edge | 双边沿事件 |
NA | n/a | 不适用 |
speed — GPIO 速度
| 值 | 说明 |
|---|---|
GPIO_OSPEED_10MHZ | 10MHz |
GPIO_OSPEED_2MHZ | 2MHz |
GPIO_OSPEED_50MHZ | 50MHz |
NA | 不适用 |
level — 输出电平
| 值 | 说明 |
|---|---|
Low | 低电平 |
High | 高电平 |
NA | 不适用 |
3. functions — 功能函数
GPIO 表格定义
GPIO 配置界面以表格形式呈现,表头定义如下:
{
name: {
label: 'Pin',
type: 'text'
},
mode: {
label: 'Mode',
type: 'select',
options: [ /* 上述14种模式 */ ]
},
speed: {
label: 'Speed',
type: 'select',
options: [ /* 上述4种速度 */ ]
},
level: {
label: 'OutputLevel',
type: 'select',
options: [ /* 上述3种电平 */ ]
},
alternate: {
label: 'Alternate',
type: 'text'
}
}
引脚选择逻辑
手动选择引脚
当用户在芯片引脚图上手动点击某个引脚并选择一个复用功能时,系统调用 headPeripheralPinAf(pin, af) 函数:
headPeripheralPinAf(pin, af) {
// 如果 af 是 Reset_State,表示复位引脚
if (af.name === 'Reset_State') {
// 从 GPIO 表中删除该引脚
this.deletePinByName(pin.name)
return
}
// 获取该复用功能的默认参数
const params = this.getPinParameter(af)
// 如果该复用功能是唯一的(不支持多引脚)
if (isUniqueAlternate(af)) {
// 删除原来绑定该功能的引脚
this.deletePinByAlternate(af.name)
}
// 更新当前引脚的复用功能
this.updatePinByAlternate(pin, af)
}
自动选择引脚
由系统自动调用 auto_set_pins(target, name) 函数:
auto_set_pins(target, name) {
if (target) {
// 启用时:查找支持该功能的空闲引脚并添加
this.addPinByAlternate(name)
} else {
// 禁用时:删除未锁定的引脚
if (!this.hasLockedPin(name)) {
this.deletePinByAlternate(name)
}
}
}
GPIO 数据合并优化
在生成 GPIO 初始化代码时,相同 PORT 下配置相同的引脚可以合并为一条初始化语句,减少代码体积:
合并前(不合并):
gpio_init(GPIOE, GPIO_MODE_OUT_OD, GPIO_OSPEED_10MHZ, GPIO_PIN_2);
gpio_init(GPIOE, GPIO_MODE_OUT_OD, GPIO_OSPEED_10MHZ, GPIO_PIN_3);
gpio_init(GPIOE, GPIO_MODE_OUT_OD, GPIO_OSPEED_10MHZ, GPIO_PIN_4);
合并后:
gpio_init(GPIOE, GPIO_MODE_OUT_OD, GPIO_OSPEED_10MHZ, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4);
合并规则:相同 PORT + Mode + Speed 的引脚可以合并。User Label、Level、Alternate 参数不影响合并判断。
代码生成流程
GPIO 初始化代码的生成遵循以下流程:
// pre.cjs 入口函数
module.exports = ({ ip, ide, locals }) => {
const global = locals.global
// 1. 获取 GPIO 初始化数据(包含所有引脚配置)
let ip_pins = global.GPIO.get_ip_gpios([
ip.$name, 'EVENTOUT', 'IR', 'MCO'
])
// 2. 转换为对应固件平台的参数
global.GPIO.set_gpio_define_map(
locals.defineMap,
ip.firmware.value,
ip_pins.pins
)
// 3. 将数据合并到 ip 对象
Object.assign(ip, ip_pins)
// 4. 生成中断处理函数
set_nvic_handlers(global, ip)
}
get_ip_gpios 函数内部流程
- 根据
gpioTypes过滤出所有符合要求的引脚 - 生成引脚描述数据(用于代码注释)
- 根据引脚 PORT 进行分组
- 对相同 PORT 下的引脚,按配置进行分组合并
- 检查引脚是否有 EXTI 中断
- 生成
resetPins和setPins(用于设置初始电平)
注意事项
- 函数命名:
auto_set_pins函数名不可修改,由程序自动调用 - 引脚可见性:系统引脚(NRST、BOOT0、SWDIO、SWCLK)默认不在表格中显示
- 引脚锁定:通过
_locked: true标记的引脚不能被删除 - 正则匹配:支持
${num}模式匹配多个实例(如TIM${num}_CH1) - 参数映射:需要配合
chip/{系列}/define.cjs中的GPIO.mode和GPIO.speed映射
