GPIO的意思是通用输入输出端口,可通过软件配置成输入或者输出端。即可以通过GPIO口输出高低电平或者通过GPIO口读入引脚状态是高电平还是低电平。大部分引脚除了当GPIO使用外,还可以复用为外设功能引脚。如UART外设与其他硬件进行数据交互。 CW32L083的每组GPIO都有20个寄存器,除了基础的配置寄存器、数据寄存器、位置位/清零寄存器、位清零寄存器、锁定寄存器、复用功能寄存器外,还配置了位翻转寄存器,中断数字滤波寄存器等,让操作更加快捷。同时提供完善的固件库函数,方便工程师快速开发测试,增强可移植性。
GPIO端口可配置成8种工作模式
4种输入模式 1.浮空输入 2.输入上拉 3.输入下拉 4.模拟输入
4种输出模式 1.开漏输出 2.推挽式输出 3.开漏复用功能 4.推挽式复用功能
GPIO功能框图
初始化GPIO可使用void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)函数,此函数在cw32l083_gpio.c中有详细的配置,其中的第一个参数GPIO_TypeDef是一个结构体类型,在cw32l083.h中有详细定义,包含着每组GPIO的20个寄存器。 typedef struct {写法 DIR_f; //输入输出方向寄存器 OPENDRAIN_f; //输出模式寄存器 PDR_f; //下拉电阻寄存器 PUR_f; //上拉电阻寄存器 AFRH_f; //复用功能寄存器高段 AFRL_f; //复用功能寄存器低段 ANALOG_f; //模拟数字配置寄存器 RISEIE_f; //上升沿中断使能寄存器 FALLIE_f; //下降沿中断使能寄存器 HIGHIE_f; //高电平中断使能寄存器 LOWIE_f; //低电平中断使能寄存器 ISR_f; //中断标志寄存器 ICR_f; //中断标志清除寄存器 LCKR_f; //配置锁定寄存器 FILTER_f; //中断数字滤波器配置寄存器 IDR_f; //输入数据寄存器 ODR_f; //输出数据寄存器 BRR_f; //位清零寄存器 BSRR_f; //位置位清零寄存器 TOG_f; //位翻转寄存器 } GPIO_TypeDef;
第二个参数GPIO_InitTypeDef也是一个结构体,在cw32l083_gpio.h中可查看其定义。 typedef struct { uint32_t Pins; uint32_t Mode; uint32_t IT; } GPIO_InitTypeDef;
下面举一个简单的例子配置一个IO口翻转,可实现闪灯实验。 int32_t main(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_HSI_Enable(RCC_HSIOSC_DIV6); __RCC_GPIOC_CLK_ENABLE(); //打开GPIOC的时钟
GPIO_InitStruct.IT = GPIO_IT_NONE; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pins = GPIO_PIN_2|GPIO_PIN_3;
GPIO_Init(CW_GPIOC, &GPIO_InitStruct);
while (1) { GPIO_TogglePin(CW_GPIOC,GPIO_PIN_2|GPIO_PIN_3); Delay(0xFFFF); //延迟函数 } } void Delay(__IO uint16_t nCount) { /* Decrement nCount value */ while (nCount != 0) { nCount--; } }
例程中使用PC02和PC03引脚进行IO口翻转实验,官方CW32L083VCT6的评估板上这两个引脚外接LED灯,可以看到LED周期闪烁的效果。 其中CW_GPIOC,GPIO_PIN_0以及GPIO_MODE_OUTPUT_PP都是定义在头文件中的宏定义,如下面第一行为CW_GPIOC端口地址映射。 #define CW_GPIOC ((GPIO_TypeDef*) GPIOC_BASE) #define GPIOC_BASE 0x48000800UL
#define GPIO_PIN_0 ((uint16_t)0x0001) /* Pin 0 selected */ #define GPIO_PIN_1 ((uint16_t)0x0002) /* Pin 1 selected */ #define GPIO_PIN_2 ((uint16_t)0x0004) /* Pin 2 selected */ #define GPIO_PIN_3 ((uint16_t)0x0008) /* Pin 3 selected */ #define GPIO_PIN_4 ((uint16_t)0x0010) /* Pin 4 selected */ #define GPIO_PIN_5 ((uint16_t)0x0020) /* Pin 5 selected */ #define GPIO_PIN_6 ((uint16_t)0x0040) /* Pin 6 selected */ #define GPIO_PIN_7 ((uint16_t)0x0080) /* Pin 7 selected */ #define GPIO_PIN_8 ((uint16_t)0x0100) /* Pin 8 selected */ #define GPIO_PIN_9 ((uint16_t)0x0200) /* Pin 9 selected */ #define GPIO_PIN_10 ((uint16_t)0x0400) /* Pin 10 selected */ #define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */ #define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */ #define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */ #define GPIO_PIN_14 ((uint16_t)0x4000) /* Pin 14 selected */ #define GPIO_PIN_15 ((uint16_t)0x8000) /* Pin 15 selected */ #define GPIO_PIN_All ((uint16_t)0xFFFF) /* All pins selected */
#define GPIO_MODE_ANALOG (0X00) #define GPIO_MODE_INPUT (0x10) #define GPIO_MODE_INPUT_PULLUP (0x11) #define GPIO_MODE_INPUT_PULLDOWN (0x12) #define GPIO_MODE_OUTPUT_PP (0x20) #define GPIO_MODE_OUTPUT_OD (0x21)
上面的例子是使用库函数配置的,也可以用寄存器操作,具体步骤如下。 //开启HSI并6分频,Sysclk=HCLK=PCLK=8MHz CW_SYSCTRL->HSI=(5<<SYSCTRL_HSI_DIV_Pos)|(*((volatile uint16_t*) RCC_HSI_TRIMCODEADDR)); CW_SYSCTRL->CR1|=SYSCTRL_BYPASS|SYSCTRL_CR1_HSIEN_Msk; //Enable HSI while ((CW_SYSCTRL->HSI & SYSCTRL_HSI_STABLE_Msk) != SYSCTRL_HSI_STABLE_Msk) //Wait stable { ; } //开启GPIOC的时钟 REGBITS_SET(CW_SYSCTRL->AHBEN, RCC_AHB_PERIPH_GPIOC);
//配置PC02和PC03为数字功能推挽输出 CW_GPIOC->ANALOG_f.PIN2 = 0; //将PC02设置为数字功能 CW_GPIOC->DIR_f.PIN2 = 0; //将PC02设置成输出模式 CW_GPIOC->OPENDRAIN_f.PIN2 = 0; //将PC02设置成推挽模式 CW_GPIOC->ANALOG_f.PIN3 = 0; //将PC03设置为数字功能 CW_GPIOC->DIR_f.PIN3 = 0; //将PC03设置为输出模式 CW_GPIOC->OPENDRAIN_f.PIN3 = 0; //将PC03设置为推挽模式 while (1) { CW_GPIOC->TOG=bv2; //PC02翻转 Delay(0xFFFF); CW_GPIOC->TOG=bv3; //PC03翻转 Delay(0xFFFF); }
下面以CW32L083为例通过配置GPIO引脚来驱动SPI2外设,查阅CW32L083的用户手册可以得知, SPI2可以通过PA00,PA01,PA02,PA03复用实现,这个就是上文提到的GPIO口的复用功能。 PA00_AFx_SPI2MISO() //将PA00复用成SPI2的MISO线 PA01_AFx_SPI2MOSI() //将PA01复用成SPI2的MOSI线 PA02_AFx_SPI2SCK() //将PA02复用成SPI2的时钟线 PA03_AFx_SPI2CS() //将PA03复用成SPI2的片选线,如需操作片选线,有特定函数
GPIO_InitTypeDef GPIO_InitStructure = {0}; GPIO_InitStructure.Pins =GPIO_PIN_1|GPIO_PIN_2 |GPIO_PIN_3; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.IT= GPIO_IT_NONE; GPIO_Init(CW_GPIOA, &GPIO_InitStructure); GPIO_InitStructure.Pins =GPIO_PIN_0; GPIO_InitStructure.Mode = GPIO_MODE_INPUT; GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
另外我们的GPIO支持外部中断,具体的配置如下,如果有不清楚的小伙伴可以查看下节CW32中断控制器,会有详细讲解。 GPIO_InitTypeDef GPIO_InitStruct = {0};
__RCC_GPIOA_CLK_ENABLE();//开启GPIOA的时钟
GPIO_InitStruct.IT = GPIO_IT_RISING | GPIO_IT_FALLING; //上升沿或者下降沿触发 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pins =GPIO_PIN_1;
GPIO_Init(CW_GPIOA, &GPIO_InitStruct);
//清除PA01中断标志并使能NVIC GPIOA_INTFLAG_CLR(bv1); NVIC_EnableIRQ(GPIOA_IRQn);
上面的例程即可实现外部输入中断,PA01在接收到上升沿或者下降沿的信号的时候会进入到中断函数中去执行相关的程序,具体的例程可以参考官方CW32L083的gpio_int_filter例子。 下面介绍的是CW32L083的GPIO所相关的函数及其功能。 1.void GPIO_SWD2GPIO(void); 将PA13/PA14 SWD设置成GPIO
2.void GPIO_GPIO2SWD(void); 将PA13/PA14设置成SWD
3.void GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pins); 锁定指定引脚配置项,可同时设置多个引脚
4.void GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pins); GPIO去初始化,可同时设置多个引脚或单个引脚
5.void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init); GPIO 初始化,可同时设置多个引脚或单个引脚
6.void GPIO_ConfigFilter(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pins, uint32_t FltClk); 配置端口滤波
7.void GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pins, GPIO_PinState PinState); 设置指定引脚电平,可同时设置多个引脚
8.void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t Value); 设置指定引脚端口的输出值
9.void GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pins); 翻转指定引脚电平,可同时设置多个引脚
10.GPIO_PinState GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); 获取一个引脚的电平
以上是CW32通用输入输出端口的介绍。 |