【STM32库函数】GPIO详解

本文详细介绍了STM32的GPIO功能,包括GPIO的八种工作模式(输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、开漏式复用功能、推挽式复用功能),以及GPIO的寄存器配置(CRL、CRH、IDR、ODR、BSRR、BRR、LCKR)和作用。此外,还阐述了GPIO的初始化函数、设置输出电平和读取输入输出状态的库函数,以及GPIO在跑马灯、蜂鸣器和按键输入实验中的应用。
摘要由CSDN通过智能技术生成

1. GPIO简介

GPIO的英文全称是General-Purpose Input/Output(通用输入输出端口),它是STM32芯片上的数字接口,可以被软件配置为输入或输出,在连接外部设备时发挥重要作用。它们可以使用寄存器级操作进行配置和控制,因此具有灵活性和可编程性。

STM32F103ZET6 芯片是 144 脚的芯片,具有112个GPIO可供我们编程使用,被分为 GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF 和 GPIOG 七组,其中37个是普通输入/输出引脚,75个是复用输入/输出引脚(当然不同的芯片引脚个数不同,但功能一致,具体可查看芯片手册)。这些GPIO都可以根据需要被配置为数字输入或数字输出模式。在输入模式下,GPIO接收外部设备产生的电信号,并将其转换为数字信号传递给处理器。在输出模式下,GPIO将处理器输出的数字信号转换为电信号,并向外部设备发送。

每个GPIO还可以根据应用需求选择不同的工作模式,例如中断输入模式、定时器输入捕获模式、PWM输出模式、模拟输入模式等。这些不同的工作模式使得GPIO可以更好地满足不同的应用需求。

在工程实践中,GPIO也经常使用一些特殊的电气特性,例如上拉电阻、下拉电阻、推挽输出、开漏输出等。这些特性可以为系统提供更多的灵活性和稳定性。例如,使用上拉电阻可以防止输入引脚的漂移,而使用推挽输出则可以提供比开漏输出更强的驱动能力。

2. GPIO的八种工作模式

GPIO的八种模式被分为四种输入模式和四种输出模式,硬件实现这里不做详细介绍,主要介绍八种模式功能及其对应的适用场景。

2.1 输入浮空模式(Floating Input Mode)

在该模式下,GPIO处于高阻态且未选通上拉或下拉电阻。这种模式适用于输入信号不需要精度要求的场合,例如控制系统中一些状态量检测的信号输入。但是,在使用输入浮空模式时,需要注意防止静电干扰和误操作。

举例:在一个控制系统中,检测两个机械部件之间是否分离时,只会输出0或1两个状态值,该状态值输入至配置为输入浮空模式的GPIO,MCU则可根据该GPIO的值判断这两个机械部件之间是否分离。

2.2 输入上拉模式(Input with Pull-up Mode)

在该模式下,GPIO通过一个上拉电阻与VDD相连。当外部电路未连接时,引脚被拉向高电平。这种模式适用于需要稳定输入信号的场合,如按键开关、震动传感器等的信号输入。此模式适用于数字输入信号需要精度要求的场合。

举例:在嵌入式设备中,如果需要使用按键来进行控制,可以将GPIO配置为输入上拉模式,并将按键连接到GPIO和地之间,在此模式下,按键不被按下时,GPIO处于高电平状态,当按键被按下时,GPIO变为低电平状态。

2.3 输入下拉模式(Input with Pull-down Mode)

在该模式下,GPIO通过一个下拉电阻与GND相连。当外部电路未连接时,引脚被拉向低电平。这种模式同样适用于需要稳定输入信号的场合,如按键开关、震动传感器等的信号输入。此模式同样适用于数字输入信号需要精度要求的场合。

举例:在嵌入式设备中,如果需要使用按键来进行控制,可以将GPIO配置为输入下拉模式,并将按键连接到GPIO和VDD之间,在此模式下,当按键不被按下时,GPIO处于低电平状态,当按键被按下时,GPIO变为高电平状态。

2.4 模拟输入模式(Analog Input Mode)

在该模式下,GPIO被配置为模拟信号输入。ADC模块可以将GPIO读取到的模拟信号转换成数字信号进行处理。这种模式适用于需要精确模拟量信号输入的场合,如温度传感器、光传感器等。

举例:在一个机器人系统中,需要使用光传感器来检测周围环境的亮度。可以将GPIO配置为模拟输入模式,并将光传感器连接到引脚和地之间,此时ADC模块可以将读取到的模拟信号转换成数字信号进行处理。

2.5 开漏输出模式(Open-drain Output Mode)

在该模式下,GPIO可以是低电平和高阻态。如果要输出高电平,需搭配一个外部上拉电阻。开漏输出模式适用于驱动外部设备,如LED灯、继电器等。此模式适用于外部设备控制信号输出的场合。

举例:使用开漏输出控制磁保持继电器时,磁保持继电器的线圈两端,一端接电源电压,另一端接配置为开漏输出的GPIO,当继电器不动作时,该GPIO始终为高阻态(断开状态),线圈不会产生磁场,当继电器动作时,GPIO变为低电平,此时线圈两端有电压,进而产生磁场,可以使继电器触点发生动作。

2.6 推挽输出模式(Push-pull Output Mode)

在该模式下,GPIO可以输出高、低电平信号。推挽输出模式适用于直接驱动负载场合,如电机、继电器等的输出驱动。此模式适用于直接驱动负载的控制信号输出的场合。

举例:在一个机器人系统中,需要通过GPIO来控制机器人的运动方向,例如前进、后退、左转、右转等。可以将GPIO配置为推挽输出模式,并将GPIO连接到电机驱动芯片,此时GPIO可以直接驱动电机的正反转来控制机器人的运动方向。

2.7 开漏式复用功能模式(Open-drain Alternate Function Mode)

在该模式下,GPIO可以被配置为一些预设的功能,例如I2C总线通讯的SCL时钟线(I2C总线的SCL时钟线在物理层被定义为开漏模式),也可以用作普通的开漏输出模式。这种模式下,GPIO适用于驱动外部电路也可用于总线通讯。

2.8 推挽式复用功能模式(Push-pull Alternate Function Mode)

在该模式下,GPIO可以被配置为一些预设的功能,例如USART通信的TX、RX数据线(USART的TX、RX在物理层被定义为推挽模式),也可以用作普通的推挽输出模式。这种模式下,GPIO适用于直接驱动负载或者数据通讯。

3. GPIO寄存器

GPIO对应的寄存器个数为7个,包括2 个 32位端口配置寄存器(CRL 和 CRH)、2 个 32 位端口数据寄存器(IDR 和 ODR)、1 个 32 位端口置位/复位寄存器(BSRR)、1 个 16 位端口复位寄存器(BRR)、1 个 32 位端口锁定寄存器 (LCKR)。下面将一一介绍。

3.1 2 个 32位端口配置寄存器(CRL 和 CRH)

作用:用来配置GPIO的模式和端口的速度,可写可读。
CRH
在这里插入图片描述
CRL
在这里插入图片描述
每组有16个GPIO,CRL 控制低8个(0——7)GPIO, CRH 控制高8个(8——15)GPIO,每个GPIO由4个位控制。

  • 高2位为CNF配置GPIO工作方式:
    在这里插入图片描述

  • 低2位为MODE配置GPIO的速度
    在这里插入图片描述

3.2 2 个 32 位端口数据寄存器(IDR 和 ODR)

作用:用来控制GPIO数据输出和检测GPIO数据输入。

  • ODR(可写可读)
    在这里插入图片描述
    这里低16位为1时,对应GPIO输出高电平;低16位为0时,对应GPIO输出低电平。
  • IDR(只能读)
    在这里插入图片描述
    这里低16位为对应GPIO的状态(高或低)

3.3 1 个 32 位端口置位/复位寄存器(BSRR)

