zoukankan      html  css  js  c++  java
  • 2-LPC1778之GPIO

    其实这篇文章主要是介绍自己为其写的GPIO库,自己借鉴了原子写的STM32,野火写的K60,还有LPC官方库,然后按照自己平时用的,然后写了一个..其实写库的主要目的是为了方便(主要是方便操作)以后自己用,还想着分享给别人用,加快项目开发的速度,,本想着后期的各种功能库都自己写一套...不过就今天看来应该到此为止了.......

    其实现在也没心情介绍了,直接说一下有什么实用的功能

    第一点哈,支持位带操作

    //IO口操作宏定义
    #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
    #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
    #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
    
    //IO口地址映射
    #define GPIO0_PIN_Addr    (LPC_GPIO0_BASE+20) 
    #define GPIO1_PIN_Addr    (LPC_GPIO1_BASE+20)
    #define GPIO2_PIN_Addr    (LPC_GPIO2_BASE+20)
    #define GPIO3_PIN_Addr    (LPC_GPIO3_BASE+20)
    #define GPIO4_PIN_Addr    (LPC_GPIO4_BASE+20)
    #define GPIO5_PIN_Addr    (LPC_GPIO5_BASE+20)    
    
    #define P0out(n)   BIT_ADDR(GPIO0_PIN_Addr,n)  //输出
    #define P0in(n)    BIT_ADDR(GPIO0_PIN_Addr,n)  //输入
    
    #define P1out(n)   BIT_ADDR(GPIO1_PIN_Addr,n)  //输出
    #define P1in(n)    BIT_ADDR(GPIO1_PIN_Addr,n)  //输入
    
    #define P2out(n)   BIT_ADDR(GPIO2_PIN_Addr,n)  //输出
    #define P2in(n)    BIT_ADDR(GPIO2_PIN_Addr,n)  //输入
    
    #define P3out(n)   BIT_ADDR(GPIO3_PIN_Addr,n)  //输出
    #define P3in(n)    BIT_ADDR(GPIO3_PIN_Addr,n)  //输入
    
    #define P4out(n)   BIT_ADDR(GPIO4_PIN_Addr,n)  //输出
    #define P4in(n)    BIT_ADDR(GPIO4_PIN_Addr,n)  //输入
    
    #define P5out(n)   BIT_ADDR(GPIO5_PIN_Addr,n)  //输出
    #define P5in(n)    BIT_ADDR(GPIO5_PIN_Addr,n)  //输入

    好处就不言而喻了,,简直太方便了和实用了

    第二点

    void GPIO_Conf_Bit(uint8_t GPIOx, uint32_t GPIO_Pinx,uint32_t mode);//配置指定引脚的模式
    void GPIO_Conf_Bits(uint8_t GPIOx, uint32_t StartPinx,uint32_t PinNum,uint32_t mode);//配置多个连续引脚的模式
    void GPIO_Init_Bit(GPIO_InitTypeDef * GPIO_InitStruct);//初始化一个引脚的模式--内部调用,用户不使用
    void GPIO_Init_Bits(GPIO_InitTypeDef *GPIO_InitStruc,uint32_t PinNum);//初始化多个连续引脚的配置--内部调用,用户不使用
    void GPIO_Dir_Bit(uint8_t GPIOx, uint32_t GPIO_Pinx,uint8_t Dir);//设置指定引脚的输入输出方向
    void GPIO_Dir_Bits(uint8_t GPIOx, uint32_t StartPinx,uint32_t PinNum,uint8_t Dir);//设置多个连续引脚的输入输出方向
    void GPIO_Write_Bit(uint8_t GPIOx, uint32_t GPIO_Pinx,uint8_t BitVal);//设置指定引脚输出高低电平
    void GPIO_Write_Bits(uint8_t GPIOx,uint32_t BitVal);//将数据写入指定的GPIO数据端口
    uint8_t GPIO_Read_Bit(uint8_t GPIOx, uint32_t GPIO_Pinx);//读取指定引脚的电平状态
    uint32_t GPIO_Read_Bits(uint8_t GPIOx);//读取指定的GPIO端口的电平状态
    void GPIO_Mask_Bit(uint8_t GPIOx,uint32_t GPIO_Pinx,uint8_t Mask);//屏蔽或清除屏蔽引脚
    void GPIO_Mask_Bits(uint8_t GPIOx, uint32_t StartPinx,uint32_t PinNum,uint8_t Mask);//屏蔽或清除屏蔽多个连续引脚

    其实有了位带操作自己感觉应该去掉上面的设置一个引脚的电平,,,不过呢!位带操作我是访问的PIN寄存器,而函数里面用的是SET和CLR

    先说第一个函数的实现过程

    先看内部

    /**
      * @brief  配置指定引脚的模式
      * @param  GPIOx:设置的端口0-5
      * @param  GPIO_Pinx:设置的引脚0-32
            * @param  mode:引脚的模式  
                                                                                                                GPIO_Mode_IFT         //无上下拉 
                                                                                                                GPIO_Mode_IPD         //内部下拉 
                                                                                                                GPIO_Mode_IPU         //内部上拉 
                                                                                                                GPIO_Mode_TRA         //转发模式
                                                                                                                GPIO_Mode_HYS         //迟滞模式
                                                                                                                GPIO_Mode_INV         //输入反向
                                                                                                                GPIO_Mode_SWI         //转换速率
                                                                                                                GPIO_Mode_OOD         //开漏输出
      * @retval None
            * @example GPIO_Conf_Bit(GPIO0,1,GPIO_Mode_IPD);//P0_1下拉
      */
    void GPIO_Conf_Bit(uint8_t GPIOx, uint32_t GPIO_Pinx,uint32_t mode)
    {
            GPIO_InitTypeDef GPIO_InitStruct;
            GPIO_InitStruct.GPIOx = GPIOx;
            GPIO_InitStruct.mode  = mode;
            GPIO_InitStruct.Pinx  = GPIO_Pinx;
            GPIO_Init_Bit(&GPIO_InitStruct);
    }

    我定义了一个结构体

    /* 端口初始化结构体 */
    typedef struct
    {
      uint8_t      GPIOx;    //引脚端口号
      uint32_t     mode;     //工作模式
      uint32_t     Pinx;     //引脚号0~31
    }GPIO_InitTypeDef;

    /**
      * @brief  初始化一个引脚的配置--用户不使用
      * @param  *GPIO_InitStruc:端口初始化结构体指针
      * @param  
            * @param  
      * @retval None
      * @example GPIO_Init_Bit(&GPIO_InitStruc);
      */
    void GPIO_Init_Bit(GPIO_InitTypeDef *GPIO_InitStruc)
    {
         switch(GPIO_InitStruc->GPIOx)
         {
                 case 0:GPIO_Type->GPIO0_Table[GPIO_InitStruc->Pinx] = GPIO_InitStruc->mode;break;
                 case 1:GPIO_Type->GPIO1_Table[GPIO_InitStruc->Pinx] = GPIO_InitStruc->mode;break;
                 case 2:GPIO_Type->GPIO2_Table[GPIO_InitStruc->Pinx] = GPIO_InitStruc->mode;break;
                 case 3:GPIO_Type->GPIO3_Table[GPIO_InitStruc->Pinx] = GPIO_InitStruc->mode;break;
                 case 4:GPIO_Type->GPIO4_Table[GPIO_InitStruc->Pinx] = GPIO_InitStruc->mode;break;
                 case 5:GPIO_Type->GPIO5_Table[GPIO_InitStruc->Pinx] = GPIO_InitStruc->mode;break;
                 default:break;
        }
    }

    然后呢

    /* 引脚初始化结构体 */
    typedef struct
    {
         __IO uint32_t GPIO0_Table[32];
         __IO uint32_t GPIO1_Table[32];
         __IO uint32_t GPIO2_Table[32];
         __IO uint32_t GPIO3_Table[32];
         __IO uint32_t GPIO4_Table[32];
         __IO uint32_t GPIO5_Table[4];
    }GPIO_Type_Config;

    LPC_ICON_BASE这个地址到LPC_ICON_BASE+32+32+32+32+32+4这个地址分别对应P0,P1,P2,P3,P4,P5的各个引脚的配置寄存器

    那么

    GPIO_Type->GPIO0_Table[0] 就是配置P0_0引脚

    GPIO_Type->GPIO1_Table[1] 就是配置P1_1引脚

    GPIO_Type->GPIO2_Table[2] 就是配置P2_2引脚

    其实写成数组也是为了便于区分是哪个端口

    因为我传入的是

    端口号  还有  引脚号后面的  模式(mode)  一开始用的枚举,后来一想为了能一下子写入多种配置,所以就宏定义的,这样的话模式或运算写入就好啦

    /*
    宏定义引脚的所有配置
    */
    #define   GPIO_Mode_IFT  (0x0000)       /* 无上下拉 */
    #define   GPIO_Mode_IPD  (0x0008)       /* 内部下拉 */
    #define   GPIO_Mode_IPU  (0x0010)       /* 内部上拉 */
    #define      GPIO_Mode_TRA  (0x0018)       /* 转发模式*/
    #define      GPIO_Mode_HYS  (0x0020)       /* 迟滞模式*/
    #define      GPIO_Mode_INV  (0x0040)       /* 输入反向*/
    #define      GPIO_Mode_SWI  (0x0200)       /* 转换速率*/
    #define   GPIO_Mode_OOD  (0x0400)       /* 开漏输出 */

    看最后一个函数

    /**
      * @brief  设置指定引脚输出高低电平
      * @param  GPIOx:设置的端口0-5
      * @param  GPIO_Pinx:设置的引脚0-32
            * @param  BitVal:0-输入低电平,1-输出高电平
      * @retval None
      * @example GPIO_Write_Bit(GPIO0,1,1);//P0_1输出高电平
      */
    void GPIO_Write_Bit(uint8_t GPIOx, uint32_t GPIO_Pinx,uint8_t BitVal)
    {
         if(BitVal)
         {
           PORT_Table[GPIOx]->SET |= (1<<GPIO_Pinx);
         }
         else
         {
           PORT_Table[GPIOx]->CLR |= (1<<GPIO_Pinx);
         }
    }

    #define GPIO_BASES {LPC_GPIO0,LPC_GPIO1,LPC_GPIO2,LPC_GPIO3,LPC_GPIO4,LPC_GPIO5}//存储地址
    
    static LPC_GPIO_TypeDef * const PORT_Table[] = GPIO_BASES;

    这个呢我是利用的他自带的结构体实现的

    LPC_GPIO_TypeDef

    /*------------- General Purpose Input/Output (GPIO) --------------------------*/
    /** @brief General Purpose Input/Output (GPIO) register structure definition */
    typedef struct
    {
      __IO uint32_t DIR;
      uint32_t RESERVED0[3];
      __IO uint32_t MASK;
      __IO uint32_t PIN;
      __IO uint32_t SET;
      __O  uint32_t CLR;
    } LPC_GPIO_TypeDef;

    原先的程序

    #define LPC_GPIO0             ((LPC_GPIO_TypeDef      *) LPC_GPIO0_BASE    )
    #define LPC_GPIO1             ((LPC_GPIO_TypeDef      *) LPC_GPIO1_BASE    )
    #define LPC_GPIO2             ((LPC_GPIO_TypeDef      *) LPC_GPIO2_BASE    )
    #define LPC_GPIO3             ((LPC_GPIO_TypeDef      *) LPC_GPIO3_BASE    )
    #define LPC_GPIO4             ((LPC_GPIO_TypeDef      *) LPC_GPIO4_BASE    )
    #define LPC_GPIO5             ((LPC_GPIO_TypeDef      *) LPC_GPIO5_BASE    )

    这样的话

    如果把P0_12置一只需要

    LPC_GPIO0->SET |= 1<<12; 

    我为了让前面这个LPC_GPIO0是个可变的,,,因为方便控制嘛
    所以才有了
    #define GPIO_BASES {LPC_GPIO0,LPC_GPIO1,LPC_GPIO2,LPC_GPIO3,LPC_GPIO4,LPC_GPIO5}//存储地址
    
    static LPC_GPIO_TypeDef * const PORT_Table[] = GPIO_BASES;
    这样的话PORT_Table[0]正好是 LPC_GPIO0 ,
    PORT_Table[1]正好是 LPC_GPIO1
    这个函数就诞生了....
    
    
    void GPIO_Write_Bit(uint8_t GPIOx, uint32_t GPIO_Pinx,uint8_t BitVal)
    {
         if(BitVal)
         {
           PORT_Table[GPIOx]->SET |= (1<<GPIO_Pinx);
         }
         else
         {
           PORT_Table[GPIOx]->CLR |= (1<<GPIO_Pinx);
         }
    }
    
    

    还有一个地方,我为了可以直接设置某些引脚的高低电平状态呢,,,,由于SET和CLR实现起来需要做判断,耽误时间,我看了下直接PIN就可以,所以就直接用的PIN

    /**
      * @brief   将数据写入指定的GPIO数据端口
      * @param   GPIOx:设置的端口0-5
      * @param   BitVal:指定端口的值写入输出数据寄存器
      * @param                    
            * @param            
      * @retval  None
      * @example GPIO_Write_Bits(GPIO0,0xffffffff);//P0_0--P0_31输出高电平
      */
    void GPIO_Write_Bits(uint8_t GPIOx,uint32_t BitVal)
    {
      PORT_Table[GPIOx]->PIN = BitVal;
    }

    读取呢

    /**
      * @brief  读取指定引脚的电平状态--如果不先设置引脚方向,读出来一直是1
      * @param  GPIOx:初始化的端口0-5
      * @param  GPIO_Pinx:读取的引脚0-32
            * @param  
      * @retval 1-状态高,0-状态低
      * @example Value = GPIO_Read_Bit(GPIO0,1,1);//读取P0_1的电平状态
      */
    uint8_t GPIO_Read_Bit(uint8_t GPIOx, uint32_t GPIO_Pinx)
    {
      return  ((PORT_Table[GPIOx]->PIN >>GPIO_Pinx)&0x01);
    }
    /**
      * @brief  读取整个端口的电平状态--如果不先设置引脚方向,读出来一直是1
      * @param  GPIOx:初始化的端口0-5
      * @param  
            * @param  
      * @retval bit=1--状态高,bit=0--状态低
      * @example Value = GPIO_Read_Bits(GPIO0);//读取GPIO0的电平状态
      */
    uint32_t GPIO_Read_Bits(uint8_t GPIOx)
    {
      return  (PORT_Table[GPIOx]->PIN);
    }
    其余的就没有什么说的了....可惜....我可能以后再也用不到了
    工程呢为了方便,把Keil和IAR建到了一块,文件的.c和.h共用,,也是为了方便实用

    对于程序的风格还是走的当年学操作系统时的代码风格,没说的,程序大了提高方便性

     结束....老感觉伤感,,,,,,,,竟然写了一篇就写到头了

     源码


    链接:http://pan.baidu.com/s/1dE2X5uT 密码:78ic

  • 相关阅读:
    linux安装kibana
    linux安装6.5.3版本elastic search
    linux非root用户安装nginx
    linux非root用户安装ncurses-devel依赖
    linux无网络情况下安装rpm包
    linux非root用户安装rabbitmq
    linux非root用户安装4.0.14版本redis
    (初)Knockout 监控属性(Observables)
    ECMAScript6
    SonarLint 代码质量管理
  • 原文地址:https://www.cnblogs.com/yangfengwu/p/6891981.html
Copyright © 2011-2022 走看看