STM32一文通(3) GPIO

作者: 熊爸天下_56c7 | 来源:发表于2020-07-26 22:55 被阅读0次

    预置知识: 开时钟

    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一文通(3) GPIO

        本文链接:https://www.haomeiwen.com/subject/wfewlktx.html