作用:用来直接控制ODR寄存器的低16位为0还是为1,间接控制GPIO输出的高低电平,只能写不能读。
在这里插入图片描述

  • 高16位控制ODR寄存器高16位为0还是保持(复位):0为保持,1为设置为0并且GPIO输出低电平。
  • 低16位控制ODR寄存器低16位为1还是保持(置位):0为保持,1为设置为1并且GPIO输出高电平。

另外,当该寄存器为全1,那么就意味着置位和复位同时满足,是不成立的,规定此时置位满足。

3.4 1 个 16 位端口复位寄存器(BRR)

作用:用来直接将ODR寄存器的低16位置为0,间接将GPIO输出置为低电平 ,只能写。
在这里插入图片描述
将低16位全写1后,对应ODR寄存器的低16位变为0;将低16位全写0后,对对应的ODR寄存器的低16位不产生影响。

3.5 1 个 32 位端口锁定寄存器 (LCKR)

作用:用来控制GPIO锁定状态,可读可写。
在这里插入图片描述

高15位保留,始终为0,第16位控制某个GPIO是否被锁,低16位为每组0-15个GPIO中某个GPIO,第16位实际上是控制CRL和CRH对应某个GPIO对应的4个位是否能被修改。
第16位具体使用方式如下:
当LCKR寄存器的第16位被设置为1时,表示将要锁定相应的GPIO,此时需要向LCKR寄存器写入两次特定的数据来最终锁定该端口。具体步骤如下:

  • 将控制寄存器(CR寄存器)中的相应GPIO配置为输入模式或输出模式,并设置所需的速度、推挽/开漏等其他参数。
  • 将LCKR寄存器的第16位设置为1以锁定该GPIO。
    说明:将LCKR寄存器的第16位(即LCKK位)设置为1表示准备锁定相应的GPIO。此时GPIO还未被锁定,但已经被预备锁定。如果在锁定状态下尝试修改GPIO的配置,则会导致错误发生。
  • 向LCKR寄存器中写入任意值。以便进行第4步的确认操作。
  • 将LCKR寄存器中的值反转后再次写入相同的值,确认锁定操作。
    说明:在确认操作之前,需要反转LCKR寄存器中的值(即按位取反),然后再次向LCKR寄存器中写入相同的值。如果确认操作成功,GPIO将被锁定并且无法再被修改。

如果需要解除GPIO的锁定状态,则需要通过复位整个芯片或者读取LCKR寄存器来检查其状态,并执行相应的解锁操作。例如,可以使用特殊的序列来解锁GPIO:先将LCKR寄存器的第16位(LCKK位)清零,然后再向LCKR寄存器中写入任意值,最后再次向LCKR寄存器中写入相同的值即可解锁GPIO。

需要注意的是,在使用GPIO锁定功能时,只有部分GPIO支持锁定功能,具体请参考芯片的数据手册或相关技术文档。

3.5 GPIO寄存器地址映象

在这里插入图片描述

4. GPIO库函数

GPIO的库函数声明都在"stm32f10x_gpio.h"文件中。

4.1 初始化函数

4.1.1 初始化GPIO_InitTypeDef结构体为默认值

函数声明如下:

void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
  • GPIO_InitStruct:结构体的指针参数,用于对该结构体进行初始化。在调用该函数之前,必须先定义并分配内存空间给GPIO_InitTypeDef 结构体变量。该结构体是用于配置 GPIO的一种数据类型,包含了各种控制引脚模式、输入/输出方向、输出速度和上拉/下拉等属性的成员变量。

默认值包括:

  • 引脚GPIO_Pin:0
  • 速度GPIO_Speed: GPIO_Speed_2MHz。
  • 模式GPIO_Mode: GPIO_Mode_IN_FLOATING,浮空输入。

使用方式:将一个结构体设置为默认值:

// 定义一个GPIO_InitTypeDef类型的结构体变量gpio_init_structure
GPIO_InitTypeDef gpio_init_structure;

// 使用GPIO_StructInit()函数对gpio_init_structure结构体进行初始化
GPIO_StructInit(&gpio_init_structure);

将该结构体配置为默认值后,就可以用GPIO_Init函数对这个结构体中参数设置为指定值并映射到指定端口。

