STM32单片机学习(11)——GPIO输入实验

文章目录

实验一:按住按键LED点亮实验

题目要求

综合GPIO通用输出和输入模式,以及LED,按键,蜂鸣器等元器件,完成以下实验。

先将蜂鸣器和LED接入面包板,接线可以自由完成。然后将两个按键接入面包板:

  1. 第一个按键的一脚要接在电源轨正极,另一脚接入某个IO引脚(比如PB1),长按按键1,实现切换LED流水灯,松开按键则熄灭所有LED灯
  2. 第二个按键的一脚要接在电源轨负极,另一脚接入某个IO引脚(比如PA9),按下按键1,蜂鸣器响,再次按下,蜂鸣器关闭。

接线与程序框架

  • A0, A2, A4, A6接LED灯, 采用推挽输出模式
  • 流水灯按键一端接B1, 另外一端接电源正极,采用上拉输入模式。
  • 蜂鸣器I/O口接在A10
  • 蜂鸣器按键一端接在A9,另外一段接在电源负极,采用下拉输入模式
c 复制代码
// 初始化led和按键
void init_led_and_key(GPIO_InitTypeDef GPIO_InitStruct);
// 初始化蜂鸣器和按键
void init_buzzer_and_key(GPIO_InitTypeDef GPIO_InitStruct);
// 初始化接口
void init();
// 流水灯
void stream_led();
// 切换蜂鸣器状态
void stitch_state_of_buzzer();

程序实现

  1. led流水灯部分实现
c 复制代码
//初始化led及其按键
void init_led_and_key(GPIO_InitTypeDef GPIO_InitStruct){
	//初始化LED
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_6 ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA , &GPIO_InitStruct);
	// 初始化按键1
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;	//采用上拉输入模式
	GPIO_Init(GPIOB , &GPIO_InitStruct);
}
//初始化
void init(){
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
		GPIO_InitTypeDef GPIO_InitStruct;
		init_led_and_key(GPIO_InitStruct);
}
// 流水灯实现
void stream_led(){
	uint16_t Pin_x = 1;
	// GPIO_ReadInputDataBit()函数读取当前引脚电平状态,以此作为循环终止的判断
	while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == Bit_RESET){
		if(Pin_x > GPIO_Pin_6) Pin_x = GPIO_Pin_0;
		// 点亮
		GPIO_SetBits(GPIOA, Pin_x);
		// 延时
		Delay_Ms(200);
		// 熄灭
		GPIO_ResetBits(GPIOA, Pin_x);
		// 轮换
		Pin_x = Pin_x << 2;
		}
}

int main(){
	init();
	while(1){
		// 默认输出高电平,当按下按键,引脚接地,则输出低电平
		// 所以输出低电平时,开始流水灯,否则全部熄灭。
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == Bit_RESET) stream_led();
		else GPIO_ResetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_6);
	}
}

知识点温故:

引脚设置为上拉输入模式时:

  • 若引脚输入了高电平,此时读取输入数据寄存器,可以读到代表高电平的"1"信号。
  • 若引脚输入了低电平或者引脚接地,此时读取输入数据寄存器,可以读到代表低电平的"0"信号。
  • 若引脚处于悬空状态,无外部信号输入时,由于上拉电阻的影响,此时读取输入数据寄存器,仍然可以读到代表> 高电平的"1"信号。上拉输出模式:引脚默认输出高电平
  1. 蜂鸣器部分实现
c 复制代码
// 初始化蜂鸣器和按键2
void init_buzzer_and_key(GPIO_InitTypeDef GPIO_InitStruct){
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA , &GPIO_InitStruct);
	
	// 初始化按键(A9) 采用下拉输入模式
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 ;
	GPIO_Init(GPIOA , &GPIO_InitStruct);
}
// 初始化
void init(){
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
		GPIO_InitTypeDef GPIO_InitStruct;
		init_buzzer_and_key(GPIO_InitStruct);
}
// 切换蜂鸣器状态
void switch_state_of_buzzer(){
	if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_10) == Bit_SET) GPIO_ResetBits(GPIOA, GPIO_Pin_10);
	else GPIO_SetBits(GPIOA, GPIO_Pin_10);
}

int main(){
	init();
	while(1){
		if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == Bit_SET) switch_state_of_buzzer();
	}
}

知识点温故:

