Created
February 17, 2010 21:51
-
-
Save projectgus/307053 to your computer and use it in GitHub Desktop.
Arduino sketch to monitor a 125 kbps VAN automotive bus (similar to CAN bus)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <avr/interrupt.h> | |
const int pin_rx = 3; // Pin hooked to the output of the CAN transceiver | |
enum message_state { vacant, loading, ready }; | |
const int ticksPerTs = 132; // 16Mhz clock / 125Kbps / 8 = 128 ticks/bit | |
const int timerLoadValue = 257 - ticksPerTs; | |
const int oneHalfTimerLoadValue = 257 - (ticksPerTs * 1.25); // Add an extra 1/4TS when we know we're at start of a bit | |
const int buffer_len = 36; | |
unsigned char buffer[buffer_len]; | |
unsigned char buffer_size = 0; | |
volatile message_state state = vacant; | |
struct message | |
{ | |
unsigned char contents[buffer_len]; | |
unsigned char size; | |
}; | |
void SetupTimer2() | |
{ | |
// Prescaler 0/0/1 means run at no prescaler | |
TCCR2A = 0; | |
TCCR2B = 0<<CS22 | 0<<CS21 | 1<<CS20; | |
//load the timer for its first cycle | |
TCNT2= timerLoadValue - (ticksPerTs); // Not sure we need to wait 2 full TS here for the first bitread, but it works... | |
//Timer2 Overflow Interrupt Enable | |
TIMSK2 |= 1<<TOIE2; | |
} | |
void DisableTimer2() | |
{ | |
TIMSK2 &= ~(1<<TOIE2); | |
} | |
// Enable interrupt 1 (pin 3) | |
void SetupInterrupt1() | |
{ | |
// Accidentally had this set to "low level" instead...? | |
EICRA = (1<<ISC11) | (0<<ISC10); // Falling edge (Rising 1/1, Falling 1/0, Any 0/1) | |
EIMSK |= (1<<INT1); // Interrupt 1 (pin 3) | |
sei(); | |
} | |
void DisableInterrupt1() { | |
EIMSK &= ~(1<<INT1); | |
} | |
// rx pin interrupt | |
ISR(INT1_vect) | |
{ | |
if(state != vacant) | |
return; | |
DisableInterrupt1(); // No more interrupts | |
state = loading; | |
SetupTimer2(); // Start timer to clock in next bit | |
} | |
/*Timer2 overflow interrupt vector handler | |
* | |
* Called each time we have a new bit on rx pin, apart from the 5th "Manchester" bit which comes via interrupt | |
* | |
*/ | |
ISR(TIMER2_OVF_vect) | |
{ | |
TCNT2+=timerLoadValue; | |
static unsigned char current = 0; | |
static unsigned char bitCounter = 1; | |
unsigned char byteFlag; // 0 = midbyte, 1 = finished full byte, 2 = finished packet or error | |
asm volatile( | |
"LDI %[byteFlag], 0" "\n\t" | |
"CLC" "\n\t" | |
"SBIC %[portd], %[pin_rx]" "\n\t" | |
"SEC" "\n\t" | |
"ROL %[current]" "\n\t" | |
"LSL %[bitCounter]" "\n\t" | |
"BRCS readFullByte" "\n\t" // Carry bit set means entire byte clocked in, waiting on Manchester | |
"SBRC %[bitCounter], 4" "\n\t" // Bit 5 set means first nibble clocked in, waiting on Manchester | |
"JMP waitManchester" "\n\t" | |
"JMP done" "\n\t" | |
"readFullByte:" "\n\t" | |
"LDI %[byteFlag], 1" "\n\t" | |
"waitManchester:" "\n\t" | |
// Wait for the last bit we read to flip over for Manchester | |
"MOV __tmp_reg__, __zero_reg__" "\n\t" // tmpreg for timeout | |
"SBRS %[current], 0" "\n\t" | |
"JMP waitForOne" "\n\t" | |
"waitForZero:" "\n\t" // 10 cycles top to bottom | |
"STS %[tcnt2], __zero_reg__" "\n\t" | |
"SBIS %[portd], %[pin_rx]" "\n\t" | |
"JMP gotManchester" "\n\t" | |
"INC __tmp_reg__" "\n\t" | |
"SBRS __tmp_reg__, 4" "\n\t" // bit 5 == 16 == >15 iterations = >150 clock cycles | |
"JMP waitForZero" "\n\t" | |
"JMP badManchesterBit" "\n\t" | |
"waitForOne:" "\n\t" // Almost same as waitForZero. Code reuse fail (should make a macro) | |
"STS %[tcnt2], __zero_reg__" "\n\t" | |
"SBIC %[portd], %[pin_rx]" "\n\t" | |
"JMP gotManchester" "\n\t" | |
"INC __tmp_reg__" "\n\t" | |
"SBRS __tmp_reg__, 4" "\n\t" | |
"JMP waitForOne" "\n\t" | |
// "JMP badManchesterBit" "\n\t" | |
"badManchesterBit:" "\n\t" // A "bad" Manchester bit hopefully means EOF, so bail out at this point | |
"LDI %[byteFlag], 2" "\n\t" | |
"JMP done" "\n\t" | |
"gotManchester:" "\n\t" | |
"MOV __tmp_reg__, r24" "\n\t" // Store r24 so we can use it. This is risky, but no other registers are used in these ops so it's OK. | |
"LDI r24, %[oneHalfTimerLoad]" "\n\t" | |
"STS %[tcnt2], r24" "\n\t" // We're at the beginning of the Manchester bit, so reset the timer to wait 1.5 bits to hit middle of next bit | |
"MOV r24, __tmp_reg__" "\n\t" | |
"done:" "\n\t" | |
: [current] "+r" (current), [byteFlag] "=r" (byteFlag), [bitCounter] "+r" (bitCounter) | |
: [oneHalfTimerLoad] "M" (oneHalfTimerLoadValue), | |
[pin_rx] "M" (pin_rx), [portd] "I" (_SFR_IO_ADDR(PIND)), [tcnt2] "i" (_SFR_MEM_ADDR(TCNT2)) | |
); | |
if (byteFlag) // Read entire byte, or done | |
{ | |
buffer[buffer_size++] = current; | |
if(byteFlag == 2 || buffer_size == buffer_len) // Done, due to bad bit or full buffer | |
{ | |
state = ready; | |
DisableTimer2(); | |
} | |
current = 0; | |
bitCounter = 1; | |
} | |
} | |
/* Scan the bytes in the buffer, read them into a new message structure | |
* | |
*/ | |
boolean readMessage(struct message *to) | |
{ | |
to->size = buffer_size; | |
for(unsigned char i = 0; i < buffer_size; i++) { | |
to->contents[i] = buffer[i]; | |
} | |
memset((void *)&buffer, 0, sizeof(buffer)); | |
buffer_size = 0; | |
state = vacant; | |
SetupInterrupt1(); | |
} | |
void setup(void) { | |
pinMode(pin_rx, INPUT); | |
//Start up the serial port | |
Serial.begin(115200); | |
//Signal the program start | |
Serial.println("Startup"); | |
Serial.println(timerLoadValue, DEC); | |
Serial.println(oneHalfTimerLoadValue, DEC); | |
struct message dummy; | |
readMessage(&dummy); // Initialise the buffer | |
} | |
void loop(void) { | |
while(state != ready) // Wait for a message | |
{ | |
#ifdef MEASURE_LATENCY | |
checkLatency(); | |
#endif | |
} | |
struct message msg; | |
readMessage(&msg); | |
Serial.print(millis()); | |
Serial.print(" "); | |
for(int i = 0; i < msg.size; i++) | |
{ | |
if(msg.contents[i] < 16) | |
Serial.print("0"); | |
Serial.print(msg.contents[i], HEX); | |
} | |
Serial.println(""); | |
} | |
Hello, I try to use you sketch for monitor the VANBUS of my citroen Xsara, but i connect the CAN transceiver (PCA82C250) and don't work. My board is an arduino mega 2560. Can you help me? Thank you.
Nowadays with ESP32 you can do it pretty easily (also from Arduino IDE): https://github.com/morcibacsi/esp32_rmt_van_rx
Bonjour
J'aimerais savoir a ce que je peux utiliser sa pour ma voiture
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Someone emailed me about this gist some 4+ years after it was posted here, so I figured I'd reproduce my email reply for future reference: