熊爸的学习时间

STM32一文通(3) GPIO

2020-07-26  本文已影响0人  熊爸天下_56c7

预置知识: 开时钟

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如何定义呢?

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

例子:

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整体读端口:

上一篇 下一篇

猜你喜欢

热点阅读