如题,利用Keil uVision5为平台,通过GPIO寄存器向STM32F103VE进行控制LED的亮暗和颜色变化(红,绿,蓝,白循环)

题目和解析

        按KEY1,控制LED灯在“红光-绿光-蓝光-白光”四种方式之间切换
        按KEY2,控制LED灯熄灭

前置知识

        首先根据电路板的介绍中可知Key1,Key2接口是GPIO PA0和PC13,同时根据结构原理图

可知这些按键在没有被按下的时候,GPIO引脚的输入状态为低电平 (按键所在的电路不通,引脚接地),当按键按下时,GPIO 引脚的输入状态为高电平 (按键所在的电路导通,引脚接到电源)。只要我们检测引脚的输入电平,即可判断按键是否被按下。也就是需要用到函数KEY_Scan()。

        同时要记住R,G,B不同颜色灯光的引脚,并在之后的define或是头文件中将其对应。这里笔者将R,G,B分别定义为LED1,LED2,LED3(当然为了可读性建议定义为LEDR,G,B)分别对应的引脚是Pin5,Pin0和Pin1。

读取GPIO的输入

        GPIO引脚的输入电平可通过读取IDR寄存器对应的数据位来感知;而STM32标准库提供库函数GPIO_ReadInputDataBit来获取位状态;那么我们怎么确定这个按键按下之后发生了什么呢?笔者在这里提供一个并不完美或者令人满意的算法:

        若检测到按键按下,则使用 while 循环持续检测按键状态,直到按键释放,按键释放后Key_Scan函数返回一个“KEY_ON”值;若没有检测到按键

按下,则函数直接返回“KEY_OFF” 。
        读者可以自行优化包括按键是否松开的判定(经过笔者实验后发现二者在开发板上区别不大只是该算法比较丑陋)

算法

        整体的算法分为两部分,第一部分利用库函数和头文件将key1key2与LED123绑定,第二部分解决循环问题。在这里的电路板其实具有计数功能,可以利用循环设置模四的判断进行输出。笔者这里选择了较为简单的switch函数。有兴趣的同学可以试试for和while.

编写头文件

        这里我们需要include三个头文件,其中stm32f10x.h和stm32f10x_gpio.h是库中自带的,另一个则需要自己创建(当然直接在main.c中直接编写也没问题,主要是一旦编写好后以后需要用到LED的项目可以直接调用)。这里笔者将其命名为bsp_led.h。

        

#ifndef __LED_H
#define	__LED_H


#include "stm32f10x.h"


//red
#define LED1_GPIO_PORT    	GPIOB /*GPIO端口*/    
#define LED1_GPIO_CLK 	    RCC_APB2Periph_GPIOB/*GPIO端口时钟*/
#define LED1_GPIO_PIN		GPIO_Pin_5	/*连接到SCL时钟线的GPIO*/		       

//green
#define LED2_GPIO_PORT    	GPIOB			              
#define LED2_GPIO_CLK 	    RCC_APB2Periph_GPIOB	
#define LED2_GPIO_PIN		GPIO_Pin_0			     

//bule
#define LED3_GPIO_PORT    	GPIOB			            
#define LED3_GPIO_CLK 	    RCC_APB2Periph_GPIOB	
#define LED3_GPIO_PIN		GPIO_Pin_1			      



#define ON  0 /*这里和高低电平输入输出有关,有兴趣可以搜结构原理图*/
#define OFF 1

/*使用标准固件库控制IO*/
#define LED1(a)	if (a)	
					GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);
					else		
					GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN)

#define LED2(a)	if (a)	
					GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);
					else		
					GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN)

#define LED3(a)	if (a)	
					GPIO_SetBits(LED3_GPIO_PORT,LED3_GPIO_PIN);
					else		
					GPIO_ResetBits(LED3_GPIO_PORT,LED3_GPIO_PIN)

/*这里定义操控IO的宏*/
#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)
/*注意以上的引脚LED1_GPIO_PIN实际上和GPIO_PIN_5是同样的可以相互替换*/

void LED_GPIO_Config(void);

#endif 

初始化和杂项

        利用GPIO是如何控制KEY来控制LED的亮灭呢,这里就需要利用两个函数:GPIO_ResetBits()和GPIO_WriteBit()控制亮灭就是在后面函数输入Bit_Reset或Bit_Set.

        那么在我们进行编写main函数之前需要对LED端口和GPIO外设进行初始化,代码如下

  GPIO_InitTypeDef GPIO_InitStructure;/*为避免报错建议一定将此代码写在所有初始化的最上方*/
  RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK|LED_GPIO_CLK,ENABLE); 
  GPIO_InitStructure.GPIO_Pin=LED1_GPIO_PIN|LED2_GPIO_PIN|LED3_GPIO_PIN; 
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; 
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; 
  GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure) ; 
  GPIO_InitStructure.GPIO_Pin=KEY1_GPIO_PIN; 
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; 
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; 
  GPIO_Init(KEY1_GPIO_PORT,&GPIO_InitStructure); 
  GPIO_InitStructure.GPIO_Pin=KEY2_GPIO_PIN; 
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; 
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; 
  GPIO_Init(KEY2_GPIO_PORT,&GPIO_InitStructure); 
  GPIO_WriteBit(LED_GPIO_PORT,LED1_GPIO_PIN|LED2_GPIO_PIN|LED3_GPIO_PIN,Bit_SET);
  //以上皆是GPIO外设的初始化
  LED_GPIO_Config();	 /*LED初始化and如果报错建议将它放在第二行*/

