搜索
bottom↓
回复: 70

为何AVR使用写1作为清0中断标志位的手段。

[复制链接]

出0入0汤圆

发表于 2007-11-8 00:27:33 | 显示全部楼层 |阅读模式
AVR-GCC的帮助文档中的FAQ24

Why are (many) interrupt flags cleared by writing a logical 1?

Usually, each interrupt has its own interrupt flag bit in some control register, indicating the specified interrupt condition has been met by representing a logical 1 in the respective bit position. When working with interrupt handlers, this interrupt flag bit usually gets cleared automatically in the course of processing the interrupt, sometimes by just calling the handler at all, sometimes (e. g. for the U[S]ART) by reading a particular hardware register that will normally happen anyway when processing the interrupt.

From the hardware's point of view, an interrupt is asserted as long as the respective bit is set, while global interrupts are enabled. Thus, it is essential to have the bit cleared before interrupts get re-enabled again (which usually happens when returning from an interrupt handler).

Only few subsystems require an explicit action to clear the interrupt request when using interrupt handlers. (The notable exception is the TWI interface, where clearing the interrupt indicates to proceed with the TWI bus hardware handshake, so it's never done automatically.)

However, if no normal interrupt handlers are to be used, or in order to make extra sure any pending interrupt gets cleared before re-activating global interrupts (e. g. an external edge-triggered one), it can be necessary to explicitly clear the respective hardware interrupt bit by software. This is usually done by writing a logical 1 into this bit position. This seems to be illogical at first, the bit position already carries a logical 1 when reading it, so why does writing a logical 1 to it clear the interrupt bit?

The solution is simple: writing a logical 1 to it requires only a single OUT instruction, and it is clear that only this single interrupt request bit will be cleared. There is no need to perform a read-modify-write cycle (like, an SBI instruction), since all bits in these control registers are interrupt bits, and writing a logical 0 to the remaining bits (as it is done by the simple OUT instruction) will not alter them, so there is no risk of any race condition that might accidentally clear another interrupt request bit. So instead of writing

TIFR |= _BV(TOV0); /* wrong! */

simply use
TIFR = _BV(TOV0);

注:SBI指令的执行周期是2个CK,在对寄存器操作指令中仅有的几个需要2个CK的指令之一。

出0入0汤圆

发表于 2007-11-8 08:47:25 | 显示全部楼层
谢谢马老师!

出0入0汤圆

发表于 2007-11-12 00:04:30 | 显示全部楼层
This seems to be illogical at first, the bit position already carries a logical 1 when reading it, so why does writing a logical 1 to it clear the interrupt bit?
下面好像不是这个问题的答案,下面说的是只需要清某一位即可,不需要重写其它位了。
希望马老师再对上面的问题解释一下,谢谢。

出0入0汤圆

 楼主| 发表于 2007-11-13 14:43:14 | 显示全部楼层
关于“为何AVR使用写1作为清0中断标志位的手段”这个问题我看过很多的相关资料。在AVR的手册中并没有给出为什么的解释,只是强调了“写1清0中断标志位”。同时我也看到很多新的芯片,如DSP等,也是采用写1清零标志位的。但没有找到更专业的,或从根源上的说明,如果那位有这方面的知识或资料,欢迎深入的讨论学习。

下面是我个人的分析和解释,供参考。

1。首先从硬件上的考虑,通常的读写处理单元是以8BIT字节为单位的,因为数据总线一般是8位的倍数。这样对位的操作就不方便,不能直接写1位(会改变其它的位),需要先读到寄存器,然后改动1位,最后回写,需要更多的时间。

2。对于RAM操作一般采用直接写的方式,所以对RAM基本上没有直接的位操作指令。而对于寄存器是可以直接位操作的,但如果对所有的寄存器都能实现位操作,那么硬件结构上就非常复杂和庞大了,所以必须采用一种折中的处理方法。

3。现在的趋势是采用C语言编写系统程序,而标准的C中,没有位变量的概念,最小的单位也是字节。因此硬件的设计上面也要考虑能发挥C语言的优势。

以上是我分析的原因。因为已经超出了我研究的方向(我侧重于应用),可能不全面或有偏差。下面回到AVR本身。

我们可以注意到:
1。AVR没有“位”空间,也就是说没有单独的“位”地址,所有的位寻址是基于8位的寄存器的,所以基本寻址方式是以寄存器为主的。

2。因此AVR没有专门的位寻址指令,它本身的位操作指令很少,都是在寄存器寻址的基础上,对寄存器的某位进行操作。

3。除了对状态寄存器SREG中的位有直接的操作指令外(SREG太特殊了,必须要有专用的位操作指令),能够对其它寄存器的位操作的指令只有2个。
   a)BST、BLD。这个指令的周期是1CK,他是将SREG中的T标志位与32个通用寄存器的位之间交换数据的指令。如果要对32个寄存器的1位进行设置的话(比如置1),必须先使用指令将SREG中的T置1,然后使用BLD指令将T的值写到寄存器的某位。需要2个CK时间。
   b)SBI、CBI。这2条指令是对前32个(注意:仅对前32个I/O空间!)I/O空间的寄存器的位进行设置的指令。这2个指令的执行时间是2个CK。AVR对寄存器操作的指令大多数都是1个CK,而这2个指令为何需要2个CK?原因在与写的时候还是8位一起写,因此改变1位需要先读,修改1位,再回写。这样保证了其它位不变,但时间需要2个CK了。

