03 GPIO通用输入输出口

一、GPIO通用输入输出口

1.GPIO简介

  • GPIO(General Purpose Input Output)通用输入输出口
  • 可配置为8种输入输出模式
  • 引脚电平:0V~3.3V,部分引脚可容忍5V[FT](可以在这个端口输入5V的电压,也认为是高电平,但是最大只能输出3.3V)
  • 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
  • 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等

2.GPIO基本结构

GPIO基本结构
APB2外设中心

在STM32中,所有的GPIO都是挂载在APB2外设总线上的

GPIO外设总共有16个引脚,编号0-15

一般把GPIOA的第0号引脚称作PA0,接着就是PA1、PA2、……、PA15
GPIOB同理,PB1、PB2、……、PB15
GPIO模块内主要包含了寄存器和驱动器等

寄存器:一段特殊的存储器内核,只负责存储数据。

  • 通过APB2总线对寄存器进行读写,可以完成输出电平和读取电平的功能。
  • 寄存器每一位对应一个引脚,输出寄存器写1对应的引脚输出高电平,写0对应的引脚输出低电平;输入寄存器读取为1对应的端口目前是高电平,读取为0是低电平。
  • 因为STM32是32位的单片机,所以STM32内部的寄存器都是32位的。但这个端口只有16位,所以这个寄存器只有低16位对应的有端口,高16位是没有用到的。

驱动器:用来增加信号的驱动能力。

3.GPIO位结构

I/O端口位的基本结构

左边:3个寄存器
中间:驱动器
右边:某一个IO的引脚
整体结构——上面:输入部分,下面:输出部分

输入部分

IO引脚:接了2个保护二极管——对输入电压进行限幅

  • 如果输入电压比3.3V高,上方二极管就会导通,输入电压产生的电流就会直接流入VDD,而不会流入内部电路——避免过高的电压对内部这些电路产生伤害
  • 如果输入电压比0V低,下方二极管就会导通,电流会从VSS直接流出去,而不会从内部电路汲取电流——保护内部电路
  • 如果输入电压在0-3.3V之间,两个二极管均不会导通,对电路没有影响

IO引脚和肖特基触发器之间

  • 连接了一个上拉电阻和一个下拉电阻,上拉电阻至VDD,下拉电阻至VSS
  • 这个开关是可以通过程序进行配置
  • 上面导通、下面断开——上拉输入模式/默认为高电平的输入模式
  • 下面导通、上面断开——下拉输入模式/默认为低电平的输入模式
  • 两个都断开——浮空输入模式
  • 上拉和下拉给输入提供一个默认的输入电平,如果输入啥都不接,输入就会处于一种浮空的状态,引脚的输入电平极易受外界干扰而改变

肖特基触发器[施密特触发器]——对输入电压进行整形

  • 如果输入电压大于某一阈值,输出就会瞬间升为高电平
  • 如果输入电压小于某一阈值,输出就会瞬间降为低电平
  • 只有高于上限或者低于下限输出才会变化
  • 使用了两个比较阈值来进行判断,中间留有一定的变化范围,有效的避免因信号波动造成的输出抖动现象

肖特基触发器之后

  1. 直接写入输入数据寄存器,再用程序读取输入数据寄存器对应某一位的数据。就可以知道端口的输入电平
  2. 模拟输入(连接到ADC上),因为ADC需要接收拟量,所以这根线接到施密特触发器前面
  3. 复用功能输入(连接到其他需要读取端口的外设上,比如串口的输入引脚等),这根线接收的是数字量,所以在施密特触发器后面
输出部分

数字部分可由输出数据寄存器或片上外设控制,两种控制方式通过数据选择器接到了输出控制部分。

输出数据寄存器——普通的IO口输出:写数据寄存器的某一位就可操作对应的某个端口.

  • 输出数据寄存器同时控制16个端口,且只能整体读写

位设置/清除寄存器:用来单独操作输出数据寄存器的某一位,而不影响其他位

单独控制某一个端口而不影响其他端口的操作方式
①读出寄存器→用按位与和按位或的方式更改某一位→将更改后的数据写回去(C语言&=和|=的操作)
比较麻烦,效率不高,对于IO口的操作不太合适
设置位设置/清除寄存器——库函数

  • 一步到位的操作(只操作其中某一位而不影响其他位)
  • 位设置/清除寄存器的作用:

对某一位进行置1:在位设置寄存器的对应位写1,剩下不需要操作的位写0
对某一位进行清0:在位清除寄存器的对应位写1,内部电路会把这一位清零

③读写单片机STM32中的"位带"区域(跟51的位寻址作用相似)
在STM32中专门分配的有一段地址区域,这段地址映射了RAM和外设寄存器所有的位。
读写这段地址中的数据,就相当于读写所映射位置的某一位。

输出控制之后接到两个MOS管——P-MOS和N-MOS
MOS管是一种电子开关,信号控制开关的导通和关闭,开关负责将IO口接到VDD或者VSS
三种输出方式

①推挽输出模式/强推输出模式——P-MOS和N-MOS均有效

  • 数据寄存器为1,上管导通、下管断开,输出直接接到VDD——输出高电平
  • 数据寄存器为0,上管断开、下管导通,输出直接接到VSS——输出低电平
  • 高低电平均有较强的驱动能力
  • STM32对IO口具有绝对的控制权,高低电平都由STM32说了算

②开漏输出模式——P-MOS无效,N-MOS工作

  • 数据寄存器为1,下管断开,输出相当于断开——高阻模式
  • 数据寄存器为0,下管导通,输出直接接到VSS——输出低电平
  • 只有低电平有驱动能力,高电平没有驱动能力
  • 可以作为通信协议的驱动方式
    eg.I²C通信的引脚就是使用的开漏模式,在多机通信的情况下,这个模式可以避免各个设备的相互干扰
  • 可以用于输出5V的电平信号
    eg.在IO外接一个上拉电阻到5V的电源,当输出低电平时,由内部的N-MOS直接接VSS,当输出高电平时,由外部的上拉电阻拉高至5V,这样就可以输出5V电平信号,用于兼容一些5V电平的设备。

③关闭输出模式——两个MOS管均无效

当引脚配置为输入模式的时候,输出关闭,端口的电平由外部信号来控制。

4.GPIO模式

通过配置GPIO的端口配置寄存器,端口可以配置成以下8种模式
GPIO模式
浮空/上拉/下拉输入
浮空/上拉/下拉输入
模拟输入
模拟输入
开漏/推挽输出
开漏/推挽输出
复用开漏/推挽输出
复用开漏/推挽输出

5.LED和蜂鸣器简介

  • LED:发光二极管,正向通电点亮,反向通电不亮
  • 有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定
  • 无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音
    在这里插入图片描述
LED/蜂鸣器的硬件电路

LED/蜂鸣器的硬件电路
使用STM32的GPIO口驱动LED1的电路
①低电平驱动

  • LED正极接3.3V,负极通过一个限流电阻接到PA0上
  • 当PA0输出低电平时,LED两端产生电压差,形成正向导通电流——LED点亮
  • 当PA0输出高电平时,LED两端都是3.3V的电压,不会形成电流——LED熄灭

限流电阻一般都要接的原因:
a.防止LED因为电流过大而烧毁
b.调整LED的亮度。LED太亮——适当增大限流电阻的阻值

③高电平驱动

  • LED负极接到GND,正极通过一个限流电阻接到PA0——高电平点亮、低电平熄灭
  • 注:两种驱动方式的选择取决于IO口高低电平的驱动能力
  • 在推挽输出模式下,高低电平均有比较强的驱动能力——两种接法均可
  • 在单片机的电路里,一般倾向使用低电平驱动——很多单片机/芯片都使用高电平弱驱动、低电平强驱动的规则,一定程度上避免高低电平打架

