历史上的今天
返回首页

历史上的今天

今天是:2026年02月02日(星期一)

正在发生

2023年02月02日 | 分享几种管理C程序中标志位的方法

2023-02-02 来源:zhihu

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


当然,如果仅仅是几个标志,直接拿个uint8_t的整形来进行标识也不会影响什么,但如果特别多的话似乎就比较废RAM了。然而,为了更好的管理这些标志位等,有个如下几种方式供大家更好的管理这些标志位 :


1


位域直接标识


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


 1typedef union _tag_SystemFlag

 2{

 3    uint16_t all;

 4    struct 

 5    {

 6        uint16_t Run         :1;

 7        uint16_t Alarm       :1;

 8        uint16_t Online      :1;

 9        uint16_t TimerOver   :1;

10        uint16_t Reserver    :12;

11    }bit;

12

13} uSystemFlag;

14

15uSystemFlag  unSystemFlag;

16

17int main(int argc, char *argv[]) {

18

19    unSystemFlag.all = 0x00; //系统标志清除

20

21    unSystemFlag.bit.Run       = 1; //置位

22    unSystemFlag.bit.Alarm     = 1;

23    unSystemFlag.bit.Online    = 1;

24    unSystemFlag.bit.TimerOver = 1;

25

26    unSystemFlag.bit.Run       = 0; //清零

27    unSystemFlag.bit.Alarm     = 0;

28    unSystemFlag.bit.Online    = 0;

29    unSystemFlag.bit.TimerOver = 0;

30

31    return 0;

32}

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


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


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


2


枚举+移位


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


 1typedef enum _tag_Flag {

 2cEmRun = 0,

 3cEmAlarm,

 4cEmOnline,

 5cEmTimerOver

 6}emSystemFlag;

 7

 8uint16_t SystemFlag ;

 9//置位

10void SetFlag(emSystemFlag flag)

11{

12    SystemFlag |=  ((uint16_t)0x01) << flag;

13}

14//清除

15void ClrFlag(emSystemFlag flag)

16{

17    SystemFlag &=  ~(((uint16_t)0x01) << flag);

18}

19//获得状态

20uint8_t  GetFlag(emSystemFlag flag)

21{

22    return (((SystemFlag & (((uint16_t)0x01) << flag)) != 0)? true:false);  

23}

24

25int main(int argc, char *argv[]) {

26

27    SetFlag(cEmAlarm);

28

29    if(GetFlag(cEmAlarm) == true)

30    {

31        printf("ClrFlagrn");

32        ClrFlag(cEmAlarm);

33    }

34    else

35    {

36        printf("SetFlagrn");

37        SetFlag(cEmAlarm);

38    }

39    return 0;

40}

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


3


宏列表


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


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


 1#include

 2#include

 3

 4typedef unsigned char uint8_t;

 5typedef unsigned int uint16_t;

 6typedef signed char int8_t;

 7typedef int  int16_t;

 8

 9#define true  1

10#define false 0

11

12

13//宏列表

14#define TAG_LIST(tag)

15tag(Run)

16tag(Alarm)

17tag(Online)

18tag(TimerOver)

19

20

21//枚举处理

22#define DEFINE_TAG(_tag) _tag,

23enum Flag {

24None = 0,

25TAG_LIST(DEFINE_TAG)

26EmMAX

27};

28#undef DEFINE_TAG

29

30//位定义变量

31uint16_t SysFlag = 0x0000;

32

33

34//通用方法定义

35uint8_t GetFlags(uint16_t mask)

36{

37    return ((SysFlag & mask) != 0)? true:false;

38}

39

40void SetFlags(uint16_t mask)

41{

42     SysFlag |=  mask;

43}

44

45void ClrFlags(uint16_t mask)

46{

47     SysFlag &=  ~mask;

48}

49

50

51//自动生成三类函数定义

52#define FLAG_Operater(flag)

53uint8_t  get##flag()  {

54return GetFlags(1 << flag);

55}

56void set##flag() {

57SetFlags(1 << flag);

58}

59void clr##flag() {

60ClrFlags(1 << flag);

61}

62

63//反向函数关联

64TAG_LIST(FLAG_Operater)

65

66int main(int argc, char *argv[]) {

67

68    setRun();

69    setAlarm();

70

71    if(getAlarm() == true)

72    {

73        printf("set rn");

74    }

75    else

76    {

77        printf("clr rn");

78    }

79

80    return 0;

81}

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


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


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


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


推荐阅读

史海拾趣

ETC2公司的发展小趣事