当引脚设置为下拉输入模式时:

  • 若引脚输入了高电压,此时读取输入数据寄存器,可以读到代表高电平的"1"信号。
  • 若引脚输入了低电压或者引脚接地,此时读取输入数据寄存器,可以读到代表低电平的"0"信号。
  • 若引脚处于悬空状态,无外部信号输入时,由于下拉电阻的影响,此时读取输入数据寄存器,仍然可以读到代表低电平的"0"信号。

总体的代码如下:

c 复制代码
#include "stm32f10x.h"
#include "../tools/Delay.h"

/*
- A0, A2, A4, A6接LED灯, 采用推挽输出模式
- 流水灯按键一端接B1, 另外一端接电源正极,采用上拉输入模式。
- 蜂鸣器I/O口接在A10
- 蜂鸣器按键一端接在A9,另外一段接在电源负极,采用下拉输入模式
*/

void init_led_and_key(GPIO_InitTypeDef GPIO_InitStruct){
	//初始化LED
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_6 ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA , &GPIO_InitStruct);
		
	// 初始化按键1
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOB , &GPIO_InitStruct);
}

void init_buzzer_and_key(GPIO_InitTypeDef GPIO_InitStruct){
	// 初始化蜂鸣器
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA , &GPIO_InitStruct);
		
	// 初始化按键2 - A9 采用下拉输入模式
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 ;
	GPIO_Init(GPIOA , &GPIO_InitStruct);
}

void init(){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	init_led_and_key(GPIO_InitStruct);
	init_buzzer_and_key(GPIO_InitStruct);
}

void stream_led(){
	uint16_t Pin_x = 1;
	while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == Bit_RESET){
		if(Pin_x > GPIO_Pin_6) Pin_x = GPIO_Pin_0;
		// 点亮
		GPIO_SetBits(GPIOA, Pin_x);
		// 延时
		Delay_Ms(200);
		// 熄灭
		GPIO_ResetBits(GPIOA, Pin_x);
		// 轮换
		Pin_x = Pin_x << 2;
	}
}

void switch_state_of_buzzer(){
	if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_10) == Bit_SET) GPIO_ResetBits(GPIOA, GPIO_Pin_10);
	else GPIO_SetBits(GPIOA, GPIO_Pin_10);
}

int main(){
	init();
	while(1){
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == Bit_RESET) stream_led();
		else GPIO_ResetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_6);
		
		if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == Bit_SET) switch_state_of_buzzer();
			
	}
}

存在的问题 ------ 按键抖动

在上面的程序中我们成功实现了,按下按键运行流水灯,以及按下按键切换蜂鸣器的工作状态的功能。

但是在控制蜂鸣器的过程中会发现经常会有不灵敏或是没反应的现象这就需要我们考虑更多的因素了,从程序逻辑上来讲这个代码是完全没问题的,但是嵌入式开发往往从实际触发。所以这里涉及到一个新的概念,按键抖动

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

为了规避抖动对最终电平结果的影响,一个常用好用的办法就是加延时。

于是,我们可以提取一个函数用于确定按键是否按下弹起,即返回按键状态的函数,此函数的行为是这样的:

若按键按下且已弹起,则程序需要切换LED状态,此时函数返回一个整数值1。

若按键没有按下,则程序需要保持LED当前状态,此时函数返回一个整数值0。

考虑到代码中直接写字面值不够规范,我们可以定义两个宏或者直接定义一个枚举类型。

最后此函数的参考代码如下:

c 复制代码
typedef enum{
	UNPRESSED = 0;
	PRESSED;
}KeyState;

KeyState KEY_GetState(){

  if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == Bit_RESET){
  // 延时20ms
	Delay_Ms(20);
	// 跳过按下的过程
	while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == Bit_RESET);
	Delay_Ms(20);
	// 引脚输入稳定
	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == Bit_SET) return PRESSED;
  }
  return UNPRESSED;
}

优化后的程序代码

c 复制代码
#include "stm32f10x.h"
#include "../tools/Delay.h"

/*
- A0, A2, A4, A6接LED灯, 采用推挽输出模式
- 流水灯按键一端接B1, 另外一端接电源正极,采用上拉输入模式。
- 蜂鸣器I/O口接在A10
- 蜂鸣器按键一端接在A9,另外一段接在电源负极,采用下拉输入模式
*/

