STM32F103GPIO的配置
STM32的IO口丰富,但是无论通过使用ST提供的官方库还是直接寄存器配置端口都十分的麻烦,所以通过学习,我将32的部分IO做了一个集中的整理,代码如下:
#ifndef __GPIO_H
#define __GPIO_H
#include "sys.h"
#include "stdio.h"
#define PX(PX_n) (PX_n>>4) //获取模块号 PX_n/16
#define PN(PX_n) (PX_n&0x0f)//获取引脚号 PX_n%16
typedef enum
{
/* PA端口 0~15 */
PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA13, PA14, PA15,
/* PB端口 15~31 */
PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15,
/* PC端口 32~47 */
PC0, PC1, PC2, PC3, PC4, PC5, PC6, PC7, PC8, PC9, PC10, PC11, PC12, PC13, PC14, PC15,
/* PD端口 48~63*/
PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7, PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15,
/* PE端口 64~79*/
PE0, PE1, PE2, PE3, PE4, PE5, PE6, PE7, PE8, PE9, PE10, PE11, PE12, PE13, PE14, PE15,
} GPIO_PIN;
//定义管脚方向
typedef enum
{
GPI = 0, //定义管脚输入方向 in
GPO = 3, //定义管脚输出方向 out 注:输出默认是50MHZ
GPI_analog = 0x0, //模拟输入0000
GPI_floated = 0x4, //浮空输入0100
GPI_UP_DOWN = 0x8, //上拉/下拉输入1000
GPI_KEEP =0xC, //保留1100
GPO_Push_pull=0x3, //推挽输出0011
GPO_Open_drain=0x7, //开漏输出0111
GPO_Push_pull_reuse=0xB, //复用推挽输出1011
GPO_Open_drain_reuse=0xF, // 复用开漏输出1111
} GPIO_CFG; //最低位为0,肯定是输入;GPI_UP 和 GPI_UP_PF的最低位为1,其他为输出
void GPIO_Init(GPIO_PIN n, GPIO_CFG dir, uint8_t data);
void GPIO_change(GPIO_PIN n, u8 data);
u32 GPIO_find(GPIO_PIN n);
#endif
以上是头文件部分,主要定义了32部分引脚并加以命名,以下是源文件部分:
void GPIO_Init(GPIO_PIN n, GPIO_CFG dir, u8 data)
{
//获取GPIO组、引脚
u8 PX,PN;
PX=PX(n);//获取模块号 PX_n/16
PN=PN(n);//获取引脚号 PX_n%16
//初始化时钟
RCC->APB2ENR|=1<<( PX+2 );
switch (PX)
{
case 0://A
{
//端口配置
if(PN<=7)//低八位
{
GPIOA->CRL&=~(0XF<<(PN*4));//位先清零,特别强调清零必须先取反
GPIOA->CRL|=( (dir) <<(PN*4));//向右移PN位
}
else
{
GPIOA->CRH&=~(0XF<<( PN-8)*4 );//位先清零
GPIOA->CRH|=( (dir) <<( ( PN-8)*4 ) );//向右移PN位
}
//初始状态配置
GPIOA->ODR|=data<<PN; //PA 输出data
break;
}
case 1://B
{
//端口配置
if(PN<=7)//低八位
{
GPIOB->CRL&=~(0XF<<(PN*4));//位先清零
GPIOB->CRL|=( (dir) <<(PN*4));//向右移PN位
}
else
{
GPIOB->CRH&=~(0XF<<( PN-8)*4 );//位先清零
GPIOB->CRH|=( (dir) <<( PN-8)*4 );//向右移PN位
}
//初始状态配置
GPIOB->ODR|=data<<PN; //PB 输出data
break;
}
case 2://C
{
//端口配置
if(PN<=7)//低八位
{
GPIOC->CRL&=~(0XF<<(PN*4));//位先清零
GPIOC->CRL|=( (dir) <<(PN*4) );//向右移PN位
}
else
{
GPIOC->CRH&=~(0XF<<( PN-8)*4 );//位先清零
GPIOC->CRH|=( (dir) <<( PN-8)*4 );//向右移PN位
}
//初始状态配置
GPIOC->ODR|=data<<PN; //PC 输出data
break;
}
case 3://D
{
//端口配置
if(PN<=7)//低八位
{
GPIOD->CRL&=~(0XF<<(PN*4));//位先清零
GPIOD->CRL|=( (dir) <<(PN*4) );//向右移PN*4位
}
else
{
GPIOD->CRH&=~(0XF<<( PN-8)*4 );//位先清零
GPIOD->CRH|=( (dir) <<( PN-8)*4 );//向右移PN位
}
//初始状态配置
GPIOD->ODR|=data<<PN; //PD 输出data
break;
}
case 4://E
{
//端口配置
if(PN<=7)//低八位
{
GPIOE->CRL&=~(0XF<<(PN*4));//位先清零
GPIOE->CRL|=( (dir) <<(PN*4));//向右移PN位
}
else
{
GPIOE->CRH&=~(0XF<<( PN-8)*4 );//位先清零
GPIOE->CRH|=( (dir) <<( PN-8)*4 );//向右移PN位
}
//初始状态配置
GPIOE->ODR|=data<<PN; //PE 输出data
break;
}
}
}
void GPIO_change(GPIO_PIN n, u8 data)
{
//获取GPIO组、引脚
u8 PX,PN;
PX=PX(n);//获取模块号 PX_n/16
PN=PN(n);//获取引脚号 PX_n%16
switch (PX)
{
case 0:
{
if(data==0)
GPIOA->BRR|=1<<PN;//GPIOA->ODR&=0<<PN;
else
GPIOA->BSRR|=1<<PN;//GPIOA->ODR|=1<<PN;
break;
}
case 1:
{
if(data==0)
GPIOB->BRR|=1<<PN;//GPIOB->ODR&=0<<PN;
else
GPIOB->BSRR|=1<<PN;//GPIOB->ODR|=1<<PN;
break;
}
case 2:
{
if(data==0)
GPIOC->BRR|=1<<PN;//GPIOC->ODR&=0<<PN;
else
GPIOC->BSRR|=1<<PN;//GPIOC->ODR|=1<<PN;
break;
}
case 3:
{
if(data==0)
GPIOD->BRR|=1<<PN;//GPIOD->ODR&=0<<PN;
else
GPIOD->BSRR|=1<<PN;//GPIOD->ODR|=1<<PN;
break;
}
case 4:
{
if(data==0)
GPIOE->BRR|=1<<PN;//GPIOE->ODR&=0<<PN;
else
GPIOE->BSRR|=1<<PN;//GPIOE->ODR|=1<<PN;
break;
}
}
}
u32 GPIO_find(GPIO_PIN n)
{
//获取GPIO组、引脚
u32 temp,PN_i=1;
u8 PX,PN,i=0;
PX=PX(n);//获取模块号 PX_n/16
PN=PN(n);//获取引脚号 PX_n%16
for(i=0;i<PN;i++)
{
PN_i=PN_i*2;
}
switch (PX)
{
case 0:
{
temp=GPIOA->IDR;
temp=temp/PN_i;
temp=temp%2;
return temp;
break;
}
case 1:
{
temp=GPIOB->IDR;
temp=temp/PN_i;
temp=temp%2;
return temp;
break;
}
case 2:
{
temp=GPIOC->IDR;
temp=temp/PN_i;
temp=temp%2;
return temp;
break;
}
case 3:
{
temp=GPIOD->IDR;
temp=temp/PN_i;
temp=temp%2;
return temp;
break;
}
case 4:
{
temp=GPIOE->IDR;
temp=temp/(PN+1);
temp=temp%2;
return temp;
break;
}
}
}
这样,我们就可以在工程中直接使用函数,减少配置IO口带来的不便。
注:介于本人水平有限,所写的代码执行效率不是很高,如有不当之处,敬请指出。