2.LED灯的控制和按键检测

目录

STM32F103的GPIO口

GPIO口的作用

GPIO口的工作模式

[input输入检测 -- 向内检测](#input输入检测 -- 向内检测)

[output控制输出 -- 向外输出](#output控制输出 -- 向外输出)

寄存器

寄存器地址的确定

配置GPIO口的工作模式

时钟的开启和关闭

[软件编程驱动 LED 灯](#软件编程驱动 LED 灯)

硬件

软件

[软件编程驱动 KEY 按键](#软件编程驱动 KEY 按键)

硬件

软件

按键消抖

代码


STM32F103的GPIO口

根据 STM32F103ZET6 的命名规范,总共 144 个引脚

PA0--PA15

PB0--PB15

PC0--PC15

PD0--PD15

PE0--PE15

PF0--PF15

PG0--PG15 16*7=112

以上称为 GPIO(通用的输入输出接口)

11 个 VDD -- 数字电源的正

11 个 VSS -- 数字电源的负 134

1 个 VDDA -- 模拟电源的正

1 个 VSSA -- 模拟电源的负

1 个 VREF+ -- 参考电压的正

1 个 VREF- 参考电压的负

1 个 NRST -- 复位引脚 139

2 个 OSC_IN OSC_OUT 141 --高速晶振

1 个 VBAT 142 -- 接电池

1 个 NC -- 未使用

1 个 BOOT0 -- 下载引脚

PE2 P 指端口(Port),E 指 E 端口,1 个端口一般有 16 个 GPIO 口 0--15

PE2 指 E 端口的第二个引脚

PG11 指 G 端口的第十一引脚

PA0 指 A 端口的第零引脚

上面信息从:数据手册中文:图 5,芯片的外观中获取

GPIO口的作用

芯片内部和外部通信的媒介。

如何确定配置输入还是输出?

⚫ 向内输入检测:检测开关是否按下,检测屏幕是否按下。

⚫ 向外输出控制:控制 LED,蜂鸣器,控制继电器,控制显示屏

⚫ 后期程序配置中,是否需要配置 GPIO,取决于单片机是否需要借助 GPIO 口和外界连接

GPIO口的工作模式

总共八种

四种输入

模拟输入

上拉输入

下拉输入

浮空输入

四种输出

通用推挽输出

通用开漏输出

复用推挽输出

复用开漏输出

input输入检测 -- 向内检测

极端(数字)输入:3.3V 或者 0V,正好对应数字量的逻辑 1 和逻辑 0

模拟输入: 0v~3.3V 之间的中间值,一般结合 ADC 使用

TTL 肖特基触发器:把高低电平电压值转换为逻辑值(0 和 1);

如何确定选择哪种输入模式:

⚫ 外部电路的电压是 0---3.3 的任意电压,选择模拟输入

⚫ 外部电路有靠谱高和低两种状态,选择浮空输入

⚫ 外部电路只有靠谱的高,选择下拉输入

⚫ 外部电路只有靠谱的低,选择上拉输入

output控制输出 -- 向外输出

输出模拟量:0--3.3V 之间的电压,DAC 功能使用
输出数字量:只有逻辑 0(对应 0V),逻辑 1(对应 3.3V)

推挽输出:既可以输出高电平,也可以输出低电平,PMOS 和 NMOS 都可以导通,直接把逻辑值输出成高低电平 。
开漏输出:只能输出低电平,输出为 1 时是高阻态(未知状态,电路上电压由电路来决定);经常应用在总线,需要增加上拉电阻配合使用;具有读取(线与)的功能。

DHT11 的采集电路 -- 用开漏模式

复用:来至于片上外设,USART,硬件 SPI,硬件 IIC,定时器 PWM

通用:普通的 GPIO 口,控制 LED、蜂鸣器、继电器

如何确定输出选择哪种模式:

⚫ 既需要输出高电平,也需要输出低电平,选择推挽模式

⚫ 只需要输出低电平,选择开漏模式

⚫ 普通 GPIO 口的输出,例如控制 LED,蜂鸣器,继电器等通过操作 ODR 寄存器实现的,配置通用输出

⚫ 外设控制的输出,例如 USART,硬件 SPI,硬件 IIC,定时器 PWM,选择复用功能

寄存器

寄存器存放的是:工作模式,输入或者输出的电平状态。

(GPIOx_CRL) (x=A..G) :每个端口都有 1 个这样的寄存器(STM32F103ZET6 有 7 个端口)

偏移地址:相对于这个外设基地址的偏移

复位值:单片机复位之后,寄存器中存放的值

寄存器地址的确定

GPIOA_CRL 寄存器地址

  1. 确定属于哪个端口,找到该端口的外设基地址 -- 从数据手册 存储器映像找
  1. 找到 CRL 寄存器相对于外设基地址的偏移值 -- 从参考手册对应寄存器介绍

偏移地址:0x00

  1. 计算该寄存器的地址

寄存器的地址=外设基地址+偏移地址=0x40010800+0x00=0x40010800

配置GPIO口的工作模式

(1) 确认端口

(2) 确认哪个 GPIO 口,0--7 在 CRL 寄存器中,8--15 在 CRH 寄存器中

(3) 先配置 MODE 的两位,确定好输入和输出模式

(4) 在配置 CNF 位,确定具体的模式

时钟的开启和关闭

各个功能模块有独立的时钟开关,设备厂商考虑到功耗的问题,如果不使用该模块,时钟默认是关闭的。开启时钟之后,对应模块才可以正常工作

使用功能模块 一定要开启时钟

RCC_APB2ENR 寄存器地址

(1) 找 RCC 的外设基地址

(2) 找到寄存器相对于外设基地址的偏移

(3) 计算寄存器地址

RCC_APB2ENR=0x40021000+0x18=0x40021018

软件编程驱动 LED 灯

硬件

软件

1.看硬件图,LED分别接在PE2-PE5

2.开启PE端口时钟

3.设置工作模式,以PE2为例

结合硬件电路, 首先选择通用还是复用,复用功能需要结合外设(UART_TX 等)使用,我们选择通用输出,开漏和推挽选择,推挽一定可以,为了有稳定的状态,所以选择推挽,所以最终选择通用推挽输出。

4.分别控制输出高低电平。假如以 PE2 为例,操作 GPIOE_ODR 的位 2,分别设置为 0 和 1

软件编程驱动 KEY 按键

硬件

软件

1.结合硬件原理图,确定 KEY 分别接在 PA0 PC4 PC5 PC6

2.开启对应的端口时钟,开启 C 端口和 A 端口时钟

RCC_APB2ENR 的位 2 置 1,开启端口 A 的时钟

RCC_APB2ENR 的位 4 置 1,开启端口 C 的时钟

3.设置工作模式,假如以 PC4 PC5 为例,操作 GPIOE_CRL 寄存器的位 23:16 结合硬件电路, 按键按下和未按下有稳定的高电平和低电平,所以选择浮空输入 GPIOC_CRL 寄存器的位 23:16 写入 0100 0100

4.分别检测(读取)PC4,PC5 的引脚电平,来确定按键是否按下GPIOC_IDR 的位 4 或者位 5 判断按键是否按下

按键消抖

代码

led.c

cpp 复制代码
#include "led.h"
#include "main.h"
void LED_Config(void)
{
#if (USB_STD_LIB==0)

	//1.开启GPIOE的时钟	结合硬件:LED4接在PE5上	6.3.7RCC_APB2ENR
	/*RCC的外设地址 0x40021000
		RCC_APB2ENR寄存器相对于基地址偏移0x18
		RCC_APB2ENR寄存器地址:0x40021000+0x18 + 0x18= 0x40021018
	*/
	//*(uint32_t *)(0x40021018) = (*(uint32_t *)(0x40021018)) | (0x01<<6);
	//RCC->APB2ENR = RCC->APB2ENR | (0x01<<6);
	RCC->APB2ENR |= (0x01<<6);
	//2.设置gpio模式,设置通用推挽输出
	//LED4初始化
	GPIOE->CRL &= ~(0xFFFF << 8);//先清0
	GPIOE->CRL |= (0x1111 << 8);//在置1
	
	/* GPIOE->CRL &= ~(0xF << 20);//先清0
	GPIOE->CRL |= (0x01 << 20);//在置1
	//LED3初始化
	GPIOE->CRL &= ~(0xF << 16);//先清0
	GPIOE->CRL |= (0x01 << 16);//在置1
	//LED2初始化
	GPIOE->CRL &= ~(0xF << 12);//先清0
	GPIOE->CRL |= (0x01 << 12);//在置1
	//LED1初始化
	GPIOE->CRL &= ~(0xF << 8);//先清0
	GPIOE->CRL |= (0x01 << 8);//在置1  */
#elif (USB_STD_LIB==1)
	//1.开E端口时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	//2.定义结构体 xxx需要传递结构体地址
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	//3.给结构体赋值
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 |GPIO_Pin_5;//代配置引脚
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//工作模式
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;//引脚速率
	//4.调用xxx_Init函数,将参数写入寄存器中
	GPIO_Init(GPIOE, &GPIO_InitStruct);
#endif
}

void LED1_ON(void)//开灯,低电平亮
{
#if (USB_STD_LIB==0)
	GPIOE->ODR &= ~(0x01 << 2);
#elif (USB_STD_LIB==1)
	GPIO_ResetBits(GPIOE, GPIO_Pin_2);
#endif
}
void LED1_OFF(void)//关灯,高电平灭
{
#if (USB_STD_LIB==0)
	GPIOE->ODR |= (0x01 << 2);
#elif (USB_STD_LIB==1)
	GPIO_SetBits(GPIOE, GPIO_Pin_2);
#endif
}
void LED1_TOGGLE(void)//翻转 ^相同为0不同为1
{
#if (USB_STD_LIB==0)
	GPIOE->ODR ^= (0x01 << 2);
#elif (USB_STD_LIB==1)
	GPIO_WriteBit(GPIOE, GPIO_Pin_2, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_2)));
#endif
}
void LED4_ON(void)//开灯,低电平亮
{
#if (USB_STD_LIB==0)
	GPIOE->ODR &= ~(0x01 << 5);
#elif (USB_STD_LIB==1)
	GPIO_ResetBits(GPIOE, GPIO_Pin_5);
#endif
}
void LED4_OFF(void)//关灯,高电平灭
{
#if (USB_STD_LIB==0)
	GPIOE->ODR |= (0x01 << 5);
#elif (USB_STD_LIB==1)
	GPIO_SetBits(GPIOE, GPIO_Pin_5);
#endif
}
void LED3_ON(void)//开灯,低电平亮
{
#if (USB_STD_LIB==0)
	GPIOE->ODR &= ~(0x01 << 4);
#elif (USB_STD_LIB==1)
	GPIO_ResetBits(GPIOE, GPIO_Pin_4);
#endif
}
void LED3_OFF(void)//关灯,高电平灭
{
#if (USB_STD_LIB==0)
	GPIOE->ODR |= (0x01 << 4);
#elif (USB_STD_LIB==1)
	GPIO_SetBits(GPIOE, GPIO_Pin_4);
#endif
}
void LED2_ON(void)//开灯,低电平亮
{
#if (USB_STD_LIB==0)
	GPIOE->ODR &= ~(0x01 << 3);
#elif (USB_STD_LIB==1)
	GPIO_ResetBits(GPIOE, GPIO_Pin_3);
#endif
}
void LED2_OFF(void)//关灯,高电平灭
{
#if (USB_STD_LIB==0)
	GPIOE->ODR |= (0x01 << 3);
#elif (USB_STD_LIB==1)
	GPIO_SetBits(GPIOE, GPIO_Pin_3);
#endif
}

led.h

cpp 复制代码
#ifndef __LED_H__
#define __LED_H__

#include "stm32f10x.h"

void LED_Config(void);
void LED4_ON(void);
void LED4_OFF(void);
void LED4_TOGGLE(void);

void LED3_ON(void);
void LED3_OFF(void);
void LED3_TOGGLE(void);

void LED2_ON(void);
void LED2_OFF(void);
void LED2_TOGGLE(void);

void LED1_ON(void);
void LED1_OFF(void);
void LED1_TOGGLE(void);

#endif

key.c

cpp 复制代码
#include "key.h"
#include "delay.h"
#include "led.h"
#include "BUZZER.h"
#include "main.h"
#include "RGB.h"
#include "relay.h"
uint8_t RBG_state = 3;
void KEY_Config(void)
{
#if (USB_STD_LIB==0)
	//RCC->APB2ENR |= (0x01<<2);
	//RCC->APB2ENR |= (0x01<<4);
	RCC->APB2ENR |= (0x05 << 2);

	//KEY1
	GPIOA->CRL &= ~(0xF << 0);//先清0
	GPIOA->CRL |= (0x4 << 0);//在置1
	//KEY2 KEY3 KEY4
	GPIOC->CRL &= ~(0xFFF << 16);//先清0
	GPIOC->CRL |= (0x444<< 16);//在置1
#elif (USB_STD_LIB==1)
	//1.开A C端口时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC,ENABLE);
	//2.定义结构体 xxx需要传递结构体地址
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	//3.
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//工作模式,浮空输入
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;//待配置的引脚
	//GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; 输入可以不配置引脚速率
	//4.调用xxx_Init函数,将参数写入寄存器中
	GPIO_Init(GPIOA, &GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6;//代配置引脚
	GPIO_Init(GPIOC, &GPIO_InitStruct);
	
#endif
}

//按键扫描函数
/*返回值
	0无按键按下
	1按键1按按下并松手
	2按键2按按下并松手
	3按键3按按下并松手
	4按键4按按下并松手
*/
uint8_t KEY_SCAN(void)
{
#if (USB_STD_LIB==0)	
	uint8_t KEY_Sate;
	if((GPIOA->IDR & (0x01 << 0)) != 0)//判断KEY1是否按下,按下为1高电平
	{
		Delay_ms(10);//消抖
		if((GPIOA->IDR & (0x01 << 0)) != 0)
		{
			while((GPIOA->IDR & (0x01 << 0)) != 0)//确定松手
			{}
			KEY_Sate = 1;
		}
	}
	
	if((GPIOC->IDR & (0x01 << 4)) == 0)//判断KEY2是否按下,按下为0低电平
	{
		Delay_ms(10);//消抖
		if((GPIOC->IDR & (0x01 << 4)) == 0)
		{
			while((GPIOC->IDR & (0x01 << 4)) == 0)
			{}
			KEY_Sate = 2;
		}
	}
	
	if((GPIOC->IDR & (0x01 << 5)) == 0)//判断KEY3是否按下,按下为0低电平
	{
		Delay_ms(10);//消抖
		if((GPIOC->IDR & (0x01 << 5)) == 0)
		{
			while((GPIOC->IDR & (0x01 << 5)) == 0)
			{}
			KEY_Sate = 3;
		}
	}
	
	if((GPIOC->IDR & (0x01 << 6)) == 0)//判断KEY4是否按下,按下为0低电平
	{
		Delay_ms(10);//消抖
		if((GPIOC->IDR & (0x01 << 6)) == 0)
		{
			while((GPIOC->IDR & (0x01 << 6)) == 0)
			{}
			KEY_Sate = 4;
		}
	}
	return KEY_Sate;
#elif (USB_STD_LIB==1)
	uint8_t KEY_Sate = 0;
	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_SET)
	{
		Delay_ms(10);//消抖
		if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_SET)
		{
			while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_SET)//确定松手
			{}
			KEY_Sate = 1;
		}
	}
	
	if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_4) == Bit_RESET)
	{
		Delay_ms(10);//消抖
		if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_4) == Bit_RESET)
		{
			while(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_4) == Bit_RESET)//确定松手
			{}
			KEY_Sate = 2;
		}
	}
	
	if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5) == Bit_RESET)
	{
		Delay_ms(10);//消抖
		if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5) == Bit_RESET)
		{
			while(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5) == Bit_RESET)//确定松手
			{}
			KEY_Sate = 3;
		}
	}
	
	if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_6) == Bit_RESET)
	{
		Delay_ms(10);//消抖
		if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_6) == Bit_RESET)
		{
			while(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_6) == Bit_RESET)//确定松手
			{}
			KEY_Sate = 4;
		}
	}
	return KEY_Sate;
#endif
	
	
}

void KEY_Handle(void)
{
	uint8_t KEY_Sate = 0;
	KEY_Sate = KEY_SCAN();
	switch(KEY_Sate)
	{
		case 0: 
			break;
		case 1: LED1_ON(); LED2_ON(); LED3_ON(); LED4_ON();
			break;
		case 2: LED1_OFF(); LED2_OFF(); LED3_OFF(); LED4_OFF();
			break;
		case 3: Relay_ON();
			break;
		case 4: Relay_OFF();
				//Update_RGB();
			break;
	}
}

key.h

cpp 复制代码
#ifndef __KEY_H__
#define __KEY_H__

#include "stm32f10x.h"

void KEY_Config(void);
uint8_t KEY_SCAN(void);
void KEY_Handle(void);
//void Tim2_Init();
//void TIM2_IRQHandler();
//void Update_RGB();

#endif

main.c

cpp 复制代码
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "RGB.h"
#include "relay.h"
#include "key.h"
#include "BUZZER.h"
#include "exti.h"
#include "main.h"

int main(void)
{
	#if (USB_STD_LIB==0)	
	//开机调用一次配置的抢占和次级优先级必须符合该分组情况
	//抢占0-3,次级0-3
	NVIC_SetPriorityGrouping(5);
	#elif (USB_STD_LIB==1)
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	#endif
	LED_Config();
	LED4_OFF();
	LED3_OFF();
	LED2_OFF();
	LED1_OFF();
	RGB_Config();
	R_OFF();
	G_OFF();
	B_OFF();
	Relay_Init();
	Relay_OFF();
	KEY_Config();
	BUZZER_Config();
	EXIT_Confing();
	while(1)
	{
		R_ON();
		Delay_ms(100);
		R_OFF();
		Delay_ms(100);
	}
}
相关推荐
小智学长 | 嵌入式5 小时前
单片机-STM32部分:0、学习资料汇总
stm32·单片机·嵌入式硬件
2401_8729905311 小时前
stm32 HAI库 SPI(一)原理
stm32·单片机·嵌入式硬件
小智学长 | 嵌入式12 小时前
单片机-89C51部分:12 pwm 呼吸灯 直流电机
单片机·嵌入式硬件
佛大第一深情16 小时前
ESP32 在Platform Arduino平台驱动外部PSAM,进行内存管理
c语言·单片机·嵌入式硬件
hallo-ooo17 小时前
【STM32】定时器的外部时钟模式
stm32·单片机
hallo-ooo18 小时前
【STM32】定时器输入捕获
stm32·单片机
小智学长 | 嵌入式19 小时前
单片机-STM32部分:1、STM32介绍
stm32·单片机·嵌入式硬件
zhbi9819 小时前
STM32移植U8G2
stm32·单片机·嵌入式硬件·u8g2
Hans_Rudle20 小时前
MSP430G2553驱动0.96英寸OLED(硬件iic)
单片机·嵌入式硬件·msp430