4.1.2 将指定GPIO端口配置为指定参数

函数声明如下:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
  • GPIOx :一个指向 GPIO_TypeDef 类型结构体的指针,表示要被初始化的GPIO端口(如GPIOA、GPIOB等)。
  • GPIO_InitStruct:一个指向 GPIO_InitTypeDef类型结构体的指针,表示要应用于指定GPIO端口的配置信息。该结构体成员变量有GPIO引脚号,GPIO速度结构体,GPIO模式结构体。

在使用GPIO_Init函数时,我们需要先定义一个GPIO_InitTypeDef结构体并对其中的各个参数进行赋值或者使用GPIO_StructInit函数将定义的结构体设置为默认值,然后将其作为参数传递给GPIO_Init函数,以完成对GPIO口的初始化配置。
使用方法:初始化PB5口为推挽输出模式,速度为50MHz,无上下拉电阻,代码如下:

// 声明一个 GPIO_InitTypeDef 类型的结构体变量 GPIO_InitStruct
GPIO_InitTypeDef GPIO_InitStruct;

// 设置 GPIO_InitStruct 结构体中的 GPIO_Pin 成员变量为 GPIOB 的第 5 个引脚
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;

// 设置 GPIO_InitStruct 结构体中的 GPIO_Mode 成员变量为推挽输出模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;

// 设置 GPIO_InitStruct 结构体中的 GPIO_Speed 成员变量为 50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

// 设置 GPIO_InitStruct 结构体中的 GPIO_OType 成员变量为推挽输出
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

// 设置 GPIO_InitStruct 结构体中的 GPIO_PuPd 成员变量为无上下拉电阻
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;

// 调用 GPIO_Init 函数,将 GPIOB 第 5 个引脚的配置设置为 GPIO_InitStruct 中的值
GPIO_Init(GPIOB, &GPIO_InitStruct);

注意:在调用该函数之前,需要先使能所需的 GPIO 端口时钟。
在配置完 GPIO 引脚后,可以调用其他函数(如GPIO_SetBits()、GPIO_ResetBits()和GPIO_WriteBit())来设置或重置 GPIO 引脚的输出状态。

4.1.3 将指定的GPIO外设寄存器初始化为默认值

函数声明如下:

void GPIO_DeInit(GPIO_TypeDef* GPIOx)
  • GPIOx:要操作的GPIO端口

这个函数适用于需要对一个已经被初始化过的GPIO端口进行重置或释放的情况。
使用方法:将已经具体初始化的PB5口初始化恢复到默认状态

//
// 声明一个 GPIO_InitTypeDef 类型的结构体变量 GPIO_InitStruct
GPIO_InitTypeDef GPIO_InitStruct;

// 设置 GPIO_InitStruct 结构体中的 GPIO_Pin 成员变量为 GPIOB 的第 5 个引脚
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;

// 设置 GPIO_InitStruct 结构体中的 GPIO_Mode 成员变量为推挽输出模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;

// 设置 GPIO_InitStruct 结构体中的 GPIO_Speed 成员变量为 50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

// 设置 GPIO_InitStruct 结构体中的 GPIO_OType 成员变量为推挽输出
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;

// 设置 GPIO_InitStruct 结构体中的 GPIO_PuPd 成员变量为无上下拉电阻
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;

// 调用 GPIO_Init 函数,将 GPIOB 第 5 个引脚的配置设置为 GPIO_InitStruct 中的值
GPIO_Init(GPIOB, &GPIO_InitStruct);

//恢复默认值
GPIO_DeInit(GPIOB);

4.2 设置输出电平函数

4.2.1 将指定引脚设置为指定电平

函数声明如下:

void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
  • GPIOx:GPIO 寄存器组,例如 GPIOA、GPIOB 等。
  • GPIO_Pin:要操作的 GPIO 引脚编号,可以使用宏定义 GPIO_Pin_x(x 表示引脚编号)来指定某个具体的引脚。
  • BitVal:要设置的 GPIO 引脚的输出值,可以是 Bit_SET 或 Bit_RESET。

使用方法:将 GPIOB 的第 5 个引脚设置为高电平:

GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_SET);

注意:使用该函数前必须先初始化对应的 GPIO 引脚,通常使用 GPIO_Init 函数完成初始化。

4.2.2 将指定引脚设置为高电平

函数声明如下:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
  • GPIOx:要操作的GPIO端口,可以是GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF或GPIOG。
  • GPIO_Pin:要设置为高电平的引脚编号,取值范围为0-15。

使用方法:将PA5引脚设置为高电平:

GPIO_SetBits(GPIOA, GPIO_Pin_5);

4.2.3 将指定引脚设置为低电平

函数声明如下:

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
  • GPIOx:要操作的GPIO端口,可以是GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF或GPIOG。
  • GPIO_Pin:要设置为低电平的引脚编号,取值范围为0-15。

使用方法:将PA5引脚设置为低电平:

GPIO_ResetBits(GPIOA, GPIO_Pin_5);

4.2.4 将指定端口所有引脚设置为指定值

函数声明如下:

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
  • GPIOx:要操作的GPIO端口,可以是GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF或GPIOG。
  • PortVal:要输出的值,取值范围为0到FFFF(16位)。

使用方法:将PA0~PA7引脚全部设置为高电平:

GPIO_Write(GPIOA, 0xFF);

4.3 读取输入输出状态函数

4.3.1 读取指定GPIO端口和引脚号对应的输入状态

函数声明如下:

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
  • GPIOx:GPIO端口号(例如GPIOA、GPIOB等)。
  • GPIO_Pin:引脚号(例如GPIO_Pin_0、GPIO_Pin_1等)。
  • 函数返回值为uint8_t类型。

实际使用时,要先对GPIO对应的端口和引脚配置为输入模式,然后才能正确读取其输入状态。
使用方法:读取PB2的输入状态:

// 配置PB2为输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);

// 读取PB2的输入状态
uint8_t input_status = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2);

首先将PB2配置为上拉输入模式,然后调用GPIO_ReadInputDataBit函数读取PB2的输入状态,并将结果存储在input_status变量中。如果PB2为高电平,则input_status为1;如果PB2为低电平,则input_status为0。

4.3.2 读取指定GPIO端口的输入数据

函数声明如下:

uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
  • GPIOx:可以是A、B、C、D、E、F或G端口之一。
  • 返回值是一个16位的无符号整数,表示指定GPIO端口上各个引脚的电平状态(1为高电平,0为低电平)。

使用方法:读取GPIOB端口的输入数据,保存在16位变量input_data中:

GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO初始化结构体
uint16_t input_data; //定义用于存储读取到的GPIO输入数据的变量

//配置GPIOB.5为输入引脚
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  //读取GPIOB端口的输入数据
  input_data = GPIO_ReadInputData(GPIOB);

此时通过观察input_data变量的第五位二进制数可以得到PB5的引脚状态。

4.3.3 读取指定GPIO端口的指定引脚的输出状态

函数声明如下:

uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
  • GPIOx:要访问的GPIO端口。
  • GPIO_Pin:要读取状态的引脚。
  • 函数返回值为一个无符号8位整数(uint8_t),表示指定引脚的状态,如果该引脚为高电平,则返回1,否则返回0。

使用方法:读取PA0引脚的状态:

uint8_t PA0_state;

    // 将PA0配置为输入引脚
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 设置上拉输入模式
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; // 速度设置为2MHz
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 读取PA0的状态
    PA0_state = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0);

4.3.4 读取指定GPIO端口的输出数据

函数声明如下:

uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
  • GPIOx :指定要读取的GPIO端口。
  • 返回值是一个16位的无符号整数,表示指定GPIO端口上各个引脚的电平状态(1为高电平,0为低电平)。

使用方法:读取GPIOB端口的输出数据,保存在16位变量output_data中:

// 初始化GPIO模块并配置引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6; // 配置第5和第6引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 配置为推挽输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 输出速度设置为50MHz
    GPIO_Init(GPIOB, &GPIO_InitStructure); // 应用上述配置

    // 写入输出数据
    GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_SET); // 设置第5引脚输出高电平
    GPIO_WriteBit(GPIOB, GPIO_Pin_6, Bit_RESET); // 设置第6引脚输出低电平

    // 读取输出数据
    uint16_t output_data;
    output_data = GPIO_ReadOutputData(GPIOB); // 将GPIOB端口的输出数据读取到 output_data 变量中

设置GPIOB端口的第5引脚输出高电平,第6引脚输出低电平,此时output_data变量的第五位和第六位分别为1和0。

5. GPIO实验

代码实现在注释中已详细解释。

5.1 跑马灯实验

实现开发板上两个LED灯实现跑马灯的效果。
LED硬件电路如下:
在这里插入图片描述
根据硬件电路图知,两个LED灯对应的IO引脚分别为PortB5,PortB5,当两个引脚为低电平时,二极管导通;当两个引脚为高电平时,二极管截止,因此对这两个IO引脚设置高低电平变化即可。
led.h文件内容如下:

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

#define LED0_OFF GPIO_SetBits(GPIOB, GPIO_Pin_5)		// PB5灭
#define LED0_ON GPIO_ResetBits(GPIOB, GPIO_Pin_5)		// PB5亮

#define LED1_OFF GPIO_SetBits(GPIOE, GPIO_Pin_5)		// PE5灭	
#define LED1_ON GPIO_ResetBits(GPIOE, GPIO_Pin_5)		// PE5亮	

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

led.c文件内容如下:

#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 "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"

 int main(void)
 {	
	delay_init();	    //延时函数初始化	  
	LED_Init();		  	//初始化与LED连接的硬件接口
	while(1)
	{
		LED0_OFF;
		LED1_ON;
		delay_ms(300);	 //延时300ms
		LED0_ON;
		LED1_OFF;
		delay_ms(300);	//延时300ms
	}
 }

5.2 蜂鸣器实验

实现开发板上蜂鸣器发声,并用一个指示灯LED0指示蜂鸣器正在发声。
蜂鸣器硬件电路如下:
在这里插入图片描述
根据硬件电路图可知,当 Port8 输出高电平的时候,蜂鸣器将发声, 当 Port8 输出低电平的时候,蜂鸣器停止发声。因此对该IO引脚设置高低电平变化即可。
beep.h文件内容如下:

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

#define LED0_OFF GPIO_SetBits(GPIOB, GPIO_Pin_5)            // PB5灭
#define LED0_ON GPIO_ResetBits(GPIOB, GPIO_Pin_5)           // PB5亮

//蜂鸣器端口定义
#define BEEP_ON GPIO_SetBits(GPIOB, GPIO_Pin_8)	            // BEEP发声		   
#define BEEP_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_8)	        // BEEP停止发声		   

void BEEP_Init(void);	//蜂鸣器初始化
void LED_Init(void);    //LEDO初始化
		 				    
#endif

led.c文件内容如下:

#include "beep.h"
//初始化PB8为输出口.并使能这个口的时钟		    
//蜂鸣器初始化
void BEEP_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能GPIOB端口时钟
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;				 //BEEP-->PB.8 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 //速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);	 //根据参数初始化GPIOB.8
 
 GPIO_ResetBits(GPIOB,GPIO_Pin_8);//输出0,关闭蜂鸣器输出

}

void LED_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PB端口时钟
	
 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 输出高
}

main.c文件内容如下:

#include "sys.h"	
#include "delay.h"	
#include "beep.h" 

 int main(void)
 {
	delay_init();	    	 //延时函数初始化	  
	LED_Init();		  	 	 //初始化与LED连接的硬件接口
	BEEP_Init();         	 //初始化蜂鸣器端口
	while(1)
	{
		LED0_ON;
		BEEP_ON;		  
		delay_ms(300);//延时300ms
		LED0_OFF;	  
		BEEP_OFF;  
		delay_ms(300);//延时300ms
	}
 }

5.3 按键输入实验

使用开发板上三个按键控制两LED灯亮灭和一个蜂鸣器发声。
按键硬件电路如下:
在这里插入图片描述
根据硬件电路图可知,当WK_UP为高电平时,按键按下才有效;当KEY0和KEY1为低电平时,按键按下才有效。并且外部都没有上下拉电阻,所以,需要在内部设置上下拉。
led.h文件内容如下:

#ifndef __LED_H
#define __LED_H	 
#include "sys.h"
//LED电平翻转
//这里Bit_RESET实际值为0,类型为BitAction结构体,只需用1-GPIO_ReadOutputDataBit函数读到的电平值即可
//当GPIO_ReadOutputDataBit读到高电平返回值为1,那么1-GPIO_ReadOutputDataBit为0对应即为Bit_RESET,再用GPIO_WriteBit即可实现电平翻转
//当GPIO_ReadOutputDataBit读到低电平返回值为0,那么1-GPIO_ReadOutputDataBit为1对应即为Bit_SET,再用GPIO_WriteBit即可实现电平翻转
#define _LED0_ GPIO_WriteBit(GPIOB, GPIO_Pin_5, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_5)))// PB5
#define _LED1_ GPIO_WriteBit(GPIOE, GPIO_Pin_5, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_5)))// PE5	

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

led.c文件内容如下:

#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 输出高 
}

beep.h文件内容如下:

#ifndef __BEEP_H
#define __BEEP_H	 
#include "sys.h"
//蜂鸣器电平翻转,与led电平翻转相同
#define _beep_ GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_8)))

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

beep.c文件内容如下:

#include "beep.h"
//初始化PB8为输出口.并使能这个口的时钟		    
//蜂鸣器初始化
void BEEP_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能GPIOB端口时钟
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;				 //BEEP-->PB.8 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 //速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);	 //根据参数初始化GPIOB.8
 
 GPIO_ResetBits(GPIOB,GPIO_Pin_8);//输出0,关闭蜂鸣器输出
}

key.h文件内容如下:

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

#define KEY0  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键0
#define KEY1  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键1
#define WK_UP   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP) 

#define KEY0_PRES 	1	//KEY0按下
#define KEY1_PRES	2	//KEY1按下
#define WKUP_PRES   3	//KEY_UP按下(即WK_UP/KEY_UP)

void KEY_Init(void);//IO初始化
u8 KEY_Scan(u8);  	//按键扫描函数					    
#endif

key.c文件内容如下:

#include "stm32f10x.h"
#include "key.h"
#include "sys.h" 
#include "delay.h"
								    
//按键初始化函数
void KEY_Init(void) //IO初始化
{ 
 	GPIO_InitTypeDef GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟

	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4|GPIO_Pin_3;//KEY0-KEY1
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
 	GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE4,3

	//初始化 WK_UP-->GPIOA.0	  下拉输入
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉	  
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0

}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY_UP!!
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按		  
	if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(KEY0==0)return KEY0_PRES;
		else if(KEY1==0)return KEY1_PRES;
		else if(WK_UP==1)return WKUP_PRES;
	}else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1; 	    
 	return 0;// 无按键按下
}

main.c文件内容如下:

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

 int main(void)
 {
 	vu8 key=0;	
	delay_init();	    	 //延时函数初始化	  
	LED_Init();		  		//初始化与LED连接的硬件接口
	BEEP_Init();         	//初始化蜂鸣器端口
	KEY_Init();         	//初始化与按键连接的硬件接口
	while(1)
	{
 		key=KEY_Scan(0);	//得到键值
	   	if(key)
		{						   
			switch(key)
			{				 
				case WKUP_PRES:	//控制蜂鸣器
					_beep_;     //蜂鸣器翻转
					break; 
				case KEY1_PRES:	//控制LED1翻转	 
					_LED1_;     //LED1翻转
					break;
				case KEY0_PRES:	//同时控制LED0,LED1翻转 
					_LED0_;     //LED0翻转
					_LED1_;     //LED1翻转
					break;
			}
		}else delay_ms(10); 
	}	 
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值