typedef enum {
    UNPRESSED,
    PRESSED
} KeyState;

void init_led_and_key(GPIO_InitTypeDef GPIO_InitStruct) {
    //初始化LED
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_6 ;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 初始化按键1
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

}

void init_buzzer_and_key(GPIO_InitTypeDef GPIO_InitStruct) {
    // 初始化蜂鸣器
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 ;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_SetBits(GPIOA, GPIO_Pin_10);

    // 初始化按键2 - A9 采用下拉输入模式
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 ;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

void init() {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStruct;
    init_led_and_key(GPIO_InitStruct);
    init_buzzer_and_key(GPIO_InitStruct);
}

void stream_led() {
    uint16_t Pin_x = 1;

    while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == Bit_RESET) {
        if (Pin_x > GPIO_Pin_6) {
            Pin_x = GPIO_Pin_0;
        }

        // 点亮
        GPIO_SetBits(GPIOA, Pin_x);
        // 延时
        Delay_Ms(200);
        // 熄灭
        GPIO_ResetBits(GPIOA, Pin_x);
        // 轮换
        Pin_x = Pin_x << 2;
    }
}

void switch_state_of_buzzer() {
    if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_10) == Bit_SET) {
        GPIO_ResetBits(GPIOA, GPIO_Pin_10);
    } else {
        GPIO_SetBits(GPIOA, GPIO_Pin_10);
    }
}

KeyState KEY_GetState() {

    if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == Bit_RESET) {
        // 延时20ms
        Delay_Ms(20);

        // 跳过按下的过程
        while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == Bit_RESET);

        Delay_Ms(20);

        // 引脚输入稳定
        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_9) == Bit_SET) {
            return PRESSED;
        }
    }

    return UNPRESSED;
}

int main() {
    init();

    while (1) {
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == Bit_RESET) {
            stream_led();
        } else {
            GPIO_ResetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_6);
        }

        if (KEY_GetState() == PRESSED) {
            switch_state_of_buzzer();
        }

    }
}

实验二:光敏电阻传感器控制LED实验

光敏电阻

光敏电阻通常由半导体材料制成,如硫化镉(CdS)、硒化镉(CdSe) 等。其外形一般如下图所示:

我们使用的光敏电阻传感器上就存在这样的一个光敏电阻,用于检测光照的强度。光敏电阻的半导体材料在光照下,其内部的电子吸收光子能量后,会从价带跃迁到导带,从而产生更多的自由电子 - 空穴对,使得半导体的电导率增加,电阻值降低。光照强度越大,产生的自由载流子(电子和空穴)就越多,电阻值下降得越明显。总之,光敏电阻是这样的一种电子元器件:

  • 光照强度提高,电阻的阻值减少。
  • 光照强度降低,电阻的阻值将会增大。

光敏电阻传感器

一个典型的光敏电阻传感器的内部电路如下图所示:

首先这张图的左上角是电源指示灯的相关电路,电容(两个横杠)在电路中的作用是稳定信号,过滤干扰,分析电路时也可以去掉,传感器的AO针脚的目的是获取光敏电阻上的电压模拟信号,我们暂时用不到,所以也可以去掉。

于是我们可以把这个电路图简化成如下图所示:

下面我们简单介绍一下图中的重要元器件。

各部分元器件介绍

LM393电压比较器:

LM393 是一种双路电压比较器,能够对两个输入电压(正极 + 和负极 -)进行比较,并且根据比较的结果使得DO口输出低电平或者进入高阻态状态。所以LM393的输出模式是开漏输出模式,可以输出低电平或高阻态两种状态。

LM393比较器,和施密特触发器一样,同样是一种将模拟信号转换到数字信号的元器件。

具体工作逻辑如下:

  1. 当正极(+)输入电压 大于 负极(-)输入电压时:
  • 相当于开漏输出模式,输出1,即DO 引脚进入高阻抗状态,电路在DO引脚处完全断开。
  • 在高阻态下,DO 引脚本身没有确定的电压,但由于电路中存在 上拉电阻 ,于是将 DO 引脚电压稳定拉到和VCC一致,此时DO输出高电平。
  • 总之,当正极(+)电压 > 负极(-)电压时,DO 引脚输出高电平。
  1. 当正极(+)输入电压 小于 负极(-)输入电压时:
  • 相当于开漏输出模式,输出0,相当于DO引脚接地,此时DO引脚输出低电平。
  • 此时 DO 引脚的电压被拉到低电平(接近 0V)。
  • 总之,当正极(+)电压 < 负极(-)电压时,DO 引脚输出低电平。
    所以在这个电路中,我们分析DO引脚输出电平的核心点,在于分析比较器的正负极电压分别是多少,以及它们如何变化。

DO-LED指示灯:

这个DO-LED指示灯,可以直接在元器件上面看到,如下图所示:

在上面的电路图中,和DO针脚指示灯相关联的其实只有这一部分:

这个LED指示灯的阳极接入了电源正极VCC,阴极通过一个1K电阻,和DO引脚相连。

那么这个LED什么时候亮起呢?显然:

  • 当DO引脚输出高电平时,VCC和DO引脚之间没有电势差,LED熄灭。
  • 当DO引脚输出低电平时,DO引脚接地,此时形成完整通路,LED点亮。
    所以这个DO-LED指示灯,当它亮起时,其实说明DO引脚输出了低电平。

比较器正极输入电压分析

光敏电阻传感器的DO针脚连接到了STM32的PB6引脚A.

PB6引脚则通过输入模式读取DO针脚的高低电平,从而确定当前的光照强度。

现在我们已知DO针脚输出高低电平的规律如下:

  • 当正极(+)电压 > 负极(-)电压时,DO 引脚输出高电平。
  • 当正极(+)电压 < 负极(-)电压时,DO 引脚输出低电平。
    那么比较器的正极输入电压如何来计算呢?

首先我们把和比较器正负极电压输入无关的部分删除,简化后的电路图如下所示:

首先分析比较器正极的电压输入:

比较器正极输入的电压,来源于 10kΩ 电阻和光敏电阻之间的分压点,其相关的电流通路是:

"VCC --> 10K电阻 --> 光敏电阻 --> GND"

10K电阻和光敏电阻是串联连接的,串联分压,当电流流过10K电阻时会分走一部分电压。

而到了比较器正极接入点时,比较器正极输入电压的就是光敏电阻在串联电路中分得的电压。

于是我们可以得出以下结论:

比较器正极输入的电压就是光敏电阻在电路中分到的电压:

  • 由于串联分压,光敏电阻分到的电压和其阻值成正比,而光敏电阻的阻值和光照强度成反比。
  • 所以光线越暗,比较器正极输入的电压就越大,光照越强,比较器正极输入的电压就越小。

比较器负极输入电压分析

再分析比较器负极的电压输入:

比较器负极和滑动变阻器的滑动端相连,其相关的电流通路是:

"VCC --> 滑动电阻器VR1 --> GND"

比较器的负极和滑动变阻器的滑动端相连,可以认为此时: 滑动变阻器的上半部分和下半部分串联起来的了。

于是比较器负极输入的电压就是滑动变阻器的下半部分分到的电压,这样负极就可以输入一个0V~VCC的电压。

比较器负数输入电压就是滑动变阻器(电位器)的下半部分在电路中分到的电压。

这个电位器可以直接使用螺丝刀(配件盒中有)拧动螺丝进行调节:

  • 顺时针拧动电位器,滑动变阻器的下半部分就越多,分到的电压就越大,比较器负极输入的电压就越大。
  • 逆时针拧动电位器,滑动变阻器的下半部分就越少,分到的电压就越小,比较器负极输入的电压就越小。

最终结论

结合上面的所有结论,针对我们的光敏电阻传感器实物,我们可以以下四条最终结论:

  • 顺时针拧动电位器(向右),比较器的负极输入电压就越大,DO口就越容易输出一个低电平,DO指示灯就越容易点亮。
  • 逆时针拧动电位器(向左),比较器的负极输入电压就越小,DO口就越容易输出一个高电平,DO指示灯就越难以点亮。
  • 光照强度越小,光敏电阻上方的环境越暗,比较器的正极输入电压就越大,DO口就越容易输出一个高电平,DO指示灯就越难以点亮。
  • 光照强度越大,光敏电阻上方的环境越亮,比较器的正极输入电压就越小,DO口就越容易输出一个低电平,DO指示灯就越容易点亮。

临界状态与扩展

现在我们需要使用光敏电阻传感器来判断光线的亮暗,而DO指示灯刚好可以充当这个指示标志:

  1. DO指示灯亮,说明DO口输出低电平。如果不拧动电位器,在光线比较亮,光照强度比较大时,DO指示灯更容易亮。
  2. DO指示灯熄灭,说明DO口输出高电平。如果不拧动电位器,在光线比较暗,光照强度比较小时,DO指示灯更容易熄灭。

于是,光敏电阻传感器就存在这样一个临界点:

在正常光照下,DO指示灯亮起,光线变暗或捂住光敏电阻,DO指示灯熄灭。

只需要拧动电位器,就可以找到这个临界点。找到这个临界点后,就可以使用该光敏电阻传感器指示光照强弱了。该如何拧动电位器找到临界点呢?

  1. 在正常光照下,先顺时针(向右)拧动电位器,保证DO指示灯点亮。
  2. 光照条件不变,再逆时针(向左)慢慢拧动电位器,找到DO指示灯熄灭的位置。
  3. 光照条件不变,再稍稍顺时针(向右)拧动电位器。让DO指示灯点亮,此时光敏电阻传感器就处于临界状态。

下面根据已知结论,扩展几个小问题,加深你对光敏电阻传感器的理解。

问题一:DO指示灯目前亮起,有几种方式可以使它熄灭?

可以尝试以下方式:

  1. 捂住光敏电阻或减弱环境光照。
  2. 逆时针拧动电位器。

问题二:DO指示灯目前熄灭,有几种方式可以使它点亮?

可以尝试以下方式:

  1. 增强环境光照,比如打开手电筒照射光敏电阻。
  2. 顺时针拧动电位器。

编码实现利用光照强度控制LED

把上述原理、结论都弄清楚后,我们就可以编码实现利用光照强度来控制LED的亮灭了。

我们想要实现的效果是:当光线比较暗时(用手遮住光敏电阻),LED点亮,当光线比较亮时(不用手遮光敏电阻),LED熄灭。

此时的思路是:

  1. 当DO引脚输出高电平时,意味着光线比较暗,PB6引脚会读到一个高电平,此时点亮LED,即让PA3引脚输出低电平。
  2. 当DO引脚输出低电平时,意味着光线比较亮,PB6引脚会读到一个低电平,此时熄灭LED,即让PA3引脚输出高电平。
    那么最后一个问题:PB6引脚的工作模式应该如何选择呢?

首先肯定要选择输入模式,那么是上拉输入,还是下拉输入,还是浮空输入,亦或者无所谓呢?

这就需要考虑DO引脚输出高低电平的工作原理了,上面已经讲过了:

  1. 当光照不足较暗时,传感器的DO引脚实际会进入高阻态,此时会由内部的上拉电阻将电平拉高,引脚对外输出高电平。
  2. 当光照充足较亮时,DO引脚会输出低电平。

根据这个规律,那么此时引脚的输入模式可以选择两种:

  1. 浮空输入模式,由于DO引脚没有悬空状态,总是稳定的输出高低电平,所以完全可以选择引脚的工作模式为浮空输入模式。
  2. 上拉输入模式,既然DO引脚输出高电平是依靠内部上拉电阻完成的,那么也可以再将引脚设置为上拉输入,这样可以增加上拉作用,使得DO口输出的高电平更加稳定。(当然这么做不是必须的!)
    注意不要选择下拉输入模式,因为下拉输入会与传感器内部上拉电阻形成分压,可能导致高电平电压不足,从而影响逻辑判断。

程序实现

利用光敏电阻的特点,实现一下功能。

当环境足够亮时,熄灭光敏电阻指示灯,点亮PC13指示灯

当环境不够亮时,点亮光敏电阻指示灯,熄灭PC13指示灯

c 复制代码
#include "stm32f10x.h"
#include "../tools/Delay.h"


void init(){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 ;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	//初始化PC13
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;	//推挽输出模式
	GPIO_Init(GPIOC, &GPIO_InitStruct);
	//初始化光敏电阻
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入模式
	GPIO_Init(GPIOC, &GPIO_InitStruct);
}

int main(void){
	init();

	while(1){
		if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14) == Bit_RESET){
			GPIO_ResetBits(GPIOC, GPIO_Pin_13);
		}
		else GPIO_SetBits(GPIOC, GPIO_Pin_13);
	}
}

代码还是很简单的重要的是理解光敏电阻特性,以及如何分析问题。只要理解了光敏电阻,那么热敏电阻也就不难理解了,本质上就是把光换成了温度,其他都是一样的

扩展:热敏电阻传感器

搞明白了光敏电阻传感器,那么热敏电阻传感器也就容易理解了。

它本质上就是把光敏电阻传感器电路中的光敏电阻换成了热敏电阻,其他分析思路基本一致。

其参考电路原理如下图所示:

图中的 NTC,即 Negative Temperature Coefficient,负温度系数热敏电阻。它的特点是:

温度越高,热敏电阻的阻值越小。

同样使用它的DO口,可以输出高低电平来指示环境温度是否超过一个阈值。

这个阈值并不是固定写死的,而是由模块上的电位器来调节的。

其工作过程可以这样理解:

  1. 热敏电阻 NTC 和固定电阻构成分压电路,产生一个随温度变化而变化的电压。
  2. 电位器提供另一个可调的参考电压。
  3. LM393 比较器不断比较这两个电压的大小关系。
  4. 当两者大小关系发生变化时,DO 口的输出高低电平也随之翻转。

电位器还是可以直接使用螺丝刀(配件盒中有)拧动螺丝进行调节:

  1. 顺时针拧动电位器,滑动变阻器的下半部分就越多,分到的电压就越大,比较器负极输入的电压就越大。
  2. 逆时针拧动电位器,滑动变阻器的下半部分就越少,分到的电压就越小,比较器负极输入的电压就越小。
    而比较器正极输入电压就是NTC热敏电阻分得的电压,而温度越高它的阻值越小。

所以:

  1. 温度越高,热敏电阻阻值越小,比较器正极输入电压就越小。
  2. 温度越低,热敏电阻阻值越大,比较器正极输入电压就越大。

结合上述两段内容,可以得出以下结论:

  1. DO口想要输出高电平,需要降低温度或者逆时针拧动电位器
  2. DO口想要输出低电平,需要升高温度或者顺时针拧动电位器
  3. 在电位器不变的前提下,可以把DO口输出从高到低,DO指示灯点亮,作为温度超过阈值的特征。
  4. 在电位器不变的前提下,可以把DO口输出从低到高,DO指示灯熄灭,作为温度低于阈值的特征。
  5. 越顺时针拧动电位器,DO口就越容易输出低电平,这可以降低传感器温度的阈值。
  6. 越逆时针拧动电位器,DO口就越容易输出高电平,这可以升高传感器温度的阈值。

debug实验一

按照实验一的方法,运行的话会发现一个问题,就是直接按流水灯按键是没反应的,原因是因为我们在KEY_GetState()函数实现中,写了一个循环,成为了阻塞函数,所以程序运行过程中,一直卡在该函数的循环中,所以当你按住key2的时候会发现按key1是可以有流水灯的。我们可以选择注释掉那个循环,虽然原写法更保险,能够保证不会有抖动问题,但是已经影响到了程序的正常运行,并且注释之后,也没有明显的抖动现象。

相关推荐
开开心心就好1 小时前
支持添加网址的资源快速打开工具
人工智能·学习·游戏·音视频·hbase·语音识别·storm
sheeta19981 小时前
TypeScript 学习笔记
笔记·学习·typescript
Honker_yhw1 小时前
大数据管理与应用系列丛书《数据挖掘》(吕欣等著)读书笔记-数据预处理
笔记·学习
sakiko_1 小时前
Swift学习笔记26-使用第三方库
笔记·学习·swift
LingLong_roar1 小时前
手搓温湿度仪(单片机普冉PY32F002AF15P6TU + 温湿度传感器 SHT40-AD1B-R2 + 0.96寸TFT IPS 显示屏)软件实现
单片机·嵌入式硬件
黑白园1 小时前
STM32F103ZET6移植-电机2804-驱动板SimpleFOC Mini实现速度开环_位置开环控制(二、代码移植及功能实现)
stm32·单片机·嵌入式硬件
深圳市晨芯阳科技有限公司1 小时前
HC9623晨芯阳400mA带载、18V耐压、低压差快速响应LDO
单片机·嵌入式硬件·ldo线性稳压ic·深圳市晨芯阳科技有限公司
Purple Coder10 小时前
BMS学习经验
学习
经济元宇宙10 小时前
摄影培训行业百科:机构选择与学习路径全解析
大数据·人工智能·学习