物联网
您现在所在的位置:首页>企业动态>物联网

三种管理 C 程序中标志位的方法,最后一种比较秀~

编辑:学到牛牛IT培训    发布日期: 2022-01-26 09:39:08  

嵌入式开发中难免会涉及到非常多的标志位处理,特别是玩单片机、裸机开发的朋友,比如跟一些模块配合联调会遇到各种信号是否到位、成功等等状态,而这些信号大多都是bool类型,1个bit即可进行标识。

当然如果仅仅是几个标志,直接拿个uint8_t的整形来进行标识也不会影响什么,但如果特别多的话似乎就比较废RAM了。

然而为了更好的管理这些标志位等,有个如下几种方式供大家更好的管理这些标志位 :

1、位域直接标识

采用位域是管理这些标志位比较直接且方便的方式,代码如下所示:

typedef union _tag_SystemFlag
{
    uint16_t all;
    struct 
    {
        uint16_t Run         :1;
        uint16_t Alarm       :1;
        uint16_t Online      :1;
        uint16_t TimerOver   :1;
        uint16_t Reserver    :12;
    }bit;
} uSystemFlag;
uSystemFlag  unSystemFlag;
int main(int argc, char *argv[]) {
    unSystemFlag.all = 0x00; //系统标志清除
    unSystemFlag.bit.Run       = 1; //置位
    unSystemFlag.bit.Alarm     = 1;
    unSystemFlag.bit.Online    = 1;
    unSystemFlag.bit.TimerOver = 1;
    unSystemFlag.bit.Run       = 0; //清零
    unSystemFlag.bit.Alarm     = 0;
    unSystemFlag.bit.Online    = 0;
    unSystemFlag.bit.TimerOver = 0;
    return 0;
}

这些标志位的操作无非就是置位,清零、以及读取三种方式。

但如代码中这样的操作方式在语句或语义表达上还是不够直观。

bug菌经常谈到,代码可以不写注释,不过你的每个变量、函数名称等需要足够的直观,所以很多朋友习惯把这些标志封装起来。

2、枚举+移位

为了更好的表达一般会对标志位进行进一步的封装,如下代码所示:

typedef enum _tag_Flag {
cEmRun = 0,
cEmAlarm,
cEmOnline,
cEmTimerOver
}emSystemFlag;
uint16_t SystemFlag ;
//置位
void SetFlag(emSystemFlag flag)
{
    SystemFlag |=  ((uint16_t)0x01) << flag;
}
//清除
void C++lrFlag(emSystemFlag flag)
{
    SystemFlag &=  ~(((uint16_t)0x01) << flag);
}
//获得状态
uint8_t  GetFlag(emSystemFlag flag)
{
    return (((SystemFlag & (((uint16_t)0x01) << flag)) != 0)? true:false);  
}
int main(int argc, char *argv[]) {
    SetFlag(cEmAlarm);
    if(GetFlag(cEmAlarm) == true)
    {
        printf("ClrFlag
");
        ClrFlag(cEmAlarm);
    }
   else
    {
        printf("SetFlag
");
        SetFlag(cEmAlarm);
    }
    return 0;
}

当然封装成函数是相对比较耗时的,不过代码也会更加的易懂,如果确实容忍不了函数封装带来的时间消耗,把函数修改为宏代码片段或者内敛函数(当然前提是编译器支持)也是可行的。

3、宏列表

或许这里才是本文的重中之重~

以前跟大家介绍过,用宏自动化的生成各种代码片段,以使得代码更加的紧凑。当然可读性会相对降低一点,但对于重复性代码就不需要太多考虑了。

#include <stdio.h>
#include <stdlib.h>
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef signed char int8_t;
typedef int  int16_t;
#define true  1
#define false 0
//宏列表
#define TAG_LIST(tag) 
tag(Run)
tag(Alarm)
tag(Online)
tag(TimerOver)
//枚举处理
#define DEFINE_TAG(_tag) _tag,
enum Flag {
None = 0,
TAG_LIST(DEFINE_TAG)
EmMAX
};
#undef DEFINE_TAG
//位定义变量
uint16_t SysFlag = 0x0000;
//通用方法定义
uint8_t GetFlags(uint16_t mask)
{
    return ((SysFlag & mask) != 0)? true:false;
}
void SetFlags(uint16_t mask)
{
     SysFlag |=  mask;
}
void ClrFlags(uint16_t mask)
{
     SysFlag &=  ~mask;
}
//自动生成三类函数定义
#define FLAG_Operater(flag) 
uint8_t  get##flag()  {
return GetFlags(1 << flag);
}
void set##flag() {
SetFlags(1 << flag);
}
void clr##flag() {
ClrFlags(1 << flag);
}
//反向函数关联
TAG_LIST(FLAG_Operater)
int main(int argc, char *argv[]) {
    setRun();
    setAlarm();
    if(getAlarm() == true)
    {
        printf("set 
");
    }
    else
    {
        printf("clr 
");
    }
    return 0;
}

如果以前有过类似代码处理的朋友,应该看这段代码还是比较轻松的吧,如果有点生疏,可以一层一层展开了解。

其主要的功能就是通过宏替换和代码拼接符号,自动的生成通用的代码片段,这样做的好处就是不再需要我们在代码中定义一大堆setflag、clrflag、getflag等函数。

通过上面的代码当我们向TAGLIST宏中添加一个标识符,即可生成一系列相关的操作函数等。

这样一方面可以及简化代码,同时也避免一些人工编码带来的错误。


来源:公众号:最后一个bug

声明:转载此文是出于传递更多信息之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与本网联系,我们将及时更正、删除,谢谢。 

免费试学
课程好不好,不如实地听一听

封闭学习

2

1

联系我们

电话:028-61775817

邮箱:1572396657@qq.com

地址:成都高新西区西芯大道4号

  • 学到牛牛在线咨询

    扫一扫,免费咨询

  • 学到牛牛公众号

    微信公众号

学一流技术,找高薪工作

7-24小时服务热线:

028-61775817

版权声明 网站地图

蜀ICP备2021001672号

课程问题轻松问