功能实现
1.设备上电后灯全灭
2.按下按键KEY1,LED1灯亮,再次按下该灯灭。
要求:按键按下时要高效实现,快速触发时也不能失效。
一、库函数编程流程
1.概念了解:
STM32的三种开发模式及各自特点:
1.寄存器:开发周期长,难度大,代码执行效率高。
2.库函数:开发方便,可读性好。
3.Hal库:开发非常快捷方便,代码冗余度高,不利于学习。
2.具体步骤
2.1导入对应的相关库
想要使用库函数编程,首先需要添加对应的标准库。标准库的获取一般由开发商处获取,若没有就要考虑另外两种开发模式。
标准库内一般分为inc文件和src文件
inc:内涵标准库的各个头文件
src:内涵标准库的源码.c文件
那么究竟该如何导入到工程中呢?
首先对于src文件我们只需在项目中新建一个文件夹(下图中的stdlib即为新建的文件夹),将src中的.c文件添加进去即可。
对于inc里的头文件,我们需要点击魔法棒,在C/C++中添加该文件路径
此时,进行Rebuild会发现有错误。
想要解决就需要在C/C++的Define中添加:STM32F10X_HD,USE_STDPERIPH_DRIVER
添加完毕即可使用标准库了。
2.2库函数的查看及使用
库函数:库函数是官方写好的一些API,开发人员可以使用这些接口来快速开发程序。
库函数的本质:封装好的一些具有某种功能的函数,内部其实还是寄存器编写。
首先,我们需要打开固件库使用手册,在目录中找到GPIO库函数的位置
在图中我们可以看到各个函数名以及作用。
不知道如何使用的话,只需在keil中写出并鼠标右键goto即可。
二、按键消抖
1.消抖前
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "beep.h"
#include "delay.h"
int main()
{
Led_Init();
key_Init();
Beep_Init();
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{
Led_Toggle(4);
}
}
}
#include "stm32f10x.h"
void Led_Init()
{
//配置好模式,然后全灭
//开APB2时钟
RCC->APB2ENR |= 0X01 << 6;
//配置PE2--PE5为通用推挽输出
GPIOE->CRL &=~(0X0F << 20);//PE5
GPIOE->CRL |= 0X03 << 20;
GPIOE->CRL &=~(0X0F << 16);//PE4
GPIOE->CRL |= 0X03 << 16;
GPIOE->CRL &=~(0X0F << 12);//PE3
GPIOE->CRL |= 0X03 << 12;
GPIOE->CRL &=~(0X0F << 8);//PE2
GPIOE->CRL |= 0X03 << 8;
//4个引脚均输出高电平
GPIOE->ODR |= (0x0F << 2);
}
//开关灯
void Led1_Ctrl(int flag)
{
if(!!flag)
{
GPIOE->ODR &= ~(0x0F << 2);
}
else
{
GPIOE->ODR |= (0x0F << 2);
}
}
void Led_Toggle(int flag)
{
GPIOE->ODR ^= 0x01<<(flag+1);
}
此时虽然程序逻辑上没有问题,但手指过快按下按键会导致按键失灵,主要原因是由于按键检测过于敏感,太快了。
2.消抖后
按键消抖就是为了防止这种现象的出现。
加入按键消抖后的主函数代码:
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "beep.h"
#include "delay.h"
int main()
{
Led_Init();
key_Init();
Beep_Init();
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{
Delay_nms(10);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{}
Led_Toggle(4);
}
}
}
}
逻辑解析:当按键按下时按键会先检测KEY1是否发出了一个高电平信号,若发出则等待10毫秒再次检测,以防止误触或错误检测。之后再通过while检测是否为高电平。若不是高电平,则代表按键已经抬起。此时就能确定以及肯定按键被用户按下了。执行LED灯反转操作。
三、完整代码
main.c
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "beep.h"
#include "delay.h"
int main()
{
Led_Init();
key_Init();
Beep_Init();
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{
Delay_nms(10);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{}
Led_Toggle(4);
}
}
}
}
led.c
#include "stm32f10x.h"
void Led_Init()
{
//配置好模式,然后全灭
//开APB2时钟
RCC->APB2ENR |= 0X01 << 6;
//配置PE2--PE5为通用推挽输出
GPIOE->CRL &=~(0X0F << 20);//PE5
GPIOE->CRL |= 0X03 << 20;
GPIOE->CRL &=~(0X0F << 16);//PE4
GPIOE->CRL |= 0X03 << 16;
GPIOE->CRL &=~(0X0F << 12);//PE3
GPIOE->CRL |= 0X03 << 12;
GPIOE->CRL &=~(0X0F << 8);//PE2
GPIOE->CRL |= 0X03 << 8;
//4个引脚均输出高电平
GPIOE->ODR |= (0x0F << 2);
}
//开关灯
void Led1_Ctrl(int flag)
{
if(!!flag)
{
GPIOE->ODR &= ~(0x0F << 2);
}
else
{
GPIOE->ODR |= (0x0F << 2);
}
}
void Led_Toggle(int flag)
{
GPIOE->ODR ^= 0x01<<(flag+1);
}
led.h
#ifndef _LED_H_
#define _LED_H_
void Led_Init();
void Led1_Ctrl(int flag);
void Led_Toggle(int flag);
#endif
key.c
#include "stm32f10x.h"
void key_Init()
{
//开时钟
RCC->APB2ENR |= 0x01<<4;//PC
RCC->APB2ENR |= 0x01<<2;//PA
//配置模式
GPIOC->CRL &=~(0X0F << 24);//PC6 key4
GPIOC->CRL |= 0X04 << 24;
GPIOC->CRL &=~(0X0F << 20);//PC5 key3
GPIOC->CRL |= 0X04 << 20;
GPIOC->CRL &=~(0X0F << 16);//PC4 key2
GPIOC->CRL |= 0X04 << 16;
GPIOA->CRL &=~0X0F;//PA0 key1
GPIOA->CRL |= 0X04;
}
int Get_Key_Val(void)
{
int key_val = 0;
if(!!(GPIOA->IDR &(0X01 << 0))==1)
key_val = 1;
if(!!(GPIOC->IDR &(0X01 << 4))==0)
key_val = 2;
if(!!(GPIOC->IDR &(0X01 << 5))==0)
key_val = 3;
if(!!(GPIOC->IDR &(0X01 << 6))==0)
key_val = 4;
return key_val;
}
key.h
#ifndef _KEY_H_
#define _KEY_H_
void key_Init();
int Get_Key_Val(void);
#endif
delay.c
#include "stm32f10x.h"
#include "delay.h"
void Delay_nus(uint32_t time)
{
uint32_t i=0;
for(i=0;i<time;i++){
delay1us();
}
}
void Delay_nms(uint32_t time)
{
uint32_t i=0;
for(i=0;i<time;i++){
Delay_nus(1000);//延时1ms
}
}
delay.h
#ifndef _DELAY_H_
#define _DELAY_H_
#include "stm32f10x.h"
#define delay1us() {__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();}
void Delay_nus(uint32_t time);
void Delay_nms(uint32_t time);
#endif
四、总结
1.了解了STM32各个开发模式的区别以及特点。
2.学会了如何对STM32进行库函数开发。
3.学会了如何对按键操作进行按键消抖。
4.学会了如何通过异或操作进行器件反转。