4。正是由于第3点(b),所以PA、PB、PC、PD等I/O口的寄存器均在前32个I/O空间,这样就实现了方便的单独的按位控制I/O口了。

5。不同C编译器,位处理是不同的。ICC、IAR基本没有扩展位处理,按标准C来处理,因为他们考虑的可移植性更加多些。而CVAVR扩展了位变量(放在32个工作寄存器中)和位操作(仅能对I/O空间前32个寄存器),因此用户使用起来更方便些。但要注意,对I/O空间后32个寄存器,CVAVR也不能实现位操作的。

====================================================
最后看一下中断标志位的处理。在AVR中对中断标志位的处理是根据不同情况采用不同的处理方法的,在上面的英文说明中已经给出了解释。有的是进入执行中断由硬件清除,有的是读某个寄存器后由硬件清除。而软件清除通常是写“1”,为什么?

看一下M16的手册,发现外部中断标志寄存器GIFR(0X3A)、和T/C的中断标志寄存器TIFR(0X38),都在I/O空间的后32个地址中,而且全部是中断标志寄存器。因此不管是ICC、IAR、还是CVAVR,肯定不能使用SBI、CBI指令对位操作了,只能是对1个寄存器8位同时写操作了。

那么,通常在C中如何改变1位置1呢?通常大家认为正确的语句是:XXXX |= 0B00000001;其功能是将XXXX先读出,然后同0B00000001或,使最低位为1,其它位保持不变。实际需要3条汇编指令的。改变1位置0:XXXX &= 0B11111110;同样需要3条汇编指令的。

AVR采用写“1”清“0”中断标志位(写“0”不影响标志位),那么语句就可以直接使用TIFR = 0B00000001了,只需要2条汇编。将最低位的标志位清“0”,同时保证了其它标志位的不变。(!!!注意,反而使用TIFR |= 0B00000001是错误的!!!因为,如果其它的位本身是1的话,这样反而也被清掉了)

另外,写“0”清“0”中断标志位的话,那么写“1”到中断标志位的话应该如何定义呢?中断标志位应该是硬件置1的,如果软件可以置1,会带来更多的麻烦。

实际上,上面的英文解释还是不全面的,容易引起一些误解。
a)只能对于TIFR、GIFR使用TIFR = 0B00000001这样的语句,因为只有这两个寄存器中,全部都是中断标志位。
b)而对于一些其它的中断标志位,如果它所在的寄存器中还有一些是非中断标志位的,就必须使用XXXX |= 0B00000001的写法了。
c)对于非中断标志位的设置,还是必须使用XXXX |= 0B00000001这样的形式的。

========================================================
以上的回答,我自己感觉并没有从根本上给出“为什么”的答案,只是从表象中的分析。只是AVR本身采用了这样的方法,我们要能在使用中能正确的应用是我的目的。

至于更专业的解答,需要微电子专业的专门设计芯片的人士给出回答了。

出0入0汤圆

发表于 2007-11-13 21:24:11 | 显示全部楼层
仍然没有说明"为什么",而且AVR这样设计搞得不同的寄存器要用不同的方法来清零,更乱.

出0入0汤圆

 楼主| 发表于 2007-11-13 21:40:42 | 显示全部楼层
除了AVR,很多其它的芯片也是采用写“1”清“0”中断标志位的,我想可能其中是有一定的道理和原因。

好在软件清除中断标志位不是频繁使用的,主要是在程序的初始化阶段,开放全局中断的前面。如果是在程序中间,则需要当心了。

出0入0汤圆

发表于 2007-11-16 23:25:12 | 显示全部楼层
很关键的一句话:对于标志寄存器,写“0”不影响标志位

实际上这个方法对凌阳单片机也是这样的

出0入0汤圆

 楼主| 发表于 2007-11-17 14:17:31 | 显示全部楼层
6楼的例子说明确实有许多的MCU等采用写"1"清中断标志的,不仅仅是AVR.
但是否还有更深层次的解释说明"为什么",能回答4楼的疑问?

出0入0汤圆

发表于 2007-11-17 20:25:54 | 显示全部楼层
这是MCU硬件设计师考虑的,估计写入逻辑实现机制来实现的.我想MCU内部实现这个逻辑电路应该不难实现,
对于使用者来说只要知道,如何使用就可以啦,

出0入0汤圆

 楼主| 发表于 2007-11-17 20:37:02 | 显示全部楼层
问题在于,就是有许多人想了解MCU硬件设计师为什么这样考虑.

出0入0汤圆

发表于 2007-11-17 21:26:44 | 显示全部楼层
这个问题,有着和为什么用符号1代表数量一的意思,它们是一样的难度,呵呵

出0入0汤圆

 楼主| 发表于 2007-11-17 22:24:07 | 显示全部楼层
不能这样说.因为在学8086、51等老的芯片时,已经习惯了写“0”清除,而且其它的寄存器位也是写“0”清除。但为何现在许多芯片采用写“1”清除标志位,而且仅仅是中断标志位的话,应该有一定的道理的。只是还没了解透。

出0入0汤圆

发表于 2007-11-18 17:47:52 | 显示全部楼层
经过本人洗一个热水澡以后,想到可能是:设计者不想让别人手工通过写1的方式来触发中断标志位,那么解决它最简单的方法干脆就用写1表示清除标志位.

出0入0汤圆

发表于 2007-11-18 22:17:46 | 显示全部楼层
我在学的时候,总是想,可能硬件上需要用一个1去 异或 原来 那个1 .
  虽然我们在外部写了1 个1 ,但是集成电路内部,设计师从某种方面来考虑,(或许是为了设计的方便等原因)(他们设计方便,就不管我们使用者了,使用者多注意的是IC的整体功能,这样做简化了工艺,提高了功能)
  但是内部的操作实际上是进行了1 次 类似的异或 操作。(这种异或可能只对1异或1有效,由于硬件的原因 0异或0可能不行)
  
  他们从多方面综合考虑,选用了此种设计方法。
  以上是我个人想法,具体为什么,只有问设计者了。

出0入0汤圆

发表于 2007-11-18 22:29:42 | 显示全部楼层
写1或者写0时间是一样的,和异或一样

出0入0汤圆

发表于 2007-11-18 23:13:36 | 显示全部楼层
我谈谈我个人的观点:
“AVR采用写“1”清“0”中断标志位”这个做法我认为除了马老师和各位朋友上面所说的功能,还有一个重要功能,就是如果有外界硬件干扰使得标志位发生改变,那么一般来说,禁止中断是比较安全的做法,而通常外界硬件干扰特别是电压瞬态变化往往是尖峰干扰,如果确实干扰到了中断标志位,那么这个电压尖峰将使得中断不响应,以策安全。

在51的编程中,也常常使用这种做法,就是:端口输出低电平有效,这样可以有效防止电压尖峰的对输出端口干扰。

以上思考,如有不当之处,万望海涵。

出0入0汤圆

发表于 2007-11-19 18:49:17 | 显示全部楼层
【15楼】 JAMESKING 说的一句话“AVR采用写“1”清“0”中断标志位”,我提个问题,不要生气,我们都是在讨论。
1  为什么写1,反而不是1 而是变成了0 ,这个机理是什么?他们也可以做成是1 的时候 禁止中断的啊,不必搞这样复杂。
2  清中断标志位,是一种软件上的行为。外界硬件干扰一般会于扰引脚。

以上思考,如有不当之处,万望海涵

出0入0汤圆

发表于 2007-11-20 07:53:08 | 显示全部楼层
设计者不想让别人手工通过写1的方式来触发中断标志位

