基于STM32F103ZET6库函数跑马灯实验

IO的8种模式

1、输入浮空
2、输入上拉
3、输入下拉
4、模拟输入

5、开漏输出
6、推挽输出
7、推挽式复用功能
8、开漏复用功能

GPIO 相关的函数

GPIO_Init函数的声明

在固件库开发中,操作寄存器 CRH 和 CRL 来配置 IO 口的模式和速度是通过 GPIO 初始化 函数完成:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

这个函数有两个参数,第一个参数是用来指定 GPIO,取值范围为 GPIOA~GPIOG。

每一个GPIO都可以配置下面这个结构体里面的这么多个寄存器

/** 
  * @brief General Purpose I/O
  */

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;

第二个参数为初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef。

/** 
  * @brief  GPIO Init structure definition  
  */

typedef struct
{
  uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
                                      This parameter can be any value of @ref GPIO_pins_define */

  GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
                                      This parameter can be a value of @ref GPIOSpeed_TypeDef */

  GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
                                      This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

GPIO_Init函数的定义

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  
  
/*---------------------------- GPIO Mode Configuration -----------------------*/
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  { 
    /* Check the parameters */
    assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
    /* Output mode */
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
/*---------------------------- GPIO CRL Configuration ------------------------*/
  /* Configure the eight low port pins */
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    tmpreg = GPIOx->CRL;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = ((uint32_t)0x01) << pinpos;
      /* Get the port pins position */
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding low control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }
        else
        {
          /* Set the corresponding ODR bit */
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
    GPIOx->CRL = tmpreg;
  }
/*---------------------------- GPIO CRH Configuration ------------------------*/
  /* Configure the eight high port pins */
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
    tmpreg = GPIOx->CRH;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
      /* Get the port pins position */
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding high control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
        /* Set the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
    GPIOx->CRH = tmpreg;
  }
}

GPIO_Init函数的使用

通过初始化结构体初始化 GPIO 的常用格式是:

GPIO_InitTypeDef  GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  //LED0-->PB.5 端口配置 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    //推挽输出 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度 50MHz 

GPIO_Init(GPIOB, &GPIO_InitStructure);//根据设定参数配置 GPIO

我们来看一下GPIO_InitStructure结构体里面的成员可以设置什么参数

结构体 GPIO_InitStructure 的第一个成员变量 GPIO_Pin 用来设置是要初始化 哪个或者哪些 IO 口

/** @defgroup GPIO_pins_define 
  * @{
  */

#define GPIO_Pin_0                 ((uint16_t)0x0001)  /*!< Pin 0 selected */
#define GPIO_Pin_1                 ((uint16_t)0x0002)  /*!< Pin 1 selected */
#define GPIO_Pin_2                 ((uint16_t)0x0004)  /*!< Pin 2 selected */
#define GPIO_Pin_3                 ((uint16_t)0x0008)  /*!< Pin 3 selected */
#define GPIO_Pin_4                 ((uint16_t)0x0010)  /*!< Pin 4 selected */
#define GPIO_Pin_5                 ((uint16_t)0x0020)  /*!< Pin 5 selected */
#define GPIO_Pin_6                 ((uint16_t)0x0040)  /*!< Pin 6 selected */
#define GPIO_Pin_7                 ((uint16_t)0x0080)  /*!< Pin 7 selected */
#define GPIO_Pin_8                 ((uint16_t)0x0100)  /*!< Pin 8 selected */
#define GPIO_Pin_9                 ((uint16_t)0x0200)  /*!< Pin 9 selected */
#define GPIO_Pin_10                ((uint16_t)0x0400)  /*!< Pin 10 selected */
#define GPIO_Pin_11                ((uint16_t)0x0800)  /*!< Pin 11 selected */
#define GPIO_Pin_12                ((uint16_t)0x1000)  /*!< Pin 12 selected */
#define GPIO_Pin_13                ((uint16_t)0x2000)  /*!< Pin 13 selected */
#define GPIO_Pin_14                ((uint16_t)0x4000)  /*!< Pin 14 selected */
#define GPIO_Pin_15                ((uint16_t)0x8000)  /*!< Pin 15 selected */
#define GPIO_Pin_All               ((uint16_t)0xFFFF)  /*!< All pins selected */

第二个成员变量 GPIO_Mode 是用来设置对应 IO 端口的输出输入模式, 这些模式是上面我们讲解的 8 个模式,在 MDK 中是通过一个枚举类型定义的:

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_Mode_AF_OD = 0x1C,   //复用开漏输出   
	GPIO_Mode_AF_PP = 0x18   //复用推挽 
}GPIOMode_TypeDef; 

第三个参数是 IO 口速度设置,有三个可选值,在 MDK 中同样是通过枚举类型定义:

typedef enum 
{    
	GPIO_Speed_10MHz = 1,   
	GPIO_Speed_2MHz,    
	GPIO_Speed_50MHz 
}GPIOSpeed_TypeDef; 

这里只有第一个枚举成员赋值为1,但后面成员的值是跟着前面的递增的

GPIO_ReadInputDataBit

在固件库中操作 IDR 寄存器读取 IO 端口数据是通过 GPIO_ReadInputDataBit 函数实现的:

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 

比如我要读 GPIOB.5 的电平状态,那么方法是:

GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_5); 

返回值是 1(Bit_SET)或者 0(Bit_RESET);

GPIO_Write

在固件库中设置 ODR 寄存器的值来控制 IO 口的输出状态是通过函数 GPIO_Write 来实现 的:

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); 

该函数一般用来往一次性一个 GPIO 的多个端口设值。

GPIO_SetBits、 GPIO_ResetBits

在 STM32 固件库中,通过 BSRR 和 BRR 寄存器设置 GPIO 端口输出是通过函数 GPIO_SetBits()和函数 GPIO_ResetBits()来完成的。

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 

我们要设 置 GPIOB.5 输出 1,那么方法为:

GPIO_SetBits(GPIOB, GPIO_Pin_5); 

反之如果要设置 GPIOB.5 输出位 0,方法为:

GPIO_ResetBits (GPIOB, GPIO_Pin_5); 

IO 操作步骤

1) 使能 IO 口时钟。调用函数为 RCC_APB2PeriphClockCmd();
2) 初始化 IO 参数。调用函数 GPIO_Init();
3) 操作 IO。操作 IO 的方法就是上面我们讲解的方法。

 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5
 GPIO_SetBits(GPIOB,GPIO_Pin_5);						 //PB.5 输出高

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	    		 //LED1-->PE.5 端口配置
 GPIO_Init(GPIOE, &GPIO_InitStructure);	  				 //根据设定参数初始化GPIOE.5
 GPIO_SetBits(GPIOE,GPIO_Pin_5); 						 //PE.5 输出高

因为两个 IO 口的模式和速度都一样,所以我们只用初始化一次,在 GPIOE.5 的初始化的时候就不需要再重复初始化速度和模式了

从系统架构可以找到PB,PE端口时钟
在这里插入图片描述
从原理图找到PB5、PE5为LED0、LED1
在这里插入图片描述
当发光二极管两边有压降时,即PB5、PE5为0时,发光二极管就会发光
在这里插入图片描述

在led.c文件里把LED初始化的函数封装起来

#include "led.h"

void LED_Init(void)
{
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PB.5 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
	GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5
	GPIO_SetBits(GPIOB,GPIO_Pin_5);						 //PB.5 输出高

 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	    		 //LED1-->PE.5 端口配置
 	GPIO_Init(GPIOE, &GPIO_InitStructure);	  				 //根据设定参数初始化GPIOE.5
	GPIO_SetBits(GPIOE,GPIO_Pin_5); 						 //PE.5 输出高
 }

在led.h中输入如下代码

#ifndef __LED_H
#define __LED_H	 
#include "sys.h"

#define LED0 PBout(5)// PB5
#define LED1 PEout(5)// PE5	

void LED_Init(void);//LED初始化

		 				    
#endif

这段代码里面最关键就是 2 个宏定义:

#define LED0 PBout(5)// PB5
#define LED1 PEout(5)// PE5	

使用的是位带操作来实现操作某个 IO 口的 1 个位

main.c

#include "led.h" 
#include "delay.h" 
#include "sys.h"  

int main(void)  
{   
	delay_init();               //延时函数初始化     
	LED_Init();              //初始化与 LED 连接的硬件接口  
	while(1)  
	{   
		LED0=0;      
		LED1=1;   
		delay_ms(300);            //延时 300ms   
		LED0=1;   
		LED1=0;   
		delay_ms(300);            //延时 300ms  
	}  
}

操作 IO 口输出高低电平的三种方法

位带操作

上面已经定义好了位带操作的两个宏定义

LED0=1;  //通过位带操作控制 LED0 的引脚 PB5 输出高电平 
LED0=0;  //通过位带操作控制 LED0 的引脚 PB5 输出低电平 

库函数操作

GPIO_SetBits(GPIOB, GPIO_Pin_5);        //设置 GPIOB.5 输出 1,等同 LED0=1; 
GPIO_ResetBits (GPIOB, GPIO_Pin_5);      //设置 GPIOB.5 输出 0,等同 LED0=0; 

寄存器操作

GPIOB->BRR=GPIO_Pin_5; //设置 GPIOB.5 输出 1,等同 LED0=1; 
GPIOE->BSRR=GPIO_Pin_5; //设置 GPIOB.5 输出 0,等同 LED0=0; 
以下是基于STM32F103ZET6芯片的库函数实现LED跑马灯的示例代码: ```c #include "stm32f10x.h" void Delay(__IO uint32_t nCount); int main(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); while(1) { GPIO_SetBits(GPIOB, GPIO_Pin_12); Delay(0x3FFFFF); GPIO_ResetBits(GPIOB, GPIO_Pin_12); GPIO_SetBits(GPIOB, GPIO_Pin_13); Delay(0x3FFFFF); GPIO_ResetBits(GPIOB, GPIO_Pin_13); GPIO_SetBits(GPIOB, GPIO_Pin_14); Delay(0x3FFFFF); GPIO_ResetBits(GPIOB, GPIO_Pin_14); GPIO_SetBits(GPIOB, GPIO_Pin_15); Delay(0x3FFFFF); GPIO_ResetBits(GPIOB, GPIO_Pin_15); } } void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); } ``` 该示例代码中,我们使用了STM32F103ZET6芯片的四个GPIO口(PB12、PB13、PB14和PB15)来控制四个LED灯的亮灭,通过轮流点亮不同的LED灯来实现LED灯的跑马灯效果。在主函数中,我们首先对四个GPIO口进行了初始化,然后通过使用库函数GPIO_SetBits和GPIO_ResetBits来控制GPIO口的亮灭,最后通过Delay函数来产生一定的延时。 需要注意的是,该示例代码中的延时函数Delay并不是一个很准确的延时函数,因为它只是通过循环来实现延时,而循环的执行时间会受到很多因素的影响,如CPU的主频、编译器的优化等。因此,在实际的应用中,我们需要使用更加准确的延时函数,如定时器中断等方式来实现延时。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

是北豼不太皮吖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值