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.文件、目录结构需要合理,可以不按照本人的来。