STM32 嵌入式学习入门(3)——STM32F103 按键输入控制LED灯

发布者:代码漫游者最新更新时间:2019-04-10 来源: eefocus关键字:STM32  按键输入  控制LED灯 手机看文章 扫描二维码
随时随地手机看文章

按键是单片机上一个很重要的输入设备,也很容易掌握,只要明白了IO口最基本的使用,就可以操作按键了。


我们的目的是控制开发板上板载的三个按键来操作开发板上板载的两个LED灯实现亮或灭(按键第一次按下时灯亮,再按下时灯灭,以此类推)。


博主所用的开发板是正点原子的mini板(STM32F103RCT6)和战舰板(STM32F103ZET6),因此下面的内容的例子以这两款开发板为例,但是基本的原理对任何开发板来说都是一样的,只要自己的开发板上板载了按键和LED灯(这两个资源应该是所有开发板上都有的资源吧),然后查看自己开发板的数据手册和硬件电路图、原理图,找到按键和LED灯对应的IO口,就可以按照本文所介绍的流程使用按键控制LED灯了。


在开发板上,板载硬件资源(比如我们这里用到的按键和LED灯)都是和确定的GPIO相连的,要使用这些硬件资源,实际上可以理解成对这些IO口的操作。


下面先大致说一下整个流程。


1.首先要使用这些硬件资源,就要对其进行初始化。初始化包括使能IO口时钟和初始化IO口参数两个部分,这两点在上一篇博文中有提到了。


2.初始化完成后就可以操作IO口了,就是写相关的逻辑代码。这里是按键控制LED灯,所以我们首先要扫描与按键相连的IO口的电平。如果IO电平发生变化,那么说明按键被按下了,我们就要让灯的状态发生反转。如果IO电平没有发生变化,那么说明按键没有被按下,我们可以过一个很小的时间间隔再去扫描与按键相连的IO口看是否发生变化,这样形成了一个死循环。


下面上一段代码,是正点原子Mini STM32F103RCT6开发板(mini板)按键实验的主函数部分:


#define LED0 PAout(8) // PA8 

#define LED1 PDout(2) // PD2 

#define KEY0_PRES 1 //KEY0 

#define KEY1_PRES 2 //KEY1 

#define WKUP_PRES 3 //WK_UP 

int main(void) 

    u8 t=0; 

    delay_init(); //延时函数初始化 

    LED_Init(); //初始化与LED连接的硬件接口 

    KEY_Init(); //初始化与按键连接的硬件接口 

    LED0=0; //点亮LED0 

    while(1){ 

        t=KEY_Scan(0); //得到键值 

        KEY_Scan(0);即不支持连续有效。如果需要支持连续按,这里参数设置为1. 

        switch(t){ 

            case KEY0_PRES: LED0=!LED0; break; 

            case KEY1_PRES: LED1=!LED1; break; 

            case WKUP_PRES: LED0=!LED0; LED1=!LED1; break; 

            default: delay_ms(10); 

        } 

    }//while(1) 

}

代码比较长,但整体逻辑还是很容易理解的,首先解释一下前两行的宏定义,这里是通过位操作,实现了对PA8/PD2的电平输出的操作的,这一行代码其实挺复杂的,准确说是博主也讲不出原理(如果你去查看stm32的官方库函数,会发现这里的PAout(n)是通过很多层宏定义得到的),但是我会调用。总之,这样定义了以后,下面的代码你对LED0赋1对应着设置IO口为高电平,赋0就是设置IO口为低电平。下面的三个宏定义是为下面switch-case服务的,增强了代码的可读性。


接下来就是主函数了,首先是调用三个初始化函数,这个等下后面具体说。初始化完成后,然后LED0=0;这一句,点亮LED0。接着就是while(1)循环了,死循环里面先调用KEY_Scan(0);函数对按键进行检测,该函数的返回值有四种情况,即KEY0_PRES、KEY1_PRES、WKUP_PRES、0。前三个返回值表明相应的按键被按下了,返回0表明当前没有按键按下。然后是switch-case结构了,如果有按键按下,执行相应分支,对LED上的电平进行反转,实现按一次亮,再按一次灭,以此类推,这样的效果。如果没有按键按下,那么延时10ms,然后继续检测有没有按键按下。整体程序的执行过程就是这样。


下面的问题就是三个初始化函数和KEY_Scan(0);函数是怎么实现的了。


1.延时函数


