寄存器操作,位与位或?你OUT啦~

2020-02-04 09:25发布

呵呵,和大家分享几个我用了很久的宏,
菜鸟表示刚刚才用STM32点了个灯,看着例程代码还是有点辛苦,主要是我比较笨吧,什么位与又什么位或的,
还好在学习51的时候写了一些宏,改了一下,代码读起来才感觉舒服一些,心情也好了一点
心情好的时候我就分享~

第一个:
#define BIT(x)        (0x01L<<(x))
这个宏是后面使用的基础,很好理解,其实这个宏是抄的,我一开始的定义是
#define BIT0 0x01L
#define BIT1 0X02l
...

第二个:
#define BitFromTo(h, l)        ((h > l)? ((BIT(h) - BIT(l) + BIT(h))): (BIT(l) - BIT(h) + BIT(l)) )
打个比方,BitFromTo(3, 0) 的结果就是 0x0f
BitFromTo(7, 4)的结果是0xf0

第三个:
#define AssignBit(type ,flag , which_bit, val)        ((((type)which_bit)&((type)val)) | ((flag)&(~((type)which_bit))))
一开始写这个宏的时候是没有type这个参数的,type是从通用性和安全上考虑的,不过又要多打几个字,不爽,如果C/C++能够支持typeof操作符就好了(自己分析,这里不详细写,在帖子的最后我会提供我代码里的全部注释)
比如说我要把变量u8 a 的 6号位到4号位的值设置为 101,我就可以
a = AssignBit(u8, a, BitFromTo(6, 4), BIT(6) | BIT(4));
这样多直观呀,
本来是要这样写的,不仅不好懂,还容易出错
a &= 0x8f;
a |= 0x50;

第四个
#define SetBit(type, flag, which_bit)                        ((flag) | ((type)which_bit))
这个好理解,如果要把变量a的1号位转置1的话就
a = SetBit(u8, a, BIT(1));

第五个
#define ClearBit(type, flag, which_bit)                        ((flag) & (~((type)which_bit)))
呵呵,这个是用来把一个变量的某些位清零的
比如把变量u8 a = 0xf4;的2号位清零
a = ClearBit(u8, a, BIT(2));
现在 a == 0xf0 了

最后说一下优缺点,
最大的优点就是可读性好,
最大的缺点就是打的字会比较多,每次要加个类型作为参数,不过在我的眼里,可读性更重要

有网友可能会提到效率问题,
这几个宏没有效率问题!如果参数which_bit和val 是常数表达式(我用到的大部分场合都是这样的),编译器是会自动优化的,不信你可以反汇编看看。如果都是变量,编译器不能优化,但是就算是手动写代码效率也是一样的。




最后附上全部代码以及说明:
/*********************************************************************************
*        AssignBit / AssignFlag
*说明:
*        这个宏用来得到一个变量的某个或某几个位被赋值后的值
*参数:
*        type        参数flag的类型
*        flag        要赋值的变量
*        which_bit       
*                        标明flag的哪几个位要被赋值
*        val                表示要赋给flag的那几个位的值
*注意:
*        1. 对于宏,其参数不要用++i;之类的表达式!也不要直接使用调用函数得到的返回值!
*        2. 如果type是寄存器,那么它必须是可读的
*示例:
*        u8 a = 0xf0;
*        AssignBit(u8, a, BIT4 | BIT0, BIT0);
*        执行完后,a == 0xe1;
*备注:
*        1. 效率上, 如果参数(除flag外)都是常量, 编译器会自动优化, 如果参数是变量, 就算手动写代码也是这个样子的
*        2. (which_bit) 括号是为了保证 AssignBit(flag, BIT0 | BIT1, 0)之类的用法不会出错
*********************************************************************************/
#define AssignBit(type ,flag , which_bit, val)                                       
                                                ((((type)which_bit)&((type)val)) | ((flag)&(~((type)which_bit))))               
#define AssignFlag                        AssignBit
#define AssignB                        AssignBit


/*********************************************************************************
//说明:
//        得到一个变量某几位位被置位后的值
//注意:
//        1. 如果flag是寄存器,必须是可读的
*********************************************************************************/
#define SetBit(type, flag, which_bit)                        ((flag) | ((type)which_bit))
#define SetB        SetBit



//说明:
//        清零位,flag变量中which_bit标明的位被清0
//注意:
//        1. flag必须是可以正常读写的
#define ClearBit(type, flag, which_bit)                        ((flag) & (~((type)which_bit)))
#define ClearB                ClearBit


//说明:
//        检查flag中的某一位是否被置位, 如果被置位, 则宏的值为非0(不一定是1), 如果没有被置位, 则宏的值为0
//注意:
//        1. flag必须是可以读的
#define CheckBit(type, flag, which_bit)                        ( (flag) & ((type)which_bit) )

#define BIT(x)        (0x01L<<(x))

//说明:
//        得到一个指定范围的BIT被置位的值
//        BitFromTo(3, 0)        会得到 0x0f
//备注:
//        1. 这个宏是为提高可读性才写的,对于常数参数是表达式的,编译器会自动优化。
#define BitFromTo(h, l)                                                        ((h > l)? ((BIT(h) - BIT(l) + BIT(h))): (BIT(l) - BIT(h) + BIT(l)) )
#define BitF2                BitFromTo
#define BitFT                BitFromTo
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
47条回答
huilai
1楼-- · 2020-02-07 17:27
mark
Pony279
2楼-- · 2020-02-07 20:32
经过9楼到26楼的讨论,
用mask宏来实现在C语言里写二进制是没有问题的,好像宏的名字叫_b()会有一种标准的感觉,哈哈

再说一个东东
在照着datasheet写程序的时候,经常会遇到的情况是说
一个寄存器的某连续的两位有什么功能,某连续的三位又能设置什么
这样搞起来还真麻烦,因为我设置的时候总是难免要进行一些移位呀什么的,那我是不是可以用一个宏,来得到这几个连续位的低地址,(叫一个位段的首地址怎么样)
比如一个8位寄存器r的6到7位是设置xxx的模式的,那么长度就是2,那么我定义
#define xxxmode BIT(6)
再想办法用一个宏来解决模式的设置
r = AssignBitSection( r, xxxmode, 2, mask(10))
名字写复杂了点,当然可以简化的了。。。
理论上这几个参数完全是可以的,至于实现。。。我先去刷牙先~
p7885572
3楼-- · 2020-02-08 00:55
有创意,确实很直观,
不过把16进制当2进制使用,感觉会有点混乱呀
主要问题是如果是16位的,32位的,写起来可能会比较辛苦了

BIT(x)
用这种宏定义的原因是看datasheet的时候总是有说一个寄存器的几号位有什么功能,几号位又有什么功能
所以就直接用BIT(x),边看datasheet边写代码
结合就SetBit, AssignBit, ClearBit ...
zzz1367
4楼-- · 2020-02-08 01:07
 精彩回答 2  元偷偷看……
wtiechen1969
5楼-- · 2020-02-08 06:16
mark
Pony279
6楼-- · 2020-02-08 07:47
回复【33楼】Pony279 霍斯
-----------------------------------------------------------------------

这种方式已经测试通过啦

这个是定义
//说明:
//        得到变量的一个位段被赋值后的值,这人宏不会改变变量的值
//参数:
//        type        变量var的类型
//        var                变量
//        LSB                该位段的最低位
//        len                位段的长度,最大值为8
//        val                位段的值
//注意:
//        如果var是一个寄存器,那么它必须是正常可读的
#define AssignBitSection(type, var, LSB, length, value)                AssignBit(type, var, ((1<<(length)) - 1)*LSB, (value)*LSB)
#define AssignBS        AssignBitSection

测试的代码截个图来吧,是STM32的寄存器设置的,单片机都差不多,都是一堆寄存器,看datasheet:
看看返汇编,其实我不太懂汇编,不过看简写,好像是两条从内存到通用寄存器指令,两条运算指令(位与位或,必须的),两条从通用寄存器到内存的指令(存储结果),这六条指令都是必须的
足以证明,宏展开来的代码是很长的,但是编译器是会自动优化的



一周热门 更多>