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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM