基于CAN总线(MCP2515)的双Arduino按钮状态传输问题咨询
问题描述
我计划用两台通过CAN总线(MCP2515模块)连接的Arduino检测按钮输入变化。发射端按钮使用内部下拉电阻,引脚设为外部中断。目前发射端代码未给CAN数据帧canMsg1和canMsg2的data字段赋值,想知道接收端能否通过这类消息识别输入引脚的状态?
发射端代码(原代码含笔误,已标注修正点)
#include <SPI.h> #include <mcp2515.h> struct can_frame canMsg1; struct can_frame canMsg2; MCP2515 mcp2515(10); int incPin(2); int decPin(3); unsigned long current_time = 0; unsigned long previous_time = 0; void setup() { Serial.begin(9600); SPI.begin(); mcp2515.reset(); mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ); mcp2515.setNormalMode(); canMsg1.can_id = 0xAA; canMsg1.can_dlc = 1; canMsg2.can_id = 0xBB; // 原代码缺分号,已修正 canMsg2.can_dlc = 1; pinMode(incPin, INPUT_PULLUP); pinMode(decPin, INPUT_PULLUP); // 原代码写为decnPin,已修正 // 原代码直接用引脚号,建议用digitalPinToInterrupt适配不同Arduino板型 attachInterrupt(digitalPinToInterrupt(incPin), inc, FALLING); attachInterrupt(digitalPinToInterrupt(decPin), dec, FALLING); } void loop() {} void inc() { current_time = millis(); if (current_time - previous_time > 200) { // 200ms消抖 mcp2515.sendMessage(&canMsg1); } previous_time = current_time; } void dec() { current_time = millis(); if (current_time - previous_time > 200) { // 200ms消抖 mcp2515.sendMessage(&canMsg2); } previous_time = current_time; }
接收端代码(原代码优化版)
#include <SPI.h> #include <mcp2515.h> struct can_frame canMsg; // 无需定义两个帧,用一个变量接收所有消息 MCP2515 mcp2515(10); int pos = 0; void setup() { Serial.begin(9600); SPI.begin(); mcp2515.reset(); mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ); mcp2515.setNormalMode(); } void loop() { if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) { switch(canMsg.can_id) { case 0xAA: pos++; Serial.print("当前pos值:"); Serial.println(pos); break; case 0xBB: pos--; Serial.print("当前pos值:"); Serial.println(pos); break; default: break; } } }
解答
核心结论
当前代码无法让接收端可靠识别引脚状态,原因如下:
- 发射端未初始化
canMsg1.data和canMsg2.data字段,发送的是内存随机值,接收端读取到的data[0]完全不确定,无法对应引脚的LOW/HIGH状态。 - 原接收端依赖
data[0] == LOW触发逻辑,但随机值可能偶然满足条件导致误触发,也可能永远不满足导致功能失效。
修正方案
发射端修改:明确赋值数据帧
在发送消息前,给data字段赋予明确的状态值,比如直接发送按钮按下时的LOW状态:
void inc() { current_time = millis(); if (current_time - previous_time > 200) { canMsg1.data[0] = LOW; // 明确标记按钮按下状态 mcp2515.sendMessage(&canMsg1); } previous_time = current_time; } void dec() { current_time = millis(); if (current_time - previous_time > 200) { canMsg2.data[0] = LOW; mcp2515.sendMessage(&canMsg2); } previous_time = current_time; }
也可以读取实时引脚状态发送:canMsg1.data[0] = digitalRead(incPin);,不过因为是下降沿中断触发,此时引脚状态必然是LOW,直接赋值更高效。
接收端适配
如果保留原接收端的data[0]判断逻辑,只需确保接收逻辑与发送端的赋值对应即可;如果仅通过can_id区分按钮动作(比如0xAA对应加、0xBB对应减),则可以完全忽略data字段,如优化后的接收端代码所示,逻辑更简洁。
内容的提问来源于stack exchange,提问作者Ktray
相关产品推荐
相关产品推荐

