LED和按键实验

###LED硬件原理

  LED也叫发光二极管,具有单向导通信,当给发光二极管加上正向电压后,会产生自发辐射的荧光,根据制作材料的不同发出光的颜色也不同,常用的是发红光、绿光或黄光的二极管。
  原理图包含的东西比较多,看起来有点杂乱无章,那么如何在原理图中找到LED的部分呢,可以看开发板的丝印层,找到LED的标号,在原理图中进行搜索,如上图中第一个框里面的标号便是丝印层的标号;第二个框里面的标号表示的LED类型,因为一个开发板上可能会有很多类型的LED;第三个框里面的标号表示线的走向,通过搜索这个框中的四个标号,便找到了这四个LED连接到了MCU的PF9、PF10、PE13、PE14这四个I/O引脚;从上面的LED原理图中可以看出,当I/O引脚为低电平时,LED被导通将会被点亮。
  为了防止LED因为电流过大,LED过热烧坏,就加了一个330欧姆的限流电阻,通过查询当取通过LED的电流为5mA,压降为1.80V,通过欧姆定律计算的出限流电阻的大小=(3.3-1.80)/0.005=300欧姆,考虑到电阻的误差,选用330欧姆的限流电阻比较合适。
###按键硬件原理

  根据查找LED所使用I/O引脚的办法,查找出按键所使用的I/O引脚,检测按键是否按下需要将I/O引脚配置为输入模式。根据上图可以看出,当按键没有按下的时候检测按键s1所连接的端口是为高电平的,因为按键的I/O引脚接了一个外部上拉电阻;如果当按键s2按下的时候,引脚PE2就会被接地,检测该引脚电平时应为低电平。
  如果不使用中断的办法检测按键,就只能使用轮询的办法实现按键检测。
###实验程序
  因为开发板有四个按键,真好对应四个LED,所以可以编写一个当按键1按下时,LED1亮、按键2按下时,LED2亮...
  在《keil5 环境搭建》中创建好的工程模板来进行编程,根据里面的内容可以知道需要在Manage Run-Time Environment中添加RCC和GPIO的库文件;
  在《STM32F4 时钟体系》可以知道当需要使用哪个I/O引脚,便需要打开对应端口的时钟;
common.h

#ifndef		__COMMON_H__
#define		__COMMON_H__

//位带操作,实现51类似的GPIO控制功能
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 
//IO口操作,只对单一的IO口
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

//LED相关
#define		LED_PORT1		GPIOE
#define 	LED_PIN_1		GPIO_Pin_14
#define		LED_PIN_2		GPIO_Pin_13

#define		LED_PORT2		GPIOF
#define 	LED_PIN_3 		GPIO_Pin_10
#define 	LED_PIN_4		GPIO_Pin_9

#define 	LED1ON	  		PEout(14) = 0
#define 	LED1OFF	  		PEout(14) = 1

#define 	LED2ON	  		PEout(13) = 0
#define 	LED2OFF	  		PEout(13) = 1

#define 	LED3ON	  		PFout(10) = 0
#define 	LED3OFF	  		PFout(10) = 1

#define 	LED4ON	  		PFout(9) = 0
#define 	LED4OFF	  		PFout(9) = 1

#define		FLASH_LED1		PEout(14) ^= 1
#define		FLASH_LED2		PEout(13) ^= 1
#define		FLASH_LED3		PFout(10) ^= 1
#define		FLASH_LED4		PFout(9) ^= 1

//key相关
#define		KEY_PORT1		GPIOA
#define		KEY_PIN_1		GPIO_Pin_0
#define		KEY_PORT2		GPIOE
#define		KEY_PIN_2		GPIO_Pin_2
#define		KEY_PIN_3		GPIO_Pin_3
#define		KEY_PIN_4		GPIO_Pin_4

#define		GET_KEY1_LEVEL		PAin(0)
#define		GET_KEY2_LEVEL		PEin(2)
#define		GET_KEY3_LEVEL		PEin(3)
#define		GET_KEY4_LEVEL		PEin(4)

