通过GPIO探究STM32F10x的寄存器与函数
将地址用标识符定义出来可以增加可读性
/* 定义引脚 */
#define GPIO_PIN_0 ((uint16_t)0x0001)
#define GPIO_PIN_1 ((uint16_t)0x0002)
#define GPIO_PIN_2 ((uint16_t)0x0004)
#define GPIO_PIN_3 ((uint16_t)0x0008)
#define GPIO_PIN_4 ((uint16_t)0x0010)
#define GPIO_PIN_5 ((uint16_t)0x0020)
#define GPIO_PIN_6 ((uint16_t)0x0040)
#define GPIO_PIN_7 ((uint16_t)0x0080)
#define GPIO_PIN_8 ((uint16_t)0x0100)
#define GPIO_PIN_9 ((uint16_t)0x0200)
#define GPIO_PIN_10 ((uint16_t)0x0400)
#define GPIO_PIN_11 ((uint16_t)0x0800)
#define GPIO_PIN_12 ((uint16_t)0x1000)
#define GPIO_PIN_13 ((uint16_t)0x2000)
#define GPIO_PIN_14 ((uint16_t)0x4000)
#define GPIO_PIN_15 ((uint16_t)0x8000)
#define GPIO_PIN_ALL ((uint16_t)0xFFFF)
函数的作用是对寄存器操作,知道对应寄存器的地址就能直接对寄存器操作
#define _D *(unsigned int*) //强制转换为地址指针后再用*去访问
#define GPIOA_Base 0x40010800
#define GPIOB_Base 0X40010C00
#define GPIOC_Base 0x40011000
#define RCC_APB2_Base 0x40021000
int main()
{
_D (RCC_APB2_Base+0x18) |= (0x1<<2); //ABP2外部时钟GPIOA使能
_D GPIOA_Base &= (0x0F<<16); //清空Pin4配置
_D GPIOA_Base |= (0x11<<16); //Pin4输出模式
_D (GPIOA_Base+0x0C) |= (1<<4); //Pin4高电平
}
如果寄存器在地址上是连续的可以巧妙的应用结构体来定义对应的寄存器,结构体的地址就是结构体首个成员变量的地址,确定了首地址成员变量的地址也就一一对应了,
typedef struct
{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
}GPIO_TypeDef;
这样做的好处在于可以直接通过结构体访问寄存器
RCC->APB2ENR |= 1<<2; //使能GPIOA时钟
GPIOA->CRL &= ~(0x0F<<20); //清空Pin4配置
GPIOA->CRL |= 0x11<<20; //Pin4输出模式
GPIOA->ODR |= 0x01<<4; //Pin4输出高电平
对于寄存器的位上的参数也可以用枚举来定义
typedef enum
{
GPIO_MODE_AIN = 0x0,
GPIO_MODE_IN_FLOATING = 0x04,
GPIO_MODE_IPD = 0x28,
GPIO_MODE_IPU = 0x48,
GPIO_MODE_OUT_OD = 0x14,
GPIO_MODE_OUT_PP = 0x10,
GPIO_MDOE_AF_OD = 0x1C,
GPIO_MODE_AF_PP = 0x18,
}GPIOMODE_TypeDef;
进一步封装得到了GPIO初始化结构体的原形
typedef struct
{
uint16_t GPIO_PIN;
GPIOMODE_TypeDef GPIO_MODE;
GPIOSPEED_TypeDef GPIO_SPEED;
}GPIO_InitTypeDef;
接下来要将数据写入到寄存器需要一个函数来处理,这需要熟悉参考手册
void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIOx_InitStructure)
{
uint32_t pinpos=0x00,pos=0x00,currentpin=0x00,currentmode=0x00;
uint32_t tmpreg=0x00,pinmask=0x00;
/*---------------------------GPIO模式配置------------------------*/
currentmode |= (uint32_t)GPIOx_InitStructure->GPIO_MODE&0x0F;//保留mod低四位
//判断bit4是0还是1,即输入还是输出
if(((uint32_t)GPIOx_InitStructure->GPIO_MODE&(uint32_t)0x10) != 0)
{
currentmode |= (uint32_t)GPIOx_InitStructure->GPIO_SPEED;//如果是输出要设置速度
}
/*------------------------判断是否为低8位-----------------------*/
if(((uint32_t)GPIOx_InitStructure->GPIO_PIN & (uint32_t)0x00FF) != 0)
{
tmpreg = GPIOx->CRL;//保存GPIOx低位值配置的值
for(pinpos = 0x00;pinpos < 0x08;pinpos++)//找出具体的Pin
{
pos = (uint32_t)1<<pinpos;
currentpin = GPIOx_InitStructure->GPIO_PIN & pos;//为下面配对pin做准备
if(currentpin == pos)
{
pos = pinpos<<2; //对应Pin值,每4位操作一个pin
pinmask = ((uint32_t)0x0F)<<pos; //写入清空位值
tmpreg &= ~pinmask; //清空操作位,其他位不变
tmpreg |= (currentmode<<pos); //向寄存器写入要配置的引脚模式
if(GPIOx_InitStructure->GPIO_MODE == GPIO_MODE_IPD)//判断是否为下拉输入模式
{
//BRR置1
GPIOx->BRR = (((uint32_t)0x01)<<pinpos);
}
if(GPIOx_InitStructure->GPIO_MODE == GPIO_MODE_IPU)//判断是否为上拉输入模式
{
//BSRR置1
GPIOx->BSRR = ((uint32_t)0x01<<pinpos);
}
}
}
GPIOx->CRL = tmpreg;
}
/*---------------------判断是否为高位----------------------------*/
if(GPIOx_InitStructure->GPIO_PIN > 0x00FF)
{
tmpreg =GPIOx->CRH;
for(pinpos=0x00;pinpos<0x08;pinpos++)
{
pos = ((uint32_t)0x01)<<(pinpos + 0x08);
currentpin = ((GPIOx_InitStructure->GPIO_PIN) & pos);
if(currentpin == pos)
{
pos = pinpos<<2;
pinmask = ((uint32_t)0x0F)<<pos;
tmpreg &= ~pinmask;
tmpreg |= (currentmode<<pos);
if(GPIOx_InitStructure->GPIO_MODE == GPIO_MODE_IPD)
{
GPIOx->BRR = ((uint32_t)0x01)<<(pinpos + 0x08);
}
if(GPIOx_InitStructure->GPIO_MODE == GPIO_MODE_IPU)
{
GPIOx->BSRR = ((uint32_t)0x01)<<(pinpos + 0x08);
}
}
}
GPIOx->CRH = tmpreg;
}
}
诸如此类的寄存器都是如此封装成函数来操作
根据函数的作用将函数大致可以分为三种
- 初始化函数GPIO_Init\GPIO_DeInit\GPIO_AFIODeInit\GPIO_AFIODeInit
- 功能函数GPIO_Writ\...
- 控制函数GPIO_PinLockConfig\GPIO_EventOutputCmd\GPIO_PinRemapConfig\GPIO_EXTILineConfig...