明解STM32—GPIO应用设计篇之API函数及配置使用技巧

科技资讯 投稿 4800 0 评论

明解STM32—GPIO应用设计篇之API函数及配置使用技巧

一、前言

        了解过STM32的GPIO相关的理论知识,这样在应用GPIO开发过程中,能更好的理解GPIO的特点,应用起来会更加的得心应手。

图1 GPIO应用设计


二、API函数

图2 GPIO库函数接口分类

1、关键参数

(1、GPIO_TypeDef* GPIOx

(2、GPIO_InitTypeDef* GPIO_InitStruct

1 typedef struct
2 {
3     uint32_t GPIO_Pin;            //GPIO端口的引脚
4     GPIOMode_TypeDef GPIO_Mode;   //GPIO的端口模式                                         
5     GPIOSpeed_TypeDef GPIO_Speed; //GPIO的输出速度频率
6     GPIOOType_TypeDef GPIO_OType; //GPIO输出时的类型
7     GPIOPuPd_TypeDef GPIO_PuPd;   //GPIO上下拉电阻设置                                       
8 }GPIO_InitTypeDef;

(a、GPIO端口的引脚:可选范围为GPIO_Pin_0~GPIO_Pin_15,也可以选所有引脚GPIO_Pin_All。

1 typedef enum
2 {
3     GPIO_Mode_IN  = 0x00, //普通IO口输入
4     GPIO_Mode_OUT = 0x01, //普通IO口输出
5     GPIO_Mode_AF  = 0x02, //管脚复用功能
6     GPIO_Mode_AN  = 0x03  //模拟输入,用于ADC功能
7 }GPIOMode_TypeDef;

        (c、GPIO的输出速度频率:当GPIO引脚用于普通功能输出或复用功能输出时,GPIO的输出速度频率,可选的输出速率如下。

1 typedef enum 2 { 3 GPIO_Low_Speed = 0x00, //GPIO_Speed_2MHz 4 GPIO_Medium_Speed = 0x01, //GPIO_Speed_25MHz 5 GPIO_Fast_Speed = 0x02, //GPIO_Speed_50MHz 6 GPIO_High_Speed = 0x03 //GPIO_Speed_100MHz 7 }GPIOSpeed_TypeDef;

        速度高的IO耗电大、噪声也大,速度低的IO耗电小、噪声也小。使用合适的速度可以降低功耗和噪声。高频的驱动电路,噪声也高,当不需要高的输出频率时,请选用低频驱动电路,这样非常有利于提高系统的EMI性能,也可以降低功耗。当然如果要输出较高频率的信号,但却选用了较低频率的速度,很可能会得到失真的输出信号。关键是GPIO的引脚速度跟应用匹配。

1 typedef enum
2 {
3     GPIO_OType_PP = 0x00, //推挽结构
4     GPIO_OType_OD = 0x01 //开漏结构
5 }GPIOOType_TypeDef;

        推挽输出时,可以输出高或者低电平;开漏输出时,如果要输出高电平,则需要在芯片内部配置上拉电阻(弱上拉)或者在芯片IO外部连接上拉电阻。

1 typedef enum
2 {
3     GPIO_PuPd_NOPULL = 0x00, //无上拉或者下拉
4     GPIO_PuPd_UP     = 0x01, //带上拉电阻
5     GPIO_PuPd_DOWN   = 0x02 //带下拉电阻
6 }GPIOPuPd_TypeDef;

        STM32芯片GPIO的上拉电阻和下拉电阻最小值,典型值和最大值如下:

(3、uint16_t GPIO_PinSource和uint8_t GPIO_AF

        GPIO_PinSource:指需配置的复用功能引脚源,可选范围GPIO_PinSource0~GPIO_PinSource15。

2、函数接口

        下面就对具体的函数接口进行逐个的介绍。由于使用的是STM32的标准库,GPIO 相关的函数及配置定义和可以调用的接口放置在官方提供的标准库文件 stm32fxx_gpio.c和头文件 stm32fxx_gpio.h 文件中。

(1、void GPIO_DeInit(GPIO_TypeDef* GPIOx;

        举例:GPIO_DeInit(GPIOA,将GPIOA端口所有引脚复位到默认状态。

(2、void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct;

        举例:将GPIOA的pin1引脚设为普通输出功能,IO驱动速率可达50MHz,推挽模式,带上拉电阻。

1 gpio_InitStruct.GPIO_Pin = GPIO_Pin_1; 2 gpio_InitStruct. GPIO_Mode = GPIO_Mode_OUT; 3 gpio_InitStruct.GPIO_Speed = GPIO_Fast_Speed; 4 gpio_InitStruct. GPIO_OType = GPIO_OType_PP; 5 gpio_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; 6 GPIO_Init(GPIOA, &gpio_InitStruct;

 (3、void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct;

1 GPIO_InitStruct->GPIO_Pin  = GPIO_Pin_All;
2 GPIO_InitStruct->GPIO_Mode = GPIO_Mode_IN;
3 GPIO_InitStruct->GPIO_Speed = GPIO_Speed_2MHz;
4 GPIO_InitStruct->GPIO_OType = GPIO_OType_PP;
5 GPIO_InitStruct->GPIO_PuPd = GPIO_PuPd_NOPULL;

        举例:使用gpio_InitStruct快速获取到了引脚的默认状态值。

1 GPIO_StructInit(&gpio_InitStruct;

(4、void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin;

        举例:锁定GPIOA的管脚pin1配置不被修改。

1 GPIO_PinLockConfig(GPIOA, GPIO_Pin_1;

(5、uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin;

        举例:读取GPIOA的pin1引脚输入电平值。

1 status = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1;

(6、uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx;

        举例:读取GPIOA端口所有引脚的输入电平值。

1 status = GPIO_ReadInputData(GPIOA;

(7、uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin;

        举例:读取GPIOA的pin1引脚输出电平值。

1 status = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1;

(8、uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx;

        举例:读取GPIOA端口所有引脚的输出电平值。

1 status = GPIO_ReadOutputData(GPIOA;

(9、void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin;

        举例:将GPIOA的pin1管脚电平置为1。

1 GPIO_SetBits(GPIOA, GPIO_Pin_1;

        也可以用于多个引脚电平的置位。

1 GPIO_SetBits(GPIOA, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;

(10、void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin;

        举例:将GPIOA的pin1管脚电平置为0。

1 GPIO_ResetBits(GPIOA, GPIO_Pin_1;

        也可以用于多个引脚电平的清零。

1 GPIO_ResetBits(GPIOA, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;

(11、void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal;

        举例:将GPIOA的pin1管脚电平置为1。

1 GPIO_WriteBit(GPIOA, GPIO_Pin_1, 1;

        也可以用于多个引脚电平操作。

1 GPIO_WriteBit(GPIOA, GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3, 1;

(12、void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal;

        举例:将GPIOA端口的所有管脚电平置为1。

1 GPIO_Write(GPIOA, 1;

(13、void GPIO_ToggleBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin;

        举例:翻转GPIOA的pin1管脚电平值。

1 GPIO_ToggleBits(GPIOA , GPIO_Pin_1;

(14、void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF;

        举例:将GPIOA的pin9管脚配置成串口USART1的功能管脚。

1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1;


三、配置流程

        配置流程主要在实际的驱动配置中对GPIO进行初始化的操作,根据实际项目应用开发中的芯片GPIO引脚的定义,进行合理的配置。图3所示为GPIO的基本配置操作流程。​

图3 GPIO配置流程

(1、使能对应GPIO的时钟

        关于STM32芯片内部整体的时钟系统,可以回顾之前明解STM32时钟系统的文章介绍。STM32的GPIO模块是挂载在芯片内部AHB1总线(AHB:高级高性能总线)上的外设,因此就需要打开GPIO在AHB1总线上对应的时钟。AHB1总线上的外设时钟开关在STM32提供的标准库函数中通过函数 RCC_AHB1PeriphClockCmd (来实现的。例如调用:

1 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE;

         这样就将GPIOA的时钟打开,也可以同时打开多个GPIO端口的时钟:

1 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB, ENABLE;

(2、引脚功能配置

用于普通IO输出时:

1 GPIO_WriteBit(GPIOA, GPIO_Pin_1, 1;//向引脚输出0或1电平,在GPIO_Init前调用 2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//设置使用引脚 3 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通IO输出 4 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//根据实际应用配置输出结构类型 5 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//根据实际应用配置输出速度 6 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//根据实际应用配置上拉或下拉电阻 7 GPIO_Init(GPIOA, &GPIO_InitStructure;//初始化PA1引脚

        需要注意的是,初始化输出电平时,需要先调用写引脚电平接口,再做初始化操作,这是因为GPIO_WriteBit是将输出的值写入寄存器输出置位/复位寄存器BSRR,BSRR寄存器复位值是0,GPIO_Init相当于将GPIO引脚初始化完打开输出开关。如果需要输出的是高电平,GPIO_WriteBit在前,GPIO_Init在后相当于在没打开开关之前就将1在BSRR中放置好,GPIO_Init将开关一打开就可以输出高电平;如果GPIO_Init在前,GPIO_WriteBit在后,GPIO_Init完会将BSRR中的0先输出,过了一个函数指令周期后调用GPIO_WriteBit才输出高电平,因此若驱动时序对函数指令周期敏感的外围器件时,可能带来驱动时序问题!

用于普通IO输入时:

1 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//设置使用引脚 2 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通IO输入 3 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//根据实际应用配置输出速度 4 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//根据实际应用配置上拉或下拉电阻 5 GPIO_Init(GPIOA, &GPIO_InitStructure;//初始化PA1引脚

用于复用功能时:

1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1; //PA9 复用为 USART1
2 GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1; //PA10复用为USART1
3 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //设置使用引脚
4 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
5 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //根据实际应用配置输出速度
6 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //根据实际应用配置输出结构类型
7 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //根据实际应用配置上拉或下拉电阻
8 GPIO_Init(GPIOA,&GPIO_InitStructure; //初始化PA9和PA10引脚

用于模拟管脚时:

1 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道 5 2 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入 3 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉 4 GPIO_Init(GPIOA, &GPIO_InitStructure;//初始化PA5引脚

        当STM32需要进行 AD( 模数  转换采样时,需要把引脚设置为模拟输入模式,模拟输入模式下,不需要连接上拉和下拉电阻,因为GPIO用于模拟功能时,引脚的上、下拉电阻是不起作用的。这个时候即使在配置了上拉或下拉电阻,也不会影响到模拟信号的输入。

(3、对GPIO引脚进行操作

用于普通IO输出时:

也可以调用相关GPIO读接口对输出类型的GPIO进行读取引脚电平的操作GPIO_ReadOutputDataBit。

用于普通IO输入时:

用于复用功能时:

用于模拟管脚时:


四、使用技巧

        在日常程序开发调试的过程中,可以简单有效的利用GPIO驱动输出高低电平来进行辅助的测试及验证工作。下面介绍几个较为常用的使用场景,如果有其它可以利用GPIO的方法和技巧,也请大家积极留言,我们一起探讨。

1 void TIM1_IRQHandler(void //定时器 1 中断服务函数
2 {
3     if(TIM_GetITStatus(TIM1,TIM_IT_Update==SET //溢出中断
4     {
5         GPIO_ToggleBits(; //IO口信号翻转操作逻辑,用于验证定时器中断频率
6     }
7     TIM_ClearITPendingBit(TIM1,TIM_IT_Update; //清除中断标志位
8 }

        (3、在不同的程序段中使用多个IO,输出高电平,通过示波器测量IO口之间输出高电平的间隔,可以确定两个程序段之间运行的准确时间。

        (4、在板卡上没有LED进行闪烁指示的情况或没有使用外部看门狗芯片的情况下,为了确认程序是否仍然在正常运行,需要留出一个IO口,用于翻转高低电平输出,后续就可以用示波器测量该信号的有无来判断程序是否死机。

        (6、在测试验证阶段,可以将某个IO引脚配置成输入模式,利用外部给的激励信号,在程序中判断读到的信号电平的高低状态,去作为逻辑判断条件进行一些代码段的验证测试。


五、总结


更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”

编程笔记 » 明解STM32—GPIO应用设计篇之API函数及配置使用技巧

赞同 (20) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