STM32_按键点灯

学习32是一个循序渐进的过程,通过点灯可以了解到许许多多的知识,今天先讲解按键点灯,明天如果有时间会通过对比按键,写中断控制点灯的程序。

实现按键点灯的第一步还是先看原理图:

STM32_按键点灯_第1张图片
原理:当按键按下去后,会向芯片输入高电平,芯片得到之后,继而向LED灯输出低电平,则可以达到按键控制LED灯的效果。
STM32_按键点灯_第2张图片
下面我先把代码放在一起运行,这样比较直观,我用的还是GPIO_ResetBits()函数控制芯片输出低电平,但是存在一个bug,就是按键按下去,就无法退回去了,也就点亮以后无法熄灭了,必须按复位键才可以。

#include "stm32f10x.h"

/*简单延时函数*/
void delay(uint32_t i)
{
	while(i--);
}

/*按键GPIO配置*/
void KEY_GPIO_Init(void)
{	
	//引入GPIO结构体
	GPIO_InitTypeDef GPIO_InitStructure;
	
	//打开控制KEY1的GPIOA外设时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC, ENABLE);
	
	/*选择要控制的GPIOA的引脚*/
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_13 ;
	
	/*设置引脚模式为浮空输入*/
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	
	/*设置引脚速率为50MHz*/
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	/*调用库函数,初始化GPIOA*/
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	
	/*按键置0,设置为最初的状态*/
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);
	GPIO_ResetBits(GPIOC,GPIO_Pin_13);
	
}
/*LED_GPIO配置*/
void LED_GPIO_Init(void)
{	
	//引入GPIO结构体
	GPIO_InitTypeDef GPIO_InitStructure;
	
	//打开控制LED的GPIOB外设时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	/*选择要控制的GPIOB的引脚*/
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_1 ;
	
	/*设置引脚模式为通用推挽输出*/
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	
	/*设置引脚速率为50MHz*/
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	/*调用库函数,初始化GPIOB*/
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	/*按键置1,设置为最初的状态*/
	GPIO_SetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_1);
	
}


/*主函数*/
int main(void)
{
	LED_GPIO_Init();//LED初始化
	KEY_GPIO_Init();//KEY初始化
	
	while(1)
	{	
		/*如果按键1按下去,红灯点亮*/
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)//使用引脚读入函数
		{
			delay(99999);//延时是为了按键消抖
			GPIO_ResetBits(GPIOB,GPIO_Pin_5);
		}		
		/*如果按键2按下去,绿灯点亮*/
		if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13)==1)
		{
			delay(99999);
			GPIO_ResetBits(GPIOB,GPIO_Pin_1);
		}		
	}
}


这里使用的外设还是RCC外设和GPIO外设,所以只要添加这两个库文件就可以了
STM32_按键点灯_第3张图片

STM32_按键点灯_第4张图片
STM32_按键点灯_第5张图片
从原理图可以得到,它加了电容,可以达到硬件消抖的作用,所以可以不用软件延时消抖。

下面我换另一种方式控制按键点灯,文件也趋于模块化,方便修改

1. key.h

#ifndef __KEY_H	//如果没有定义头文件
#define __KEY_H	//则定义头文件

#include "stm32f10x.h"

//定义引脚
#define		KEY1_GPIO_CLK		RCC_APB2Periph_GPIOA
#define		KEY1_GPIO_PORT		GPIOA
#define 	KEY1_GPIO_PIN		GPIO_Pin_0

#define		KEY2_GPIO_CLK		RCC_APB2Periph_GPIOC
#define		KEY2_GPIO_PORT		GPIOC
#define 	KEY2_GPIO_PIN		GPIO_Pin_13

//定义按键,按下为高电平
#define		KEY_ON		1
#define		KEY_OFF		0

void KEY_GPIO_Config(void);
int KEY_Scanf(GPIO_TypeDef* GPIOX,uint16_t GPIO_PIN);

#endif	//结束

2. key.c

#include "stm32f10x.h"
#include "key.h"