这里用到了delay_init();和delay_ms(10);两个函数。延时函数要想完全搞清楚其中的原理需要了解STM32内核中定时器的知识,不是三两句可以解释清楚的,以后有时间再写一篇详细介绍一下延时函数,写完后贴到这里来。对于刚刚接触STM32的同学来说,我建议刚开始就会调用这个函数就好了,不要深究其实现原理,因为涉及其它内容比较多,零基础刚开始接触STM32,去看那些,如果看不懂挺打击人积极性的。这两个函数的调用方法:delay_ms(10);调用时候传递一个整形参数n,表示要延时n毫秒,如果程序中用到了delay_ms(n);函数,那么在调用延时函数前要调用延时初始化函数delay_init();就是这样无参调用就好了。另外,delay_ms(n);函数调用时候n的值也不能过大,n的值是有一个上限的,这就像int所能表示的最大值也是有上限的一样。具体的这个值是多少,是和时钟频率有关的,对时钟频率为72M的条件下(这算是个默认条件了),n<=1864。 


上面说的两个延时函数都不是自己写的,正点原子的每个实验代码的SYSTEM文件夹的delay.c文件中都有这个代码,所以博主就拿来主义了,很方便。如果是别的开发板,可以查查手册看怎样能实现延时的功能,实在不行,下面的代码也能实现延时……


void Delay(u32 count)

{

  u32 i=0;

  for(;i


}

 


2.LED_Init();和KEY_Init();函数


这两个函数是自己写的,就是对IO口的相关参数进行配置,先上代码:


void LED_Init(void)

{

 

GPIO_InitTypeDef  GPIO_InitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE); //使能PA,PD端口时钟


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //LED0-->PA.8 端口配置

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz

GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA.8


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;     //LED1-->PD.2 端口配置, 推挽输出

GPIO_Init(GPIOD, &GPIO_InitStructure);   //推挽输出 ,IO口速度为50MHz

}

void KEY_Init(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

 

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);//使能PORTA,PORTC时钟


GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_15; //PA15

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入

  GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA15


GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_5; //PC5

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入

  GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC5

 

GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;//PA0

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉   

GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0

}

 


关于GPIO口的初始化,可以参考博主的上一篇文章,STM32 嵌入式学习入门(2)——STM32的GPIO介绍


至于这里配置为什么是这些参数,是根据开发板硬件连接确定的,上面的代码是正点原子MiniSTM32F103RCT6开发板的代码,这款开发板的原理图如下图:



从这里可以看出来按键对应的IO口以及它们是与高电平相连还是低电平相连。比如两个LED,硬件上,它们都是与高电平相连的,所以你把另一端设置为低电平的时候它们就被点亮,如果另一端设置为高电平,那么它们就灭。另外IO口的模式也是从硬件连接上确定的。


3.KEY_Scan(0);函数的实现


这个函数的实现其实不难,和STM32的知识没多大关系,就是C语言的逻辑问题,代码如下:


//按键处理函数

//返回按键值

//mode:0,不支持连续按;1,支持连续按;

//返回值:

//0,没有任何按键按下

//KEY0_PRES,KEY0按下

//KEY1_PRES,KEY1按下

//WKUP_PRES,WK_UP按下 

//注意此函数有响应优先级,KEY0>KEY1>WK_UP!!

u8 KEY_Scan(u8 mode)

{  

static u8 key_up=1;//按键按松开标志

if(mode)key_up=1;  //支持连按   

if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))

{

delay_ms(10);//去抖动 

key_up=0;

if(KEY0==0)

return KEY0_PRES;

else 

if(KEY1==0)

return KEY1_PRES;

else

if(WK_UP==1)

return WKUP_PRES;

}else 

if(KEY0==1&&KEY1==1&&WK_UP==0)

key_up=1;

return 0;// 无按键按下

}


关键字:STM32  按键输入  控制LED灯 引用地址:STM32 嵌入式学习入门(3)——STM32F103 按键输入控制LED灯

上一篇:STM32实战 4.利用定时器与串口接收指令控制LED亮度
下一篇:总结写的stm32的KEY控制LED

推荐阅读最新更新时间:2024-03-16 16:27

stm32用ucos还是linux
  常见的嵌入式操作系统有两种:用MMU的和不用MMU的。 用MMU的是Windows、 MacOS 、Linux、 Android,不用MMU的是FreeRTOS VxWorks ucOS。 CPU有两种:带MMU的和不带MMU的,带MMU的有Cortex-A系列ARM9、 ARM11系列,不带MMU的有Cortex-M系列。 stm32用ucos还是linux STM32是M系列,不带MMU控制器,不可能运行Linux,当然, STM32能够跑μClinux,但是严格来说,μClinux是不算Linux的。所以对于很多网友有疑问的stm32用ucos还是linux这个问题,我们就知道答案了,stm32是用
