使用宏定义在STM32中灵活控制IO口

目录

一、前言

二、前置知识

1.带参宏定义

2.三目运算符

3.需要用到的寄存器

①BSRR寄存器

②BRR寄存器

③IDR寄存器

三、有以上基础或者想直接用的直接看这里

1.使用宏定义来控制IO口

四、代码解释

1.LED(x)

2.LED_RUN

五、结语


一、前言

突然想用宏定义来控制IO口,但是发现网上并没有比较满意的解决方案,所以自己就写了一个,现在分享出来给大家用。其中用到了带参宏定义和三目运算符,还有一些寄存器的知识,有基础的可以直接从《前置知识》后面看,没基础的我会在《前置知识》简单介绍一下,具体的可以自己去搜索学习一下,想直接用不想知道那么多的同学直接跳到《有以上基础或者想直接用的直接看这里》。

二、前置知识

1.带参宏定义

带参宏定义的格式一般如下,注意:严格上说并不是真正的参数,只是替换而已。

#define 宏名(参数1,参数2,...) 字符串

示例程序,如:

#define add(a,b) a+b

void add(void)
{
    int mnb;
    mnb = add(1,2);
}

因为这个宏定义会将结果返回,所以这个时候mnb就会等于3。

2.三目运算符

三目运算符为?:

其中表达式为(真/假)?(真的话执行这段):(假的话执行这段)

我习惯在三目运算符上面加括号,这样可以避免运算优先级带来的问题。

不加括号的话就是:

真/假?真的话执行这段:假的话执行这段

示例程序,如:

int AAA(int mnb)
{
    int a = (mnb>5)?(mnb-1):(mnb+1);
    return a;
}

这段代码的意思就是说先将AAA的形参mnb用来判断是否大于5,当大于5的时候返回mnb-1赋值给a,否则返回mnb+1赋值给a,最后函数返回a的值。

3.需要用到的寄存器

①BSRR寄存器

全称为“端口位设置/清除寄存器”

有高16位和低16位,这里我只用到了低16位,所以只介绍低16位。

BSRR中的低16位中某一位或者多位置1了,就会将对应的引脚拉高,而置0就是保持当前引脚状态不变。

如:

GPIOA->BSRR = 1 << 0;        //将PA0拉高
GPIOA->BSRR = GPIO_Pin_0;    //将PA0拉高

上面两个段代码是一样的效果,就看你喜欢用哪个了。

②BRR寄存器

全称为“端口位清除寄存器”

这个只有16位,且跟BSRR是反着来的,也就是说置1会将对应引脚拉低,而置0也是保持当前引脚状态不变。

如:

GPIOA->BRR = 1 << 0;        //将PA0拉低
GPIOA->BRR = GPIO_Pin_0;    //将PA0拉低

上面两个段代码也是一样的效果。

③IDR寄存器

IDR是只读寄存器,全称为“端口输入数据寄存器”,是用来读取引脚状态的寄存器。

如:

int a = GPIOA->IDR & GPIO_Pin_0;

上面的代码就是检测PA0引脚的状态,并且将返回值赋值给a。

注意:它是按位返回,如pin0为高它返回0x01,pin3为高它返回0x08。

三、有以上基础或者想直接用的直接看这里

1.使用宏定义来控制IO口

前置知识都会的话,我们就可以进入正题了。

我这里以我们最熟悉的控制LED来举例,并且写出两种控制模式,其他的你们就可以举一反三了。

GPIO口的初始化之类的代码我这里就不给出了,有需要的可以自己搜一下。直接给出代码:

//硬件上以GPIOA中的Pin0为LED,并以高电平为亮,以低电平为灭。
//控制IO高低电平
#define LED(x) ((x==1) ? (GPIOA->BSRR=GPIO_Pin_0) : (GPIOA->BRR=GPIO_Pin_0))

//控制IO口翻转电平
#define LED_RUN ((GPIOA->IDR&GPIO_Pin_0) ? (GPIOA->BRR=GPIO_Pin_0) : (GPIOA->BSRR=GPIO_Pin_0))

int main(void)
{
    LED(1);        //拉高LED
    LED(0);        //拉低LED
    while(1)
    {
        LED_RUN;   //翻转LED的IO口,使LED闪烁
    }
}

上面代码可以直接拿去用,只需要改下IO端口、Pin脚和宏名就行了,比如我现在想用PB13口控制LED灯:

#define LED(x) ((x==1) ? (GPIOB->BSRR=GPIO_Pin_13) : (GPIOB->BRR=GPIO_Pin_13))

就可以了,控制IO口翻转也同理

#define LED_RUN ((GPIOB->IDR&GPIO_Pin_13) ? (GPIOB->BRR=GPIO_Pin_13) : (GPIOB->BSRR=GPIO_Pin_13))

四、代码解释

1.LED(x)

#define LED(x) ((x==1) ? (GPIOA->BSRR=GPIO_Pin_0) : (GPIOA->BRR=GPIO_Pin_0))

先解释LED(x)的宏定义,这个宏定义是用来直接控制IO高低电平的。

使用了带参宏定义、寄存器和三目运算符的知识。

调用这个宏定义,就会调用((x==1) ? (GPIOA->BSRR=GPIO_Pin_0) : (GPIOA->BRR=GPIO_Pin_0))这一整段代码,在这整段代码中,三目运算符会首先判断x是否等于1,等于1的话就为真,执行GPIOA->BSRR=GPIO_Pin_0这段代码,这段代码的意思就是将PA0电平拉高;否则为假,执行GPIOA->BRR=GPIO_Pin_0这段代码,这段代码的意思就是将PA0电平拉低。

以此达到了拉高拉低的解决方案。

2.LED_RUN

#define LED_RUN ((GPIOA->IDR&GPIO_Pin_0) ? (GPIOA->BRR=GPIO_Pin_0) : (GPIOA->BSRR=GPIO_Pin_0))

LED_RUN(x)的宏定义是用来翻转电平的,这里我用作LED闪烁,也可以有其他用处。

进来先判断PA0脚是高电平还是低电平,并且&对应的引脚,这样就可以在高电平的时候按位返回,低电平的时候返回0。当大于0时判断为真,执行(GPIOA->BRR=GPIO_Pin_0)来将电平拉低;当为0时判断为假,执行(GPIOA->BSRR=GPIO_Pin_0)来将电平拉高。

五、结语

个人感觉比库函数简单方便,还可以用来做一些特殊操作,就比如GPIOx不是同一端口,库函数需要不停的切换GPIOx的x字母,很不方便,特别是在写数组类或者算法类的IO口控制的时候,这样写也能简单明了的知道这个端口是干嘛的,并且比直接宏定义要简洁,直接宏定义要两个才能控制拉高拉低,而这个只需要一个宏定义。

最后写出来的代码看着很简单,但是实际上写了这么一大篇,本来两句话就可以写完的,还是太照顾了大家了,希望大家都有所收获,有帮助的话就点个赞收藏一下,最好可以关注一下,不定时更新实用的东西。

物联沃分享整理
物联沃-IOTWORD物联网 » 使用宏定义在STM32中灵活控制IO口

发表评论