打印
[技术问答]

关于时钟配置 宏定义 理解---请高手指点

[复制链接]
2201|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
背水一战|  楼主 | 2017-9-27 18:30 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
M0 240中时钟配置中宏定义理解
有看过的来说说

#define MODULE_APBCLK(x)        (((x) >>30) & 0x3)    /*!< Calculate APBCLK offset on MODULE index, 0x0:AHBCLK, 0x1:APBCLK, 0x2:APBCLK1 */   

--------其中AHBCLK,APBCLK,APBCLK1 为三个寄存器,这个宏定义是指定三个寄存器?
我理解是这个宏定义是指的哪一位吧? x=0,x=1 代表0位,1位

我初学者,高手搞这些宏定义,超级难懂,没有任何解释,很难理解!
沙发
wahahaheihei| | 2017-9-27 18:44 | 只看该作者
库函数中宏多的很。。

使用特权

评论回复
板凳
wahahaheihei| | 2017-9-27 18:44 | 只看该作者
不懂的就不要问,就简单的看成后面的可以实现前面的内容就行了。

使用特权

评论回复
地板
huahuagg| | 2017-9-27 20:52 | 只看该作者
只看到了一个数而已。。怎么出来三个寄存器。莫非这个数是赋值给三个寄存器的。

使用特权

评论回复
5
wanduzi| | 2017-9-27 22:00 | 只看该作者
宏定义是C语言提供的三种预处理功能的其中一种,这三种预处理包括:宏定义、文件包含、条件编译。宏定义和操作符的区别是:宏定义是替换,不做计算,也不做表达式求解。

使用特权

评论回复
6
wanduzi| | 2017-9-27 22:01 | 只看该作者
宏定义又称为宏代换、宏替换,简称“宏”。

格式:
#define 标识符 字符串
其中的标识符就是所谓的符号常量,也称为“宏名”。
预处理(预编译)工作也叫做宏展开:将宏名替换为字符串。
掌握"宏"概念的关键是“换”。一切以换为前提、做任何事情之前先要换,准确理解之前就要“换”。
即在对相关命令或语句的含义和功能作具体分析之前就要换:
例:
#define Pi 3.1415926
把程序中出现的Pi全部换成3.1415926
说明:
(1)宏名一般用大写
(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。例如:数组大小常用宏定义
(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
(4)宏定义末尾不加分号;
(5)宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
(6)可以用#undef命令终止宏定义的作用域
(7)宏定义允许嵌套
(8)字符串( " " )中永远不包含宏
(9)宏定义不分配内存,变量定义分配内存。
(10)宏定义不存在类型问题,它的参数也是无类型的。[2]
带参数
除了一般的字符串替换,还要做参数代换
格式:
#define宏名(参数表) 字符串
例如:#define S(a,b) a*b
area=S(3,2);第一步被换为area=a*b; ,第二步被换为area=3*2;
类似于函数调用,有一个哑实结合的过程:
(1)实参如果是表达式容易出问题
#define S(r) r*r
area=S(a+b);第一步换为area=r*r;,第二步被换为area=a+b*a+b;
正确的宏定义是#define S(r) ((r)*(r))
(2)宏名和参数的括号间不能有空格
(3)宏替换只作替换,不做计算,不做表达式求解
(4)函数调用在编译后程序运行时进行,并且分配内存。宏替换在编译前进行,不分配内存
(5)宏的哑实结合不存在类型,也没有类型转换。
(6)宏展开使源程序变长,函数调用不会
(7)宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值)

使用特权

评论回复
7
wanduzi| | 2017-9-27 22:02 | 只看该作者
#define用法
1、 用无参宏定义一个简单的常量
#define LEN 12
这个是最常见的用法,但也会出错。
比如下面几个知识点你会吗?可以看下:
(1)#defineNAME "zhangyuncong"
程序中有"NAME"则,它会不会被替换呢?
(2)#define 0x abcd
可以吗?也就是说,可不可以用不是标识符的字母替换成别的东西?
(3)#define NAME "zhang
这个可以吗?
(4)#define NAME "zhangyuncong"
程序中有上面的宏定义,并且,程序里有句:
NAMELIST这样,会不会被替换成"zhangyuncong"LIST
四个题答案都是十分明确的。
第一个,""内的东西不会被宏替换。这一点应该大家都知道。
第二个,宏定义前面的那个必须是合法的用户标识符
第三个,宏定义也不是说后面东西随便写,不能把字符串的两个""拆开。
第四个:只替换标识符,不替换别的东西。NAMELIST整体是个标识符,而没有NAME标识符,所以不替换。
也就是说,这种情况下记住:#define第一位置第二位置
(1) 不替换程序中字符串里的东西。
(2) 第一位置只能是合法的标识符(可以是关键字)
(3) 第二位置如果有字符串,必须把""配对。
(4) 只替换与第一位置完全相同的标识符
还有就是老生常谈的话:记住这是简单的替换而已,不要在中间计算结果,一定要替换出表达式之后再算。
2、 带参宏一般用法
比如#define MAX(a,b) ((a)>(b)?(a):(b))
则遇到MAX(1+2,value)则会把它替换成:
((1+2)>(value)?(1+2):(value))
注意事项和无参宏差不多。
但还是应注意
#define FUN(a) "a"
则,输入FUN(345)会被替换成什么?
其实,如果这么写,无论宏的实参是什么,都不会影响其被替换成"a"的**。
也就是说,""内的字符不被当成形参,即使它和一模一样。
那么,你会问了,我要是想让这里输入FUN(345)它就替换成"345"该怎么实现呢?
请看下面关于#的用法
3、 有参宏定义中#的用法
#define STR(str) #str
#用于把宏定义中的参数两端加上字符串的""
比如,这里STR(my#name)会被替换成"my#name"
一般由任意字符都可以做形参,但以下情况会出错:
STR())这样,编译器不会把“)”当成STR()的参数。
STR(,)同上,编译器不会把“,”当成STR的参数。
STR(A,B)如果实参过多,则编译器会把多余的参数舍去。(VC++2008为例)
STR((A,B))会被解读为实参为:(A,B),而不是被解读为两个实参,第一个是(A第二个是B)。
4、 有参宏定义中##的用法
#define WIDE(str) L##str
则会将形参str的前面加上L
比如:WIDE("abc")就会被替换成L"abc"
如果有#defineFUN(a,b) vo##a##b()
那么FUN(id ma,in)会被替换成void main()

使用特权

评论回复
8
wanduzi| | 2017-9-27 22:02 | 只看该作者
呵呵,所以你根据基础理论,慢慢理解啊。

使用特权

评论回复
9
捉虫天师| | 2017-9-27 22:15 | 只看该作者
你这说的我也没看懂。。

使用特权

评论回复
10
背水一战|  楼主 | 2017-9-27 23:32 | 只看该作者
不好意思,我没有写明白,我的目的不是问宏定义问题,再详细些:
1、按照我做的话,直接往寄存器地址,赋值就好了,我想很多人都是这么写,简单明了,但是高手这么写,程序非常有层次化,调用也非常多,非常复杂,很难理解。
2、我看PWM,RGB_LED例子,里边就有这个时钟配置问题,这个函数,我也是云里来雾里去,涉及很多宏,层层定义和调用,还有复杂数据结构,大家一起来看看,既然复杂我就要学习,但没看懂,所以问问大家。
void CLK_EnableModuleClock(uint32_t u32ModuleIdx)
{
    uint32_t u32OffsetTbl[4] = {0x0, 0x4, 0x2C, 0x0};

    *(volatile uint32_t *)((uint32_t)&CLK->AHBCLK + u32OffsetTbl[MODULE_APBCLK(u32ModuleIdx)])  |= 1 << MODULE_IP_EN_Pos(u32ModuleIdx);
}

这一句,还在研究,前面要看很多宏,很多调用,寄存器数据结构,看过的来说说。

使用特权

评论回复
11
天灵灵地灵灵| | 2017-9-27 23:36 | 只看该作者
CLK->AHBCLK 这个应该是这个指针变量。。

使用特权

评论回复
12
背水一战|  楼主 | 2017-9-27 23:49 | 只看该作者
再补充:
1,宏定义:这个宏定义是下面函数的参数
#define ADC_MODULE     (MODULE_APBCLK_ENC( 1)|MODULE_IP_EN_Pos_ENC(CLK_APBCLK_ADC_EN_Pos)  |\
                        MODULE_CLKSEL_ENC( 1)|MODULE_CLKSEL_Msk_ENC( 3)|MODULE_CLKSEL_Pos_ENC( 2)|\
                        MODULE_CLKDIV_ENC( 0)|MODULE_CLKDIV_Msk_ENC(0xFF)|MODULE_CLKDIV_Pos_ENC(16))    /*!< ADC Module */


----------------
void CLK_EnableModuleClock(uint32_t u32ModuleIdx)
{
    uint32_t u32OffsetTbl[4] = {0x0, 0x4, 0x2C, 0x0};

    *(volatile uint32_t *)((uint32_t)&CLK->AHBCLK + u32OffsetTbl[MODULE_APBCLK(u32ModuleIdx)])  |= 1 << MODULE_IP_EN_Pos(u32ModuleIdx);
}

够我喝好几回了!

使用特权

评论回复
13
玛尼玛尼哄| | 2017-9-28 11:23 | 只看该作者
哈哈,我学的时候,用库函数的话,根本不考虑人家库函数怎么写的,只管用就行了。

使用特权

评论回复
14
fhsxycq| | 2017-9-28 17:37 | 只看该作者
我觉得这样写主要是方便程序后期移植和修改,而且比直接往寄存器赋值看起来会更直观

使用特权

评论回复
15
gujiamao12345| | 2017-9-29 11:56 | 只看该作者
你这个宏是看最高两位值的

使用特权

评论回复
16
5574293| | 2017-9-29 14:25 | 只看该作者
我也觉得你只管用就行了,不用在乎这么多 。等你真正需要去理解具体的步骤的时候再去看,也没得关系吧

使用特权

评论回复
17
datouyuan| | 2017-9-29 16:45 | 只看该作者
本帖最后由 datouyuan 于 2017-9-30 09:00 编辑
背水一战 发表于 2017-9-27 23:49
再补充:
1,宏定义:这个宏定义是下面函数的参数
#define ADC_MODULE     (MODULE_APBCLK_ENC( 1)|MODULE_ ...

单从语法说

ADC_MODULE的bit31、bit30对函数CLK_EnableModuleClock()有效。

bit31  bit30   CLK_EnableModuleClock()的动作
0       0         地址(&CLK->AHBCLK +0)被赋值
0       1         地址(&CLK->AHBCLK +4)被赋值,这应该是APBCLK
1       0         地址(&CLK->AHBCLK +0x2C)被赋值,这应该是APBCLK1
1       1         地址(&CLK->AHBCLK +0)被赋值



使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

13

帖子

0

粉丝