/*配置按键GPIO*/
void KEY_GPIO_Config(void)
{	
	//引入GPIO结构体
	GPIO_InitTypeDef GPIO_InitStructure;
	
	//打开控制KEY1和KYE2的GPIO外设时钟
	RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK, ENABLE);
	
	/*选择要控制的KEY1的引脚*/
	GPIO_InitStructure.GPIO_Pin=KEY1_GPIO_PIN;	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;/*设置引脚模式为浮空输入*/
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;	/*设置引脚速率为50MHz*/
	GPIO_Init(KEY1_GPIO_PORT,&GPIO_InitStructure);/*初始化控制按键KEY1*/
	
	/*选择要控制的KEY2的引脚*/
	GPIO_InitStructure.GPIO_Pin=KEY2_GPIO_PIN;	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;/*设置引脚模式为浮空输入*/
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;	/*设置引脚速率为50MHz*/
	GPIO_Init(KEY2_GPIO_PORT,&GPIO_InitStructure);/*初始化控制按键KEY2*/
		
}
/*按键检测,当有按键按下(KEY_ON),向芯片输入高电平*/
int KEY_Scanf(GPIO_TypeDef* GPIOX,uint16_t GPIO_PIN)
{
	/*检测按键是否被按下*/
	if(GPIO_ReadInputDataBit(GPIOX,GPIO_PIN)==KEY_ON)
	{
		/*等待按键释放,按一次就被置位高电平*/
		while(GPIO_ReadInputDataBit(GPIOX,GPIO_PIN)==KEY_ON);
		return	KEY_ON;
	}
	else
		return KEY_OFF;//返回值表现在哪里
}

3. led.h

#ifndef 	__LED_H	//如果没有定义头文件
#define 	__LED_H	//则定义头文件

#include "stm32f10x.h"

/*定义引脚*/
//红色
#define	LED1_GPIO_CLK		RCC_APB2Periph_GPIOB
#define	LED1_GPIO_PORT		GPIOB
#define	LED1_GPIO_PIN		GPIO_Pin_5

//绿色
#define	LED2_GPIO_CLK		RCC_APB2Periph_GPIOB
#define	LED2_GPIO_PORT		GPIOB
#define	LED2_GPIO_PIN		GPIO_Pin_0

//蓝色
#define	LED3_GPIO_CLK		RCC_APB2Periph_GPIOB
#define	LED3_GPIO_PORT		GPIOB
#define	LED3_GPIO_PIN		GPIO_Pin_1

//定义LED的高低电平(根据原理图可知电平低电平点亮LED,所以用0N比较好看些)
#define	ON		0
#define OFF		1

/* 直接操作寄存器的方法控制IO */
#define digitalHi(p,i)		 {p->BSRR=i;}	  //输出为高电平		
#define digitalLo(p,i)		 {p->BRR=i;}	 //输出低电平
#define digitalToggle(p,i) 	 {p->ODR ^=i;} //输出反转状态

//定义控制LED的宏
#define LED1_TOGGLE		digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF		digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON		    digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)

#define LED2_TOGGLE		digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_OFF		digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_ON		    digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)

#define LED3_TOGGLE		digitalToggle(LED3_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_OFF		digitalHi(LED3_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_ON			digitalLo(LED3_GPIO_PORT,LED3_GPIO_PIN)

void LED_GPIO_Config(void);

#endif	//结束

4.led.c

#include "stm32f10x.h"
#include "led.h"


void LED_GPIO_Config(void)
{	
	//引入GPIO结构体
	GPIO_InitTypeDef GPIO_InitStructure;
	
	//打开控制LED的GPIO外设时钟
	RCC_APB2PeriphClockCmd(LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK,ENABLE);
	
	/*选择要控制的GPIOX的引脚*/
	GPIO_InitStructure.GPIO_Pin=LED1_GPIO_PIN|LED2_GPIO_PIN|LED3_GPIO_PIN;
	
	/*设置引脚模式为通用推挽输出*/
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	
	/*设置引脚速率为50MHz*/
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	/*调用库函数,初始化GPIOB*/
	GPIO_Init(GPIOB,&GPIO_InitStructure);
		
	/*按键置0,设置为最初的状态*/
	GPIO_SetBits(GPIOB,LED1_GPIO_PIN|LED2_GPIO_PIN|LED3_GPIO_PIN);
	
}

5.main.c

#include "stm32f10x.h"
#include "led.h"
#include "key.h"

