CH5xx 芯片的 GPIO 使用说明以及注意事项 ...... 矜辰所致 ...... 增加晶振引脚的说明 2025/12/5 ...... 增加中断标志寄存器的读取说明 2026/3/2 ...... 增加GPIO上电默认状态说明 2026/9/30前言
官方并没有单独为 GPIO 写一个示例(主要是太简单了= =!)但依然有些小伙伴一个简单的 GPIO 中断使用起来都出现问题。
另外对于新学习一款芯片,需要知道有没有哪些 “ 特殊 ” 的 GPIO 引脚,这种往往是使用者最关心也最容易踩坑的地方。所以本文我们内容就是说明一下 CH5xx 芯片 GPIO 如何使用中断,以及芯片在 GPIO 口使用注意事项。
我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!
目录
- 前言
- 一、 基础介绍
- 1.1 模式设置
- 1.2 电平输出读取
- 1.3 重映射
- 1.4 中断设置
- 1.5 复位默认状态
- 二、 GPIO 中断示例
- 2.1 中断使用注意点
- 2.2 关于中断标志寄存器的读取
- 三、GPIO 引脚说明
- 3.1 特殊引脚
- 3.2 晶振引脚
- 结语
一、 基础介绍
本文以 CH585 为例介绍,其他芯片使用方式类似。
GPIO 基础功能实在是太简单了,随便打开一个工程,都会有串口初始化,随便一跳转 GPIO 操作函数都出来了。 比如GPIOA_SetBits(GPIO_Pin_14);跳转到定义CH58x_gpio.h文件里面,里面包括使用者需要了解的 GPIO 操作的所有函数,都有注释。
1.1 模式设置
模式设置:
voidGPIOB_ModeCfg(uint32_tpin,GPIOModeTypeDef mode);...typedefenum{GPIO_ModeIN_Floating,//浮空输入GPIO_ModeIN_PU,//上拉输入GPIO_ModeIN_PD,//下拉输入GPIO_ModeOut_PP_5mA,//推挽输出最大5mAGPIO_ModeOut_PP_20mA,//推挽输出最大20mA}GPIOModeTypeDef;示例:
GPIOA_ModeCfg(GPIO_Pin_14,GPIO_ModeOut_PP_5mA);//推挽输出1.2 电平输出读取
电平设置针对输出而言:
#defineGPIOA_ResetBits(pin)(R32_PA_CLR|=pin)//输出低电平#defineGPIOA_SetBits(pin)(R32_PA_OUT|=pin)//输出高电平#defineGPIOA_InverseBits(pin)(R32_PA_OUT^=pin)//翻转IO电平读取针对输入而言:
#defineGPIOA_ReadPort()(R32_PA_PIN)//读取PA口所有IO状态#defineGPIOA_ReadPortPin(pin)(R32_PA_PIN&(pin))//读取PA口指定IO状态示例:
GPIOA_SetBits(GPIO_Pin_14);GPIOA_ReadPortPin(GPIO_Pin_14);1.3 重映射
重映射使用GPIOPinRemap,官方代码直接有对应说明:
重映射部分为了兼容系列不同型号的芯片,有部分没有写,比如 I2C , UART3 ,具体查看自己使用芯片的使用手册
**********************************************************************@fn GPIOPinRemap**@brief 外设功能引脚映射**@param s-是否使能映射*@param perph-RB_RF_ANT_SW_EN-RF antennaswitchcontrol output on PA4/PA5/PA12/PA13/PA14/PA15*RB_PIN_U0_INV-RXD0/RXD0_/TXD0/TXD0_ invert input/output*RB_PIN_INTX-INTX:INT24/INT25 PB8/PB9->INT24_/INT25_ PB22/PB23*RB_PIN_MODEM-MODEM:PA6/PA7->PB12/PB13*RB_PIN_I2C-I2C:PB14/PB15->PB14/PB15*RB_PIN_PWMX-PWMX:PA12/PA13/PB7/PB6/PB4->PA6/PA7/PB3/PB2/PB1*RB_PIN_SPI0-SPI0:PA12/PA13/PA14/PA15->PB12/PB13/PB14/PB15*RB_PIN_UART3-UART3:PA4/PA5->PA4/PA5*RB_PIN_UART2-UART2:PB22/PB23->PA6/PA7*RB_PIN_UART1-UART1:PA8/PA9->PB12/PB13*RB_PIN_UART0-UART0:PB4/PB7->PA15/PA14*RB_PIN_TMR3-TMR2:PB22->PB22*RB_PIN_TMR2-TMR2:PA11->PB11*RB_PIN_TMR1-TMR1:PA10->PB10*RB_PIN_TMR0-TMR0:PA9->PB23**@returnnone*/voidGPIOPinRemap(FunctionalState s,uint16_tperph)示例:
//串口GPIOA_SetBits(GPIO_Pin_14);GPIOPinRemap(ENABLE,RB_PIN_UART0);GPIOA_ModeCfg(GPIO_Pin_15,GPIO_ModeIN_PU);GPIOA_ModeCfg(GPIO_Pin_14,GPIO_ModeOut_PP_5mA);UART0_DefInit();//I2CGPIOPinRemap(ENABLE,RB_PIN_I2C);GPIOB_ModeCfg(GPIO_Pin_21|GPIO_Pin_20,GPIO_ModeIN_PU);1.4 中断设置
CH585 支持中断的引脚有 PA0-PA15,PB0-PB15,PB22,PB23,所以PB17不支持GPIO中断,PB22 和PB23 与 PB8 和 PB9 属于映射关系,使用 PB22 中断功能需要调用映射函数。
中断配置:
voidGPIOA_ITModeCfg(uint32_tpin,GPIOITModeTpDef mode);typedefenum{GPIO_ITMode_LowLevel,//低电平触发GPIO_ITMode_HighLevel,//高电平触发GPIO_ITMode_FallEdge,//下降沿触发GPIO_ITMode_RiseEdge,//上升沿触发}GPIOITModeTpDef;中断标志状态读取:
#defineGPIOA_ReadITFlagPort()(R16_PA_INT_IF)//读取PA口所有IO中断标志状态#defineGPIOA_ReadITFlagBit(pin)(R16_PA_INT_IF&(pin))//读取PA口指定IO中断标志状态清楚中断标志位:
#defineGPIOA_ClearITFlagBit(pin)(R16_PA_INT_IF=pin)中断示例:
/* 配置唤醒源为 GPIO - PA5 */GPIOA_ModeCfg(GPIO_Pin_5,GPIO_ModeIN_PU);GPIOA_ITModeCfg(GPIO_Pin_5,GPIO_ITMode_FallEdge);// 下降沿唤醒PFIC_EnableIRQ(GPIO_A_IRQn);__INTERRUPT __HIGH_CODEvoidGPIOA_IRQHandler(void){GPIOA_ClearITFlagBit(GPIO_Pin_5);}1.5 复位默认状态
芯片引脚的复位默认状态都是: 浮空输入。
通过手册 GPIO 相关寄存器列表可知:
二、 GPIO 中断示例
在文章上面我们已经介绍过中断设置函数以及使用示例,虽然官方没有单独的 GPIO 中断示例,但是在 PM 例程里面用到了 GPIO 中断:
这里我们自己写一个简单的程序,大家可以先看一下,按照我们一般的理解,下面的程序应该是没问题的:
如果上面的程序大家能发现问题,基本上就掌握了 CH5xx 芯片 GPIO 的用法了。
2.1 中断使用注意点
上面的程序只能运行一次,就 " 跑飞 " 了,有几个问题。
中断必须在前面加__INTERRUPT。
告诉编译器软件自动压栈,要不然程序进去一次就跑飞。
但是在前面的代码即便,我们把__INTERRUPT加上还是会有问题,会发现 while 里面的if(test_flag)永远为假, 但是我们中断中PRINT("!!!!!!\r\n");是执行到了的,说明在中断中test_flag = 1是有的,这就是上面程序的第二个问题。
在中断中改变的变量必须在前面加volatile修饰 。
修改完毕我们测试才能正常,但是对于我们使用 CH5xx 芯片来说,我们除了在中断前面加__INTERRUPT还会加上__HIGH_CODE,把代码搬运到 RAM 中运行提高响应速度。
不加__HIGH_CODE编译后的 .map 文件中(中断响应函数存放地址为 Flash 区域):
.text.GPIOA_IRQHandler0x0000000000000cea0x2c./src/Main.o0x0000000000000ceaGPIOA_IRQHandler加了__HIGH_CODE编译后的 .map 文件中(中断响应函数存放地址为 RAM 区域):
*(.highcode)*(.highcode.*).highcode.GPIOA_IRQHandler0x00000000200001f80x30./src/Main.o0x00000000200001f8GPIOA_IRQHandler对于这个简单的代码,我测试了加不加都可以,但是强烈建议大家加上,只要是中断,__INTERRUPT __HIGH_CODE都加上。
最后这个 GPIO 中断测试代码如下:
#include"CH58x_common.h"voidDebugInit(void){GPIOA_SetBits(GPIO_Pin_14);GPIOPinRemap(ENABLE,RB_PIN_UART0);GPIOA_ModeCfg(GPIO_Pin_14,GPIO_ModeOut_PP_5mA);UART0_DefInit();}volatileuint8_ttest_flag=0;intmain(){HSECFG_Capacitance(HSECap_18p);SetSysClock(SYSCLK_FREQ);DebugInit();GPIOA_ModeCfg(GPIO_Pin_5,GPIO_ModeIN_PU);//上拉输入GPIOA_ITModeCfg(GPIO_Pin_5,GPIO_ITMode_FallEdge);//下降沿双向触发PFIC_EnableIRQ(GPIO_A_IRQn);//中断使能PRINT("GPIO TEST begin...\r\n");while(1){if(test_flag){// PFIC_DisableIRQ(GPIO_A_IRQn);PRINT("IRQ ON..\r\n");test_flag=0;// PFIC_EnableIRQ( GPIO_A_IRQn );}}}__INTERRUPT __HIGH_CODEvoidGPIOA_IRQHandler(void){if(R16_PA_INT_IF&GPIO_Pin_5)// check enable gpio it flag{DelayMs(10);//简单测试使用if(!GPIOA_ReadPortPin(GPIO_Pin_5)){R16_PA_INT_IF=GPIO_Pin_5;//clear it flagPRINT("!!!!!!\r\n");test_flag=1;}}}2.2 关于中断标志寄存器的读取
GPIOA 和 GPIOB 分别有一个中断标志寄存器,官方给好了宏定义可以直接读取:
但是实际上直接读取这两个寄存器是不准确,无意义的,如下图:
有时候因为中断问题,想把寄存器的值打印出来差错,不能这样直接读取。
需要与上中断使能寄存器,如下方式,方可读到正常值:
uint16_ttt=R16_PB_INT_IF&R16_PB_INT_EN;uint16_ttt2=R16_PA_INT_IF&R16_PA_INT_EN;PRINT("PB_INT_IF = 0x%04X\n",tt);PRINT("PA_INT_IF = 0x%04X\n",tt2);在中断中也可以验证一下,具体效果如下图 :
三、GPIO 引脚说明
3.1 特殊引脚
PA12引脚,在上电/上电复位时会产生一个15ms左右的高电平。这是芯片原厂产测用的信号,无法规避。
谨慎使用此 IO 作为输出。PA9引脚(CH585/4M 型号,其他型号串口免按键引脚查看下一点的推荐博文),在上电时会产生一个40~50ms的高电平。这是由于启用了串口免按键下载,原厂 boot 中配置了 UART 功能,TX 脚空闲状态上拉了。可以在烧录前配置关闭串口免按键下载。
PB22为默认的 BOOT 引脚(CH585/CH592 部分/CH579),低电平触发
几个特殊的型号的默认 BOOT 引脚:
CH592DPB15低电平触发
CH591DPB7低电平触发
CH572RPA4低电平
CH572D/572Q/570D/570QPA1高电平且PA0是低电平
关于下载引脚大家请参考博文:www.cnblogs.com/risc5-ble/p/18868018
PB22只有上电时会检测低电平进boot,进APP后就是普通引脚
PB22一直接地会导致上电后过60秒(BOOT 超时)才能运行用户代码。PB23为复位引脚,若做输入输出使用时在使用 ISP 下载时需关闭 RST 的使能,否则可能导致下次无法正常下载或代码一直复位。
GPIO 中断标志寄存器,只有16位,也就是说只有 PA0 ~ PA15、PB0~ PB15 是可以被用作GPIO中断的。
PB22和PB23与PB8和PB9属于映射关系,使用 PB22 中断功能需要调用映射函数。
在 IO 比较少的封装中,比如说 CH592F,MCU上只有PB22\PB23而没有PB8\PB9,此时PB22\PB23两脚本身就是支持 GPIO 中断的,无需额外配置;GPIO 的灌入电流和输出电流,最大值均为100mA。
上面提到的PA9和PB23的配置,在 ISP 下载工具中对应的位置如下:
3.2 晶振引脚
对于高频晶振:
CH5xx 系列芯片必须要接,而且引脚也不能用于其他用途,这个型号对应大家自行查看手册:
32MHz 用于芯片的基本外设,整个射频 PLL 的参考时钟, ISP 下载,需要高精度。
对于低频晶振:
CH572/ CH570 是没有外接低频晶振引脚的。
对于 CH585/CH584/CH592 来说,他们的引脚都是PA11和PA10:
有些芯片也没有(这个自己查看手册),或者说有些芯片有这个引脚,但是自己不接外接晶振,也可以作为普通的 GPIO 引脚。但是要注意,如果需要使用蓝牙主机功能,是一定需要外接 32.768Khz 晶振的。
蓝牙主机功能,对蓝牙数据传输收发包的时间要求较高。
如果后期有一些特殊的芯片需要说明再来更新。
结语
本文主要介绍了一下 蓝牙芯片 CH5xx 系列 GPIO 使用,给出了一个中断使用的示例,以及芯片一些特殊 GPIO 口的说明,希望对大家使用芯片的时候有帮助。
好了,本文就到这里。谢谢大家!