ATmega328p单片机C语言三变量XOR逻辑LED输出异常求助
问题描述
在学校项目中用C语言给ATmega328p编程,需实现对应真值表的三变量XOR逻辑并输出到LED。其余三个LED的逻辑门(AND、OR、NOR)功能正常,但XOR部分存在异常:本该在按下2个按钮时熄灭,却仅在无按钮按下时熄灭。尝试过布尔表达式y = A'B'C + A'BC' + AB'C' + ABC,问题仍未解决。
真值表

异常代码片段
if ( ((BTS & _BV(BT1)) ^ (BTS & _BV(BT2))) || ((BTS & _BV(BT2)) ^ (BTS & _BV(BT3)))) //EXOR { LEDS |= _BV(LED2); //Set 1 on LED2 } else { LEDS &= ~_BV(LED2); //Set 0 on LED2 }
完整代码
#include <avr/io.h> #define F_CPU 16000000UL #include <util/delay.h> #define delay 500 //--Tags //-Inputs #define BTS PINB #define BT1 PINB0 #define BT2 PINB1 #define BT3 PINB2 //-Outputs #define LEDS PORTD #define LED0 PORTD2 #define LED1 PORTD3 #define LED2 PORTD4 #define LED3 PORTD5 void init_ports(void); int main(void) { init_ports(); while (1) { //Output 1 if ((BTS & _BV(BT1)) && (BTS & _BV(BT2)) && (BTS & _BV(BT3))) // AND { LEDS |= _BV(LED0); //Set 1 on LED0 } else { LEDS &= ~ _BV(LED0); //Set 0 on LED0 } //Output 2 if ((BTS & _BV(BT1)) || (BTS & _BV(BT2)) || (BTS & _BV(BT3))) // OR { LEDS |= _BV(LED1); //Set 1 on LED1 } else { LEDS &= ~ _BV(LED1); //Set 0 on LED1 } //Output 3 if ( ((BTS & _BV(BT1)) ^ (BTS & _BV(BT2))) || ((BTS & _BV(BT2)) ^ (BTS & _BV(BT3)))) //EXOR { LEDS |= _BV(LED2); //Set 1 on LED2 } else { LEDS &= ~_BV(LED2); //Set 0 on LED2 } //Output 4 if (!((BTS & _BV(BT1)) && (BTS & _BV(BT2)) && (BTS & _BV(BT3)))) // NOR { LEDS |= _BV(LED3); //Set 1 on LED3 } else { LEDS &= ~_BV(LED3); //Set 0 on LED3 } } } void init_ports (void) { //--Inputs DDRB &= ~(_BV(BT1) | _BV(BT2) | _BV(BT3)); //-PULL-UP PORTB &= ~(_BV(BT1) | _BV(BT2) | _BV(BT3)); //--Outputs DDRD |= (_BV(LED0) | _BV(LED1) | _BV(LED2) | _BV(LED3)); //-Off PORTD &= ~(_BV(LED0) | _BV(LED1) | _BV(LED2) | _BV(LED3)); }
故障原因
1. 三变量异或逻辑表达式错误
你编写的((BT1 ^ BT2) || (BT2 ^ BT3))完全不符合三变量异或(奇偶校验)的核心逻辑。三变量异或的正确逻辑是输入中1的个数为奇数时输出1,偶数时输出0,对应的最简表达式是A^B^C,和你尝试的A'B'C + A'BC' + AB'C' + ABC等价。
你的错误表达式会导致:
- 任意两个输入不同时就输出1,比如BT1=1、BT2=0、BT3=0时,
BT1^BT2为1,整体表达式为1,LED点亮(符合预期); - 但当BT1=1、BT2=1、BT3=0时,
BT2^BT3为1,整体表达式为1,LED点亮(不符合预期,此时应熄灭); - 只有三个输入全0时,两个异或结果都为0,表达式为0,LED熄灭,这就是你看到的异常现象。
2. 按钮输入的电平稳定性问题
从初始化代码看,你关闭了PORTB的内部上拉电阻:PORTB &= ~(_BV(BT1) | _BV(BT2) | _BV(BT3));,这意味着按钮未按下时输入引脚处于浮空状态,电平不确定。虽然其他逻辑门看似正常,但浮空状态可能导致输入电平波动,也会干扰XOR的判断。按钮输入必须确保未按下时电平稳定,通常启用内部上拉或外接下拉电阻。
修正方案
方案1:修正三变量异或逻辑表达式
将XOR部分代码替换为正确的异或逻辑,推荐两种写法:
写法1:转换为布尔值后异或
// 把每个按钮输入转换为0/1的布尔值 uint8_t a = (BTS & _BV(BT1)) ? 1 : 0; uint8_t b = (BTS & _BV(BT2)) ? 1 : 0; uint8_t c = (BTS & _BV(BT3)) ? 1 : 0; if (a ^ b ^ c) { // 三变量异或,奇数个1时为真 LEDS |= _BV(LED2); } else { LEDS &= ~_BV(LED2); }
写法2:用内置函数统计1的个数
// 提取三个按钮的输入位,统计1的个数是否为奇数 uint8_t btn_input = BTS & (_BV(BT1)|_BV(BT2)|_BV(BT3)); if (__builtin_popcount(btn_input) % 2 == 1) { LEDS |= _BV(LED2); } else { LEDS &= ~_BV(LED2); }
__builtin_popcount是GCC内置函数,可直接计算二进制中1的个数,适配AVR平台。
方案2:修复按钮输入的电平稳定性
启用PORTB的内部上拉电阻,修改init_ports函数:
void init_ports (void) { //--Inputs DDRB &= ~(_BV(BT1) | _BV(BT2) | _BV(BT3)); //-启用内部PULL-UP PORTB |= (_BV(BT1) | _BV(BT2) | _BV(BT3)); //--Outputs DDRD |= (_BV(LED0) | _BV(LED1) | _BV(LED2) | _BV(LED3)); //-Off PORTD &= ~(_BV(LED0) | _BV(LED1) | _BV(LED2) | _BV(LED3)); }
此时按钮未按下时输入为高电平(1),按下时接地为低电平(0),需同步修改逻辑判断(比如将BTS & _BV(BT1)改为!(BTS & _BV(BT1)),表示按下时为有效输入1),具体根据硬件接线调整。
内容的提问来源于stack exchange,提问作者Daniel Eduardo

