在嵌入式开发中难免会涉及到非常多的标志位处理,特别是玩单片机、裸机开发的朋友,比如跟一些模块配合联调会遇到各种信号是否到位、成功等等状态,而这些信号大多都是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 ClrFlag(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
声明:转载此文是出于传递更多信息之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与本网联系,我们将及时更正、删除,谢谢。