历史上的今天
返回首页

历史上的今天

今天是:2025年04月10日(星期四)

正在发生

2019年04月10日 | STM32 嵌入式学习入门(3)——STM32F103 按键输入控制LED灯

2019-04-10 来源:eefocus

按键是单片机上一个很重要的输入设备,也很容易掌握,只要明白了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;// 无按键按下

}


推荐阅读

史海拾趣

DS-IMP公司的发展小趣事

在电子行业,供应链管理是企业发展的关键因素之一。DS-IMP公司深知这一点,因此在发展过程中不断优化供应链管理。公司建立了完善的供应链体系,与供应商和客户建立了长期稳定的合作关系。同时,公司还引入了先进的供应链管理软件和技术手段,实现了对供应链的实时监控和精细化管理。这些措施不仅提高了公司的运营效率,还降低了成本风险,为公司的发展提供了有力保障。

Elcoma公司的发展小趣事

面对日益变化的市场环境和客户需求,Elcoma公司始终保持创新的精神。他们不断投入研发资金,引进高端人才,加强技术创新和产品研发。同时,Elcoma还积极探索新的商业模式和市场机会,以适应市场变化并抓住发展机遇。这种创新驱动的发展策略使Elcoma在电子行业中保持领先地位,并为企业的未来发展奠定了坚实的基础。

超音(CY)公司的发展小趣事

品质是超音一直以来的追求。公司注重产品质量管理,建立了完善的质量检测体系,确保每一件产品都符合高标准的质量要求。同时,超音还加强了品牌建设,通过广告宣传、赞助活动等方式提升品牌知名度和美誉度。这些努力使得超音在消费者心中树立了良好的品牌形象,为公司的长期发展奠定了坚实的基础。

B.B. Battery公司的发展小趣事

随着科技的不断发展,电池行业也在经历着深刻的变革。B.B. Battery公司紧跟时代步伐,不断加大技术创新力度。他们引进先进的生产设备和技术,研发出更高效、更环保的电池产品。同时,公司还注重与高校、科研机构的合作,共同推动电池技术的创新与发展。这些努力使得B.B. Battery公司的产品在市场上具有更强的竞争力。

Degson Electronics Co Ltd公司的发展小趣事

为了进一步扩大市场份额,Degson开始实施国际化战略,并积极开拓全球市场。公司参加了多个国际电子展会,与全球客户建立了广泛的联系。同时,Degson还在海外设立了多个办事处和生产基地,以便更好地服务当地客户。这些举措使Degson的产品逐渐进入了美国、德国、英国、日本等90多个国家和地区的市场,实现了全球市场的布局。

Good Will Instrument Co., Ltd.公司的发展小趣事

随着市场需求的不断变化,Degson意识到只有不断创新和升级产品,才能在激烈的市场竞争中保持领先地位。因此,公司投入大量资金用于研发,并在技术上取得了重大突破。Degson成功研发出了一系列新型接线端子和精密模具,不仅提高了产品的性能和质量,还降低了生产成本。这些新产品迅速在市场上获得了广泛应用,进一步巩固了Degson在电子连接器领域的领先地位。

问答坊 | AI 解惑

vc2005开发嵌入式设备,有没有比较好的打开图象(jpg,png..tiff....)的开源库?

vc2005开发嵌入式设备,有没有比较好的打开图象(jpg,png..tiff....)的开源库?…

查看全部问答>

刚刚飞奔把板子取回来!~

刚把板子取回来,由于今天有事情,所以明天来个开箱图片贡献给大家!~…

查看全部问答>

【MSP430共享】气压与心电相关数据采集系统的设计

设计了一种气压与心电同步数据采集系统. 利用该系统对气压和心电信号进行长时间同步采集, 揭示了气压变化与心电信粤之间的对应关系. 为满足系统低功耗的需要, 通过对MS P 4 3 0 F 1 4 9 以及外围电路的分析, 分别给出软硬件解决方法; 另一方 ...…

查看全部问答>

寻迹蓝牙射频小车制作

最近做了个小车,目前是用蓝牙功能来控制的,不过正反,正反转就把驱动IC给烧了     是P,NMOS的死区时间没加的原因吗? [ 本帖最后由 蓝雨夜 于 2011-9-16 09:24 编辑 ]…

查看全部问答>

基于FPGA技术的RFID系统的解码模块设计

 RFID技术(radiofrequencyidentification)是一种非接触式智能识别技术,它通过射频信号自动识别目标对象并获得相关信息。整个识别过程无需人工介入,可同时识别多个对象并可以识别高速运动的物体,操作简单,广泛应用在车辆自动识别系统、物流管理 ...…

查看全部问答>

无刷电机入门者的福音--STM8S 无刷电机控制器 DEMO 开源(转)

硬件是 一个PCB 是功率板,可以用来做 hall 和SENSORLESS的,sensorLESS 工作在比较器模式下。当然也可以做HALL 模式下的FOC算法。 MCU板就是一个最小系统板。两个模拟电位计,两个直拨开关。还有一个电源开关。 图片是一个做的风扇,实际测量20W ...…

查看全部问答>

SCI 怎么接收数据。。。急!!!!!!

各位大神,我是新手,请教SCI 怎么接收数据?用串口调试助手发个数,板子怎么接收,有接收子程序吗?…

查看全部问答>

2812运行异常help

2812在调试过程中 发现,随着代码的增加 烧写进入FLASH出现上电不允运行的现像。一旦退回到上一个版本的代码 运行即恢复正常。现在可以肯定硬件没有问题。经过反复的试验,发现在工程中的一个C文件里增加定义了一个全局变量后烧写进Flash就出现复位 ...…

查看全部问答>

EEWORLD大学堂----Altera 2014技术巡展(13)数据中心加速和成功案例

Altera 2014技术巡展(13)数据中心加速和成功案例:https://training.eeworld.com.cn/course/2042Altera 2014技术巡展(13)数据中心加速和成功案例…

查看全部问答>