各项杂七杂八的define(笔者对RGB又重新进行了PIN的绑定并在之后使用,但可以不需要,直接从头文件中寻找正确的PIN即可)

#define SOFT_DELAY Delay(0x0FFFFF);

#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 LED_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED_GPIO_PORT GPIOB
#define LEDG_GPIO_PIN GPIO_PIN_0
#define LEDB_GPIO_PIN GPIO_PIN_1
#define LEDR_GPIO_PIN GPIO_PIN_5

#define KEY_ON 1/*注意不要和LED搞混了*/
#define KEY_OFF 0

void Delay(__IO u32 nCount); /*设个delay函数*/

整体代码实现

        抄作业的同学可以直接看这个(记得先把前面的头文件加上qwq)

#include "stm32f10x.h"
#include "bsp_led.h"
#include "stm32f10x_gpio.h"

#define SOFT_DELAY Delay(0x0FFFFF);

#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 LED_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED_GPIO_PORT GPIOB
#define LEDG_GPIO_PIN GPIO_PIN_0
#define LEDB_GPIO_PIN GPIO_PIN_1
#define LEDR_GPIO_PIN GPIO_PIN_5

#define KEY_ON 1
#define KEY_OFF 0

uint8_t Key_Scan(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;
}/*前文提到的丑陋循环验证算法*/

void Delay(__IO u32 nCount); 

int main(void)
{	
	int flag=0;
	GPIO_InitTypeDef GPIO_InitStructure;

	LED_GPIO_Config();	 
	RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK|LED_GPIO_CLK,ENABLE); 

  GPIO_InitStructure.GPIO_Pin=LED1_GPIO_PIN|LED2_GPIO_PIN|LED3_GPIO_PIN; 
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; 
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; 
  GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure) ; 
  GPIO_InitStructure.GPIO_Pin=KEY1_GPIO_PIN; 
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; 
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; 
  GPIO_Init(KEY1_GPIO_PORT,&GPIO_InitStructure); 
  GPIO_InitStructure.GPIO_Pin=KEY2_GPIO_PIN; 
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; 
  GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; 
  GPIO_Init(KEY2_GPIO_PORT,&GPIO_InitStructure); 
  GPIO_WriteBit(LED_GPIO_PORT,LED1_GPIO_PIN|LED2_GPIO_PIN|LED3_GPIO_PIN,Bit_SET);


	while (1)
	{
		if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==KEY_ON)
		{
			
			switch(flag)
			{   /*这里一定要确定一个灯亮的时候确保其他两个关闭否则会合成其他颜色的光*/
				case 0:
				flag++;
				GPIO_ResetBits(LED_GPIO_PORT,LED1_GPIO_PIN);
                GPIO_WriteBit(LED_GPIO_PORT,LED1_GPIO_PIN,Bit_RESET);
				GPIO_ResetBits(LED_GPIO_PORT,LED2_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED2_GPIO_PIN,Bit_SET);
				GPIO_ResetBits(LED_GPIO_PORT,LED3_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED3_GPIO_PIN,Bit_SET);
				break;
				
				case 1:
				flag++;
				GPIO_ResetBits(LED_GPIO_PORT,LED1_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED1_GPIO_PIN,Bit_SET);
				GPIO_ResetBits(LED_GPIO_PORT,LED2_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED2_GPIO_PIN,Bit_RESET);
				GPIO_ResetBits(LED_GPIO_PORT,LED3_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED3_GPIO_PIN,Bit_SET);
				break;
				
				case 2:
				flag++;
				GPIO_ResetBits(LED_GPIO_PORT,LED1_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED1_GPIO_PIN,Bit_SET);
				GPIO_ResetBits(LED_GPIO_PORT,LED2_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED2_GPIO_PIN,Bit_SET);
				GPIO_ResetBits(LED_GPIO_PORT,LED3_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED3_GPIO_PIN,Bit_RESET);
				break;
				
				case 3:
				flag=0;
				GPIO_ResetBits(LED_GPIO_PORT,LED1_GPIO_PIN);
                GPIO_WriteBit(LED_GPIO_PORT,LED1_GPIO_PIN,Bit_RESET);
				GPIO_ResetBits(LED_GPIO_PORT,LED2_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED2_GPIO_PIN,Bit_RESET);
				GPIO_ResetBits(LED_GPIO_PORT,LED3_GPIO_PIN);
				GPIO_WriteBit(LED_GPIO_PORT,LED3_GPIO_PIN,Bit_RESET);
				break;
			}
			
		}
		else if(Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==KEY_ON)
		{
			GPIO_ResetBits(LED_GPIO_PORT,LED1_GPIO_PIN);
      GPIO_WriteBit(LED_GPIO_PORT,LED1_GPIO_PIN,Bit_SET);
			GPIO_ResetBits(LED_GPIO_PORT,LED2_GPIO_PIN);
      GPIO_WriteBit(LED_GPIO_PORT,LED2_GPIO_PIN,Bit_SET);
			GPIO_ResetBits(LED_GPIO_PORT,LED3_GPIO_PIN);
      GPIO_WriteBit(LED_GPIO_PORT,LED3_GPIO_PIN,Bit_SET);
		}
		
		}

}

不足之处

        首先是前文的循环验证问题,会在按下key后一直循环,站内有更为简洁的算法实现,同学们可以去学习一下,其次就是switch算法是否过于麻烦,介于可以计数key1按键按下的次数可以对此进行循环判断,应当更加简洁。祝大家愉快。

转自:https://blog.csdn.net/2301_76803958/article/details/132483641