From 900e4e436edca65e2f22a9a3abfd7ef9c4046a14 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Tue, 31 Oct 2023 17:17:40 +0100 Subject: [PATCH] first working implementation of timer-based RS485 Signed-off-by: Stephan Richter --- SoftRS485.cpp | 191 +++++++++++++++++++-- SoftRS485.h | 15 +- examples/SendAndReceive/SendAndReceive.ino | 44 +++++ 3 files changed, 220 insertions(+), 30 deletions(-) create mode 100644 examples/SendAndReceive/SendAndReceive.ino diff --git a/SoftRS485.cpp b/SoftRS485.cpp index e0c11ac..92e9948 100644 --- a/SoftRS485.cpp +++ b/SoftRS485.cpp @@ -6,30 +6,183 @@ #include "Arduino.h" #include "SoftRS485.h" -SoftRS485 SoftRS485::instance; +//#define DEBUG +#define IDLE -1 +#define START -2 +#define MSG_LEN 64 -SoftRS485 SoftRS485::singleton(){ - return instance; +int _RO, _RE, _DE, _DI; + +/* INCOMING */ +byte _recv_byte = 0x0; +boolean _recv_bit = false; +char _recv_message[MSG_LEN]; +int _recv_idx = 0; // used as bit-index for _recv_byte +int _recv_pos = IDLE; // used as char-index for _recv_message + +/* OUTGOING */ +char _message_out[MSG_LEN]; +int _send_pos = IDLE; +int _send_idx = 0; + +ISR(TIMER1_COMPA_vect){ + OCR1A += 62500; // Advance The COMPA Register + + _recv_bit = !digitalRead(_RO); // input line is HIGH by default + if (_recv_pos == IDLE){ + if (_recv_bit) { // first 1 indicates incoming message + _recv_pos = 0; +#ifdef DEBUG + Serial.println(F("start of message reception")); +#endif + } + } else { // we are reading! + _recv_byte |= _recv_bit << _recv_idx; +#ifdef DEBUG + Serial.print(F("received bit #")); + Serial.print(_recv_idx); + Serial.print(": "); + Serial.println(_recv_bit); + Serial.print(F("byte is now ")); + Serial.println(_recv_byte,BIN); +#endif + if (_recv_idx == 7){ // byte complete +#ifdef DEBUG + Serial.print(F("byte complete: ")); + Serial.println(_recv_byte); +#endif + _recv_idx = 0; + _recv_message[_recv_pos] = _recv_byte; + if (_recv_byte == 0x00){ // we encountered a 0-byte! End of message! +#ifdef DEBUG + Serial.print(F("got 0-byte!\nmessage reception complete: ")); + Serial.println(_recv_message); +#endif + _recv_idx = 0; + _recv_pos = IDLE; + } else { + _recv_byte = 0x00; + if (_recv_pos= 0){ + char c = _message_out[_send_pos]; + if (c == 0x00) { // end of message + digitalWrite(_DI,HIGH); + digitalWrite(_DE,LOW); + _send_pos = IDLE; + _send_idx = 0; + _message_out[0] = 0x00; +#ifdef DEBUG + Serial.println("Message transmission completed!"); +#endif + } else { + bit = !(c & 1<<_send_idx); + digitalWrite(_DI,bit); +#ifdef DEBUG + Serial.print("Sending bit #"); + Serial.print(_send_idx); + Serial.print(" ("); + Serial.print(bit); + Serial.print(") of "); + Serial.print(c); + Serial.print(" ("); + Serial.print(c,BIN); + Serial.println(")"); +#endif + _send_idx++; + if (_send_idx>7){ + _send_idx =0; + _send_pos++; + } + } + } + + if (_send_pos == START && _recv_pos == IDLE && _message_out[0] != 0x00) { // outgoig message awaits, line is idle + digitalWrite(_DE,HIGH); // enable driver +#ifdef DEBUG + Serial.println(F("starting transmission")); +#endif + digitalWrite(_DI,bit); // send start bit + _send_pos = 0; + } + + if (_send_pos != IDLE && digitalRead(_RO) != bit){ // driver output state mismatch! + digitalWrite(_DE,LOW); // disable driver + _send_pos = START; +#ifdef DEBUG + Serial.println(F("collision!")); +#endif + } + + +} + +void init485(int RO, int nRE, int DE, int DI){ + _RO=RO; + _RE=nRE; + _DE=DE; + _DI=DI; + _recv_message[0] = 0x00; // empty + _message_out[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 = 512 + OCR1A = 500; // 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 } -void SoftRS485::isr(){ - instance.bam(); +boolean available485(){ + return _recv_pos == IDLE && _recv_message[0] != 0; } -void SoftRS485::begin(int RO, int nRE, int DE, int DI){ - _RO = RO; - _RE = nRE; - _DE = DE; - _DI = DI; - Serial.begin(115200); - pinMode(_RE, OUTPUT); - pinMode(_DE, OUTPUT); - pinMode(_DI, OUTPUT); - pinMode(_RO, INPUT); - attachInterrupt(digitalPinToInterrupt(_RO),isr,CHANGE); - Serial.println("attached to interrupt!"); +String get485Message(){ + String res = String(_recv_message); + _recv_message[0] = 0; + return res; } -void SoftRS485::bam(){ - Serial.println("bam!"); +boolean send485(char message[]){ + if (_message_out[0] != 0x00) return false; // we already have a queued message, abort! + for (int idx = 0; idx + +int count = 0; +String message = "Message "; + +void setup() { + Serial.begin(115200); + + init485(2,3,4,5); // library initialization: + // connect pin 2 to RO of Max485 + // connect pin 3 to ^RE of Max485 + // connect pin 4 to DE of Max485 + // connect pin 5 to DI of Max485 + + if (send485("Test")) { + // This should send "Test" if the line is free. + Serial.println("Test sent!"); + } + if (!send485("Test2")) { + // This shoult fail, as transmission of "Test" should still be on the way + Serial.println("Test2 failed!"); + } +} + +void messageReceived(String message){ +} + +void loop(){ + if (available485()){ // check if we have received anything + Serial.print("messageReceived: "); + Serial.println(get485Message()); // read the received message + + // send another message everytime we have received one + count++; + char msg[128]; + (message+count).toCharArray(msg,128); + send485(msg); + } +}