关闭

STM32F10X便利的GPIO驱动设计(C++,基于基础库)

标签: stm32
168人阅读 评论(0) 收藏 举报
分类:

目的

该驱动的目的是使用户更加简单的配置GPIO,并且封装成为类后,相较于基础库,管脚信息获取与设置更加清晰简单,层次结构更好

使用效果

#include "GPIO.h"
#include "Delay.h"

GPIO RedLed(GPIOA,1,GPIO_Mode_Out_PP,GPIO_Speed_50MHz);

int main()
{
        bool Level = false;
        RedLed.SetLevel(1);//设置管脚电平为高
        while(1)
        {
            RedLed.Toggle(); //翻转电平
            Level=RedLed.GetLevel();//获取当前管脚电平
            Delay::Ms(100);

        }
}

还可以将定于的管脚作为参数传入到其他C++驱动类中去。

基础知识

GPIO模式

  1. 输入浮空
  2. 输入上拉
  3. 输入下拉
  4. 模拟输入
  5. 开漏输出
  6. 推挽式输出
  7. 推挽式复用功能
  8. 开漏复用功能
    这里写图片描述

GPIO速率

这里写图片描述

外设的GPIO配置

引用至官方手册

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

代码中使用的库函数

1、函数GPIO_Init
这里写图片描述

2、函数GPIO_SetBits
这里写图片描述

3、函数GPIO_ResetBits
这里写图片描述

4、函数GPIO_ReadInputDataBit
这里写图片描述

代码设计

因为这个驱动相对简单,就没有进行文件设计,而直接从代码讲起。

首先第一步就是对GPOI进行初始化话了,而进行初始化需要知道GPIOx和Pin_y,管脚模式和速率。我们决定在定义类的对象的时候就进行相关配置,于是这些未知量就成为了构造函数的参数了

GPIO(GPIO_TypeDef *port,uint16_t pin,
                        GPIOMode_TypeDef mode,
                        GPIOSpeed_TypeDef speed);

构造函数的第二个参数,未使用库函数的GPIO_Pin_X这种表示方式,主要是觉得输入麻烦,要使用2号管脚直接输入2就好了。而在系统封装库中,数字和管脚不是对应的,因为要操作寄存器嘛

#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 */

于是我们就定义了一个转化数组

static const  uint16_t      mPin[16];

定义如下

const  uint16_t  GPIO::mPin[16]={GPIO_Pin_0,GPIO_Pin_1,GPIO_Pin_2,GPIO_Pin_3,GPIO_Pin_4,GPIO_Pin_5,GPIO_Pin_6,GPIO_Pin_7,                   GPIO_Pin_8,GPIO_Pin_9,GPIO_Pin_10,GPIO_Pin_11,GPIO_Pin_12,GPIO_Pin_13,GPIO_Pin_14,GPIO_Pin_15};

由于每一类GPIO开启的时钟不同,在构造函数获取到GPIOx后,以此判断开启对应的时钟。

void GPIO::RCC_Configuration()
{
    if(mPort==GPIOA)
    {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    }
    else if(mPort==GPIOB)
    {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    }
    else if(mPort==GPIOC)
    {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
    }
    else if(mPort==GPIOD)
    {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
    }
    else if(mPort==GPIOE)
    {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
    }
    else if(mPort==GPIOF)
    {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
    }
    else if(mPort==GPIOG)
    {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
    }
}

接下来构造函数就简单多了,执行上面函数后进行GPIO配置即可

GPIO::GPIO(GPIO_TypeDef *port,uint16_t pin,GPIOMode_TypeDef mode,GPIOSpeed_TypeDefspeed)
:mMode(mode),mSpeed(speed),mPort(port),mSelectPin(pin)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_Configuration();
    GPIO_InitStructure.GPIO_Pin=mPin[pin];
    GPIO_InitStructure.GPIO_Speed=speed;
    GPIO_InitStructure.GPIO_Mode=mode;
    GPIO_Init(mPort,&GPIO_InitStructure);   
}

之后就是封装使用的各个接口了,这里只编了读取管脚电平,设置管脚电平,翻转电平函数,其他函数就你们自己去写啦~

void GPIO::SetLevel(u8 level)
{
    if(level>0)
        GPIO_SetBits(mPort,mPin[mSelectPin]);
    else
        GPIO_ResetBits(mPort,mPin[mSelectPin]);
}

bool GPIO::GetLevel()
{
    if(!GPIO_ReadInputDataBit(mPort,mPin[mSelectPin]))
        return false;
    else
        return true;
}

void GPIO::Toggle()
{
    if(GPIO_ReadInputDataBit(mPort,mPin[mSelectPin]))
        GPIO_ResetBits(mPort,mPin[mSelectPin]);
    else
        GPIO_SetBits(mPort,mPin[mSelectPin]);
}

附言

和之前写的定时器一样。没有在一个封装里面加入其他东西,如输入捕获、外部中断什么的,主要原因是要经量分割各个驱动,方便维护和修改,也让结构层次清晰一些。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:10552次
    • 积分:355
    • 等级:
    • 排名:千里之外
    • 原创:26篇
    • 转载:0篇
    • 译文:0篇
    • 评论:5条
    文章分类
    最新评论