STM32一文通(3) GPIO

发布者:SparklingStar最新更新时间:2025-01-08 来源: jianshu关键字:STM32  GPIO  片上外设 手机看文章 扫描二维码
随时随地手机看文章

预置知识: 开时钟

STM32 每一个片上外设资源都有自己的时钟,这些时钟被一个叫做RCC的外设统一管理,
所以,每一个片上外设想要应用第一件事就是: 开时钟!!!!!
根据系统结构图,GPIO都在APB2总线上


所以,我们要用void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)函数操作开启GPIO的时钟:


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能或者失能APB2外设时钟


一. GPIO基础知识、寄存器及库函数

二. (重要)GPIO位带操作

如果我们想读取某个推挽输出GPIO引脚现在的状态,怎么做呢?
我们可以读取它的ODR寄存器状态, 比如GPIOB的0脚可以这样读:

GPIOB->ODR ^= GPIO_Pin_0;

由于我们不喜欢寄存器操作的方式,  现在我们想把它封装起来, 怎么封装呢?

这里我们用到一个位带别名区(可以理解为一大片寄存器), 位带区把每个GPIO寄存器的每个位(每个引脚)的状态映射到了自己的寄存器中. 我们根据GPIO的寄存器地址+位(引脚)偏移,可以算出去这个寄存器的哪个映射地址中可以读到这个位(引脚)的状态, 我们称之为地址转换计算。计算公式推导过程忽略, 直接记住结果:

地址转换计算公式

 ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))


程序中, 我们定义这样一个宏:

#define BITBAND(addr, bitnum) *(unsigned int*)((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))


当我们想获取GPIOB的ODR寄存器的某个位的状态时:

#define GPIOB_ODR_Addr      (GPIOB_BASE + 0X0C)   //找到GPIOB的基地址

#define GPIOB_ODR_bitnum    0                     //想读GPIOB的ODR寄存器的第0位

#define BITBAND(addr, bitnum) *(unsigned int*)((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2)) //位带操作地址转换公式


当函数调用时:


BITBAND(GPIOB_ODR_Addr,GPIOB_ODR_bitnum)=0; //写位


a= BITBAND(GPIOB_ODR_Addr,GPIOB_ODR_bitnum); //读位


事实上,并非只有GPIO. 位带区分为外设位带区和SRAM位带区(两者的地址计算方法还不一样). 它们为STM32提供了按位读取的基础.就像51单片机里的sbit

这里举一个位带操作的完整例子:

main.c

#include 'stm32f10x.h'

#include 'led.h'

#include 'key.h'


#ifndef BITBAND

#define BITBAND(addr, bitnum) *(unsigned int*)((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2)) //位带操作地址转换公式

#endif  /*BITBAND*/


int a;

int main(void)

{

    LED_GPIO_Config();

    KEY1_GPIO_Config();

    KEY2_GPIO_Config();

    LED_G(OFF);//其实这里也可以改成LED_G_Out_Sbit=1

    LED_R(OFF);

    LED_B(OFF); 

    while(1)

    {

        if(KEY1_In_Sbit)

        {

            while(KEY1_In_Sbit);

            LED_G_Out_Sbit=!LED_G_Out_Sbit;

        }

            

        if(KEY2_In_Sbit)

        {

            while(KEY2_In_Sbit);

            LED_R_Out_Sbit=!LED_R_Out_Sbit;

        }

    }   

}


led.h

#ifndef __LED_H

#define __LED_H


#include 'stm32f10x.h'




#define LED_G_GPIO                          GPIOB

#define LED_G_Pin                           GPIO_Pin_0

#define LED_G_GPIO_ODR_Addr         (GPIOB_BASE + 0X0C)   //绿灯位带操作:找到GPIOB的基地址

#define LED_G_ODR_bitnum                0                     //绿灯位带操作:想读GPIOB的ODR寄存器的第0位

#define LED_G_Out_Sbit                          BITBAND(LED_G_GPIO_ODR_Addr,LED_G_ODR_bitnum)   //将绿灯的位定义出来了


#define LED_R_GPIO                          GPIOB

#define LED_R_Pin                           GPIO_Pin_5

#define LED_R_GPIO_ODR_Addr         (GPIOB_BASE + 0X0C)   //红灯位带操作:找到GPIOB的基地址

