Hello. I have a problem with the LIN bus on Arduino Mega. It seems to be okay when it comes to the physical aspect. MASTER and SLAVE built on Arduino MEGA + MCP2003B. CS and RxD WAKE to be pulled etc., Here is certainly good. MASTER connected to Serial1 first Arduino and SLAVE to Serial1 second Arduino.
Sending works, for example, if I send a MASTER data frame with data as in the screenshot below, SLAVE receives it and displays it on the Monitor series.
The problem is when I try to send a frame with only the ID and SLAVE has to answer by sending the data + checksum. First of all, it does not always do this, as you can see below, and secondly if it suits, these ID frames start to mix with the SLAVE answer. What am I doing wrong?
MASTER:
#include "lin_stack1.h"
uint8_t id = 0xEC;
lin_stack MASTER(1,id); // Creating LIN Stack objects
byte dane[4];
unsigned long lastMilli = 0; // loop timing
byte b;
HardwareSerial * active;
void setup() {
active = &Serial;
active -> begin(19200);
MASTER.setSerial();
}
void loop() {
////wyświetlanie informacji na LCD
if((millis()-lastMilli) >= 1000) {
lastMilli = millis();
MASTER.writeRequest(id);
b = MASTER.read(dane,4);
active -> println(b);
active -> println(dane[0],HEX);
active -> println(dane[1],HEX);
active -> println(dane[2],HEX);
active -> println(dane[3],HEX);
active -> println(" ");
}
}
SLAVE:
#include "lin_stack1.h"
uint8_t id = 0xEC;
lin_stack SLAVE(1,id); // Creating LIN Stack objects, 2 - second channel
byte package[4] = {0xDF, 0xFF, 0xBC, 0xCA};
void setup() {
SLAVE.setSerial();
}
void loop() {
if(SLAVE.readRequest())SLAVE.writeResponse(package,4);
}
Library for MASTER and SLAVE:
#include "lin_stack1.h"
/* LIN PACKET:
It consist of:
___________ __________ _______ ____________ _________
| | | | | |
|Synch Break|Synch Byte|ID byte| Data Bytes |Checksum |
|___________|__________|_______|____________|_________|
Every byte have start bit and stop bit and it is send LSB first.
Synch Break - 13 bits of dominant state ("0"), followed by 1 bit recesive state ("1")
Synch Byte - Byte for Bound rate syncronization, always 0x55
ID Byte - consist of parity, length and address; parity is determined by LIN standard and depends from address and message length
Data Bytes - user defined; depend on devices on LIN bus
Checksum - inverted 256 checksum; data bytes are sumed up and then inverted
*/
// CONSTRUCTORS
lin_stack::lin_stack(byte channel){
sleep_config(channel); // Configurating Sleep pin for transceiver
}
lin_stack::lin_stack(byte channel, byte ident){
sleep_config(channel); // Configuration of Sleep pin for transceiver // Transceiver is always in Normal Mode
identByte = ident; // saving idet to private variable
}
// PUBLIC METHODS
// WRITE methods
// Creates a LIN packet and then send it via USART(Serial) interface.
int lin_stack::write(byte ident, byte data[], byte data_size){
// Calculate checksum
unsigned int checksum =0;
unsigned int suma = 0;
for(int i=0;i<data_size;i++){ suma = suma + data[i];
//suma = suma + 1;
if(suma>=256)suma-=255;
}
suma = 0xFF&(~suma);
checksum = suma;
// Start interface
// Synch Break
serial_pause(13);
// Send data via Serial interface
HardwareSerial * active;
if (ch == 1){ // For LIN1 or Serial1
active = &Serial1;
} else if (ch == 2) {
active = &Serial2;
}
active -> begin(bound_rate); // config Serial
active -> write(0x55); // write Synch Byte to serial
active -> write(ident); // write Identification Byte to serial
for(int i = 0; i < data_size; i++)
active -> write(data[i]); // write data to serial
active -> write(checksum); // write Checksum Byte to serial
active -> end(); // clear Serial config
return 1;
}
int lin_stack::writeRequest(byte ident){
// Synch Break
serial_pause(13);
// Start interface
HardwareSerial * active;
if (ch == 1){ // For LIN1 or Serial1
active = &Serial1;
} else if (ch == 2) {
active = &Serial2;
}
active -> begin(bound_rate); // config Serial
active -> write(0x55); // write Synch Byte to serial
active -> write(ident); // write Identification Byte to serial
active -> end(); // clear Serial config
return 1;
}
int lin_stack::writeResponse(byte data[], byte data_size){
// Calculate checksum
unsigned int checksum =0;
unsigned int suma = 0;
for(int i=0;i<data_size;i++){ suma = suma + data[i];
if(suma>=256)suma-=255;
}
suma = 0xFF &(~suma);
checksum = suma;
HardwareSerial * active;
if (ch == 1){ // For LIN1 or Serial1
active = &Serial1;
} else if (ch == 2) {
active = &Serial2;
}
active -> begin(bound_rate); // config Serial
active -> write(data, data_size); // write Synch Byte to serial
active -> write(checksum); // write Identification Byte to serial
active -> end(); // clear Serial config
return 1;
}
// READ methods
// Read LIN traffic and then proces it.
int lin_stack::setSerial(){ // Only needed when receiving signals
HardwareSerial * active;
if (ch == 1){ // For LIN1 or Serial1
active = &Serial1;
} else if (ch == 2) {
active = &Serial2;
}
active -> begin(bound_rate); // config Serial
}
int lin_stack::read(byte data[], byte data_size){
byte rec[data_size+3];
byte c=0;
HardwareSerial * active;
if (ch == 1){ // For LIN1 or Serial1
active = &Serial1;
} else if (ch == 2) {
active = &Serial2;
}
active -> begin(bound_rate); // config Serial
if(active -> read() > 0){ // Check if there is an event on LIN bus
active -> readBytes(rec,data_size+3);
if((validateParity(rec[1]))&(validateChecksum(rec,data_size+3))){
for(int j=0;j<data_size;j++){
data[j] = rec[j+2];
}
}else{
active -> end(); // clear Serial config
return -1;
}
}
active -> end(); // clear Serial config
return 0;
}
int lin_stack::readRequest(void){
byte rec[3];
HardwareSerial * active;
if (ch == 1){ // For LIN1 or Serial1
active = &Serial1;
} else if (ch == 2) {
active = &Serial2;
}
active -> begin(bound_rate); // config Serial
if(active -> read() != -1){ // Check if there is an event on LIN bus
active -> readBytes(rec,3);
if((validateParity(rec[1]))){
active -> end(); // clear Serial config
return 1;
}else{
active -> end(); // clear Serial config
return -1;
}
}
return 0;
}
// PRIVATE METHODS
int lin_stack::serial_pause(int no_bits){
HardwareSerial * active;
// Calculate delay needed for 13 bits, depends on bound rate
unsigned int del = period*no_bits; // delay for number of bits (no-bits) in microseconds, depends on period
if(ch==1){
pinMode(18, OUTPUT);
digitalWrite(18, LOW);
delayMicroseconds(del); // delay
digitalWrite(18, HIGH);
}else if(ch==2){
pinMode(16, OUTPUT);
digitalWrite(16, LOW);
delayMicroseconds(del); // delay
digitalWrite(16, HIGH);
}
return 1;
}
int lin_stack::sleep_config(byte serial){
if(serial==1){ // When using LIN1 channel - usign Serial1
pinMode(A4, OUTPUT);
digitalWrite(A4, HIGH);
ch=1; // saved as private variable, used for determening Serial port
}else if(serial==2){ // When using LIN2 channel - usign Serial2
pinMode(A5, OUTPUT);
digitalWrite(A5, HIGH);
ch=2; // saved as private variable, used for determening Serial port
}
return 1;
}
boolean lin_stack::validateParity(byte ident) {
if(ident == identByte)
return true;
else
return false;
}
boolean lin_stack::validateChecksum(unsigned char data[], byte data_size){
unsigned int v_checksum = 0;
unsigned int checksum = data[data_size-1];
unsigned int suma = 0;
for(int i=2;i<data_size-1;i++){ suma = suma + data[i];
if(suma>=256)suma-=255;
}
suma = 0xFF&(~suma);
v_checksum = suma;
if(checksum==v_checksum)
return true;
else
return false;
}