引言
主要是自己最近在学习stm32固件库的使用(对于一定要求不使用固件库的,实际上没有特别必要,因为这是统一官方版的库,就和stdio.h之类的差不多,不过最好也学会自己写,过一段时间,我也会写不使用固件库的)
(后面写了一个中断实现的https://blog.csdn.net/qq_44885018/article/details/103070394)
任务要求
1.按键点灯,键按下灯发亮,按键松开,灯熄灭
2.当两个键同时按下的时候,蜂鸣器鸣叫
电路
作为学习单片机或者arm,首先第一件事情是就电路图进行分析
先让我们看看LED的电路图
先分析一下二极管,二极管负极结的芯片,正极接3.3v的电压,想要二极管发光,就需要芯片输出一个低电平,而且可以从图中看出灯D1-D3对应引脚是PH10-PH12(也就是GPIO口H组的第10-12号引脚)
然后分析一下按键电路
首先可以从图中看出如果按键没有按下,芯片引脚电平是高电平,而且外部电路提供上拉电阻,内部引脚设置可以是悬空,当按键按下,芯片引脚与地相连,为低电平。实现就是通过读取芯片引脚电平的状态来判断是否按键的(这里的stm32单片机与c51不一样,他的引脚需要设置模式才可以输入读取,而c52只需要直接读或者写就可以)
也可以看到K2用的是GPIO口的A组,0号引脚
K3用的是GPIO口的C组,13号引脚
下面分析一下蜂鸣器的电路图
在不是硬件设计过程中,我们基本上可以将二级管的状态看成只有两个状态(忽略放大)一个反向截止,一个饱和导通,导通的条件的话就是发射结和集电结均正偏,截止就是均反偏,以后看见这种电路,直接看发射结就可以判断需要什么电平。实现这个三极管很明显是NPN型,所有需要基极高电平,才可以蜂鸣器鸣叫。
而且可以看出基极接的GPIO口为B组5号引脚。
代码
个人建议直接复制,然后用source_insight软件(一款适合写项目代码的软件,不过好像不可以写makefile,shell脚本)
下面代码基本上按照工程规范去书写的,所有可能用了很多宏定义
(注意工程中能用宏,就尽量少用常量)
1.LED的代码和头文件
#ifndef __LED_H__
#define __LED_H__
#define GEC_LED_ON 0
#define GEC_LED_OFF 1
#define GEC_F429_D3 3
#define GEC_F429_D4 4
#define GEC_F429_D5 5
//GEC_led_init:初始化led所使用的端口,默认初始化为高电平
void GEC_led_init(void);
/*GEC_led_ctl:对灯进行控制
@led_n:表示灯的编号
例如:GEC_F429_D3:表示LED灯3
GEC_F429_D4:表示LED灯4
GEC_F429_D5:表示LED灯5
@on_off:表示灯的状态
例如:
GEC_LED_OFF:表示灯灭
GEC_LED_ON: 表示灯亮
*/
void GEC_led_ctl(int led_n,int on_off);
#endif
LEDcode
#include<stdio.h>
#include"led.h"
#include"stm32f4xx.h"
#include"stm32f4xx_gpio.h"
//GEC_led_init:初始化led所使用的端口,默认初始化为高电平
void GEC_led_init(void)
{
//使能GPIOH端口时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;//设置模式为输出模式
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;//输出类型为推挽
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//输出速率为50MHz
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;//初始化的引脚为10、11、12
GPIO_Init(GPIOH,&GPIO_InitStruct);//进行初始化
//对端口设置默认电平----高电平
GPIO_WriteBit(GPIOH, GPIO_Pin_10,Bit_SET);
GPIO_WriteBit(GPIOH, GPIO_Pin_11,Bit_SET);
GPIO_WriteBit(GPIOH, GPIO_Pin_12,Bit_SET);
}
/*GEC_led_ctl:对灯进行控制
@led_n:表示灯的编号
例如:GEC_F429_D3:表示LED灯3
GEC_F429_D4:表示LED灯4
GEC_F429_D5:表示LED灯5
@on_off:表示灯的状态
例如:
GEC_LED_OFF:表示灯灭
GEC_LED_ON: 表示灯亮
*/
void GEC_led_ctl(int led_n,int on_off)
{
switch (led_n)
{
case GEC_F429_D3:
{
if(GEC_LED_OFF==on_off)
{
GPIO_WriteBit(GPIOH, GPIO_Pin_10,Bit_SET);
}
else if(GEC_LED_ON==on_off)
{
GPIO_WriteBit(GPIOH, GPIO_Pin_10,Bit_RESET);
}
}break;
case GEC_F429_D4:
{
if(GEC_LED_OFF==on_off)
{
GPIO_WriteBit(GPIOH, GPIO_Pin_11,Bit_SET);
}
else if(GEC_LED_ON==on_off)
{
GPIO_WriteBit(GPIOH, GPIO_Pin_11,Bit_RESET);
}
}break;
case GEC_F429_D5:
{
if(GEC_LED_OFF==on_off)
{
GPIO_WriteBit(GPIOH, GPIO_Pin_12,Bit_SET);
}
else if(GEC_LED_ON==on_off)
{
GPIO_WriteBit(GPIOH, GPIO_Pin_12,Bit_RESET);
}
}break;
default:;
}
}
2.KEY的代码和头文件
#ifndef __KEY_H__
#define __KEY_H__
#define GEC_F429_K2 2
#define GEC_F429_K3 3
#define GEC_F429_K2_K3 4
#define GEC_KEY_UP 1
#define GEC_KEY_DOWN 0
#define KEY_ALL_DOWN 0
#define KEY_K2_DOWN 1
#define KEY_K3_DOWN 2
#define KEY_ALL_UP 3
//GEC_key_init:初始化key所使用的端口
void GEC_key_init(void);
/*
GEC_key_ctl:实现对按键的状态的监控
@key_n:用于保存键返回状态的主源
例如:
GEC_F429_K2_K3:表示主源为两个键
GEC_F429_K2:表示主源是键K2
GEC_F429_K3:表示主源是键k3
返回值:返回主源键的状态
例如:
GEC_KEY_DOWN:表示主源键按下
GEC_KEY_UP:表示主源键松开
(注意当主源键为GEC_F429_K2_K3表示两个键按下或者松开,当主源键为一个时表示另一个为松开状态)
*/
int GEC_key_ctl(int *key_n);
void time_sleep(int x);
#endif
KEY的头文件
#include"key.h"
#include<stdio.h>
#include"stm32f4xx.h"
#include"stm32f4xx_gpio.h"
//GEC_key_init:初始化key所使用的端口
void GEC_key_init(void)
{
//使能GPIOA、GPIOC端口时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
//设置模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;//设置为输入模式
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;//设置相应端口
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;//输入模式为悬空
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//速率为50MHz
GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化端口
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13;//切换引脚
GPIO_Init(GPIOC,&GPIO_InitStruct);//切换组号,并且初始化
}
/*
GEC_key_ctl:实现对按键的状态的监控
@key_n:用于保存键返回状态的主源
例如:
GEC_F429_K2_K3:表示主源为两个键
GEC_F429_K2:表示主源是键K2
GEC_F429_K3:表示主源是键k3
返回值:返回主源键的状态
例如:
GEC_KEY_DOWN:表示主源键按下
GEC_KEY_UP:表示主源键松开
(注意当主源键为GEC_F429_K2_K3表示两个键按下或者松开,当主源键为一个时表示另一个为松开状态)
*/
int GEC_key_ctl(int *key_n)
{
//读取键的电平状态
uint8_t status_a=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);//获取键k2的电平
uint8_t status_c=GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);//获取键k3的电平
//将键k2的电平左移1位和键k3的电平组合去满足宏的范围
/*
KEY_ALL_DOWN:表示键k2、k3按下
KEY_K2_DOWN:表示键k2按下,k3松开
KEY_K3_DOWN:表示键k3按下,k2松开
KEY_ALL_UP:表示键k2、k3松开
*/
status_c+=status_a<<1;
switch (status_c)
{
case KEY_ALL_DOWN:
{
*key_n=GEC_F429_K2_K3;
return GEC_KEY_DOWN;
}
case KEY_K2_DOWN:
{
*key_n=GEC_F429_K2;
return GEC_KEY_DOWN;
}
case KEY_K3_DOWN:
{
*key_n=GEC_F429_K3;
return GEC_KEY_DOWN;
}
case KEY_ALL_UP:
{
*key_n=GEC_F429_K2_K3;
return GEC_KEY_UP;
}
default:;
}
return GEC_KEY_UP;
}
status_c+=status_a<<1;
语句解释:利用status_c低两个bit为记录按键的所有情况,
例如:status_a表示KEY2的情况,实际上是只有一个bit有用,我们通过左移移位将其加到status_c的第二个bit(1-8bit算),就用两个bit实现所有情况,当然你也可以改,将status_c或者status_a省掉用一个status_c变量就可以实现。
3.蜂鸣器的代码和头文件
#ifndef __BEEP_H__
#define __BEEP_H__
#define GEC_BEEP_ON 1
#define GEC_BEEP_OFF 0
//初始化蜂鸣器端口
void GEC_beep_init(void);
/*
GEC_beep_ctl:实现对蜂鸣器的控制
@on_off:表示蜂鸣器的鸣叫或者不鸣
例如:
GEC_BEEP_ON:表示蜂鸣器鸣叫
GEC_BEEP_OFF:表示蜂鸣器不鸣
*/
void GEC_beep_ctl(int on_off);
#endif
蜂鸣器的code
#include<stdio.h>
#include"stm32f4xx.h"
#include"stm32f4xx_gpio.h"
#include"beep.h"
//初始化蜂鸣器端口
void GEC_beep_init(void)
{
//使能蜂鸣器端口时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;//设置蜂鸣器端口为输出模式
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;//将输出模式设置为推挽
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//设置蜂鸣器的端口为pin_5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//设置输出速率为50MHz
GPIO_Init(GPIOB,&GPIO_InitStruct);//初始化蜂鸣器端口模式
GPIO_WriteBit(GPIOB, GPIO_Pin_5,Bit_RESET);//设置默认为蜂鸣器不鸣
}
/*
GEC_beep_ctl:实现对蜂鸣器的控制
@on_off:表示蜂鸣器的鸣叫或者不鸣
例如:
GEC_BEEP_ON:表示蜂鸣器鸣叫
GEC_BEEP_OFF:表示蜂鸣器不鸣
*/
void GEC_beep_ctl(int on_off)
{
if(GEC_BEEP_ON==on_off)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_5, Bit_SET);
}
else
{
GPIO_WriteBit(GPIOB, GPIO_Pin_5,Bit_RESET);
}
}
4.主函数的代码
#include<stdio.h>
#include"led.h"
#include"key.h"
#include"stm32f4xx.h"
#include"stm32f4xx_gpio.h"
#include"beep.h"
int main()
{
GEC_led_init();//初始化LED灯
GEC_key_init();//初始化按键灯
GEC_beep_init();//初始化蜂鸣器
int i,key_n;
while(1)
{
i=GEC_key_ctl(&key_n);//判断键的状态
if(GEC_KEY_DOWN==i)
{
switch (key_n)
{
case GEC_F429_K2:
{
GEC_led_ctl(GEC_F429_D3,GEC_LED_ON);
GEC_led_ctl(GEC_F429_D4,GEC_LED_OFF);
GEC_beep_ctl(GEC_BEEP_OFF);
}break;
case GEC_F429_K3:
{
GEC_led_ctl(GEC_F429_D4,GEC_LED_ON);
GEC_led_ctl(GEC_F429_D3,GEC_LED_OFF);
GEC_beep_ctl(GEC_BEEP_OFF);
}break;
case GEC_F429_K2_K3:
{
GEC_led_ctl(GEC_F429_D4,GEC_LED_ON);
GEC_led_ctl(GEC_F429_D3,GEC_LED_ON);
GEC_beep_ctl(GEC_BEEP_ON);
}break;
default:;
}
}
else
{
GEC_led_ctl(GEC_F429_D4,GEC_LED_OFF);
GEC_led_ctl(GEC_F429_D3,GEC_LED_OFF);
GEC_beep_ctl(GEC_BEEP_OFF);
}
}
}
结语
也许你会感觉有点疑惑为什么要写的这样麻烦,主要是为了每个代码函数的高内聚低耦合,例如按键,不管怎么样,这个函数只返回按键的情况,至于做什么操作,这是交给主函数去操作。
如果有什么疑问可以留言,看到了,我会尽量解答,如果有什么需要讨论的,也可以留言,欢迎一起讨论