OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 11734|回复: 29

共享几个我用了很久的宏

[复制链接]

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
发表于 2012-3-8 19:15:35 | 显示全部楼层 |阅读模式
我的与或非等等各种逻辑计算一直不好,以前学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是不兼容的,如果想拿来用,麻烦自己删改了,如果只是看看,忽略它们即可。

MYLIB.zip

8.07 KB, 下载次数: 934

https://github.com/roxma
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-8 19:25:54 | 显示全部楼层
然后再来推荐一种我用宏辅助来调试的方式:
做一个头文件 debug.h
里面这样定义:

#ifndef N0_DEBUG
#define MYDEBUG
#endif

#include "USART.H"
#ifdef MYDEBUG
//注意:这里的每一个宏定义在#else中必需要有一个对应的宏定义!!!
#define DB_SendString SendString
#else
//直接定义为空即可
#define DB_SendString
#endif

比较短,我只是为了表达意思。
比如:我现在在调试一个新硬件,那么我就在那个模块的.c文件里加上这个头文件
#include "debug.h"
然后,当我需要发送串口调试信息的时候,使用DB_SendString而不是SendString
当我这个新的模块调试结束的时候,在包含头文件之前加个定义,这个定义只会取消这个模块的调试,而又不影响其他模块。
#define N0_DEBUG
#include "debug.h"

这样就搞定了,
手动一行一行删除代码是很痛苦的,而且,当我又需要重新调试的时候,又要重新写一遍,
而用上面的方式可以很好的解决这个问题。
https://github.com/roxma
回复 支持 反对

使用道具 举报

20

主题

187

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
288
金钱
288
注册时间
2011-12-14
在线时间
0 小时
发表于 2012-3-8 20:30:01 | 显示全部楼层
收下,慢慢地学,谢谢楼主!
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165524
金钱
165524
注册时间
2010-12-1
在线时间
2116 小时
发表于 2012-3-8 20:43:50 | 显示全部楼层
不错,帮顶.
回复 支持 反对

使用道具 举报

27

主题

274

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
472
金钱
472
注册时间
2011-11-2
在线时间
11 小时
发表于 2012-3-8 21:24:42 | 显示全部楼层
好呀,一直想用C++编程只可惜一直没时间入门。
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-8 21:51:35 | 显示全部楼层
回复【5楼】zenghi:
---------------------------------
C++编程不难,会C基本上已经算是入门了,有些地方使用C++会更方便一些。
已经会C的学C++,主要需要了解的就是类与对象(其实这个和结构体很相似),
至于继承多态,也不难,但是在小程序里用的不多,可以慢慢来。
还有一个就是模板,C++里面很重要的一个语法特性,是用来减少书写的代码量的。
https://github.com/roxma
回复 支持 反对

使用道具 举报

39

主题

597

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
2115
金钱
2115
注册时间
2011-9-3
在线时间
121 小时
发表于 2012-3-8 23:08:04 | 显示全部楼层
谢谢分享!
回复 支持 反对

使用道具 举报

12

主题

216

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
313
金钱
313
注册时间
2011-4-7
在线时间
3 小时
发表于 2012-3-10 00:33:19 | 显示全部楼层
mark
回复 支持 反对

使用道具 举报

48

主题

376

帖子

0

精华

高级会员

Rank: 4

积分
621
金钱
621
注册时间
2011-3-18
在线时间
7 小时
发表于 2012-3-10 10:18:00 | 显示全部楼层
good
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-13 12:48:48 | 显示全部楼层
我发现STM32的3.5版的官方库也有这些宏的,哈哈,这种实现和我早期在51上的实现是一样的
因为后来我觉得在宏内部不应该对变量赋值,
(学JAVA的时候受到讲师一句话的影响,在讲函数设计思想的时候,说函数应该做些什么,不应该做些什么,"这样的代码的设计思想有问题")
所以后来全部重写了
想起一句话,
"如有雷同,你抄我的~",呵呵,不过人家3.5版的库发布的时候我还不怎么会51呢。。。所以还得算是我抄他们的。。。

#define SET_BIT(REG, BIT)     ((REG) |= (BIT))

#define CLEAR_BIT(REG, BIT)   ((REG) &= ~(BIT))

#define READ_BIT(REG, BIT)    ((REG) & (BIT))

#define CLEAR_REG(REG)        ((REG) = (0x0))

#define WRITE_REG(REG, VAL)   ((REG) = (VAL))

#define READ_REG(REG)         ((REG))

#define MODIFY_REG(REG, CLEARMASK, SETMASK)  WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
https://github.com/roxma
回复 支持 反对

使用道具 举报

3

主题

33

帖子

0

精华

初级会员

Rank: 2

积分
66
金钱
66
注册时间
2012-2-2
在线时间
0 小时
发表于 2012-3-13 13:07:11 | 显示全部楼层
楼主强悍
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-15 14:50:49 | 显示全部楼层
刚刚在封装STM32的USART的寄存器,写的时候没有留意太多,
因为有些寄存器是连续几位统一表示一个值的,然后我写的时候就随手用了BitFromTo。。。
然后。。。我发现当我想设置寄存器的时候我居然没有配套的宏来操作这东西。。。
我定义了这个。。。
enum USART_CR2{
...
, STOP = BitFromTo(13, 12) // stop bits
...
};
enum StopBits{
_1StopBit = BByte(00),    //一个停止位
...
}
虽然我的代码里有assignbs和assignb,但是都不适合用这些来解决,

写好的代码。。。我可不想删啊。。。再写个函数来支持这个吧,反正也能方便以后的寄存器封装
这个是用C++模板的方式写的,测试通过了

//说明:
//得到变量的连续几个位被赋值后的值,这个函数本身不会改变传入的变量的值
//参数:
// flag 变量
// bits 连续的几个位
// val 那几个位要被赋的值
//注意:
// val 不需要预先移位,这个函数和assignb的使用不同。
//示例:
// u16 a = 0; a = assignbs(a, BitFromTo(11, 4), 0x5a); //那么执行完后,a = 0x05a0;
template<class Flag_Type, class BITS, class V>
finline Flag_Type assignbs(Flag_Type flag, BITS bits, V val)
{ return assignb(flag, bits, (((Flag_Type)val)*((Flag_Type)( ((bits<<1)|bits) -(bits<<1) )))); }
https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-15 15:17:40 | 显示全部楼层
回复【12楼】Pony279:
---------------------------------
{ return assignb(flag, bits, (((Flag_Type)val)*((Flag_Type)( ((bits<<1)|bits) -(bits<<1) )))); }


这个算法貌似使用异或效果可能会更好一点呢,在编译器没法自动优化的时候,逻辑运算应该比加减要快一点吧。
{ return assignb(flag, bits, (((Flag_Type)val)*((Flag_Type)( ((bits<<1)|bits) ^ (bits<<1) )))); }

里面的
( (bits << 1) | bits) ^ (bits<<1))
是想得到连续的几个位中的最低位,
比如如果bits == BByte(00111000);
那么上面的式子到的就是
BByte(00001000)
https://github.com/roxma
回复 支持 反对

使用道具 举报

3

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
52
金钱
52
注册时间
2011-12-28
在线时间
0 小时
发表于 2012-3-21 14:26:51 | 显示全部楼层
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-29 19:12:13 | 显示全部楼层
回复【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;
};
https://github.com/roxma
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-3-29 19:25:06 | 显示全部楼层
算了,我没必要去优化了,不得不赞叹,MDK的编译器好强大,
直接被优化了,看图



我把优化等级设置到了最高。
其中的gpioa的读写操作是为了不让编译器把我的变量a 和 变量b给优化掉,
然后。。。assignf (就是上面的AssignBitField)居然就这样被优化成了一条语句!!!BFI指令!

MDK好强大!!!赞!


PS: 代码是用C++写的。。。所以不要觉得语法古怪。。。

https://github.com/roxma
回复 支持 反对

使用道具 举报

25

主题

163

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
443
金钱
443
注册时间
2012-4-29
在线时间
38 小时
发表于 2012-12-12 08:44:43 | 显示全部楼层
像楼主学习
回复 支持 反对

使用道具 举报

27

主题

274

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
472
金钱
472
注册时间
2011-11-2
在线时间
11 小时
发表于 2012-12-12 21:47:28 | 显示全部楼层
现在再细看还是有许多值得学习的地方,例如##,#的用法。再顶。
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-12-14 12:09:06 | 显示全部楼层
回复【18楼】zenghi:
---------------------------------

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

使用道具 举报

27

主题

274

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
472
金钱
472
注册时间
2011-11-2
在线时间
11 小时
发表于 2012-12-14 20:08:25 | 显示全部楼层
回复【19楼】Pony279:
---------------------------------
明天到公司玩玩看。C环境有啥问题,超过32位会提示不,再加个LL?明天实践下,这电脑纯娱乐。
回复 支持 反对

使用道具 举报

11

主题

56

帖子

0

精华

初级会员

Rank: 2

积分
120
金钱
120
注册时间
2012-12-8
在线时间
0 小时
发表于 2012-12-14 21:03:40 | 显示全部楼层
学习了
回复 支持 反对

使用道具 举报

36

主题

1105

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2200
金钱
2200
注册时间
2012-2-8
在线时间
35 小时
 楼主| 发表于 2012-12-15 18:22:23 | 显示全部楼层
回复【20楼】zenghi:
---------------------------------
定义里只考虑到了 8 位的情况
#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)  \
)

如 BByte(10101010)
在宏里面会先转成 0x10101010, 再一位一位的提取最后组合成 0xAA

超过 8 位的部分会被直接忽略掉

在宏里面加多几句支持 16 位可能是可以的, 主要是不知道转出来的 0x 10101010 10101010 能不能编译器都认识. 
不过写那么长的二进制也不好读, 所以干脆就没加上去了 ...
https://github.com/roxma
回复 支持 反对

使用道具 举报

1

主题

15

帖子

0

精华

新手上路

积分
39
金钱
39
注册时间
2012-12-16
在线时间
0 小时
发表于 2012-12-31 15:35:56 | 显示全部楼层
学习学习
回复 支持 反对

使用道具 举报

5

主题

100

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
361
金钱
361
注册时间
2012-8-10
在线时间
40 小时
发表于 2013-1-9 18:18:23 | 显示全部楼层
学习
回复 支持 反对

使用道具 举报

4

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
51
金钱
51
注册时间
2013-3-4
在线时间
1 小时
发表于 2013-5-2 11:35:21 | 显示全部楼层
最喜欢看这种贴
实践出真知
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2013-1-4
在线时间
2 小时
发表于 2013-5-2 15:21:30 | 显示全部楼层
楼主大牛,学习经验
回复 支持 反对

使用道具 举报

3

主题

15

帖子

0

精华

新手上路

积分
47
金钱
47
注册时间
2011-6-2
在线时间
0 小时
发表于 2015-5-15 09:09:04 | 显示全部楼层
谢谢楼主分享。
回复 支持 反对

使用道具 举报

0

主题

4

帖子

0

精华

新手入门

积分
33
金钱
33
注册时间
2015-8-11
在线时间
1 小时
发表于 2016-9-19 16:38:37 | 显示全部楼层
好好啊好啊好
回复 支持 反对

使用道具 举报

10

主题

147

帖子

0

精华

高级会员

Rank: 4

积分
602
金钱
602
注册时间
2015-7-11
在线时间
94 小时
发表于 2016-12-14 19:28:54 | 显示全部楼层
谢谢分享!!!
回复 支持 反对

使用道具 举报

10

主题

147

帖子

0

精华

高级会员

Rank: 4

积分
602
金钱
602
注册时间
2015-7-11
在线时间
94 小时
发表于 2016-12-14 22:11:38 | 显示全部楼层
谢谢分享!!!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-26 08:42

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表