蜂鸣器电路——三极管开关的驱动电路——最简单
对于功率稍微大一点的,直接用IO口驱动会导致STM32负担过重,用一个三极管驱动电路可以完成驱动的任务
②PNP三极管的驱动电路

  • 三极管的左边是基极,带箭头的是发射极,剩下的是集电极
  • 基极给低电平,三极管导通,通过3.3V和GND给蜂鸣器提供驱动电流
  • 基极给高电平,三极管截止,蜂鸣器就没有电流

④NPN三极管的驱动电路(与②的逻辑相反)

  • 三极管的左边是基极,带箭头的是发射极,剩下的是集电极
  • 基极给高电平,三极管导通,通过3.3V和GND给蜂鸣器提供驱动电流
  • 基极给低电平,三极管截止,蜂鸣器就没有电流

6.面包板

面包板实物图

7.按键简介

  • 按键:常见的输入设备,按下导通,松手断开

上面白色的是按钮,下面有两个引脚。
按下按钮后下面两个引脚接通,松手后两个引脚自动断开。

  • 按键抖动:由于按键内部使用的是机械式弹簧片来进行通断的,所以在按下和松手的瞬间会伴随有一连串的抖动

要对这个抖动进行过滤,否则就会出现按键按一下,单片机却反映了多次的现象。
过滤方法:加延时Delay(20);

在这里插入图片描述

按键的硬件电路

在这里插入图片描述
①按键的最常用接法

  • 随便选取一个GPIO口,比如PA0,然后通过K1接到地。
  • 当按键按下时,PA0被直接下拉到GND,此时读取PA0口的电压就是低电平;
  • 当按键松手时,PA0被悬空,引脚电压不确定,这种接法下,必须要求PA0是上拉输入的模式,否则就会出现引脚电压不确定的错误现象。
  • 如果PA0是上拉输入的模式,引脚悬空,PA0就是高电平。
  • 所以在上拉输入的模式下,按下按键引脚为低电平,松手引脚为高电平。

②外部接了一个上拉电阻

  • 当按键松手时,引脚由于上拉作用自然保持为高电平;
  • 当按键按下时,引脚直接接到GND,把这个引脚往下拉,为低电平。
  • 这种状态下引脚不会出现悬空状态,此时PA0引脚可以配置为浮空输入或者上拉输入。
  • 上拉输入:内外两个上拉电阻共同作用——高电平更强一些,对应高电平更加稳定,但当引脚被强行拉到低时损耗会大一些。

③PA0通过按键接到3.3V

  • 要求PA0必须要配置成下拉式的模式
  • 当按键按下时,引脚为高电平;松手时,引脚回到默认值低电平。
  • 这要求单片机的引脚可以配置为下拉输入的模式,一般单片机不一定有下拉输入的模式

④再外接一个下拉电阻

  • PA0需要配置为下拉输入模式或者浮空输入模式

8.传感器模块简介

传感器模块:传感器元件的电阻会随外界模拟量的变化而变化,通过与定值电阻分压即可得到模拟电压输出,再通过电压比较器进行二值化即可得到数字电压输出

光敏电阻:光线越强,光敏电阻的阻值就越小
热敏电阻:温度越高,热敏电阻的阻值就越小
红外接收管:红外光线越强,红外接收管的阻值就越小

传感器模块

  • N1:传感器元件所代表的可变电阻——其阻值可以根据环境的光线、温度等模拟量进行变化。
  • R1是和N1进行分压的定值电阻,R1和N1串联,一端接在VCC正极,一端接在GND负极,构成了基本的分压电路。
  • C2是滤波电容,是为了给中间的电压输出进行滤波的,用来滤除一些干扰,保证输出电压波形的平滑。