出0入0汤圆

发表于 2007-12-21 21:47:56 | 显示全部楼层
实际上设计者只是特别强调此标志位与众不同。没有其他意思。

出0入0汤圆

发表于 2008-4-2 20:59:06 | 显示全部楼层
没有彻底弄清楚
今天同学在看飞思卡尔的单片 也碰上了同样的问题

出0入0汤圆

发表于 2008-4-6 18:24:05 | 显示全部楼层
硬件上---单片机内部---在逻辑电路上---用高电平“异或”---使受控的这一位---产生低电平,从而清0了。

我们写的是1 ,但硬件上这样一异或,那一位就成了反的,也就是0 了

也可以这样来理解:写1既是输入高电平,这样可以使内部的一个三极管导通接地。

最大的可能是为了简化工艺。

设计单片机的工程师,在设计一款高性能的单片机,是非常的不容易的,这种方法总是出现在中断位上,能自动清0 ,也能手动清0,我们应理解工程师,他们也是出于某种原因,而不得已,

出0入0汤圆

发表于 2008-4-9 21:02:44 | 显示全部楼层
马老师的楼主贴提的问题,我看后汗啊。。。。。

TIFR |= _BV(TOV0); /* wrong! */  

simply use  
TIFR = _BV(TOV0);

我一直用的那个wrong的!!!!

出0入0汤圆

 楼主| 发表于 2008-4-9 21:27:15 | 显示全部楼层
楼上的,没有必要那么紧张.要看仔细,真正理解它的含义.

对于TIFR寄存器,因为里面都是中断标志位,所以可以直接使用TIFR = _BV(TOV0);,其实使用TIFR |= _BV(TOV0);也是可以的,只是比前者多花费一个指令周期.

而对于寄存器中不都是中断标志的,还是应该使用TIFR |= _BV(TOV0);的,这样才能保证其它的位不会改变.

具体问题要具体分析的.

出0入0汤圆

发表于 2008-4-20 14:52:39 | 显示全部楼层
  清中断咋个清法,应该没有太多的猫腻吧。设计者的思路通常是会尽量沿用本公司的同一或相近系列产品的做法,以求互换性。
  以前做过用FPGA扩展中断,也碰到过需要软件清掉中断标志位的问题,无非是用这几个信号的组合做成:地址/数据/片选/读脉冲/写脉冲等。

  我的做法是——
  ①.假如空余地址有得是,用不完,可以只用地址+片选+读或写来完成。这个办法是花费一个地址去清一个标志,对设计处理器来说太浪费了,不合适。
  ②.用地址+数据+片选+写脉冲来完成。

  办法②因加进了数据线,假如是8位的话,用一个地址最多可选定做256种事。不过一般中断源没那么多,于是折衷成了每位管一个中断源,以求让程序员直观些——事实上多数CPU都采用了这个办法。
  但因CPU的每个处理都不是以单个bit进行的,要处理某特定bit时还不许动别的bit,于是就有必要定义“操作”和“非操作”两种状态的数据。
  按人们通常的习惯(不管科学不科学),喜欢把0看作没有、不是,把1看作有或是,于是把要想操作的那一位置1,其余不想动的位置0的做法就很自然了。当然反过来定义也未尝不可,这完全是习惯,或公司老板的心血来潮,或是那设计家伙的嗜好罢了,反正怎么滴都行。

出0入0汤圆

发表于 2008-4-21 21:31:21 | 显示全部楼层
想了一下,清中断确实不该写成 TIFR |= _BV(TOV0);
因编译后会生成多条机器指令,如果在“读入TIFR”和“写出TIFR”之间发生了另一个中断,那么新的中断标志位会被无意清掉而丢失一次正常的服务。
当然,如果该语句是在禁止中断的状态下,或本系统并无其他中断源的情况下用是无碍的。

出0入0汤圆

发表于 2008-4-22 09:58:33 | 显示全部楼层
又想了一下,俺说错了,需订正。
用这个写法禁止中断也不行,因为标志位是硬件置的,只能在无其他中断源的情况下用。

出0入0汤圆

发表于 2008-5-8 10:07:35 | 显示全部楼层
【22楼】 machao

马老师,这个说法可能有问题
如果寄存器中有多个中断标志位,其中两位以上硬件置1,使用 R|=_BV(B),会导致所有被硬件置1的中断标志位清零(如果清零规则是软件置1)
这种写法只能用在只有一个中断标志位的寄存器中
全是中断标志位的只能用R = _BV(B)

如果有两个中断标志位在同一个寄存器中,而其他位有别的用途可真麻烦,不过我不知道有没有这样的寄存器

出0入0汤圆

 楼主| 发表于 2008-5-9 18:10:50 | 显示全部楼层
to 26楼:在3楼我已经解释了.

出0入0汤圆

发表于 2008-5-9 19:29:58 | 显示全部楼层
"硬件上---单片机内部(自动置1)---在逻辑电路上---用高电平“异或”---使受控的这一位---产生低电平,从而清0了。"

软件上---用程序语言把1放到ram--在逻辑电路上---用高电平“异或”---使受控的这一位---产生低电平,从而清0了。
个人理解.

出0入0汤圆

发表于 2008-5-10 09:04:21 | 显示全部楼层
AVR采用写“1”清“0”中断标志位(写“0”不影响标志位),那么语句就可以直接使用TIFR = 0B00000001了,只需要2条汇编。将最低位的标志位清“0”,同时保证了其它标志位的不变。(!!!注意,反而使用TIFR |= 0B00000001是错误的!!!因为,如果其它的位本身是1的话,这样反而也被清掉了)


这样的话岂不是写0写1都会清除标志位

出0入0汤圆

发表于 2008-6-30 18:29:22 | 显示全部楼层
ATMEL非要搞点新名堂,只要存在,就是合理!

出0入0汤圆

发表于 2008-7-10 15:08:00 | 显示全部楼层
要是想让中断标志位的内容为‘1’,
该往里面写 什么东西?

出0入46汤圆

发表于 2008-7-10 17:19:01 | 显示全部楼层
一直不明白只能照做

出0入4汤圆

发表于 2008-7-10 21:54:01 | 显示全部楼层
各位难道还不明白?

26楼的朋友已经有点苗头了,不错。

我来谈一下:

1、首先,可以看一下,avr后期的芯片其中断标志寄存器是不可位寻址的,只能通过8bit方式访问。所以,如果avr芯片在设计时按照传统的写0写1方式,则使能采用“读 - 改 - 写”的方式进行;(这一点马老师也提了)

2、那么,这就出现一种困局,就是26楼的朋友提到的,如果同时有两个中断标志置位,如0b0000 0011,想要将其修改为0b0000 0001,则可以 用 0b0000 0011 & 0b1111 1101实现。 但是如果在执行这个操作的时候,另外一个中断标志置位了,怎么办?变成0b0001 0011,则按照我们本来的意愿,只是清除掉0b0000 0010位,则执行完,由于出现了新的中断标志位,结果应该是0b0001 0001,但实际上,计算出来的结果只能是0b0000 0001,即在此期间发生的中断标记将会被!!误清除掉!!。 (这一点是致命的!根本和效率无关)
  各位可以考虑一下,一个不能位寻址的中断标志寄存器,如果避免这种情况?即便有,代价是什么?

3、如果采用写1清0,写0无影响,则仅有写1的位被清0,其余位不受影响,相当于屏蔽了对写0位的操作,而上面描述的传统方法确实位位受影响。同样如上例,如果当前中断标志寄存器为 0b0000 0011,则写入0b0000 0010后,理论上结果应该为0b0000 0001,即便是期间发生产生新的中断标志位0b0001 0000,则操作后结果为0b0001 0001,结果正确,不影响系统正常运行,不阻挡后来者……对不对?你可以不理解为什么写1清0,但是你得理解,即使采用其他方法也会与之相似,如写0清0等等。(我的理解,基本上不得不这么做,除非改为位操作)

4、再来谈谈对这类寄存器的操作,虽然号称可读写,但读取是正常的读取,写操作应该和传统的总线式不同,中间应该有其他的微电路或其他形式进行操作。两个操作理论依据不同,而大家通常只是按照传统的总线读写来理解,突然遇到这个写1清0的异类,自然会感到怪异,甚至不理解了。

不知我的发言能不能为大家解去这一惑,还请继续讨论。

请了。


***不好意思,想编辑,却总是点向删除,幸亏反应快,又点了编辑……

出0入0汤圆

 楼主| 发表于 2008-7-11 02:36:57 | 显示全部楼层
楼上的分析有一定的道理,支持。

出0入4汤圆

发表于 2008-7-11 08:44:20 | 显示全部楼层
哦,马老师也是夜猫子

出0入0汤圆

发表于 2008-7-11 10:21:24 | 显示全部楼层
【33楼】 guantingwei 有一定的道理
1、首先,可以看一下,avr后期的芯片其中断标志寄存器是不可位寻址的,只能通过8bit方式访问。所以,如果avr芯片在设计时按照传统的写0写1方式,则使能采用“读 - 改 - 写”的方式进行;(这一点马老师也提了)

没错,就是这样的

2、那么,这就出现一种困局,就是26楼的朋友提到的,如果同时有两个中断标志置位,如0b0000 0011,想要将其修改为0b0000 0001,则可以 用 0b0000 0011 & 0b1111 1101实现。 但是如果在执行这个操作的时候,另外一个中断标志置位了,怎么办?变成0b0001 0011,则按照我们本来的意愿,只是清除掉0b0000 0010位,则执行完,由于出现了新的中断标志位,结果应该是0b0001 0001,但实际上,计算出来的结果只能是0b0000 0001,即在此期间发生的中断标记将会被!!误清除掉!!。 (这一点是致命的!根本和效率无关)
  各位可以考虑一下,一个不能位寻址的中断标志寄存器,如果避免这种情况?即便有,代价是什么?

对,就是这样的

3、如果采用写1清0,写0无影响,则仅有写1的位被清0,其余位不受影响,相当于屏蔽了对写0位的操作,而上面描述的传统方法确实位位受影响。同样如上例,如果当前中断标志寄存器为 0b0000 0011,则写入0b0000 0010后,理论上结果应该为0b0000 0001,即便是期间发生产生新的中断标志位0b0001 0000,则操作后结果为0b0001 0001,结果正确,不影响系统正常运行,不阻挡后来者……对不对?你可以不理解为什么写1清0,但是你得理解,即使采用其他方法也会与之相似,如写0清0等等。(我的理解,基本上不得不这么做,除非改为位操作)

好像写0清0更容易理解,and运算即可

4、再来谈谈对这类寄存器的操作,虽然号称可读写,但读取是正常的读取,写操作应该和传统的总线式不同,中间应该有其他的微电路或其他形式进行操作。两个操作理论依据不同,而大家通常只是按照传统的总线读写来理解,突然遇到这个写1清0的异类,自然会感到怪异,甚至不理解了。

没错
=======================================================================================================================
最后补充一点,这些可以看看芯片最基本的知识。
一般这种标志位是由触发器构成的,而具有单独的置1和清0功能的触发器是RS触发器,同时,由于需要受mcu时钟控制,一般用同步触发器,而同步触发器的触发是高电平有效。
在芯片中,多数电路是由与非门组成的,这样实现简单占用的面积最小


rs触发器 (原文件名:rs.jpg)

出0入4汤圆

发表于 2008-7-11 13:30:40 | 显示全部楼层
ls的,写0清0似乎要麻烦些,
比如0b0000 0011 要清为0b0000 0001
就得写 0b1111 1101,不如写0b0000 0010直观。

出0入0汤圆

发表于 2008-11-3 11:45:39 | 显示全部楼层
23, 36楼的观点有参考的价值。

其实这要看硬件的资料才能有正确的答案。就像51系列的IO口,有原理图,就容易明白。

个人猜测:写的“1”清“0”不是直接写入寄存器,而是写“控制线”,所以才有写0不影响标志位的说法。

如果是中断寄存器,这更容易令人接受。它可能不是一般意义上的类似于内存的寄存器。它可能(往往)是与众多中断源相关的

一系列逻辑电路。写控制线这种说法,就可能更贴近。



  以下摘自楼主:



最后看一下中断标志位的处理。在AVR中对中断标志位的处理是根据不同情况采用不同的处理方法的,在上面的英文说明中

已经给出了解释。有的是进入执行中断由硬件清除,有的是读某个寄存器后由硬件清除。而软件清除通常是写“1”,为什么? 



------------------

可能就是在电路上保持与其硬件电路清除的方法一致。

本贴被 zswlb 编辑过,最后修改时间:2008-11-03,12:01:40.

出0入0汤圆

发表于 2008-11-15 18:01:51 | 显示全部楼层
我认为最主要的就是一点:

   由于不能位寻址,所以要避免“读-修改-写”过程中出现的意外,如:恰好有其他中断产生。

 

