Skip to content

Instantly share code, notes, and snippets.

@projectgus
Created February 17, 2010 21:51
Show Gist options
  • Save projectgus/307053 to your computer and use it in GitHub Desktop.
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)
#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("");
}
@Mokrane82
Copy link

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