在电路中,一端接在电路,另一端接地的电容,通常是滤波电容。
滤波电容的作用是用来保证电路稳定的,并不是电路的主要框架。
分析电路时可以先把这个电容给抹掉,使电路分析更加简单。

  • 上下拉电阻分析:
    1. N1阻值变小,下拉作用增强,中间的AO端的电压拉低,极端情况下N1阻值为零,AO输出被完全下拉,输出0V。
    2. N1阻值变大,下拉作用减弱,中间的引脚由于R1的上拉作用,电压升高,极端情况下N1阻值无穷大(相当于断路),输出电压被R1拉高至VCC。
  • 二极化通过电压比较器LM393完成。
    LM393:芯片里面有两个独立的电压比较器电路;

剩下的是VCC和GND供电,VCC接到电路的VCC,GND接到电路的GND;
电容是一个电源供电的滤波电容。

  • 电压比较器→运算放大器[51单片机AD/DA]
传感器硬件电路

传感器硬件电路
VCC接3.3V,GND接GND,用于供电
DO数字输出随便接一个端口,比如PA0用于读取数字量
AO模拟输出-学ADC模数转换器再用


二、补充:C语言相关知识

C语言数据类型

C语言数据类型

C语言宏定义

关键字:#define
用途:用一个字符串代替一个数字,便于理解,防止出错;提取程序中经常出现的参数,便于快速修改

定义宏定义:
	#define ABC 12345
引用宏定义:
	int a = ABC;	//等效于int a = 12345

C语言typedef

关键字:typedef
用途:将一个比较长的变量类型名换个名字,便于使用

定义typedef:
	typedef unsigned char uint8_t;
引用typedef:
	 uint8_t a;	//等效于unsigned char

C语言结构体

关键字:struct
用途:数据打包,不同类型变量的集合

定义结构体变量:
	struct{char x; int y; float z;} StructName;
	因为结构体变量类型较长,所以通常用typedef更改变量类型名
引用结构体成员:
	StructName.x = 'A';
	StructName.y = 66;
	StructName.z = 1.23;
或	pStructName->x = 'A';	//pStructName为结构体的地址	pStructName->y = 66
	pStructName->z = 1.23;

C语言枚举

关键字:enum
用途:定义一个取值受限制的整型变量,用于限制变量取值范围;宏定义的集合

定义枚举变量:
	enum{FALSE = 0, TRUE = 1} EnumName;
	因为枚举变量类型较长,所以通常用typedef更改变量类型名
引用枚举成员:
	EnumName = FALSE;
	EnumName = TRUE;

三、实操

3.1 输出:LED闪烁

接线图

LED闪烁

代码实现

main.c

#include "stm32f10x.h"   // Device header
#include "Delay.h"

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    while(1)
    {
        GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
        Delay_ms(100);
        GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
        Delay_ms(100);
    }
}
具体分析
  • 现象:LED闪烁——每隔100毫秒,LED的亮灭状态会切换一次
  • 作用:将STM32的GPIOA的0号引脚设置为输出模式,并且循环地将引脚置为低电平和高电平,每个状态持续100毫秒。
  • 具体执行流程:
    1. 使能GPIOA的时钟。
    2. 初始化GPIOA的0号引脚为推挽输出模式,最大输出速率为50MHz。
    3. 进入无限循环。
      ·将GPIOA的0号引脚置为低电平。
      ·延时100毫秒。
      ·将GPIOA的0号引脚置为高电平。
      ·延时100毫秒。
    4. 重复步骤4-7,实现引脚的周期性高低电平切换。

3.2 输出:LED流水灯

接线图

LED流水灯

代码实现

main.c

