STM32F429入门(七):学着自己写库

接着前天的内容,今天要试着写一下设置引脚为高电平的函数:

//函数功能:设置引脚为高电平,使用置位的寄存器
//参数说明:GPIOx:该参数为GPIO_TypeDef类型的结构体指针,指向GPIO端口的地址
//		  GPIO_Pin:表示某个端口的引脚号
//Example:GPIO_SetBits(GPIOH,10)
void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
    GPIOx->BSRRL = (1<<GPIO_Pin);
}
//函数功能:设置引脚为低电平,使用置位的寄存器
//参数说明:GPIOx:该参数为GPIO_TypeDef类型的结构体指针,指向GPIO端口的地址
//		  GPIO_Pin:表示某个端口的引脚号
//Example:GPIO_ResetBits(GPIOH,10)
void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
	GPIOx->BSRRH = (1<<GPIO_Pin);
}

为了使主函数看起来更加简洁,我们新增两个文件,即stm32f4xx_gpio.c与stm32f4xx_gpio.h,写入以上内容,将各种寄存器配置写入stm32f4xx.h,为了防止出现函数的重复定义,我们在后缀为.h的文件中加入#ifndef:

#ifndef __STM32F4XX_H
#define __STM32F4XX_H

#define PERIPH_BASE            ((unsigned int)0x40000000)
#define AHB1PERIPH_BASE        (PERIPH_BASE+0x00020000)
#define GPIOH_BASE             (AHB1PERIPH_BASE+0x00001C00)
#define GPIOA_BASE             (AHB1PERIPH_BASE+0x00000000)

#define GPIOH_MODER            *(unsigned int *)(GPIOH_BASE+0X00)
#define GPIOH_OTYPER           *(unsigned int *)(GPIOH_BASE+0X04)
#define GPIOH_OSPEEDR          *(unsigned int *)(GPIOH_BASE+0X08)
#define GPIOH_PUPDR            *(unsigned int *)(GPIOH_BASE+0X0C)
#define GPIOH_IDR              *(unsigned int *)(GPIOH_BASE+0X10)
#define GPIOH_ODR              *(unsigned int *)(GPIOH_BASE+0X14)
#define GPIOH_BSRR             *(unsigned int *)(GPIOH_BASE+0X18)
#define GPIOH_LCKR             *(unsigned int *)(GPIOH_BASE+0X1C)
#define GPIOH_AFRL             *(unsigned int *)(GPIOH_BASE+0X20)
#define GPIOH_AFRH             *(unsigned int *)(GPIOH_BASE+0X24)

typedef unsigned int   uint32_t;
typedef unsigned short uint16_t;

typedef struct
{
	uint32_t MODER;
	uint32_t OTYPER;
	uint32_t OSPEEDR;
	uint32_t PUPDR;
	uint32_t IDR;
	uint32_t ODR;
	uint16_t BSRRL;
	uint16_t BSRRH;
	uint32_t LCKR;
	uint32_t AFR[2];
}GPIO_TypeDef;

#define GPIOH       ((GPIO_TypeDef *)GPIOH_BASE)
#define GPIOA       ((GPIO_TypeDef *)GPIOA_BASE)


#define RCC_BASE              (AHB1PERIPH_BASE+0x00003800)
#define RCC_AHB1ENR           *(unsigned int *)(RCC_BASE+0X30)
	
#endif /* __STM32F4XX_H */
#ifndef __STM32F4XX_GPIO_H

#define __STM32F4XX_GPIO_H

#include "stm32f4xx.h"

void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);

void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);

#endif /* __STM32F4XX_GPIO_H */

为什么需要加入#ifndef...来防止重复声明呢,因为在执行时,我们第一次已经定义了"stm32f4xx.h"的这个头文件,所以它在第二次调用的时候,不会再定义一次。宏定义前的下划线可以去掉,加下划线是为了区分其他的宏定义,防止重复定义。

为了表示得更直观,我们把输入的 端口数10改为GPIO_Pin_10,所以需要在文件中添加如下:

#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))

//此时调用置位寄存器时,可直接:
GPIOx->BSRRH = GPIO_Pin_4;

写出了自己可以操作置高低电平的函数之后,我们要实现GPIO各种初始化的函数,整个GPIO的初始化顺序如下:

  • 确定引脚号

  • 确定要输入还是输出

  • 配置是上拉还是下拉

  • 确定是推挽还是开漏

  • 配置输出的速度

  • 最后就开始操作ODR或者是BS的寄存器

按照上面的思路,我们需要定义下面的结构体,结构体的顺序是由上方的思路进行排序:

//GPIO初始化结构体
typedef struct

{
  uint32_t GPIO_Pin; //引脚口

  GPIOMode_TypeDef   GPIO_Mode; //输出还是输出

  GPIOOType_TypeDef  GPIO_OType; //推挽还是开漏

  GPIOSpeed_TypeDef  GPIO_Speed; //输出速度

  GPIOPuPd_TypeDef   GPIO_PuPd; //上拉还是下拉

}GPIO_InitTypeDef;

我们再使用枚举来定义以上的结构体:

 

typedef enum
{
  GPIO_Mode_IN = 0x00,  /* 输入(复位状态) */

  GPIO_Mode_OUT = 0x01, /* 通用输出模式 */

  GPIO_Mode_AF = 0x02,  /* 复用功能模式 */

  GPIO_Mode_AN = 0x03,  /* 模拟模式 */
      
}GPIOMode_TypeDef;

