【STM32学习笔记】:跑马灯实验详解

跑马灯实验

        在前面五篇STM32学习笔记中,我们已经初步认识了STM32芯片,并且了解STM32的常用寄存器,介绍了STM32的GPIO模式,STM32工程文件,以及最终讲解了如何为STM32添加源文件和头文件的步骤。但是前面的知识只能说是理论和基础,但是非常重要,如果STM32的理论知识不扎实,会导致后续开发项目时,总感觉自己并没有学过STM32,就好像自己是一片浮萍一直飘在水上。所以我们在开发STM32的项目过程中,遇到需要学习一些理论知识时,一定要好好补充自己的理论知识,在实践中检验自己学习的效果是最好的。

GPIO简介

        GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引 脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32 芯片的 GPIO 被分成很多组,每组有 16 个引脚,如型号为 STM32F103ZET6 型号的芯片有 GPIOA、GPIOB、 GPIOC、GPIOD、GPIOE、GPIOF 至 GPIOG 共 7组 GPIO,芯片一共 144 个引脚,其中 GPIO 就占了一大部分,所有的 GPIO 引脚都有基本的输入输出功能。

        最基本的输出功能是由 STM32 控制引脚输出高、低电平,实现开关控制,如把 GPIO 引脚接入 到 LED 灯,那就可以控制 LED 灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或 三极管控制外部大功率电路的通断。

        最基本的输入功能是检测外部输入电平,如把 GPIO 引脚连接到按键,通过电平高低区分按键是 否被按下。

        在固件库中,GPIO 端口操作对应的库函数函数以及相关定义在文件 stm32f10x_gpio.h 和 stm32f10x_gpio.c 中。

跑马灯硬件连接

 

 

        本章用到的硬件只有 LED(DS0 和 DS1)。其电路在 ALIENTEK 精英 STM32F103 开发板 上默认是已经连接好了的。DS0 接 PB5,DS1 接 PE5。所以在硬件上不需要动任何东西。因为LED发光二极管的阳极串联一个510欧姆的电阻与VCC3.3V电源相连。LED发光二极管的阴极与芯片的GPIO口相连。如果要控制发光二极管发光,则需要将GPIO输出的电平拉低,发光二极管就会由电流流过,电流流过时发光二极管就发光了。当两个GPIO输出的电平不断的拉高或者拉低就能实现了LED的闪烁或者跑马灯的效果了。 

       注意:510欧姆的电阻一共有两个作用:

  1. 起到上拉电阻的作用。
  2. 起到限流电阻的作用,防止流过发光二极管的电路过大而烧坏。

 

跑马灯程序设计

 

        跑马灯实验我们主要用到的固件库文件是:

        stm32f10x_gpio.c                             stm32f10x_gpio.h

        stm32f10x_rcc.c                                stm32f10x_rcc.h

        misc.c                                               misc.h

        stm32f10x_usart                               stm32f10x_usart.h

        其中 stm32f10x_rcc.h 头文件在每个实验中都要引入,因为系统时钟配置函数以及相关的外设时 钟使能函数都在这个其源文件 stm32f10x_rcc.c 中。stm32f10x_usart.h 和 misc.h 头文件在我们 SYSTEM 文件夹中都需要使用到,所以每个实验都会引用。

 

跑马灯所要用到的库函数

1、一个GPIO初始化函数

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
   /*作用:初始化一个或者多个IO口(同一组)的工作模式,输出类型,速度以及上下拉方式。也就是一组IO            
     口的4个配置寄存器。
   */
typedef struct
{
      uint16_t GPIO_Pin;  
    //指定要配置的 GPIO 引脚。此参数可以是 @ref GPIO_pins_define的任何值          
      GPIOSpeed_TypeDef GPIO_Speed;	
    //指定所选引脚的速度。此参数可以是ref GPIOSpeed_TypeDef成员变量中的值
      GPIOMode_TypeDef GPIO_Mode;  
    //指定选定引脚的工作模式。此参数可以是 GPIOMode_TypeDef成员变量中的值  
}GPIO_InitTypeDef;

#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 */

#define IS_GPIO_PIN(PIN)         ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) != (uint16_t)0x00))

#define IS_GET_GPIO_PIN(PIN) (((PIN) == GPIO_Pin_0) || \
                              ((PIN) == GPIO_Pin_1) || \
                              ((PIN) == GPIO_Pin_2) || \
                              ((PIN) == GPIO_Pin_3) || \
                              ((PIN) == GPIO_Pin_4) || \
                              ((PIN) == GPIO_Pin_5) || \
                              ((PIN) == GPIO_Pin_6) || \
                              ((PIN) == GPIO_Pin_7) || \
                              ((PIN) == GPIO_Pin_8) || \
                              ((PIN) == GPIO_Pin_9) || \
                              ((PIN) == GPIO_Pin_10) || \
                              ((PIN) == GPIO_Pin_11) || \
                              ((PIN) == GPIO_Pin_12) || \
                              ((PIN) == GPIO_Pin_13) || \
                              ((PIN) == GPIO_Pin_14) || \
                              ((PIN) == GPIO_Pin_15))

 

        在GPIO_InitTypeDef结构体中已经初始化了成员变量,所以在调用这个初始化函数的时候,我们首先需要对GPIO_InitTypeDef成员变量进行赋值。

       但是在给GPIO_InitTypeDef的成员变量赋值时,赋值的内容需要满足对应结构体类型中的成员变量。

       例如:GPIO_Mode的结构类型为GPIOMode_TypeDef,那么给它赋值的内容需要在下面这个结构体当中的成员变量中选择

 

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;