出0入0汤圆

发表于 2009-12-26 15:30:37 | 显示全部楼层
前几天也碰上这问题了,模拟仿真了两小时终于发现了。。。早看到这贴就好了,省了二小时

出0入0汤圆

发表于 2010-1-6 17:23:01 | 显示全部楼层
我认为38楼的很有道理。

比如对于51单片机的SBUF,读和写的都是同一个SBUF吗?AVR的UDR也是。

对寄存器读取的时候读取的状态信息,而写的时候写的是控制信息,两个在不同的位置。

可以这样认为中断发生时,状态位置1,要清除的时候是向控制位写1,两个1想与非,得到0……这倒像是RS触发器了。

出0入0汤圆

发表于 2010-2-3 20:07:50 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-2-4 11:33:14 | 显示全部楼层
顺便提个不懂得问题,为什么要清零中断标志,不清零可以吗?

出0入0汤圆

发表于 2010-2-9 11:29:40 | 显示全部楼层
学了stm32再来看这个问题,发现写1清零和stm32中的端口复位寄存器功能有点像。既然不方便让用户直接更改寄存器中某一位,不如整个类似映射的寄存器,告诉单片机我想改哪一位(用1表示),其他的不管(0),让后单片机自己去更改目标寄存器。

回复【43楼】C307
-----------------------------------------------------------------------

仔细看看楼主位前几段的内容。有些中断标志不自动清零,中断程序跑完以后那会咋样?

出0入0汤圆

发表于 2010-2-11 10:11:30 | 显示全部楼层
问个问题,马老师是否在98年的时候,在纺织大学给大四的电子941和通信941上过数字图像处理的课,感觉有些印象,呵呵

出0入0汤圆

发表于 2010-2-22 10:47:13 | 显示全部楼层
学习

出0入0汤圆

发表于 2010-3-4 13:41:58 | 显示全部楼层

    同样的疑惑,先做下标记。

出0入0汤圆

发表于 2010-3-15 10:00:29 | 显示全部楼层
大受启发

出0入0汤圆

发表于 2010-6-1 17:11:57 | 显示全部楼层
这个要仔细看

出0入0汤圆

发表于 2010-7-4 13:35:51 | 显示全部楼层
看得懂的都是高人啊,俺还得下些功夫了。

出0入0汤圆

发表于 2010-7-20 14:33:28 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-8-12 16:32:20 | 显示全部楼层
Mark~

出0入0汤圆

发表于 2010-10-14 08:51:20 | 显示全部楼层
受教了!

出0入0汤圆

发表于 2010-12-22 19:48:25 | 显示全部楼层
受益
谢谢大家!

出0入0汤圆

发表于 2010-12-22 21:24:43 | 显示全部楼层
cool

出0入0汤圆

发表于 2010-12-22 21:37:46 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-1-7 20:50:21 | 显示全部楼层
学习啦,谢谢马老师及各位!

出0入0汤圆

发表于 2011-2-20 12:28:40 | 显示全部楼层
回复【33楼】guantingwei  
-----------------------------------------------------------------------

很有道理

出0入0汤圆

发表于 2011-3-21 09:21:39 | 显示全部楼层
MARK

出0入0汤圆

发表于 2011-4-25 00:01:51 | 显示全部楼层
很有价值,顶。

出0入0汤圆

发表于 2011-4-25 00:33:13 | 显示全部楼层
it is really about atomicity, and has nothing to do with if the avr is clever or not.

as many of you have correctly pointed out, a regular "TIFR |= (1<<mask);" is a read-modify-write operation and it will take more than a cpu cycle where bits in TIFR could have been changed by hardware after it has been read. those interrupt flags would then be written over by their older values, if the chip allowed those flags to be cleared by 0s.

however, there are other solutions - none of which has anything to do with if C is involved.

many other mcus, like PIC, allow the flags to be cleared by writing zeros - except that they use bit flags directly. AVRs just happen to choose this particular and different approach.

出0入0汤圆

发表于 2011-9-8 23:04:01 | 显示全部楼层
在STM32中配有专门为通用io输出设置的复位与置为寄存器,写1有效,写0无效,可以通过向置位复位寄存器写1来输出高或低,这种操作极大的方便了不能位操作的寄存器(避免了读改写过程节省CPU效率)。对于为什么写1,本质上我不清楚,但是可以从使用者角度来分析,如果这类写1有效的寄存器,变为写0有效写1无效,那么程序写起来会不方便。
比如(改变最低位)
写零有效
reg=0xFE;

reg=~0x1;
换为写1有效
reg=0x01;

reg=1;
写程序上还是写1有效占些编辑的优势

胡言乱语。。。

出0入0汤圆

发表于 2011-10-13 08:36:57 | 显示全部楼层
像STC也有个别写“1”清“0”中断标志位的,挺别扭的。
头像被屏蔽

出0入0汤圆

发表于 2011-12-17 15:32:33 | 显示全部楼层
看了这个贴才迷迷糊糊了解啊,mark
头像被屏蔽

出0入0汤圆

发表于 2011-12-17 15:37:43 | 显示全部楼层
中国什么时候可以做出这样的芯片出来!

出0入0汤圆

发表于 2012-4-10 20:20:52 | 显示全部楼层
英语不行,看不懂原文,

出0入0汤圆

发表于 2012-7-9 10:57:07 | 显示全部楼层
mark!非要十个字

出0入0汤圆

发表于 2013-8-8 22:15:02 | 显示全部楼层
嗯,看到有这么多好东西果断注册了,以后常来混.
mark一个.

出0入0汤圆

发表于 2013-8-18 00:42:51 | 显示全部楼层
本帖最后由 qiu452555846 于 2013-8-18 00:44 编辑

虽然此贴已经发布很多年了,但我此时看到,也想谈一谈自己的看法。只是一家之言,纯粹一种想法。
1、中断标志位只可能有“设置”和“清除”两种状态,分别表示“中断已产生(或中断标志被设置)”和“中断尚未发生(或中断标志被清除)”两种状态;
2、从可靠性方面考虑,“设置”中断标志,应当由硬件实施;“清除”中断标志,应当由用户软件实施;禁止用户软件强行“设置”中断标志,以免用户软件意外搞乱中断。
3、实现中断标志位“由硬件设置,由软件清除”,在数学上有四套等价的逻辑:
   a、硬件通过写0”设置”中断标志,软件通过写0“清除”中断标志;
   b、硬件通过写0“设置”中断标志,软件通过写1“清除”中断标志;
   c、硬件通过写1”设置”中断标志,软件通过写0“清除”中断标志;
   d、硬件通过写1“设置”中断标志,软件通过写1“清除”中断标志;
4、尽管以上四种逻辑在数学上都是等价的,但既然AVR在架构上已将功能寄存器位的上电初始值设计为0,为了全局上的统一,中断标志位自然地也被设计为上电初始值为0。这个“0”对应着“中断尚未发生(或中断标志被清除)”的状态;于是,“中断已产生(或标志被设置)”的状态便只能用1来表示了,排除a、b逻辑方案。
5、至于c、d两套逻辑方案,通过写1来清除中断标志,还是通过写0来清除中断标志,由于在数学上是等价的,所以比较的关键不在于电路理论,而在于使用习惯,在于迎合C语言的编程习惯。其思考的内容应该是这样的:
   功能寄存器的每一位都代表不同意义,在程序设计时经常需要单独操作其中的一位或几位,对于标准C语言而言,这是通过下面两句实现的:
      var |= 1<<bit;          //写1
          var &= ~(1<<bit);    //写0
   上述两句话的执行效率是一样的,但如果让程序员从上述两种方式中选择一种作为“清除中断标志”的方法,绝大多数程序员会选择第一种,即通过写1清除中断标志位,这仅仅是因为它书写更简单。
   特别地,当var的每一位都是中断标志位时,单独清除某一位的方法还可以写成更高效的形式:
      var = 1 << bit;           //写1清除中断标志位
      var = ~(1 << bit);      //写0清除中断标志位
   这两句的效率也是一样的(但又都比上两句高效)。不过,程序员仍然会倾向于选择第一句,即写1清除中断标志位的方案。这也仅仅是因为它书写更简洁,且更容易理解。

   上面说了这么多,其实归结起来就一句话:无论写1清除中断标志位还是写0清除中断标志位,它们在数学上都是等价的,只不过写1清标志位更符合程序员的书写习惯而已。这个问题被讨论得这么激烈,也许只不过是大家多虑了而已;芯片设计者当初这样设计,或许只是希望标准C用户使用起来更加方便。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-4 15:06

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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