剩下的寄存器也如以上的操作:

typedef enum
{
  GPIO_OType_PP  = 0x00,  /* 推挽输出*/
  
  GPIO_OType_OD  = 0x01,  /* 开漏输出*/
  
}GPIOOType_TypeDef;

typedef enum
{
  GPIO_Speed_2MHZ  = 0x00,  /* 2MHZ */

  GPIO_Speed_25MHZ = 0x01,  /* 25MHZ */

  GPIO_Speed_50MHZ = 0x02,  /* 50MHZ */

  GPIO_Speed_100MHZ = 0x03,  /* 100MHZ */

}GPIOSpeed_TypeDef;

typedef enum
{
  GPIO_PuPd_NOPULL  = 0x00,  /*浮空 */
  
  GPIO_PuPd_UP      = 0x01,  /*上拉*/
  
  GPIO_PuPd_DOWN    = 0x02,  /*下拉 */
  
}GPIOPuPd_TypeDef;

做好了上述准备后,我们需要使用以上枚举或者是结构体来代替以下的代码:

/* PH10配置为输出 */
GPIOH->MODER &= ~(3<<2*10);
GPIOH->MODER |= (1<<2*10);

/* 配置为上拉 */
GPIOH->PUPDR &= ~(3<<2*10);
GPIOH->PUPDR |= (1<<2*10);

/* 设置PH10为推挽输出 */
GPIOH->OTYPER &= ~(1<<10);

/* PH10速度为50M */
GPIOH->OSPEEDR &= ~(3<<2*10);
GPIOH->OSPEEDR |= (2<<2*10);

结果如下:

#define LED_R_GPIO_PIN     GPIO_Pin_10
#define LED_R_GPIO_PORT    GPIOH

#define LED_G_GPIO_PIN     GPIO_Pin_11
#define LED_G_GPIO_PORT    GPIOH

#define LED_B_GPIO_PIN     GPIO_Pin_12
#define LED_B_GPIO_PORT    GPIOH

void LED_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_Init_struct;
    //先把除了引脚口外的其他东西设置好
	GPIO_Init_struct.GPIO_Mode  = GPIO_Mode_OUT;
	GPIO_Init_struct.GPIO_Speed = GPIO_Speed_50MHZ;
	GPIO_Init_struct.GPIO_PuPd  = GPIO_PuPd_UP;
	
	GPIO_Init_struct.GPIO_Pin = LED_R_GPIO_PIN;	
	GPIO_Init( LED_R_GPIO_PORT,&GPIO_Init_struct );
	GPIO_SetBits(LED_R_GPIO_PORT,LED_R_GPIO_PIN);
	
	GPIO_Init_struct.GPIO_Pin = LED_G_GPIO_PIN;	
	GPIO_Init( LED_G_GPIO_PORT,&GPIO_Init_struct );
	GPIO_SetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);
	
	GPIO_Init_struct.GPIO_Pin = LED_B_GPIO_PIN;	
	GPIO_Init( LED_B_GPIO_PORT,&GPIO_Init_struct );
	GPIO_SetBits(LED_B_GPIO_PORT,LED_B_GPIO_PIN);

}

我们需要一个子函数来将我们配置的结构体成员配置给寄存器:

 

//要将我们配置的结构体成员写入寄存器中,相当于GPIO初始化函数
//Example:GPIO_Init(GPIOH,&GPIO_InitStruct);
void GPIO_Init(GPIO_TypeDef *GPIOx,GPIO_InitTypeDef *GPIO_InitStruct )
{
  uint32_t pinpos =0,\     //用于位置自加
    	   pos=0,\		   //用于显示当前位置
           currentpos=0;   //用于找到与设置的引脚口相同的位置
 //首先要找到引脚号
 for( pinpos = 0x00; pinpos < 16; pinpos++ )
 {
   pos = ((uint16_t)0x01) << pinpos;
	//通过找到当前值与设置值都为1时,相与则为1,相对于找到了当前设置的引脚
   currentpos = pos & GPIO_InitStruct->GPIO_Pin;

   if ( currentpos == pos )
   {
    //模式寄存器初始化为0,这种由两个位控制的,有多种取值的,先清0,再转换
	GPIOx->MODER &= ~(3 << (2*pinpos));
	//设置为输出/输入模式
 	GPIOx->MODER |= ( ((uint32_t)GPIO_InitStruct->GPIO_Mode) << (2*pinpos) );
	//上拉/下拉寄存器清零
	GPIOx->PUPDR &= ~(3<<2*pinpos);
	//设置为上拉/下拉模式
    GPIOx->PUPDR |= ( ((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (2*pinpos));
	//设置速度时要考虑它是不是输出模式或者是复用功能模式,才可以设置寄存器的输出速度
    if( (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF) )
   {
      //速度寄存器清零
      GPIOx->OSPEEDR &= ~(3<<2*pinpos);
	  //速度寄存器置1
      GPIOx->OSPEEDR |= (((uint32_t)GPIO_InitStruct->GPIO_Speed) <<(2*pinpos));
   }
	 //推挽/开漏模式清零
 	 GPIOx->OTYPER &= ~(1<<pinpos);
	 //设置推挽/开漏模式
	 GPIOx->OTYPER |= (((uint16_t)GPIO_InitStruct->GPIO_OType)<<pinpos);     
   }
 }
}

到现在终于把写库的流程大概摸索清楚了...

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郑烯烃快去学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值