You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
5.4 KiB
204 lines
5.4 KiB
/* |
|
Library for Software Serial over RS485 connection |
|
Created by Stepan Richter, November 2023 |
|
*/ |
|
|
|
#include "Arduino.h" |
|
#include "SoftRS485.h" |
|
|
|
//#define DEBUG |
|
#define MSG_LEN 64 |
|
|
|
#define IDLE 0 |
|
#define START 1 |
|
#define RECEIVING 2 |
|
#define SENDING 3 |
|
#define COMPLETE 4 |
|
|
|
// Pins |
|
int _RO, _RE, _DE, _DI; |
|
int _idle_cycles = 0; |
|
|
|
/* INCOMING */ |
|
char _recv_message[MSG_LEN]; |
|
byte _recv_byte = 0x00; |
|
byte _recv_state = IDLE; |
|
boolean _recv_bit = false; |
|
byte _recv_idx = 0; |
|
int _recv_pos = 0; |
|
|
|
|
|
|
|
/* OUTGOING */ |
|
char _send_message[MSG_LEN]; |
|
boolean _send_bit = false; |
|
byte _send_state = IDLE; |
|
byte _send_wait = 0; |
|
byte _send_idx = 0; |
|
int _send_pos = 0; |
|
|
|
ISR(TIMER1_COMPA_vect){ |
|
OCR1A += 3228; // Advance The COMPA Register |
|
|
|
if (_send_state == SENDING){ |
|
char c = _send_message[_send_pos]; |
|
_send_bit = c & 1<<_send_idx; |
|
digitalWrite(_DI,!_send_bit); |
|
#ifdef DEBUG |
|
Serial.print("Sending bit "); Serial.print(_send_idx); Serial.print(" of "); Serial.print(c); Serial.print(": "); Serial.print(_send_bit); Serial.print(" / "); |
|
#endif |
|
_send_idx++; |
|
if (_send_idx==8){ // byte completed |
|
_send_pos++; // advance to next byte |
|
_send_idx = 0; // start with first bit |
|
if (c == 0x00 || _send_idx == MSG_LEN){ // we just send a 0-byte |
|
_send_state = COMPLETE; |
|
} |
|
} |
|
} |
|
if (_send_state == START && _idle_cycles > 9){ |
|
_send_bit = HIGH; |
|
digitalWrite(_DE,HIGH); |
|
digitalWrite(_DI,!_send_bit); |
|
_send_state = SENDING; |
|
_send_idx = 0; |
|
_send_pos = 0; |
|
#ifdef DEBUG |
|
Serial.print("Sent start bit: "); Serial.print(_send_bit); Serial.print(" / "); |
|
#endif |
|
} |
|
|
|
_recv_bit = !digitalRead(_RO); // LOW = 1 |
|
|
|
if (_recv_bit) { |
|
_idle_cycles = 0; |
|
} else if (_idle_cycles<1000) { |
|
_idle_cycles++; |
|
} |
|
#ifdef DEBUG |
|
Serial.print("idle cycles: "); Serial.print(_idle_cycles); Serial.print(" / "); |
|
Serial.print("received: "); Serial.print(_recv_bit); Serial.print(" / "); |
|
#endif |
|
|
|
if (_send_bit != _recv_bit && _send_state == SENDING){ // collision while sending |
|
digitalWrite(_DE,LOW); // disable driver |
|
_send_state = START; // start over |
|
_idle_cycles = 0; |
|
#ifdef DEBUG |
|
Serial.println("\nCOLLISION"); |
|
#endif |
|
} |
|
|
|
if (_send_state == COMPLETE){ |
|
digitalWrite(_DE,LOW); // disable driver |
|
_send_message[0]=0x00; // clear message |
|
_send_state = IDLE; |
|
_idle_cycles =0; |
|
#ifdef DEBUG |
|
Serial.print("transmission complete / "); |
|
#endif |
|
} |
|
|
|
|
|
|
|
if (_recv_state == START){ |
|
_recv_byte = 0x00; |
|
_recv_pos = 0; |
|
_recv_idx = 0; |
|
_recv_state = RECEIVING; |
|
#ifdef DEBUG |
|
Serial.print("Start reception / "); |
|
#endif |
|
} |
|
|
|
if (_recv_bit && _recv_state == IDLE){ |
|
_recv_state = START; |
|
} |
|
|
|
if (_recv_state == RECEIVING){ |
|
_recv_byte |= _recv_bit << _recv_idx; // set bit |
|
_recv_idx++; // increase bit index |
|
if (_recv_idx == 8){ // byte full |
|
#ifdef DEBUG |
|
Serial.print("pushing "); Serial.print((char)_recv_byte); Serial.print(" / "); |
|
#endif |
|
|
|
_recv_message[_recv_pos] = _recv_byte; // push character to string |
|
if (_recv_byte == 0x00) { // end of message |
|
_recv_state = IDLE; |
|
#ifdef DEBUG |
|
Serial.print("reception complete: "); Serial.print(_recv_message); |
|
#endif |
|
} |
|
_recv_idx=0; // start new byte |
|
_recv_byte = 0; |
|
_recv_pos++; // goto next string pos |
|
if (_recv_pos == MSG_LEN) _recv_pos--; // avoid overflow |
|
} |
|
} |
|
#ifdef DEBUG |
|
Serial.println(); |
|
#endif |
|
} |
|
|
|
void init485(int RO, int nRE, int DE, int DI){ |
|
_RO=RO; |
|
_RE=nRE; |
|
_DE=DE; |
|
_DI=DI; |
|
_recv_message[0] = 0x00; // empty |
|
_send_message[0] = 0x00; // empty |
|
pinMode(_RO,INPUT); |
|
|
|
pinMode(_RE,OUTPUT); |
|
digitalWrite(_RE,LOW); // enable reading |
|
|
|
pinMode(_DE,OUTPUT); |
|
digitalWrite(_DE,LOW); // disable writing |
|
|
|
pinMode(_DI,OUTPUT); |
|
digitalWrite(_DI,LOW); // output line = LOW |
|
|
|
TCCR1A = 0; // Init Timer1A |
|
TCCR1B = 0; // Init Timer1B |
|
TCCR1B |= B00000001; // Prescaler = 1 |
|
OCR1A = 3328; // Timer Compare1A Register |
|
TIMSK1 |= B00000010; // Enable Timer COMPA Interrupt |
|
#ifdef DEBUG |
|
Serial.begin(115200); |
|
Serial.println("Started timer!"); |
|
Serial.print("RO = "); |
|
Serial.println(_RO); |
|
Serial.print("^RE = "); |
|
Serial.println(_RE); |
|
Serial.print("DE = "); |
|
Serial.println(_DE); |
|
Serial.print("DI = "); |
|
Serial.println(_DI); |
|
#endif |
|
} |
|
|
|
boolean available485(){ |
|
return _recv_state == IDLE && _recv_message[0] != 0; |
|
} |
|
|
|
String get485Message(){ |
|
String res = String(_recv_message); |
|
_recv_message[0] = 0; |
|
return res; |
|
} |
|
|
|
boolean send485(char message[]){ |
|
if (_send_message[0] != 0x00) return false; // we already have a queued message, abort! |
|
for (int idx = 0; idx<MSG_LEN; idx++){ |
|
_send_message[idx] = message[idx]; // copy message |
|
if (message[idx] == 0x00) break; |
|
} |
|
if (_send_message[0] == 0x00) return false; // message is empty! abort |
|
_send_state = START; |
|
|
|
#ifdef DEBUG |
|
Serial.print("sending "); Serial.println(_send_message); |
|
#endif |
|
return true; |
|
}
|
|
|