//蜂鸣器
#define		BEEP_PORT		GPIOF
#define		BEEP_PIN		GPIO_Pin_8

//usart1 
#define		USART1_TX_PIN		GPIO_Pin_9
#define		USART1_RX_PIN		GPIO_Pin_10
#define		USART1_PORT		GPIOA
#define		USART1_TXSource	        GPIO_PinSource9
#define		USART1_RXSource	        GPIO_PinSource10

//AHB1总线
#define AHB1PERIPH_INIT_LIST  \
  ( RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOE \ 
  | RCC_AHB1Periph_GPIOF \
  )
  
//APB1总线
#define APB1PERIPH_INIT_LIST  \
  ( RCC_APB1Periph_USART3\
  
  )
 
//APB2总线
#define APB2PERIPH_INIT_LIST  \
  ( RCC_APB2Periph_USART1 \
  )

#endif

  为了方便移植编写一个common.h的头文件,如果换了一个开发板,片外外设需要更改引脚时,只需要在这个文件中修改便可,将这个文件添加到目录树的Inc组中。
hwconf.c

static void RCC_Configuration(void)
{
    RCC_AHB1PeriphClockCmd(AHB1PERIPH_INIT_LIST, ENABLE);
}

void init_board(void)
{
	RCC_Configuration();
}

  创建hwconf.c文件,将这个文件添加到目录树的Common组中,还需要创建对应的头文件hwconf.h添加到Inc组中,AHB1PERIPH_INIT_LIST为一个宏定义,里面为需要打开AHB1总线中的外设,需要添加AHB1外设时,往这个宏定义中添加就可以了,同样AHB2、APB1、APB2也可以这样。
led.c

#include "stm32f4xx.h"
#include "led.h"
#include "common.h"
#include "tim.h"

void led1_init()
{
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = LED_PIN_1;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出
	GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed; 
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(LED_PORT1, &GPIO_InitStruct);
}
      ....
      ....
      ....

  创建led.c文件,将这个文件添加到目录树的Hardware组中,还需要创建对应的头文件led.h添加到Inc组中。上面代码是LED1的初始化代码,因为剩下的LED初始化代码都是一样的只是函数名和使用的引脚不同,就不打出来了,按键的也是如此。
key.c

#include "stm32f4xx.h"
#include "key.h"
#include "common.h"

void key1_init()
{
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = KEY_PIN_1;			
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;  //输入    			
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;		
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;		
	GPIO_Init(KEY_PORT1, &GPIO_InitStruct);	
}
      ....
      ....
      ....
           

  参照LED的,按键的也是如此
led_key_test.c

#include "stm32f4xx.h"
#include "key_test.h"
#include "common.h"
#include "led.h"
#include "key.h"

void led_key_test()
{
	led1_init();
	led2_init();
	led3_init();
	led4_init();
	key1_init();
	key2_init();
	key3_init();
	key4_init();
	
	while(1)
	{
		if (0 == GET_KEY1_LEVEL)
		{
			LED1OFF;
		}
		if (0 == GET_KEY2_LEVEL)
		{
			LED2OFF;
		}
		if (0 == GET_KEY3_LEVEL)
		{
			LED3OFF;
		}
		if (0 == GET_KEY4_LEVEL)
		{
			LED4OFF;
		}			
	}
}

  创建led_key_test.c文件,将这个文件添加到目录树的APP组中,还需要创建对应的头文件led_key_test.h添加到Inc组中。
main.c

#include "hwconf.h"
#include "led_key_test.h"

int main()
{
      init_board(); //打开所需端口/外设时钟
      led_key_test();
}

  编写好主函数就可以编译,烧写、运行了。
###总结
  1.原理图中的每种标号需要搞清楚,不然会弄乱;
  2.编写代码需要注重可移植性;
  3.文件、目录结构需要合理,可以不按照本人的来。

posted @ 2021-02-18 00:48  ding-ding-light  阅读(600)  评论(0编辑  收藏  举报