共享几个我用了很久的宏

2019-10-16 01:55发布

我的与或非等等各种逻辑计算一直不好,以前学51单片机的时候每次要配置寄存器的时候,虽然都知道看着器件手册去写,
但是我还是觉得很痛苦,
后来就有了这些宏,阅读起来会直观一点,不过敲的代码也得多一点,我后来写的代码都是用这些宏来封装寄存器操作的。

第一个比较好理解的 例如 BIT(0) == 0X01, BIT(1) == 0X02:
#define BIT(x)     (0x01L<<(x))
#define Bit    BIT

第二个:
#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 SetBit(type, flag, which_bit)   ((flag) | ((type)which_bit))
这个是用来置位一个变量的某几个位的。
type是用来指明类型的,是因为在另外一个宏用的时候会有安全问题(我的意思是不加可能会出错),为了统一风格,所以这里也有这个。然后就得几敲几个字了。。。
比如:u32 a = 0;
那么 a = SetBit(u32, a, Bit(0) |Bit(4));  的结果会是 a == 0x00000011;  就是把0号位(第1个位为0号位)和4号位置1了。

第四个:
#define AssignBit(type ,flag , which_bit, val) ((((type)which_bit)&((type)val)) | ((flag)&(~((type)which_bit))))
一开始写这个宏的时候是没有type这个参数的,type是从通用性和安全上考虑的,不过又要多打几个字,不爽,如果C/C++能够支持typeof操作符就好了 
(注:用C++的template的语法特性也是可以解决这个问题的,附件里的template.h是我后来拓展到C++的方式,用C++开发STM32的比较少见,
只是个人喜欢罢了,如果有人有兴趣,再单独开帖讨论)
比如说我要把变量u8 a 的 6号位到4号位的值设置为 101,我就可以
a = AssignBit(u8, a, BitFromTo(6, 4), BIT(6) | BIT(4));
这样多直观呀,
本来是要这样写的,不仅不好懂,还容易出错
a &= 0x8f;
a |= 0x50;

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

然后是这个
#define BByte(b) (     
(((( 0x##b##L )   ) & 1 )<<0) | 
(((( 0x##b##L ) >>4  ) & 1 )<<1) | 
(((( 0x##b##L ) >>8  ) & 1 )<<2) | 
(((( 0x##b##L ) >>12 ) & 1 )<<3) | 
(((( 0x##b##L ) >>16 ) & 1 )<<4) | 
(((( 0x##b##L ) >>20 ) & 1 )<<5) | 
(((( 0x##b##L ) >>24 ) & 1 )<<6) | 
(((( 0x##b##L ) >>28 ) & 1 )<<7)  
)
这个是用来写一个最多8位的二进制变量的,16位,32位也可以,附件里面有相关的宏,位数多了阅读性不太好。
比如:BByte(01011010) == 0x5a

接下来是:
#define AssignBitSection(type, var, LSB, length, value)  AssignBit(type, var, ((1<<(length)) - 1)*LSB, (value)*LSB)
比如:我要把寄存器a的 [7:4] 位赋值为 0101b
a = AssignBitSection(u32, a, 4, 4, BByte(0101));   偶尔会有寄存器是连在一起的几个用来配置某些功能的嘛。

然后。。。好像也就这么多了。
写个注意吧,C语言的宏也是有它的问题的,多次求值的问题,
比如: a = AssignBit(u32, a++, Bit(5)|Bit(6), Bit(5) );   如果你把它当成函数看,严重性就比较大了!!!这个是基础的语法问题,我不作解释。

为了解决这些问题,使用 __inline 不仅保证了效率,同时解决了多次求值的问题。但是我没作这个拓展。
我是以这些为基础,用C++去拓展的,刚才也说了,附件里的template.h里的内容就是我后来拓展到C++的方式,
只是个人喜欢罢了,我现在对于C++还遇到一些没有解决的问题,如果有人有兴趣,再单独开帖讨论。

附件里面的macro_function.h就是这个帖子讲的宏的源代码,然后其它的几个文件,是使用这些宏来封装寄存器的例子,
有部分已经用开始用C++的方式拓展了,有此地方和C是不兼容的,如果想拿来用,麻烦自己删改了,如果只是看看,忽略它们即可。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
29条回答
lin772662623
1楼-- · 2019-10-17 11:04
Pony279
2楼-- · 2019-10-17 15:04
回复【13楼】Pony279:
---------------------------------

今天偶然发现STM32的汇编指令里有这些
Bit field clear 
BFC Rd, #lsb, #width 
Bit field insert 
BFI Rd, Rn, #lsb, #width 
Signed bit field extract
SBFX Rd, Rn, #lsb, #width
Unsigned bit field extract
UBFX Rd, Rn, #lsb, #width

其中BFI和上面的AssignBitField好相似,可是,BFI指令的效率比我这个C语言写的一大堆与或非高多了!!!

要是能把这些结合进来多好呀。。。

另外还发现结构体可以这样,为每个位专门定义的,对结构体成员的操作的语句编译出来的汇编代码就是利用了上面的语句。。。
这些对寄存器操作都很有好处,可是,要利用这些东西把寄存器操作封装起来的话,好像比较困难。不知有没有什么好的办法
struct B
{
u8 b0 :1;
u8 b1 :1;
u8 b2 :1;
u8 b3 :1;
u8 b4 :1;
u8 b5 :1;
u8 b6 :1;
u8 b7 :1;
};
Pony279
3楼-- · 2019-10-17 16:47
 精彩回答 2  元偷偷看……
ilikerome
4楼-- · 2019-10-17 22:19
像楼主学习
zenghi
5楼-- · 2019-10-17 23:07
现在再细看还是有许多值得学习的地方,例如##,#的用法。再顶。
Pony279
6楼-- · 2019-10-18 01:01
回复【18楼】zenghi:
---------------------------------

#define BByte
这个确实用着很方便, 算是我使用频率最高的一个了
不过在C语言环境下如果使用者填的数超过8位, 结果就不像预期的那样了
而这种情况在 C++  环境下是可以利用模板加上 static_assert 在编译期完全避免的, 只要使用者填的数不符合要求, 就不能编译通过.

一周热门 更多>