[单片机]
嵌入式STM32学习笔记(3)——pwm波及呼吸灯
写pwm波函数可以调用stm32固件库函数直接生成,也可以通过中断来写pwm波;下面就介绍这两种方法,这里先说一下呼吸灯,其原理就是让LED灯由暗变亮再由亮变暗循环,类似呼吸的效果,亮-暗是一个大周期,而LED灯亮或暗是由其刷新的占空比决定,高电平时间占比长则亮,反之则暗; stm32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达4路的 PWM 输出,这样, STM32 最多可以同时产生 30 路 PWM 输出。关于映射及原理大家可查手册吧,这里不做具体叙述了;个人见解:很多知识用到再仔细
[单片机]
嵌入式<font color='red'>STM32</font>学习笔记(3)——pwm波及呼吸灯
STM32 BOOT0、BOOT1的配置
STM32有三种启动模式,分别是主存储器、系统存储器和内部SRAM。在芯片的用户手册中可以查询到。 关于这三种启动模式,具体为: BOOT1=x,BOOT0=0:从主存储器启动,就是我们常说的64K、128K、256K、512K等等的片内的Flash存储器,正常情况下我们一般这么配置,此时BOOT1引脚可以悬空; BOOT1=0,BOOT0=1:从系统存储器启动,此种启动方式会运行系统存储器内的Bootloader程序,也就是我们常说的ISP程序,这个程序是出厂内置好的,不能更改,系统存储器是一个ROM,我们使用串口下载程序就需要配置为此种启动模式; BOOT1=1,BOOT0=1:从内置SRAM启动,一般用于调试,当用户需
[单片机]
STM32开发笔记72: 使用命名空间解决类名冲突问题
单片机型号:STM32L053R8T6 在程序设计中,使用了两个类,这两个类都有引脚定义并同名,程序如下: #ifndef E32_400T20S_H_ #define E32_400T20S_H_ #include io.h #include mini_uart.h #ifdef __cplusplus extern C { class CM0:public CIO_Output { public: CM0(void); }; class CM1:public CIO_Output { public: CM1(void); }; class CE32_400t20s:public CMiniUA
[单片机]
应用笔记 | MPU 子区话题
1. 问题起因 有人询问STM32F7 和STM32H7 系列库例程中有关MPU 配置中的下面这句加绿色下划线的代码的意思是什么?有何用? 图1、芯片存储空间MPU 背景配置 从上面截图中的红色框内代码我们不难看出,这里进行MPU 设置就是将从0 开始的4G 空间,即整个STM32 可寻址空间定义为Strongly Ordered 存储属性。且此时MPU Region 编号为0。可代码注释上又说只是将未定义的区域配置为Strongly Ordered,这未定义区域到底啥意思,该如何理解?难道跟绿色下划线标示的那行代码有关系。 那么,这句代码MPU_InitStruct.SubRegionDisable = 0x87;
[单片机]
应用笔记 | MPU 子区话题
太阳能LED灯控制器电路图
下图是一个基于单片机的太阳能LED灯控制器电路图,其主要实现功能叙述如下:白天采用太阳能电池板给蓄电池充电,晚上采用两段式点灯,即天黑后点亮到深夜自动关闭,第二天天亮前自动点亮,天亮后又自动关闭。
[单片机]
太阳能<font color='red'>LED灯</font><font color='red'>控制</font>器电路图
STM32-串口实验学习笔记
USART1_IRQHandler(void)函数: 当串口1发生了相应的中断,就会跳到改函数执行。这里设计了一个小小的接收协议(系统并未定义):通过这个函数,配合一个数组USART_RX_BUF ,一个接收状态寄存器USART_RX_STA实现对串口的数据的接收管理。USART_RX_BUF 最大值为64,也就是一次接收的数据最大不能超过64字节。USART_RX_STA是一个接收状态寄存器,其各位的定义如表所示: (注意:这个是作者设计的协议,怎样判断串口接收一组数据完毕?由于每次接收的数据长度不一样,少的就3个8位数据,多的时候有十多个,这个数据个数是不定的,且没规律的数据,有什么好的方法让它接收完整? 协议的设
[单片机]
STM32-串口实验学习笔记
STM32学习笔记——AFIO时钟的配置问题
最近在写程序时发现设置外部中断出了问题,之前的程序好好的怎么就不能用了呢?经过了一晚上的折腾发现问题出在AFIO时钟的配置上,我没有使能AFIO时钟。 什么时候要开启AFIO呢?参考手册: 说的很明白,操作AFIO的三类寄存器时需要开启: 1.事件控制寄存器(AFIO_EVCR) 2.复用重映射和调试I/O配置寄存器(AFIO_MAPR) 3.外部中断配置寄存器x(AFIO_EXTICRx) 这三类其实是AFIO的全部寄存器, AFIO_EVCR是事件相关的,AFIO_MAPR是与重映射和调试I/O相关的,AFIO_EXTICRx是与外部中断相关的。因此管脚重映射和调试I/O配置、外部中断、事件时需开启AFIO。 再回
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved