正文
在嵌入式开发中难免会涉及到非常多的标志位处理,特别是玩单片机、裸机开发的朋友,比如跟一些模块配合联调会遇到各种信号是否到位、成功等等状态,而这些信号大多都是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}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
但如代码中这样的操作方式在语句或语义表达上还是不够直观。
bug菌经常谈到,代码可以不写注释,不过你的每个变量、函数名称等需要足够的直观,所以很多朋友习惯把这些标志封装起来。
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("ClrFlag\r\n");
32 ClrFlag(cEmAlarm);
33 }
34 else
35 {
36 printf("SetFlag\r\n");
37 SetFlag(cEmAlarm);
38 }
39 return 0;
40}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
3.宏列表
或许这里才是本文的重中之重~
以前跟大家介绍过,用宏自动化的生成各种代码片段,以使得代码更加的紧凑。当然可读性会相对降低一点,但对于重复性代码就不需要太多考虑了。
如果以前有过类似代码处理的朋友,应该看这段代码还是比较轻松的吧,如果有点生疏,可以一层一层展开了解。
1#include <stdio.h>
2#include <stdlib.h>
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 \r\n");
74 }
75 else
76 {
77 printf("clr \r\n");
78 }
79
80 return 0;
81}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
其主要的功能就是通过宏替换和代码拼接符号,自动的生成通用的代码片段,这样做的好处就是不再需要我们在代码中定义一大堆setflag、clrflag、getflag等函数。
通过上面的代码当我们向TAGLIST宏中添加一个标识符,即可生成一系列相关的操作函数等。
这样一方面可以及简化代码,同时也避免一些人工编码带来的错误。