美文网首页
STM32(5):轮训方式让按键点亮LED

STM32(5):轮训方式让按键点亮LED

作者: 张叫兽的技术研究院 | 来源:发表于2022-01-11 06:36 被阅读0次

概述

CPU和外设通信的方式有轮训和中断两种方式;所谓轮训就是主动询问某个状态,看看是否是某个值,如果是则采取行动;中断则是一旦发生了,会主动通知CPU;
本章来研究一下通过如何轮训的方式来响应按键事件。

代码概览

#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "../lib/STM32F10x_StdPeriph_Driver/inc/stm32f10x_exti.h"
#include "../lib/STM32F10x_StdPeriph_Driver/inc/misc.h"
#include "../lib/STM32F10x_StdPeriph_Driver/inc/stm32f10x_gpio.h"

void delay(unsigned int time)
{
    unsigned int i = 0;
    while (time--)
    {
        i = 1000000;
        while (i--)
            ;
    }
}

u8 key_read()
{
    u8 result = 0;
    result = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);

    return result;
}

void led_init()
{
    GPIO_InitTypeDef led;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    led.GPIO_Pin = GPIO_Pin_13;
    led.GPIO_Mode = GPIO_Mode_Out_PP;
    led.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &led);
    GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
}

void key_init()
{
    GPIO_InitTypeDef key;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    key.GPIO_Pin = GPIO_Pin_12;
    key.GPIO_Mode = GPIO_Mode_IPD;
    // led.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &key);
    // GPIO_WriteBit(GPIOB, GPIO_Pin_12, Bit_RESET);
}

void led_opr(int opr){
    if(1 == opr){
        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
    }else{
        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
    }
}

int main(void)
{
    led_init();
    key_init();
    key_nvid_init();
    while (1)
    {
        if (1 == key_read())
        {
            GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
        }
        else
        {
            GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
        }
    }
    return 1;
}

Main函数

从main函数入手,来抽丝剥茧,把轮训方式的实现搞掂。

int main(void)
{
    led_init();
    key_init();
    while (1)
    {
                if (1 == key_read())
                {
                        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
                }
                else
                {
                        GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
                }
    }
    return 1;
}

LED初始化

void led_init()
{
    GPIO_InitTypeDef led;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    led.GPIO_Pin = GPIO_Pin_13;
    led.GPIO_Mode = GPIO_Mode_Out_PP;
    led.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &led);
    GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
}

这个在“STM32(4):基于构件库的点亮LED”有详细介绍,左拐即可看到,使能总线,做PC13的初始化/配置工作,然后配置生效,最后执行了一步操作设置PC13为SET,即高电平,这样实现了灭灯的效果,这样,在操作按下按键的时候,可以实现点亮效果。

键盘操作初始化

void key_init()
{
    GPIO_InitTypeDef key;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    key.GPIO_Pin = GPIO_Pin_12;
    key.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(GPIOB, &key);
}

可以看到,键盘初始化和LED初始化非常类似:

第一步是定义初始化GPIO配置结构体;

第二步是配置GPIO;

第三步是生效GPIO配置;

使用到的引脚

键盘某个按键和GPIOB12引脚相连,另外一端和3v3端口相连;当按键按下的时候,PB12和3.3v电源相连,于是PB12变成了高电平(取值为1),后面就可以根据PB12的值是高电平还是低电平来决定灯的亮灭。


file

上拉电阻 vs. 下拉电阻

GPIO的工作模式为IPD,即Input Push Down,下拉电阻,为什么选择这个工作模式呢?首先是外设要将电平信息传输到GPIO引脚,所以方向是输入(Input),按键在没有按下的情况下是浮空,所以需要通过下拉电阻将浮空电平下拉倒低电平。

什么是浮空?前面我们看到一个引脚,比如LED的PC13,一端要么接着电源(VCC),要么接地;接电源是高电平,接地是低电平,泾渭分明,但是还有一种情况就是既没有接地也没有接电源,其中按键就是这样情况,下按情况下,PB12和3V3的电源联通,PB12是高电平;但是,没有下按的情况下,其实一种未知的装态,电平介于高低电平之间,且电压值未知的一种状态;

对于浮空状态需要明确电平状态,比如在本次按键点亮等的场景,因为按键在按下的状态,明确应该是高电平,所以没有按下的状态应该是低电平的状态;所以需要通过设置工作模式下拉电阻的,这样浮空状态,因为有下拉电阻电路,将会被处理为低电平。

那么什么是下拉电阻?如下图所示,如果外设在联通的时候,GPIO端口(x取值范围是A,B,C等端口组,不同级别的STM32提供的端口组数量不一样,y的取值范围是0~15,共计16个引脚)应该高电平,那么在浮空/ 悬空(开关断开)的时候状态就应该低电平,怎么能够让断开的时候是低电平呢?就是在GPIO上面增加一个下拉电阻,因为开关断开,所以相当于串联了一个无限大的电阻,处于懒惰,或者说前软怕硬的电路特性,电路走的就是下拉电阻支路,于是GPIOxy在接口浮空的请跨年,处于低电平装填;


file

类似的是上拉电阻,使用的场景正好和下拉电阻相反,适合于开关另一端接地,浮空的状态应该是高电平状态的场景(开关接通,因为接地,所以铁定端口呈现的是低电平),如下所示,需要构造一个上拉电阻电路,依据电路的欺软怕硬的特性,走的是上拉电阻的支路,于是开关断开,指定的接口呈现的是高电平:


file

下面是完整的物理电路图:


file
可以看到在电路内部会有上拉/下拉电阻电路,来实现浮空处理;

GPIO速度

在LED的配置中,是需要指定工作速度,但是在KEY的配置中却没有了工作速度,这个是因为指定的GPIO_Speed其实是一个采样速度,即采样GPIO引脚的状态,例如如果指定为10MHz,即每秒10x1000000次采样,用来获取信号量,所以GPIOSpeed按照奎斯特定理,采样频率至少是要信号频率的2倍以上才能够比较准确地还原原始信号,一般实际的工作中,是5~10倍;

所以只有引脚为输出模式的引脚需要设定速度,因为一旦一个引脚被配置为输出模式,就代表是有信号输入到这个引脚,然后再从这个引脚中输出;既然是有信号,那么如果一个引脚想要表达这个信号就需要采样,采样就需要指定速度;

比如我们在点亮LED的源码解读中,信号就是在while循环里面灯的亮灭,信号的频率就是由while语句里面的delay函数决定的,然后把信号写入到PC13上面:

int main(void)
{
        ... ...
        while (1)
        {
                GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
                delay_2(1);
                GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
                delay_2(1);
        }
}

芯片的CPU将会将会基于采样频率(GPIO_Speed)来采集,即由硬件层面来进行采样;其实真正起作用的是采样的结果,采样结果直接决定了LED灯的亮灭:


file

输入vs.输出

那么,回过头来,再来看我们键盘的GPIO配置,作为PB13输入引脚,代表引脚的数据并不是由软件主动写入,输入输出是站在STM32内部芯片的角度来说的,所谓的输入是指,外设将数据到写入GPIO口,需要STM32芯片获取,比如本节按键相应,就是由外设(按键)将自己的电平状态写入到PB12;
输出,则是由STM32内部电路产生信号,写到GPIO口;比如LED灯里面控制PC13的高低电平,就是由软件层面直接控制到STM32电路实现的,所以如果是软件层面直接来配置GPIO口(寄存器)的值,就是输出;


file

封装灯的亮灭

void led_opr(int opr){
if(1 == opr){
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
}else{
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
}
}

这个函数实现了根据参数,决定向PC13写高电平还是低电平;这里面的写法在“STM32(4):基于构件库的点亮LED”里面有说明,这里将其封装为一个函数,就是为了便于重复调用;Bit_Set就是设置高电平,Bit_Rest就是低电平,还记得LED的物理电路图吗?

如果PC13设置为1,和VCC3V3端电压一致,形成不了电压差,所以LED灯处于熄灭的状态,设置PC13为0,LED2两端有电压差,于是产生电流(电流方向是VCC到PC13,电子方向是PC13到VCC,高中物理知识哦)。

GPIO_WriteBit则是(STM32固件库的)库函数,用于向GPIO引脚写入高低电平值。

轮训方式通信

while (1)
{
        if (1 == key_read())
        {
                GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
        }
        else
        {
                GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
        }
}

下面这一段就是通过轮训方式获取PB12的状态(通过key_read函数),然后根据PB12的状态来设置PC13的状态;所谓的轮训方式,就是不断的获取指定引脚的状态;

轮训和后面要将的中断形成鲜明对比;就像上海的有的快递,送到了指定地点,不通知你,于是你只能一遍一遍的刷新手机,看看是否已经送到,送到了下去去取,这个就是类似于“轮训通信”;而有的良心快递送到之后,回短信或者电话通知你,接到通知之后,你直接下楼取快递即可,这个就是“中断通信”;

读取引脚状态

我们看一下key_read()的实现:

u8 key_read()
{
        u8 result = 0;
        // delay(1);
        result = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);

        return result;
}

这里直接调用的(STM32固件库的)库函数GPIO_ReadInputDataBit函数实现了对于PB12的读取,这里如果担心频繁调用影响性能,可以在key_read或者main主函数的while轮训实现通过调用delay函数实现延迟;轮训方式的缺点就是耗费CPU时间,需要CPU不断的进行轮训;不同中断的主动通知,需要主动的不断的访问;中断优点就会节省CPU时间,但是并非所有事件都能够采用中断通知,在STM32里面预制的中断种类只有256个(其中16个内部中断,240个外部中断)。

总结

本章中主要介绍了通过轮训的方式实现基于按键来实现LED等亮灭控制;主要的步骤有LED初始化,按键初始化以及轮训读取PB12的引脚状态;

需要掌握的是轮训通信机制的原理以及实现,还有引脚控制相关上拉/ 下拉电阻,GPIO速度以及引申出来的输入/ 输出概念,

相关文章

网友评论

      本文标题:STM32(5):轮训方式让按键点亮LED

      本文链接:https://www.haomeiwen.com/subject/pmfbcrtx.html