首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >为什么是位移运算符?

为什么是位移运算符?
EN

Stack Overflow用户
提问于 2010-08-03 22:21:36
回答 5查看 2.7K关注 0票数 5

我不明白你什么时候以及为什么会在微芯片编程中使用位移位操作符:例如…

代码语言:javascript
代码运行次数:0
运行
复制
SWITCH_DDR &= ~SWITCH_BIT;   
SWITCH_PORT |= SWITCH_BIT;

为什么要使用这些运算符?

或者..。

代码语言:javascript
代码运行次数:0
运行
复制
void SerialInit(void)
{
   UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;   // 250kbps at 16Mhz
   UBRRL = (XTAL / (8 * 250000)) - 1;
   UCSRA = (1<<U2X);
   UCSRB = (1<<TXEN);
   UCSRC = (1<<URSEL) + (1<<UCSZ1) + (1<<UCSZ0);
}

这是怎么回事?请有人用0和1图形化解释一下。下面是另一个例子:

代码语言:javascript
代码运行次数:0
运行
复制
ulong MesureLC(void)
{
 int i;

 TCCR1B = 0;
 CountHigh = 0;
 TCNT1 = 0;

 for (i=0;i<25000;i++)
 {
  TCCR1B = (1<<CS12) + (1<<CS11) + (1<<CS10);   // WTF ???
  UDR = 0x55;
  while(!(UCSRA & (1<<UDRE)));
 }
 while(!(UCSRA & (1<<TXC)));
 TCCR1B = 0;

 CountLow = TCNT1;
 Count = CountLow + (CountHigh << 16);

 return Count;
}

我需要理解这些东西。任何帮助都是非常感谢的。

EN

回答 5

Stack Overflow用户

发布于 2010-08-03 22:31:22

  1. 如果您有可以启用或禁用的功能,您可以使用单个位来设置状态:1或0。
  2. 如果你需要存储值(比方说从0到7),你只需要3位(000 in binary =0 in dec,001 = 1,010 = 2,...,111 = 7)。而且你还有8位中的5位可用。

这是怎么回事?请有人用0和1来图解一下

1 << 2= 00000001 << 2= 00000100 =4

4 >> 2= 00000100 >> 2= 00000001 =1

票数 5
EN

Stack Overflow用户

发布于 2010-08-03 23:19:14

我认为真正的问题不是“这到底意味着什么”,而是“为什么为微控制器编写程序的开发人员似乎喜欢这种方式?”

我不知道他们是不是那样爱。我根本不是一个开发人员,我只是对微控制器有一点兴趣,但这是我的想法。

你见过芯片的文档吗?这里有一个来自Atmel pdf的例子。

USART控制和状态寄存器- UCSRnB

·位7- RXCIEn: RX完成中断使能n将此位写入1可使能RXCn标志上的中断。仅当RXCIEn位写入1、SREG中的全局中断标志写入1且UCSRnA中的RXCn位被设置时,才会生成USART接收完成中断。

·位6- TXCIEn: TX完成中断使能n将此位写入1使能TXCn标志上的中断。仅当TXCIEn位写入1、SREG中的全局中断标志写入1且UCSRnA中的TXCn位被设置时,才会生成USART传输完成中断。

·位5- UDRIEn:...·位4- RXENn:...·位3- TXENn:...·位2- UCSZn2:...·位1- RXB8n:...·位0- TXB8n:...

问题是,芯片是通过设置或清除某些控制寄存器中的单个位来控制的。这些寄存器在文档中有难看的名称,位也有难看的名称。开发环境已经提供了所有的宏,它们的名称与文档定义的名称相似。

现在假设有人想要在RXCn标志上启用中断,并保持所有其他设置不变。这需要在一个特定寄存器中设置一个位,而其他位保持不变。运算符|=是完成此操作的最简单方法

假设寄存器的地址是0x3F (我虚构了这个数字,这并不重要),可以这样写:

代码语言:javascript
代码运行次数:0
运行
复制
*((unsigned char*)0x3F) |= 0x80;// I want to set bit number 7 (RXCIEn)

现在,这个可读性如何?

在开发环境中,已经定义了像UCSRnBRXCIEn这样的宏,使用它们是显而易见的事情。碰巧RXCIEn是一个位数,而不是该位的值,所以要编写与上面相同的代码,必须编写

代码语言:javascript
代码运行次数:0
运行
复制
UCSRnB |= (1 << RXCIEn);

多亏了文档,比如

代码语言:javascript
代码运行次数:0
运行
复制
UCSRnB = (1<<RXENn)|(1<<TXENn);

被认为可读性比

代码语言:javascript
代码运行次数:0
运行
复制
*((unsigned char*)0x3F) = 0x18; // I want to set bits number 4 and 3 (RXENn and TXENn)

文档本身充满了使用这些定义的宏的代码示例,我猜开发人员太快地习惯了它,试图找到更好的方法。

票数 4
EN

Stack Overflow用户

发布于 2010-08-03 23:00:58

在您给出的第一个示例中:

代码语言:javascript
代码运行次数:0
运行
复制
SWITCH_DDR &= ~SWITCH_BIT;   
SWITCH_PORT |= SWITCH_BIT;

这不是位移位,而是位掩码。

具体地说,第一个语句关闭值中的一个给定位,第二个语句打开相同的位,保持所有其他位不变。

假设SWITCH_DDR和SWITCH_PORT是控制某些设备行为的特殊内存值。它的每个位打开/关闭一个功能。如果你想单独控制一个给定的功能,你必须能够在不干扰其他功能的情况下进行一点更改。也就是说,由SWITCH_BIT控制的特性是字节中最左边的一位。因此,SWITCH_BIT的值为0x80 (二进制为10000000)。当您执行第一条语句时,您将使用~运算符反转SWITCH_BIT (以二进制形式获得01111111 ),并对SWITCH_DDR应用二进制and。这有效地清除了最左边的位,并保留了其他位不变。第二个语句执行二进制OR,因此得到相反的结果。

现在,关于移位操作,有很多应用程序(很多已经在另一个答案中提到了),我会简单地在你发布的代码中解释具体的用法。因此,您需要:

代码语言:javascript
代码运行次数:0
运行
复制
void SerialInit(void)
{
   UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;   // 250kbps at 16Mhz
   UBRRL = (XTAL / (8 * 250000)) - 1;
   UCSRA = (1<<U2X);
   UCSRB = (1<<TXEN);
   UCSRC = (1<<URSEL) + (1<<UCSZ1) + (1<<UCSZ0);
}

这里的事情类似于前面的例子(在值中设置特定的位),但由于情况的不同,有一些不同:

a)虽然在第一个示例中已经制作了位掩码(SWITCH_BIT),但这里的常量只是位的位置。例如,U2X包含需要打开/关闭的位的位置(从右到左),而不是它的位掩码。执行(1<<U2X)可以有效地生成相应的位掩码。如果需要打开的位在最左边(就像我在SWITCH_BIT示例中所做的那样),U2X将是7,移位的结果将是相同的二进制10000000。

b)在最后一行中:

代码语言:javascript
代码运行次数:0
运行
复制
UCSRC = (1<<URSEL) + (1<<UCSZ1) + (1<<UCSZ0);

这是组合由每个常量产生的位掩码(同样,其值是从右起的位位置)。问题是程序员决定使用+运算符而不是二进制OR,因为他/她知道当你没有“碰撞”位时,他们的结果是相同的。就我个人而言,我总是使用二进制OR,以表明我所做的不是算术加法。

c)最后,关于第一行

代码语言:javascript
代码运行次数:0
运行
复制
UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;

看起来更复杂,显然是时钟划分的因素,我必须看文档才能更好地理解。我现在不担心,它可能会在时间上变得清晰。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3397421

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档