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