zoukankan      html  css  js  c++  java
  • 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.文件、目录结构需要合理,可以不按照本人的来。

  • 相关阅读:
    c#生成html静态文件时出现空白行,怎么去掉utf-8中的bom
    解决网站发布404返回200,301等状态
    创建本地缓存
    创建windows服务
    C#DateTime与Unix时间戳的转换
    Oracle数据访问组件ODAC的安装方法:
    用任意语言与WebService进行交互
    又一种XML的解析方法
    TopShelf框架创建Windows服务作为Remoting的宿主案例:
    bootstrap 时间控件带(时分秒)选择器(需要修改才能显示,请按照参数说明后面的步骤进行修改)
  • 原文地址:https://www.cnblogs.com/ding-ding-light/p/14410383.html
Copyright © 2011-2022 走看看