我正在研究从汽车上的单线总线读取串行数据的接口(BMW IBUS)。我使用Microchip MCP2025 LIN总线收发器芯片将单线总线转换为常规Rx / Tx,以馈入Arduino(Nano V3)的硬件串行引脚(0& 1)。
宝马的IBUS协议使用9600波特的串行8E1。消息是可变长度(在5到37个字节之间)总线消息由大约11毫秒的暂停分开(比接收时的总线高电平时间长得多)并且我需要使用此间隙来检测一条消息的结束和开始下一个。
第一个字节是源ID, 第二个字节是数据包的长度,不包括前两个字节(源和长度), 第3个字节是目标ID, 第4个字节以后是实际数据, 最后一个字节是校验和
校验和是由整个数据包的XOR生成的,不包括校验和本身。
我的问题是我不知道如何测量消息之间的总线空闲时间。我想我可以使用bitRead(PIND,0)
来判断总线何时空闲(高),但我不知道如何计算时间并在适当的时间过后继续处理消息。
我目前正在使用以下代码来阅读来自IBUS的消息。它大部分都有效,但在连接到汽车时却不同步。它不知道消息何时完成,但依赖于每条消息的第二个字节(长度字节)来知道要读入多少字节。只要它正确读取长度字节,它很好,但是如果它不同步,并且长度字节错误,它会读取错误的字节数并最终超出数组并且Arduino重置。如果我能检测到消息之间的空闲时间,我将确定我有一个完整的消息。
void ReadIBUS()
{
boolean inSTATE = 0;
byte IBUSbyte[40];
while(Serial.available() > 0 && inSTATE == 0 )
{
IBUSbyte[0] = Serial.read(); //read source byte
while (inSTATE == 0)
{
if(Serial.available() > 0)
{
IBUSbyte[1] = Serial.read(); //read length byte
inSTATE = 1;
}
delay(10);
}
inSTATE == 0;
LENGTH = IBUSbyte[1];
int i = 2;
while(i <= LENGTH + 1)
{
if(Serial.available() > 0)
{
IBUSbyte[i] = Serial.read();
i++;
delay(10);
}
}
checksumBYTE = 0;
byteSTATE = 0;
for (int i = 0; i < LENGTH + 1; i++){
checksumBYTE ^= IBUSbyte[i];
}
if (IBUSbyte[LENGTH + 1] == checksumBYTE){
for(int i = 0; i <= LENGTH + 1; i++)
{
mySerial.print(IBUSbyte[i], HEX);
mySerial.print(" ");
}
mySerial.println();
}
else {
debug();
inSTATE = 0;
byteSTATE = 0;
}
}
}
答案 0 :(得分:0)
首先,delay
很少使用。在这种情况下,您可以使用millis
来衡量字符之间的长度,但前提是您丢失了delay
:
void ReadIBUS()
{
byte IBUSbyte[40];
do {
// First, make sure we're between frames, in the quiet interval
uint32_t last_rx = millis();
while (millis() - last_rx < 5) {
// Hasn't been long enough, see if more chars are coming
if (Serial.available()) {
Serial.read(); // throw away partial frame chars
last_rx = millis();
}
}
// Haven't received any chars for a while, we must be
// between frames. Now wait for a frame.
while (!Serial.available())
; // waiting...
IBUSbyte[0] = Serial.read(); //read source byte
while (!Serial.available())
; // waiting...
IBUSbyte[1] = Serial.read(); //read length byte
LENGTH = IBUSbyte[1];
// Check the LENGTH to make sure it's not bogus. If it is, try again.
} while (LENGTH > sizeof(IBUSbyte)-1);
// LENGTH is reasonable, wait for the payload
uint8_t bytes_received = 2;
while (bytes_received <= LENGTH + 1) {
if (Serial.available()) {
IBUSbyte[ bytes_received++ ] = Serial.read(); // read frame payload byte
}
}
// All here, verify the checksum
checksumBYTE = 0;
for (int i = 0; i < LENGTH + 1; i++) {
checksumBYTE ^= IBUSbyte[i];
}
if (IBUSbyte[LENGTH + 1] == checksumBYTE) {
for(int i = 0; i <= LENGTH + 1; i++) {
mySerial.print(IBUSbyte[i], HEX);
mySerial.print(" ");
}
mySerial.println();
} else {
debug();
}
}
不再需要状态变量。
顺便说一句,这个例程“阻止”程序的其余部分做任何事情,直到收到完整的帧。它应该被重写,以便经常调用它(例如来自loop
),并且在调用中记住last_rx
和bytes_received
。收到完整的帧后,它可以返回true
。这可行,它只是次优,因为它阻止了其他任何事情发生,除了中断。