int main(void)
{	
	/* LED端口初始化 */
	LED_GPIO_Config();
	LED3_ON;//直接点亮蓝灯LED3,无按键控制,最初的状态,这里只是为了两个按键都按下去之后,可以出现白灯效果
	
	/* KEY端口初始化 */
	KEY_GPIO_Config();
	
	while(1)
	{
		if(KEY_Scanf(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==KEY_ON)
		{
			LED1_TOGGLE;//红灯
		}
		if(KEY_Scanf(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==KEY_ON)
		{
			LED2_TOGGLE;//绿灯
		}		
	}
}

第二个程序用到了寄存器控制LED,虽然寄存器不需要深究,但也要稍微了解一下。
寄存器:暂时存放数据的地方,数据在芯片中的表现形式是地址,地址用一串16进制的数字表示,寄存器形象一点理解就是房子,每个房子都有自己独特的地址(数据)。

#define digitalHi(p,i)		 {p->BSRR=i;}	  //输出为高电平		
#define digitalLo(p,i)		 {p->BRR=i;}	 //输出低电平
#define digitalToggle(p,i) 	 {p->ODR ^=i;} 	//取反

这里用到了三个寄存器BSRR、BRR、ODR,那么它们有什么作用?
GPIO_SetBits()和GPIO_ResetBits()两个库函数就是通过修改BSRR,BRR寄存器的值来实现对引脚的设置。

BSRR:端口位设置/清除寄存器,只能写,既能控制管脚为高电平(设置),也能控制管脚为低电平(清除),对寄存器高16bit 写1 ,对应管脚为低电平;对寄存器低16bit写1对应管脚为高电平;写 0 ,无动作。
STM32_按键点灯_第6张图片
BRR:端口位清除寄存器,只能写,只能使管脚状态为低电平(清除),对寄存器写1,相应管脚为低电平;写0,无动作。
STM32_按键点灯_第7张图片
如此看来,想要给GPIO引脚写入高/低电平有BSRR一个寄存器就可以实现, BRR 寄存器就显得比较多余的,而实际上,在最新的 STM32F4 系列 MCU 的 GPIO 寄存器中,已经找不到 BRR 寄存器了,仅保留了 BSRR 寄存器用于实现端口输出高低电平。

ODR:输出数据寄存器,可读可写,既能控制管脚为高电平,也能控制管脚为低电平。

位写1,GPIO 管脚为高电平;位写0, GPIO管脚为低电平。

一个 ODR 寄存器控制了一组(16位)的 GPIO 输出。因此,设置ODR也能控制引脚输出高/低电平。但是,由于对 ODR 寄存器的读写操作必须以 16 位的形式进行,也就是说要修改的话,把GPIO一组16个引脚都修改了,无法针对单个引脚,而如果非要使用 ODR 改写数据来控制输出时,须采用“读-改-写”的形式进行。

uint32_t temp;//设定一个暂时存储变量

temp = GPIOA->ODR;//将0DR的值赋给temp

temp = temp | GPIO_Pin_6;

GPIOA->ODR = temp;

设置单个 IO 口输出时,使用 BSRR 进行操作会更加方便;如果需要对单个IO口进行 Toggle(反转/取反) 操作时,使用 ODR 寄存器进行操作的比较方便。

位写1,GPIO 管脚为高电平;位写0, GPIO管脚为低电平。
STM32_按键点灯_第8张图片

在C语言中“->”代表什么?
->:指向结构体成员运算符。

#include
// 结构体的声明与定义
struct Data
{
    char name[10];
    int age;
    char sex;
}person;

void main()
{
    int i;
    // 此处就是指向结构体成员运算符(->)的用法
    i = person->age;  // 提取结构体成员变量age的值(某人的年龄),并赋值给变量i
}

最后再说一下仿真器配置,因为一开始我将程序烧录到开发板,必须先按下复位键才可以执行程序,其实就是仿真器没有配置好,直接上图就会懂了
STM32_按键点灯_第9张图片

备注:由于小白杨才疏学浅,寄存器那一部分仍有些许迷惑,请大佬指点迷津,弄懂之后,会重新修改,希望在此能遇到志同道合的朋友,一起学习技术。**

你可能感兴趣的:(单片机,stm32,嵌入式)