#include "stm32f10x.h"		// Device header
#include "Delay.h"

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    while(1)
    {
        GPIO_Write(GPIOA ,~0x0001);
        Delay_ms(500);
        GPIO_Write(GPIOA ,~0x0002);
        Delay_ms(500);
        GPIO_Write(GPIOA ,~0x0004);
        Delay_ms(500);
        GPIO_Write(GPIOA ,~0x0008);
        Delay_ms(500);
        GPIO_Write(GPIOA ,~0x0010);
        Delay_ms(500);
        GPIO_Write(GPIOA ,~0x0020);
        Delay_ms(500);
        GPIO_Write(GPIOA ,~0x0040);
        Delay_ms(500);
        GPIO_Write(GPIOA ,~0x0080);
        Delay_ms(500); 
    }
}
具体分析
  • 现象:LED流水灯
  • 作用:实现多个LED按照特定的频率依次闪烁
    具体执行流程:
    1. 使能GPIOA的时钟。
    2. 用来配置GPIO的参数。
    3. 将GPIOA的引脚配置为输出模式。
    4. 在主循环中依次控制GPIOA的引脚输出低电平来控制LED的亮灭状态。
      通过~运算符对不同的引脚进行取反操作,实现按照特定顺序闪烁的效果。
    5. 通过循环不断地执行以上操作,实现多个LED按照特定的频率依次闪烁的现象。
代码优化

main.c

#include "stm32f10x.h"		// Device header
#include "Delay.h"

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    while(1)
    {   
        for(int i=0;i<8;i++)
        {
            GPIO_Write(GPIOA ,~(0x0001 << i));
            Delay_ms(500);
        }
    }   
}

3.3 输出:蜂鸣器

接线图

蜂鸣器

代码实现

main.c

#include "stm32f10x.h"		// Device header
#include "Delay.h" 

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    
    while(1)
    {
        GPIO_ResetBits(GPIOB,GPIO_Pin_12);
        Delay_ms(200);
        GPIO_setBits(GPIOB,GPIO_Pin_12);
        Delay_ms(200);
        GPIO_ResetBits(GPIOB,GPIO_Pin_12);
        Delay_ms(200);
        GPIO_ResetBits(GPIOB,GPIO_Pin_12);
        Delay_ms(700);
    }
}
具体分析
  • 作用:通过控制GPIO引脚(GPIOB的引脚12)来控制蜂鸣器的鸣叫。
  • 具体执行情况:
    1. 打开GPIOB的时钟,使能GPIOB引脚的外设时钟。
    2. 初始化一个GPIO结构体变量GPIO_InitStructure,设置引脚模式为推挽输出(GPIO_Mode_Out_PP),设置引脚为GPIOB的所有引脚(GPIO_Pin_All),设置引脚速度为50MHz(GPIO_Speed_50MHz)。
    3. 使用GPIO_Init函数对GPIOB进行初始化,将GPIO_InitStructure结构体的配置应用于GPIOB。
    4. 进入无限循环。
      ·将GPIOB的引脚12拉低(低电平),使蜂鸣器静音。
      ·延时200毫秒。
      ·将GPIOB的引脚12拉高(高电平),使蜂鸣器鸣叫。
      ·延时200毫秒。
      ·再次将GPIOB的引脚12拉低,使蜂鸣器静音。
      ·延时200毫秒。
      ·再次将GPIOB的引脚12拉低,使蜂鸣器静音。
      ·延时700毫秒。
    5. 重复执行步骤4,蜂鸣器就会以一定的时间间隔鸣叫和静音。

3.4 输入:按键控制LED

接线图

按键控制LED

调试代码
#include "stm32f10x.h"		// Device header
#include "Delay.h"

uint8_t KeyNum;

int main(void)
{
    LED_Init();
    
    while(1)
    {
        LED1_ON();
        LED2_OFF();
        Delay_ms(500);
        LED2_ON();
        LED1_OFF();
        Delay_ms(500);
    }
}

现象:LED1和LED2交替闪烁,每次闪烁的时间间隔为500毫秒

代码实现

mian.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"

uint8_t KeyNum;

int main(void)
{
    LED_Init();
    Key_Init();
    
    while (1)
    {
        KeyNum = Key_GetNum();
        if (KeyNum == 1)
            LED1_Turn();
        if (KeyNum == 2)
            LED2_Turn();
    }
}

LED.c / LED.h

#include "stm32f10x.h"		// Device header

void LED_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	        //推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;	
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_2);
}

void LED1_ON(void)
{
    GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}

void LED1_OFF(void)
{
    GPIO_SetBits(GPIOA,GPIO_Pin_1);
}

void LED1_Turn(void)
{
    if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1)==0)
        GPIO_SetBits(GPIOA,GPIO_Pin_1);
    else
        GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}

void LED2_ON(void)
{
    GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}

void LED2_OFF(void)
{
    GPIO_SetBits(GPIOA,GPIO_Pin_2);
}

void LED2_Turn(void)
{
    if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2)==0)
        GPIO_SetBits(GPIOA,GPIO_Pin_2);
    else
        GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
#ifndef __LED_H__
#define __LED_H__

void LED_Init(void);
void LED1_ON(void);
void LED1_OFF(void);
void LED1_Turn(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED2_Turn(void);

#endif

Key.c / Key.h

#include "stm32f10x.h"		// Device header
#include "Delay.h"

void Key_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;            	//上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;	
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
}

uint8_t Key_GetNum(void)
{
    uint8_t KeyNum=0;
    
    if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
    {
        Delay_ms(20);
        while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0);
        Delay_ms(20);
        KeyNum=1;
    }
    if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0)
    {
        Delay_ms(20);
        while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0);
        Delay_ms(20);
        KeyNum=2;
    }
    
    return KeyNum;
}
#ifndef __Key_H__
#define __Key_H__

void Key_Init(void);
uint8_t Key_GetNum(void);

#endif

3.5 输入:光敏传感器控制蜂鸣器

接线图

光敏传感器控制蜂鸣器

调试代码
#include "stm32f10x.h"		// Device header
#include "Delay.h"
#include "Buzzer.h"

int main(void)
{
    Buzzer_Init();
    
    while(1)
    {
        Buzzer_ON();
        Delay_ms(500);
        Buzzer_OFF();
        Delay_ms(500);
        Buzzer_Turn();
        Delay_ms(500);
        Buzzer_Turn();
        Delay_ms(500);
    }
}

现象:响500ms,停500ms,始终循环。
作用:在特定时间间隔内开启和关闭蜂鸣器,并切换蜂鸣器的状态,以产生特定的声音模式或音乐节奏。
通过这些函数的组合,可以实现不同的蜂鸣器控制模式,从而产生不同的音乐效果或警报信号。

代码实现

main.c

#include "stm32f10x.h"		// Device header
#include "Delay.h"
#include "Key.h"
#include "LED.h"
#include "Buzzer.h"
#include "LightSensor.h"

uint8_t KeyNum;

int main(void)
{
    Buzzer_Init();
    LightSensor_Init();
    
    while(1)
    {
        if(LightSensor_Get()==1)
            Buzzer_ON();
        else
            Buzzer_OFF();
    }
}

Buzzer.c / Buzzer.h

#include "stm32f10x.h" 		// Device header

void Buzzer_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	    //推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;	
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    
    GPIO_SetBits(GPIOB,GPIO_Pin_12);
}

void Buzzer_ON(void)
{
    GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}

void Buzzer_OFF(void)
{
    GPIO_SetBits(GPIOB,GPIO_Pin_12);
}

void Buzzer_Turn(void)
{
    if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12)==0)
        GPIO_SetBits(GPIOB,GPIO_Pin_12);
    else
        GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
#ifndef __Buzzer_H__
#define __Buzzer_H__

void Buzzer_Init(void);    //Buzzer_Init() 用于初始化蜂鸣器
void Buzzer_ON(void);      //Buzzer_ON() 用于开启蜂鸣器
void Buzzer_OFF(void);     //Buzzer_OFF() 用于关闭蜂鸣器
void Buzzer_Turn(void);    //Buzzer_Turn() 用于切换蜂鸣器的状态

#endif

LightSensor.c / LightSensor.h

#include "stm32f10x.h"                  // Device header

void LightSensor_Init()
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	    //上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;	
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
}

uint8_t LightSensor_Get(void)
{
    return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13);
}
#ifndef __LightSensor_H__
#define __LightSensor_H__

void LightSensor_Init();
uint8_t LightSensor_Get(void);
    
#endif
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值