#define LED_R_ODR_bitnum                5                     //红灯位带操作:想读GPIOB的ODR寄存器的第5位

#define LED_R_Out_Sbit                          BITBAND(LED_R_GPIO_ODR_Addr,LED_R_ODR_bitnum)    //将宏灯的位定义出来了


#define LED_B_GPIO                          GPIOB

#define LED_B_Pin                           GPIO_Pin_1

#define LED_B_GPIO_ODR_Addr         (GPIOB_BASE + 0X0C)   //蓝灯位带操作:找到GPIOB的基地址

#define LED_B_ODR_bitnum                1                     //蓝灯位带操作:想读GPIOB的ODR寄存器的第1位

#define LED_B_Out_Sbit                          BITBAND(LED_B_GPIO_ODR_Addr,LED_B_ODR_bitnum)  //将蓝灯的位定义出来了


#define ON   1

#define OFF  0


#define LED_G(a) if(a) GPIO_ResetBits(LED_G_GPIO,LED_G_Pin); else GPIO_SetBits(LED_G_GPIO,LED_G_Pin);// 这里用了一个条件定义

#define LED_R(a) if(a) GPIO_ResetBits(LED_R_GPIO,LED_R_Pin); else GPIO_SetBits(LED_R_GPIO,LED_R_Pin);// 这里用了一个条件定义

#define LED_B(a) if(a) GPIO_ResetBits(LED_B_GPIO,LED_B_Pin); else GPIO_SetBits(LED_B_GPIO,LED_B_Pin);// 这里用了一个条件定义


void LED_GPIO_Config(void);


#endif /*__LED_H*/


key.h

#ifndef __KEY_H

#define __KEY_H


#include 'stm32f10x.h'


#define KEY1_GPIO                           GPIOA

#define KEY1_GPIO_Pin                       GPIO_Pin_0

#define KEY1_GPIO_IDR_Addr          (GPIOA_BASE + 0X08)   //key1位带操作:找到GPIOA的IDR基地址

#define KEY1_ODR_bitnum                 0                     //key1位带操作:想读GPIOA的IDR寄存器的第0位

#define KEY1_In_Sbit                                BITBAND(KEY1_GPIO_IDR_Addr,KEY1_ODR_bitnum)   //将蓝灯的位定义出来了


#define KEY2_GPIO                           GPIOC

#define KEY2_GPIO_Pin                       GPIO_Pin_13

#define KEY2_GPIO_IDR_Addr          (GPIOC_BASE + 0X08)   //key2位带操作:找到GPIOC的IDR基地址

#define KEY2_ODR_bitnum                 13                    //key2位带操作:想读GPIOC的IDR寄存器的第13位

#define KEY2_In_Sbit                                BITBAND(KEY2_GPIO_IDR_Addr,KEY2_ODR_bitnum)  //按位操作KEY2


#define KEY_ON                  1

#define KEY_OFF                 0


void KEY1_GPIO_Config(void);

void KEY2_GPIO_Config(void);


#endif


led.c和 key.c 都只有GPIO初始化函数, 这里省略了


三.  GPIO库函数

1. GPIO初始化函数:GPIO_Init

GPIO初始化用以下函数

参数1:可以看到,初始化时,需要一个GPIO_TypeDef类型的指针GPIOx
参数2:需要一个GPIO_InitTypeDef类型的指针GPIO_InitStruct
这个GPIO_InitTypeDef如何定义呢?

定义这个结构体,需要对以下参数进行初始化

  • 参数2的参数1: GPIO_Pin: 可选值

  • 参数2的参数2: GPIO_Speed

  • 参数2的参数3:

例子:

GPIO_InitTypeDef GPIO_InitStructure;  //创建GPIO_InitTypeDef类型的结构体GPIO_InitStructure

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//最高输出速率50MHz

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化外设GPIOx寄存器


2. GPIO位输出1函数:GPIO_SetBits

3. GPIO位输出0函数:GPIO_ResetBits

4. GPIO读取位输入  GPIO_ReadInputDataBit

5. GPIO读取位输出  GPIO_ReadOutputDataBit

6. GPIO整体写端口:

7. GPIO整体读端口:


关键字:STM32  GPIO  片上外设 引用地址:STM32一文通(3) GPIO

上一篇:Arduino IDE下用STM32点亮OLED屏幕
下一篇:基于STM32的(GSM+DHT11)果园环境监测系统

小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved