【问题标题】:explicitly setting and unsetting some pins of a port显式设置和取消设置端口的某些引脚
【发布时间】:2014-01-12 06:55:13
【问题描述】:

我有两个方便的屏蔽(即没有引脚冲突)共享一个端口,我需要能够仅操作端口上的一些引脚。但是我不能确定我是在打开还是关闭引脚,我只是想在需要时任意设置它们,即在一次操作中我可能会打开一些引脚和关闭一些引脚。

我知道:

PORTX |= B11110000 // turns on bits 4-7
PORTX &= B11000011 // turns off bits 2-5
PORTX ^= B00111111 // toggles bits 0-5

我的挑战是只打开和关闭一些引脚,而其他引脚保持不变。

我已经达到了预期的结果,并且就我认为我以安全的方式完成它而言,我想确认它实际上是安全的,并且我是否以正确(最佳)的方式进行了处理,或者我能以更简单的方式实现这一目标吗?

首先,我使用的是 PORTD,引脚 4-7。我将这些引脚设置为输出,然后将它们全部设置为低电平,以确保我的程序从它们(4x 继电器)全部关闭开始。

void initRelays(){
  RELAYDDR |= B11110000;
  RELAYPORT &= ~RELAYDDR;
}

我相信 tis 会在不修改低位的情况下将引脚 4-7 设置为关闭,因为按位与为零。我相信这将保留之前设置的 0-3 位。

将此值取反并将其与现有端口值进行与运算,将确保这些引脚处于关闭状态,而其他位保持不变。我确定这条线不是必需的,为了安全起见,我把它放在这里:)

我在下面的代码中留下了 cmets,以便您尝试了解我在做什么。

void relayPush(byte stack){
  // stack has bit 1 to relay 1 (pin 4), thru bit 4 to relay 4 (pin 7)
  // take stack and isolate the four bottom bits (the information we want to convert)
  stack &= B00001111; // (1) I think this line is probably not required

  // now shift to the position we need
  stack <<= 4;        // (2)

  // OR the new stack with the PORT 
  // (this turns on any relays set in stack)
  RELAYPORT |= stack; // (3)

  // we need to NOT modify the bottom bits of the port
  // mark those with a '1' so as to not turn them off
  // bottom of stack mask = 0x0f
  // XOR stack and mask
  stack ^= 0x0f;       // (4)

  // AND new stack and port to turn off appropriate relays  
  RELAYPORT &= stack; // (5)
}

我知道我已经在两个 PORT 操作中完成了它,我可以通过使用临时变量来完成它,这不是主要问题,因为它只是在第一个实例中打开所有需要打开的东西,然后关闭所有东西这在第二种情况下是必需的。

是否错过了一种更简单的方法?

编辑:我看过@Ignacio 所说的关于更改最终操作的内容,这就是我想出的:

     0011 0011    current port assignment
xxxx 1010         current stack assignment (we only want the lower nibble)
     1010 0011    desired result

0011 0011    current port
xxxx 1010    current stack
0000 1111    step 1 - apply this mask to stack
0000 1010    resultant stack
1010 0000    step 2 - stack << 4

0011 0011    PORT
1010 0000    STACK
1010 0011    step 3 - resultant PORT (port OR stack)

0000 1111    (MASK for step 4)
1010 0000    stack at step 4
1010 1111    step 4 - resultant stack (mask XOR stack)

1010 0011    port from step 3
1010 1111    stack from step 4
1010 0011    port AND stack (desired result)

/// changing steps 4 and 5 to drop XOR, and applying complement  

1010 0000    stack prior to step 4
0101 1111    ~stack
1010 0011    port from step 3
0000 0011    stack AND port (not the desired result)

总结:

  1. 需要 XOR 将底部半字节填充到 B00001111 并保持顶部半字节不变。因为我们知道底部半字节是零(来自之前的移位),我们可以简单地添加 0x0F。 XOR 实现了同样的目的。

  2. 对于最终的 AND 操作,我们需要关闭顶部半字节零。因此,没有补充。

我对@Ignacio 的评论中的新想法:

0011 0011    current port
xxxx 1010    current stack

1010 0000    shifted stack
0000 0011    temp = port AND 0x0F
1010 0011    stack OR temp (desired result)

抱歉,这篇文章很长,但我认为这是一个更好的解决方案,尽管它确实使用了另一个变量。

【问题讨论】:

  • Arduino UNO - ATMega328P

标签: arduino bit-manipulation avr


【解决方案1】:

RELAYPORT 的 and 操作会清除高 4 位。您不应该执行前面的异或运算,而应该只是将其与补码相加。

RELAYPORT &= ~stack;

【讨论】:

  • 感谢@Ignacio,但我尝试应用您的想法,但没有成功。我已经编辑了 OP 来说明这一点。我不确定你是否错过了它,但它是关于必须打开和关闭正确的引脚,同时保持下半字节不变。
  • 现在我正在考虑它,我认为我需要做的就是取新堆栈的上半部分(移位后下半字节为零),或者它与下半部分港口的。我可以在一次手术中做到这一点。我将返回并(再次)编辑我的帖子以进行说明。
【解决方案2】:

一些想法...基于我对 megaAVR 的经验(尤其是 AT90USB1287)

当您拆分一个端口并操作一些位作为输入和一些位作为输出时,我建议在编写整个输出端口时要格外小心。 AVR中有很好的BIT指令。如果你想写一个完整的端口,请记住写入配置为输入的位确实有效果,即激活(PORTxy&lt;-1)或停用(PORTxy&lt;-0)内部上拉电阻 -因此,根据您的硬件,您必须选择用于“不需要的”位的内容(另一个依赖于 MCUCR(PUD)。换句话说,您写出的寄存器中的(被忽略的)输入位可以' t 包含任何随机值,但正是那些支持内部上拉配置的值。在将(部分)写回端口之前读入PINx 是没有用的。(这在端口硬件较少的旧处理器上使用比 AVR 处理器更详细)

写出PORTx 时,在读回PINx 之前插入NOP(由于内部锁存器)。

initRelays() 中,我会使用一个常量,因为它编译速度更快(单指令),而不是RELAYDDR 的函数,后者涉及将RELAYDDR 读回寄存器并将寄存器写入RELAYPORT

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多