OpenEdv-开源电子网

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

请都各位大神LED0=!LED0的问题

[复制链接]

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2017-6-11
在线时间
16 小时
发表于 2017-6-19 22:51:51 | 显示全部楼层 |阅读模式
1金钱
首先在外部中断里有这样一个中断服函数,而且是可以正常工作的。
void EXTI0_IRQHandler(void)
{
    delay_ms(10);    //消抖
    if(WK_UP==1)
    {      
        LED0=!LED0;
        LED1=!LED1;   
    }
    EXTI_ClearITPendingBit(EXTI_Line0);  //清除EXTI0线路挂起位
}
和这个LED0相关的定义如下,是一个宏定义
#define LED0 PAout(8)    // PA8  
其它相关宏定义如下:
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

那我的问题来了,LED0是一个宏定义,也不是变量。怎么对一个宏定义取反。最后我们类函数宏展开,那也应该是得到GPIOA_ODR第8个位ODR8 的值为0或者为1,我们可以!0或者!1,也可以#define LED0 0然后!LED0,但是LED0=!LED0就说语法上不通了。小弟不明白,就算#define LED0 PAout(8),最后得到的#define LED0 1难道就可LED0=!LED0了,这个从C语法上来说是说不通的。还是我理解有误???请各位大神指点一下小弟。

最佳答案

查看完整内容[请看2#楼]

首先,PAout()是一个函数(这里你把宏理解成记事本里的替换就可以了,在编译器开始编译之前,会把宏用你的定义进行一遍替换), 又有#define LED0 PAout(8) ,所以LED0也是PAout()。那么PAout又是什么呢?一层一层的宏定义找下去,就会发现,归根结底,LED0是个*((vu32*)addr)。 这个*((vu32*)addr)怎么理解呢?首先,addr是个内存地址,然后被强制转换成vu32*类型,仍然是个地址(或者叫指针)。然后对这个地址取内容(数据 ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

33

主题

984

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
8062
金钱
8062
注册时间
2014-8-13
在线时间
1597 小时
发表于 2017-6-19 22:51:52 | 显示全部楼层
首先,PAout()是一个函数(这里你把宏理解成记事本里的替换就可以了,在编译器开始编译之前,会把宏用你的定义进行一遍替换),
又有#define LED0 PAout(8) ,所以LED0也是PAout()。那么PAout又是什么呢?一层一层的宏定义找下去,就会发现,归根结底,LED0是个*((vu32*)addr)。
这个*((vu32*)addr)怎么理解呢?首先,addr是个内存地址,然后被强制转换成vu32*类型,仍然是个地址(或者叫指针)。然后对这个地址取内容(数据),
并且把取出的数据看作vu32类型数据。也就是说,PAout返回值是个vu32类型,那么LED0也就是个vu32类型。

实在搞不懂的话,你可以下断点,观察反汇编代码,这有助于你理解C语言转成对应机器码的本质。只是这个本质并不是C语言的本质。
回复

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2017-6-11
在线时间
16 小时
 楼主| 发表于 2017-6-19 22:52:30 | 显示全部楼层
自先顶一个,恳请各位指教。
回复

使用道具 举报

50

主题

1805

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
6662
金钱
6662
注册时间
2016-5-29
在线时间
910 小时
发表于 2017-6-19 23:47:58 | 显示全部楼层
简单来说.编译器可以正常编译成功.那么这个写法肯定是正确的.没有问题.
至于为什么你没有理解.
那是因为你认变宏不是变量,不可以被修改.
实际上,宏是语法糖.他可以是变量,也可以是常量.
目前这具宏不单是一个变量.还是一个位变量呢.
这个宏有点小复杂.我个人还是喜欢用 GPIO->ODR ^=1<<n;简简单单,直接了当.
回复

使用道具 举报

2

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
83
金钱
83
注册时间
2015-6-12
在线时间
14 小时
发表于 2017-6-20 03:45:54 | 显示全部楼层
宏 在代码编译前就被替换掉,等效于“复制”-“粘贴”操作。一个经典的例子就是

#define A 1+1

int  a = A * 2;

楼主认为 a 的值是多少?
如果你觉得a是4,那就错了。代码在编译前,会复制 1 + 1 这段文字到 A处,所以真正的代码是

int a = 1 + 1 * 2;

这句代码才是被编译的代码,a的值是3.
还有#include语句包含头文件,在编译前也是把头文件的内容复制出来,粘贴到include语句这个地方。
所以楼主的问题,第一次复制粘贴后是
PAout(8) = !PAout(8)
然后PAout(8)也是宏,再次复制粘贴
BIT_ADDR(GPIOA_ODR_Addr,8) = !BIT_ADDR(GPIOA_ODR_Addr,8)
这个也是宏,继续复制粘贴
MEM_ADDR(BITBAND(GPIOA_ODR_Addr, 8)) = !MEM_ADDR(BITBAND(GPIOA_ODR_Addr, 8))
还是宏,继续复制粘贴
*((volatile unsigned long  *)(BITBAND(GPIOA_ODR_Addr, 8)))  = ! *((volatile unsigned long  *)(BITBAND(GPIOA_ODR_Addr, 8)))  
还是宏,继续
*((volatile unsigned long  *)(((GPIOA_ODR_Addr& 0xF0000000)+0x2000000+((GPIOA_ODR_Addr&0xFFFFF)<<5)+(8<<2)) ))  = ! *((volatile unsigned long  *)(((GPIOA_ODR_Addr& 0xF0000000)+0x2000000+((GPIOA_ODR_Addr&0xFFFFF)<<5)+(8<<2)) ))  

GPIOA_ODR_Addr应该还是宏,你没贴相关代码,我就不粘贴了。
最后那句长长的句子才是真正的代码,你可以复制那句话放到原来的地方,帮编译器省点力气。
现在明白了吗?
回复

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2017-6-11
在线时间
16 小时
 楼主| 发表于 2017-6-20 09:01:32 | 显示全部楼层
操作系统 发表于 2017-6-19 23:47
简单来说.编译器可以正常编译成功.那么这个写法肯定是正确的.没有问题.
至于为什么你没有理解.
那是因为 ...

嗯,GPIO->ODR ^=1<<n这样写就很容易明白。看来这个宏还有很多需要学习的地方。
回复

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2017-6-11
在线时间
16 小时
 楼主| 发表于 2017-6-20 09:38:57 | 显示全部楼层
本帖最后由 宝庆书生 于 2017-6-20 09:57 编辑
mack13013 发表于 2017-6-20 02:32
首先,PAout()是一个函数(这里你把宏理解成记事本里的替换就可以了,在编译器开始编译之前,会把宏用你 ...

感谢这位哥们的热心回复,其实你说的这些一直到*((vu32*)addr)我都懂。再简化一下就是*((vu32*)addr) = !*((vu32*)addr),这样就是指针做左值和右值的问题了。后来特地查了一下关于指针的问题。最后在《C和指针》6.7节指针、间接访问和左值发现一个例子:前提int a ; int *d = &a; 则对*d=10-*d 给出的解释是:右边的间接访问作为右值使用,所以它的值是d所指向的位置所存储的值(a的值),左边的间接访问是作为左值使用,所以d所指向的位置(a)把赋值符号右则的表达式计算的结果作为它的新值(标黄部分是原书翻译我个人感觉不太好,就是说a的值最后等于右则表达式的值,指针做左值时看成是*d变量a,指针做右值时*d看成是a的值)。现在问题就很清楚了*((vu32*)addr) = !*((vu32*)addr)就是对这个地址里的值非,然后再赋给这个地值变量。
回复

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2017-6-11
在线时间
16 小时
 楼主| 发表于 2017-6-20 09:40:41 | 显示全部楼层
hh312 发表于 2017-6-20 03:45
宏 在代码编译前就被替换掉,等效于“复制”-“粘贴”操作。一个经典的例子就是

#define A 1+1

嗯,我应该是要对指针部分要加强学习,我想了一下应该是指针做左值和右值的问题 。
回复

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2017-6-11
在线时间
16 小时
 楼主| 发表于 2017-6-20 09:50:44 | 显示全部楼层
本帖最后由 宝庆书生 于 2017-6-20 12:34 编辑

谢谢各位热心的伙伴
回复

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2017-6-11
在线时间
16 小时
 楼主| 发表于 2017-6-20 09:53:54 | 显示全部楼层
hh312 发表于 2017-6-20 03:45
宏 在代码编译前就被替换掉,等效于“复制”-“粘贴”操作。一个经典的例子就是

#define A 1+1

嗯,非常感谢,我得巩固一下基本功才行。好好学习C先。
回复

使用道具 举报

4

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2017-6-11
在线时间
16 小时
 楼主| 发表于 2017-6-20 12:35:54 | 显示全部楼层
hh312 发表于 2017-6-20 03:45
宏 在代码编译前就被替换掉,等效于“复制”-“粘贴”操作。一个经典的例子就是

#define A 1+1

理解了
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-8-18 02:14

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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