#define IS_GPIO_MODE(MODE) 		(((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \
                           		((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \
                            	((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \
                            	((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP))

 

        GPIO_Speed的结构类型为GPIOSpeed_TypeDef,那么给它赋值的内容需要在下面这个结构体当中的成员变量中选择。

 

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

#define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || ((SPEED) == GPIO_Speed_2MHz) || \
                              ((SPEED) == GPIO_Speed_50MHz))

 

2、两个读取输入电平函数 

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

作用:读取某个GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。

参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。

           2、GPIO_Pin:指定要读取的端口位。

返回值:输入端口引脚值。

例如:

      GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输入电平

uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

作用:读取某组GPIO的输入电平。实际操作的是GPIOx_IDR寄存器。

参数:GPIOx:其中x可以是(A..G)以选择GPIO外围设备。

返回值:GPIO输入数据端口值。

例如:

     GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平

3、两个读取输出电平函数

uint8_t GPIO_ReadOutputDataBit (GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

作用:读取某个GPIO的输出电平。实际操作的是GPIO_ODR寄存器。

参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。

           2、GPIO_Pin:指定要读取的端口位。

返回值:输出端口引脚值。

例如:

      GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5);//读取GPIOA.5的输出电平

uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);

作用:读取某组GPIO的输出电平。实际操作的是GPIO_ODR寄存器。

参数:GPIOx:其中x可以是(A..G)以选择GPIO外围设备。

返回值:GPIO输入数据端口值。

例如:

             GPIO_ReadOutputData(GPIOA);//读取GPIOA组中所有io口输出电平

4、四个设置输出电平函数

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

作用:设置某个IO口输出为高电平(1)。实际操作BSRRL寄存器

参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。

           2、GPIO_Pin:指定要写入的端口位。

返回值:无

例如:

        GPIO_SetBits(GPIOA, GPIO_Pin_5);//将GPIOA.5的输出电平置为高电平

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

作用:设置某个IO口输出为低电平(0)。实际操作的BSRRH寄存器。

参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。

           2、GPIO_Pin:指定要写入的端口位

返回值:无

例如:

        GPIO_ResetBits (GPIOA, GPIO_Pin_5);//将GPIOA.5的输出电平置为低电平

void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);

作用:设置或清除选定的数据端口位。

参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。

           2、GPIO_Pin:指定要写入的端口位。

           3、BitVal:指定要写入选定位的值。

此参数可以是BitAction枚举值之一:

typedef enum

{

          Bit_RESET = 0,    // Bit_RESET:清除端口引脚

          Bit_SET     // Bit_SET:设置端口引脚

}BitAction;

#define IS_GPIO_BIT_ACTION(ACTION)                                                                                                                                                 (((ACTION) == Bit_RESET) || ((ACTION) == Bit_SET))

返回值:无

例如:

        GPIO_WriteBit(GPIOA, GPIO_Pin_5, Bit_RESET);// 清除GPIOA.5数据端口位。

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

作用:将数据写入指定的GPIO数据端口。

参数:1、GPIOx:其中x可以是(A..G)以选择GPIO外围设备。

            2、PortVal:指定要写入端口输出数据寄存器的值。。

返回值:无

例如:

        GPIO_Write(GPIOA,1);将数据“1”写入指定的GPIOA数据端口。

        后面两个函数不常用,也是用来设置IO口输出电平。

跑马灯实验编写步骤:

      1. 使能IO口时钟。调用函数RCC_AHB1PeriphClockCmd();

           不同的外设调用的时钟使能函数可能不一样

      2. 初始化 GPIO 口模式。调用GPIO_Init();把目标引脚为推挽输出模式;

      3. 编写简单测试程序,控制 GPIO 引脚输出高、低电平。

跑马灯源文件和头文件中的代码

头文件中的代码:

#ifndef __LED_H
#define __LED_H

void LED_Init(void);//初始化
		    
#endif

 

源文件中的代码:

#include "led.h"

//初始化PB5和PE5为输出口.并使能这两个口的时钟		    
//LED IO初始化
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);	 //推挽输出 ,IO口速度为50MHz
 GPIO_SetBits(GPIOE,GPIO_Pin_5); 	 //PE.5 输出高 
}

 

跑马灯实验中主函数里的代码

        main.c里的代码:

#include "delay.h"
#include "led.h"	
/**
 *****************下面注视的代码是通过调用库函数来实现IO控制的方法*****************************************
int main(void)
{ 
 
delay_init();      //初始化延时函数
LED_Init();       //初始化LED端口
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);  //LED0对应引脚GPIOB.5拉低,亮  
GPIO_SetBits(GPIOE,GPIO_Pin_5);   //LED1对应引脚GPIOE.5拉高,灭 
delay_ms(300);       //延时300ms
GPIO_SetBits(GPIOB,GPIO_Pin_5);	   //LED0对应引脚GPIOB.5拉高,灭 GPIO_ResetBits(GPIOE,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉低,亮 
delay_ms(300);                     //延时300ms
	}
} 

 

函数执行流程如下:

        (1) 使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。

        (2) 调用库函数 RCC_APB2PeriphClockCmd 来使能 LED 灯的 GPIO 端口时钟,这个函数的调用和详细资料会在后面的一篇文章讲解。

        (3) 向 GPIO 初始化结构体赋值,把引脚初始化成推挽输出模式。

        (4) 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化。

      (5)紧接着使用两个GPIO的库函数,分别为GPIO_SetBits()与GPIO_ResetBits(),在置低和置高之间延时一段时间,就实现了两个LED灯轮流转的实验目的。

物联沃分享整理
物联沃-IOTWORD物联网 » 【STM32学习笔记】:跑马灯实验详解

发表评论