为了拓展全球市场,ETC2公司制定了国际化战略。他们积极参加国际展览和交流活动,与国际同行建立了广泛的合作关系。同时,ETC2公司还针对不同国家和地区的市场特点,推出了定制化的ETC产品和服务。这些举措不仅帮助ETC2公司成功打入国际市场,也为其未来的发展奠定了坚实的基础。

CHIMEI(奇美)公司的发展小趣事

随着电子行业的快速发展,市场竞争也日益激烈。奇美公司深知创新是企业发展的核心动力,因此不断加大研发投入,推动技术创新和产品升级。公司研发团队积极探索新技术、新工艺和新材料的应用,推出了一系列具有创新性和竞争力的产品。这些产品不仅满足了客户的需求,还为公司开拓了新的市场领域。

启臣微(Chip)公司的发展小趣事

随着公司业务的不断发展,启臣微开始积极拓展海外市场。公司在西安设立研发中心,在深圳和昆山设立销售中心,并与多家国际知名企业建立了合作关系。这些举措不仅提升了公司的国际影响力,也为公司带来了更多的发展机遇。

Efficient Power Conversion公司的发展小趣事

随着EPC公司氮化镓技术的不断完善和成熟,其产品开始受到市场的广泛认可。无线电源传送、全自动汽车、高速移动通信等领域的企业纷纷采用EPC公司的氮化镓功率半导体器件,以提高其产品的性能和效率。EPC公司的市场份额逐渐扩大,成为氮化镓功率管理技术领域的领军企业。

GSG公司的发展小趣事
智能压力测量电路采用先进的传感器技术和数字信号处理算法,能够实现更高的测量精度。
德力西(DELIXI)公司的发展小趣事

进入新世纪后,德力西开始全国化布局和多元化发展。1998年,在上海注册成立了上海德力西集团有限公司,与西安高压电器研究所联合生产高压电器和成套设备。此外,德力西还积极参与国企改革,整体并购了杭州西子(集团)公司,并进入环保领域,建设垃圾处理及水处理项目。这些举措使德力西在电子行业的地位更加稳固。

问答坊 | AI 解惑

用CH36x通过Boot-ROM扩展BIOS的方案

用CH36x通过Boot-ROM扩展BIOS的方案…

查看全部问答>

C51按键程序设计

按键程序设计,可以处理加速键、一键多功能等情况 后面有P1口行列式键盘程序实例 设计按键程序的思路 1、一般都不在键盘程序中延时,而是通过隔一段时间调用(比如10毫秒)来扫描按键,以尽量释放CPU资源; 2、所有键盘程序基本上都是一个模 ...…

查看全部问答>

工业MP3焊接历程1-焊接遇到一个小问题

我在焊接的时候发现u2的30和31脚是在一根线上,是不是焊接时候焊接连线了呢?经过问soso,soso又问了古道热肠,原来是一根线上的。但是呢在原图纸中并不是在一根线上,是图纸错还是别的原因呢?图纸我已近标出,现附上。还有视频$(\'swf_ITY\').inn ...…

查看全部问答>

IGBT半桥逆变式电火花加工脉冲电源

IGBT半桥逆变式电火花加工脉冲电源…

查看全部问答>

把CXImage移植到wince6.0上出现严重错误

把CXImage移植到wince6.0上出现的问题 成功编译后,运行却出现严重错误,参考了网上的修改方法, 修改了stack Reserve size和stack Commit Size的大小,程序单独运行没有问题。 但是在重启机器调试过第一遍之后,以后调试都会出现conmanclient2. ...…

查看全部问答>

关于PPC软件的破解

最近花3万日元买了一套日本的PDA版的GPS软件,本来想和朋友分享来着,可好像这个软件加密了,在这问了好多人可也没人会,我想自己试试看. 这个软件破起来一定不会很难,怎么说呢. 这个软件是可复制地,复制多少回都可以,但是只能在最初安装的PDA上 ...…

查看全部问答>

问一个EVC开发环境的问题

我有两个机器,都是XP的。 两个机器装EVC+SP4都可以,使用EVC默认模拟器都可以运行。 后来为了运行中文程序,下载了eeworld上sunrain_hjb上传的CHSEmulator_SDK.msi。 (这里感谢一下sunrain_hjb) 两个机器安装以后,一个可以正常使用这个中文 ...…

查看全部问答>

FPGA的时序约束

本人最近在时序约束方面的东西,但看了一写资料是云里雾里,望高手能为小弟指点迷津.…

查看全部问答>

SD卡在单片机上的应用

   SD卡在单片机上的应用     SD卡在现在的日常生活与工作中使用非常广泛,时下已经成为最为通用的数据存储卡。在诸如MP3、数码相机等设备上也都采用SD卡作为其存储设备。SD卡之所以得到如此广泛的使用,是因为它价格 ...…

查看全部问答>