每日更新教程,评论区答疑解惑,小白也能变大神!"
目录
一.GPIO概述
CW32F030C8T6是武汉芯源半导体推出的32位微控制器,基于ARM Cortex-M0内核。其GPIO(通用输入输出)模块支持多种功能配置,包括输入模式、输出模式、复用功能及模拟模式,适用于多种应用场景。
GPIO主要特性
- 工作电压:1.8V至5.5V,兼容宽电压范围。
- 驱动能力:每个IO口可配置为推挽或开漏输出,支持最大20mA sink/source电流。
- 模式配置:
- 输入模式(浮空、上拉、下拉)。
- 输出模式(推挽、开漏)。
- 复用功能(UART、SPI、I2C等外设映射)。
- 模拟模式(ADC输入)。
- 中断支持:所有GPIO均可配置为外部中断触发源。
GPIO寄存器配置
- GPIOx_DIR:控制方向(输入/输出)。
- GPIOx_AF:配置复用功能。
- GPIOx_PUPD:设置上拉/下拉电阻。
- GPIOx_OD:开漏输出使能。
示例代码(配置GPIO为推挽输出):
#include "cw32f030_gpio.h" // 包含CW32F030 GPIO模块的头文件
void GPIO_Init() {
// 定义GPIO初始化结构体变量
GPIO_InitTypeDef GPIO_InitStruct;
// 启用HSI时钟源,并设置分频系数为6
RCC_HSI_Enable(RCC_HSIOSC_DIV6);
// 使能GPIOB的时钟
__RCC_GPIOB_CLK_ENABLE();
// 配置GPIO初始化参数
GPIO_InitStruct.IT = GPIO_IT_NONE; // 不启用中断功能
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 设置为推挽输出模式
GPIO_InitStruct.Pins = LED_GPIO_PINS; // 指定需要初始化的GPIO引脚
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 设置GPIO输出速度为高速
// 初始化指定的GPIO端口(LED_GPIO_PORT)和引脚
GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
}
二.案例
案例一.LED灯交替闪烁
解析
代码实现LED闪烁程序,主要包含主函数
main()
和延时函数Delay()
两部分。主函数
/****************************************************************************** ** \brief Main function of project ** ** \return uint32_t return value, if needed ** ** LED1, LED2闪烁 ** ******************************************************************************/ #define LED_GPIO_PORT CW_GPIOB // 定义LED连接的GPIO端口为GPIOB #define LED_GPIO_PINS GPIO_PIN_8 | GPIO_PIN_9 // 定义LED连接的引脚为PB8和PB9 int32_t main(void) { GPIO_InitTypeDef GPIO_InitStruct; // 定义GPIO初始化结构体 RCC_HSI_Enable(RCC_HSIOSC_DIV6); // 启用HSI时钟,分频系数为6 __RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟 // 配置GPIO初始化参数 GPIO_InitStruct.IT = GPIO_IT_NONE; // 无中断触发 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式 GPIO_InitStruct.Pins = LED_GPIO_PINS; // 控制PB8和PB9引脚 GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // GPIO高速模式 GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct); // 初始化GPIOB while (1) // 主循环 { GPIO_TogglePin(LED_GPIO_PORT, LED_GPIO_PINS); // 翻转PB8和PB9引脚电平 Delay(0xFFFF); // 延时约65535个时钟周期 } }
延时函数
/****************************************************************************** * @brief 循环延时 * * @param nCount 延时循环次数,值越大延时越长 ******************************************************************************/ void Delay(__IO uint16_t nCount) { /* 递减nCount值直至为0 */ while (nCount != 0) { nCount--; // 每次循环减少nCount } }
案例二.时钟输出与GPIO翻转
解析:
代码实现从PA03/PA04输出系统时钟(HCLK/PCLK),并通过PA05管脚周期性电平翻转的功能。
/** ****************************************************************************** ** \brief Main function of project ** ** \return uint32_t return value, if needed ** ** 从PA03/PA04输出HCLK及PCLK,并翻转PA05管脚 ** ******************************************************************************/ int32_t main(void) { CW_SYSCTRL->AHBEN_f.GPIOA = 1; // 使能GPIOA模块的时钟(AHB总线时钟) // 配置PA3、PA4、PA5为数字输出模式(关闭模拟功能) REGBITS_CLR(CW_GPIOA->ANALOG, bv3 | bv4 | bv5); // 清除ANALOG寄存器中的bv3/bv4/bv5位,关闭模拟功能 // 配置PA3、PA4、PA5为输出方向(GPIO模式) REGBITS_CLR(CW_GPIOA->DIR, bv3 | bv4 | bv5); // 清除DIR寄存器中的bv3/bv4/bv5位,设置为输出模式 PA03_AFx_PCLKOUT(); // 将PA03复用为PCLK(外设时钟)输出功能 PA04_AFx_HCLKOUT(); // 将PA04复用为HCLK(系统时钟)输出功能 while (1) { CW_GPIOA->TOG = bv5; // 翻转PA05的输出电平(高变低/低变高) } }
关键功能说明
时钟使能:
CW_SYSCTRL->AHBEN_f.GPIOA = 1
启用GPIOA模块的AHB总线时钟,确保GPIOA可以正常工作。GPIO模式配置:
ANALOG
寄存器操作:关闭PA3/PA4/PA5的模拟功能,使其作为数字引脚。DIR
寄存器操作:设置PA3/PA4/PA5为输出方向。引脚复用:
PA03_AFx_PCLKOUT()
:配置PA03为PCLK(外设时钟)输出引脚。PA04_AFx_HCLKOUT()
:配置PA04为HCLK(系统主时钟)输出引脚。主循环:通过
TOG
寄存器持续翻转PA05的电平,产生周期性信号。
案例三.GPIO输入功能测试
解析
当PA08输入高电平时,PA0和PA1输出高电平,否则输出低电平。
当PA09输入高电平时,PA2固定输出高电平且PA3状态翻转,否则PA2输出低电平。
int32_t main(void) { GPIO_InitTypeDef GPIO_InitStruct; // 定义GPIO初始化结构体,用于配置引脚参数 // 使能GPIOA时钟(AHB总线) CW_SYSCTRL->AHBEN_f.GPIOA = 1; // 配置PA00-PA03为推挽输出模式(高速) GPIO_InitStruct.Pins = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_Init(CW_GPIOA, &GPIO_InitStruct); // 配置PA08-PA09为上拉输入模式(无中断) GPIO_InitStruct.Pins = GPIO_PIN_8 | GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP; GPIO_InitStruct.IT = GPIO_IT_NONE; GPIO_Init(CW_GPIOA, &GPIO_InitStruct); while (1) { //----------------------------------------------------------------------- // 函数调用方式实现GPIO控制 // 检测PA08输入状态 if (GPIO_ReadPin(CW_GPIOA, GPIO_PIN_8)) { // PA08为高电平时,设置PA0和PA1为高电平 GPIO_WritePin(CW_GPIOA, GPIO_PIN_0 | GPIO_PIN_1, GPIO_Pin_SET); } else { // PA08为低电平时,清除PA0和PA1的电平 GPIO_WritePin(CW_GPIOA, GPIO_PIN_0 | GPIO_PIN_1, GPIO_Pin_RESET); } //----------------------------------------------------------------------- // 宏定义方式实现GPIO控制 // 检测PA09输入状态 if (PA09_GETVALUE()) { // PA09为高电平时,设置PA02为高电平并切换PA03状态 PA02_SETHIGH(); PA03_TOG(); // TOG表示电平翻转(Toggle) } else { // PA09为低电平时,设置PA02为低电平 PA02_SETLOW(); } } }
关键功能说明
- 时钟使能:必须先启用GPIOA的时钟才能操作对应外设。
- 初始化结构体:通过
GPIO_InitTypeDef
统一配置引脚模式、速度等参数。- 模式选择:
GPIO_MODE_OUTPUT_PP
表示推挽输出GPIO_MODE_INPUT_PULLUP
表示内部上拉输入- 两种操作方式:
- 标准库函数(如
GPIO_WritePin
)- 封装宏(如
PA02_SETHIGH()
)
案例四.GPIO中断功能测试
解析
代码实现按键控制LED开关,每次按键触发中断时对应LED状态切换。
宏定义部分
#define LED_GPIO_PORT CW_GPIOB // LED连接的GPIO端口为B组 #define LED_GPIO_PINS GPIO_PIN_8 | GPIO_PIN_9 // LED使用的具体引脚为PB8和PB9 #define KEY_GPIO_PORT CW_GPIOA // 按键连接的GPIO端口为A组 #define KEY_GPIO_PINS GPIO_PIN_1 | GPIO_PIN_2 // 按键使用的具体引脚为PA1和PA2
主函数初始化
int32_t main(void) { GPIO_InitTypeDef GPIO_InitStruct; // 定义GPIO配置结构体 __RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟 __RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟 // 配置按键引脚为输入模式 GPIO_InitStruct.IT = GPIO_IT_RISING | GPIO_IT_FALLING; // 中断触发方式:上升沿和下降沿 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式 GPIO_InitStruct.Pins = KEY_GPIO_PINS; // 指定引脚PA1和PA2 GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 速度配置(输入模式下通常无效) GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStruct); // 应用配置到GPIOA // 配置LED引脚为输出模式 GPIO_InitStruct.IT = GPIO_IT_NONE; // 无中断功能 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式 GPIO_InitStruct.Pins = LED_GPIO_PINS; // 指定引脚PB8和PB9 GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct); // 应用配置到GPIOB GPIOA_INTFLAG_CLR(bv1 | bv2); // 清除GPIOA中断标志位(bv1/bv2代表具体引脚位) NVIC_EnableIRQ(GPIOA_IRQn); // 使能GPIOA中断向量 while (1) {} // 主循环空转,实际逻辑在中断中处理 }
中断服务函数
void GPIOA_IRQHandlerCallback(void) { // 检测PA1是否触发中断 if (CW_GPIOA->ISR_f.PIN1) { GPIOA_INTFLAG_CLR(bv1); // 清除PA1中断标志 PB09_TOG(); // 翻转PB9引脚状态(控制LED) } // 检测PA2是否触发中断 if (CW_GPIOA->ISR_f.PIN2) { GPIOA_INTFLAG_CLR(bv2); // 清除PA2中断标志 PB08_TOG(); // 翻转PB8引脚状态(控制LED) } }
关键功能说明
- GPIO初始化:通过结构体配置引脚模式(输入/输出)、中断类型、速度等参数。
- 中断配置:按键引脚设置为双边沿触发,LED引脚无中断。
- 中断处理:检测具体引脚中断标志,清除标志后执行LED状态翻转(
PB08_TOG()
和PB09_TOG()
为宏定义的翻转操作)。- 硬件依赖:代码基于特定硬件库(如
CW_GPIO
),需确认库文件是否包含相关宏和函数。
案例五.GPIO按键中断滤波
解析
程序加入硬件中断滤波(150KHZ ,RC滤波) ,用于消除按键抖动(150K RC滤波)。
硬件定义
#define LED_GPIO_PORT CW_GPIOB // LED连接的GPIO端口B #define LED_GPIO_PINS GPIO_PIN_8 | GPIO_PIN_9 // LED使用的引脚8和9 #define KEY_GPIO_PORT CW_GPIOA // 按键连接的GPIO端口A #define KEY_GPIO_PINS GPIO_PIN_1 | GPIO_PIN_2 // 按键使用的引脚1和2
主函数初始化
int32_t main(void) { GPIO_InitTypeDef GPIO_InitStruct; // GPIO配置结构体 // 启用GPIOA和GPIOB的时钟 __RCC_GPIOA_CLK_ENABLE(); __RCC_GPIOB_CLK_ENABLE(); // 配置按键引脚为输入模式,启用上升沿和下降沿中断 GPIO_InitStruct.IT = GPIO_IT_RISING | GPIO_IT_FALLING; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pins = KEY_GPIO_PINS; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStruct); // 配置LED引脚为推挽输出模式,无中断 GPIO_InitStruct.IT = GPIO_IT_NONE; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pins = LED_GPIO_PINS; GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct); // 配置GPIOA中断滤波(使用150K RC滤波) GPIO_ConfigFilter(CW_GPIOA, bv1, GPIO_FLTCLK_RC150K); // 清除PA1和PA2的中断标志,并启用NVIC中断 GPIOA_INTFLAG_CLR(bv1 | bv2); NVIC_EnableIRQ(GPIOA_IRQn); while (1) // 主循环保持空转,实际逻辑在中断中处理 { } }
中断回调函数
void GPIOA_IRQHandlerCallback(void) { // 检测PA1是否触发中断(按键1动作) if (CW_GPIOA->ISR_f.PIN1) { GPIOA_INTFLAG_CLR(bv1); // 清除PA1中断标志 PB09_TOG(); // 切换PB9(LED1)的状态 } // 检测PA2是否触发中断(按键2动作) if (CW_GPIOA->ISR_f.PIN2) { GPIOA_INTFLAG_CLR(bv2); // 清除PA2中断标志 PB08_TOG(); // 切换PB8(LED2)的状态 } }
关键功能说明
- 中断配置:按键引脚配置为双沿触发中断,LED引脚为输出模式。
- 滤波设置:
GPIO_ConfigFilter
用于消除按键抖动(150K RC滤波)。- 中断处理:在回调函数中通过
ISR_f.PINx
检测具体中断源,并清除对应标志位。- LED控制:
PB08_TOG()
和PB09_TOG()
直接切换对应引脚电平。
案例六.SWD引脚转换为GPIO
主函数实现
int32_t main(void) { uint32_t tmp32; // 用于存储临时时间戳的变量 // 初始化系统滴答定时器,设置频率为8MHz,每毫秒中断一次 InitTick(8000000); // 延时2000毫秒(2秒),确保系统稳定后再操作SWD引脚 SysTickDelay(2000); // 启用GPIOA的时钟 CW_SYSCTRL->AHBEN_f.GPIOA = 1; // 将PA13和PA14从SWD功能切换为普通GPIO功能 GPIO_SWD2GPIO(); // 配置PA13和PA14为数字模式(非模拟) REGBITS_CLR(CW_GPIOA->ANALOG, bv13 | bv14); // 配置PA13和PA14为输出模式 REGBITS_CLR(CW_GPIOA->DIR, bv13 | bv14); // 设置10秒超时时间点 tmp32 = GetTick() + 10000; // 在10秒内循环操作GPIO引脚 while (GetTick() < tmp32) { PA13_SETHIGH(); // PA13输出高电平 PA14_SETHIGH(); // PA14输出高电平 PA14_SETLOW(); // PA14输出低电平 PA13_SETLOW(); // PA13输出低电平 PA14_SETHIGH(); // PA14输出高电平 PA14_SETLOW(); // PA14输出低电平 } // 重新配置PA13和PA14为UART功能 REGBITS_CLR(CW_GPIOA->ANALOG, bv13 | bv14); // 确保数字模式 REGBITS_MODIFY(CW_GPIOA->DIR, bv13 | bv14, bv13); // PA13输入,PA14输出 PA13_AFx_UART1RXD(); // PA13映射为UART1接收 PA14_AFx_UART1TXD(); // PA14映射为UART1发送 // 主循环(空操作) while (1) { ; } }
系统滴答中断处理
void SysTick_Handler(void) { /* 系统时钟计数器递增 */ uwTick += uwTickFreq; }
代码功能说明:
- 系统启动后先进行2秒延时确保稳定
- 将调试用的SWD引脚转换为GPIO功能
- 进行10秒的GPIO电平切换测试
- 最终将引脚配置为UART通信功能
- 系统滴答定时器中断服务程序维护全局计时器
案例七.GPIO中断唤醒
解析
程序默认配置进入睡眠模式,由按键(GPIO下降沿)中断唤醒,输出唤醒次数
增加:LSI时钟滤波防止按键误触发
GPIO初始化配置
#define KEY_GPIO_PORT CW_GPIOA // 定义按键使用的GPIO端口为GPIOA #define KEY_GPIO_PINS GPIO_PIN_1 | GPIO_PIN_2 // 定义按键使用的引脚为PA1和PA2 int32_t main(void) { uint8_t tmp8; // 用于计数唤醒次数的临时变量 GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化结构体 __RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟 __RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟 // 配置PA1和PA2为输入模式,下降沿触发中断 GPIO_InitStruct.IT = GPIO_IT_FALLING; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pins = KEY_GPIO_PINS; GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStruct); // 配置GPIOB高8位为推挽输出模式 GPIO_InitStruct.IT = GPIO_IT_NONE; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pins = 0xFF00; GPIO_Init(CW_GPIOB, &GPIO_InitStruct);
中断配置与唤醒逻辑
// 配置GPIOA中断滤波为LSI时钟,抑制短脉冲干扰 GPIO_ConfigFilter(CW_GPIOA, bv1 | bv2, GPIO_FLTCLK_LSI); PB09_SETHIGH(); // 设置PB9输出高电平(具体用途取决于硬件设计) // 等待PA1引脚变为低电平,防止直接进入深度睡眠导致调试锁死 while (PA01_GETVALUE()); GPIO_HighByte_Write(CW_GPIOB, 0); // 初始化GPIOB高8位输出为0 // 清除PA1和PA2的中断标志位,并开启NVIC中断 GPIOA_INTFLAG_CLR(bv1 | bv2); NVIC_EnableIRQ(GPIOA_IRQn); tmp8 = 0x00; // 唤醒计数器清零
主循环与低功耗处理
while (1) { SCB->SCR = 0x04; // 设置系统控制寄存器,允许深度睡眠 __WFI(); // 进入睡眠模式,等待中断唤醒 tmp8++; // 唤醒后计数器递增 GPIO_HighByte_Write(CW_GPIOB, tmp8); // 输出唤醒次数到GPIOB高8位 } }
中断服务函数
void GPIOA_IRQHandlerCallback(void) { if (CW_GPIOA->ISR_f.PIN1) // 检查PA1中断标志 { GPIOA_INTFLAG_CLR(bv1); // 清除PA1中断标志 } if (CW_GPIOA->ISR_f.PIN2) // 检查PA2中断标志 { GPIOA_INTFLAG_CLR(bv2); // 清除PA2中断标志 } }
关键功能说明
- 低功耗设计:通过
__WFI()
指令进入睡眠模式- 抗干扰处理:使用LSI时钟滤波防止误触发
- 调试保护:进入深度睡眠前检查GPIO状态避免锁死
- 状态指示:GPIOB高8位输出唤醒次数,便于调试观测