分享几种管理C程序中标志位的方法

发布者:chenfengy818最新更新时间: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宏中添加一个标识符,即可生成一系列相关的操作函数等。


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


关键字:标志位  位域 引用地址:分享几种管理C程序中标志位的方法

上一篇:动图演示UART、SPI、 I2C等串行通信的底层原理
下一篇:电流检测电路

推荐阅读最新更新时间:2024-11-13 16:46

stm32串口的flag和it标志
今天用到stm32的串口,对串口进行配置进行接受数据。之后大家也可以想象到结果是什么,完全没有用哎! 后来慢慢的发现了问题的所在,现做笔记如下: ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT); USART_IT参数: #define USART_IT_PE ((uint16_t)0x0028) #define USART_IT_TXE ((uint16_t)0x0727) #define USART_IT_TC ((uint16_t)0x0626
[单片机]
stm32 USART中断标志什么时候清除
USART_ClearITPendingBit(USART2,USART_IT_RXNE); 我放在中断处理函数里 怎么没有效果? 答: RXNE标志不能直接清除,读取USART- DR(就是读取接收到的数据)来清除RXNE标志。
[单片机]
stm32入门笔记(二)DMA接受中断的问题
在32的点子哥的例程当中,我发现DMA的例程,竟然是没有写DMA的接受的中断问题,这是一件让人很烦恼的问题。 于是我想,能不能自己写一个DMA的接受中断。 然后有了这样的一段中断函数的代码: void DMA1_Channel2_IRQHandler(void) {undefined if(DMA_GetITStatus(DMA1_FLAG_TC3)==SET) {undefined DMA_ClearFlag(DMA1_FLAG_GL3); } } 那么问题就会出现啦,你会发现,标志位总是没有置位。 那么为什么了? 因为DMA的发送是空闲中断,标志位。 而接受中断标志位则是
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved