在 4 位数据模式下,是否可以将其他 4 位用于其他内容?

When on 4 bit data mode, is it possible to use the other 4 bits for other stuff?

所以我正在编写一个 C 程序以在 4 位数据模式下连接 LCD。然而,我想知道我是否可以使用其他 4 位来做其他事情,比如外部中断。 更具体地说,我将 PORTD 用于 arduino 上的数据线,但是我还需要引脚 PD2 才能使用 INT0 中断(按钮)。在我的程序中,我注意到我在发送命令时一直将低 4 位设置为 0:

PORTD = cmd & 0xf0;
flashLCD();
PORTD = (cmd & 0x0f) << 4;

这非常有效,但它会将其他位设置为 0。这是在计时器上调用的,这意味着我会重复发送命令。因此,我尝试保存寄存器的先前值并附加一些按位运算:

uint8_t initial_state = PORTD;
PORTD = (cmd & 0xf0) | (initial_state & 0x0f);
flashLCD();
PORTD = ((cmd & 0x0f) << 4) | (initial_state & 0x0f)

它在LCD 上发送cmd,但是它仍然不会响应中断。我想知道是否有什么我没有考虑到的,或者我的逻辑是否不正确。谢谢

编辑:Nvm 我明白了。即使在 4 位模式下,我的 LCD 库也总是将端口寄存器重置为 0,因此其他未使用的端口也被重置。我刚刚更改了库,以便在 4 位模式下可以使用其他端口。

我不知道 Arduino 上使用的控制器(ATmega/AVR 或其他),你甚至没有指定你使用的是哪个。

但通常,端口和端口引脚可以配置为输入或输出模式。由于您在 PORTD 上给出了 4 位,我假设整个 8 位端口(所有端口引脚)都配置为输出。

你应该考虑你的手册,整个端口的某些管脚是否可以同时作为输出管脚,而其他管脚可以同时作为输入管脚,以及如何。

小心切换 input/output 之间的整个端口,它可能会产生副作用,例如不需要的 H-L / L-H 转换。我有一次使用 8255 输出芯片,由于连接了 LCD 的重置线,导致我的图形 LCD 重置。

您正确地注意到可以使用按位运算保留其他位

PORTD = (PORTD & 0x0F) | (high_bits << 4);

但是!这一行将被编译成几条机器指令:

  1. 将 PORTD 值加载到寄存器中
  2. 对寄存器执行按位与运算
  3. 加载high_bits到另一个寄存器
  4. 执行其他计算(高位左移等)
  5. 按位执行或
  6. 将结果存回 PORTD

让我们想象一下,在 1 到 6 之间的某处触发中断,它停止代码执行,并更改 PORTD 的低位。中断例程完成后,程序继续执行,将中断前寄存器中存储的内容重写 PORTD 的所有第 8 位,从而覆盖 PORTD 的低位,丢弃中断例程中所做的所有更改。

因此,有两种方法可以使对 PORTx 的写操作成为原子操作。

首先: 只在 PORTx 寄存器更新时禁用中断:

uint8_t old_sreg = SREG; // save SREG register (including I flag)
cli(); // clear I flag, thus prohibiting interrupts
PORTD = (PORTD & 0x0F) | (high_bits << 4); // perform the operation
SREG = old_sreg; // restoring SREG and I flag, if it was set before

第二种 方法仅适用于新的 AVR 内核(例如 ATmega328、1284、2560 等),但不适用于旧内核(ATmega8、32 等)。请参阅数据表,I/O-Ports -> Ports as General Digital I/O -> Toggling the Pin 部分。 将 1 写入 PINx 的位将反转 PORTx 的相应位。使用它,可以仅更新 PORTx 寄存器的所需位,而其他位保持不变,从而无需锁定中断。它可能在时间紧迫的环境中很有用,在这种情况下,中断应该尽快触发。

PIND = (PORTD ^ (high_bits << 4)) & 0xF0;

当然,如果它保证中断可能只改变 PORTD 的其他(在这个例子中 - 较低的)位,它当然会起作用。如果中断也可以写入相同的位,那么这可能会导致意想不到的结果,所以要小心。