first working implementation of timer-based RS485
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
191
SoftRS485.cpp
191
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
|
||||
|
||||
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<MSG_LEN-1) {
|
||||
_recv_pos++;
|
||||
}
|
||||
}
|
||||
} else _recv_idx++;
|
||||
}
|
||||
|
||||
boolean bit = LOW;
|
||||
|
||||
if (_send_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
|
||||
}
|
||||
|
||||
|
||||
SoftRS485 SoftRS485::singleton(){
|
||||
return instance;
|
||||
}
|
||||
|
||||
void SoftRS485::isr(){
|
||||
instance.bam();
|
||||
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::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!");
|
||||
boolean available485(){
|
||||
return _recv_pos == IDLE && _recv_message[0] != 0;
|
||||
}
|
||||
|
||||
void SoftRS485::bam(){
|
||||
Serial.println("bam!");
|
||||
String get485Message(){
|
||||
String res = String(_recv_message);
|
||||
_recv_message[0] = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
boolean send485(char message[]){
|
||||
if (_message_out[0] != 0x00) return false; // we already have a queued message, abort!
|
||||
for (int idx = 0; idx<MSG_LEN; idx++){
|
||||
_message_out[idx] = message[idx]; // copy message
|
||||
if (message[idx] == 0x00) break;
|
||||
}
|
||||
if (_message_out[0] == 0x00) return false; // message is empty! abort
|
||||
_send_pos = START;
|
||||
|
||||
#ifdef DEBUG
|
||||
Serial.print("sending ");
|
||||
Serial.println(_message_out);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
15
SoftRS485.h
15
SoftRS485.h
@@ -8,15 +8,8 @@
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
class SoftRS485{
|
||||
public:
|
||||
void begin(int RO, int nRE, int DE, int DI);
|
||||
void bam();
|
||||
static SoftRS485 singleton();
|
||||
private:
|
||||
static void isr();
|
||||
static SoftRS485 instance;
|
||||
int _RO, _RE, _DE, _DI;
|
||||
};
|
||||
|
||||
extern void init485(int RO, int nRE, int DE, int DI);
|
||||
extern boolean available485();
|
||||
extern String get485Message();
|
||||
extern boolean send485(char message[]);
|
||||
#endif
|
||||
|
||||
44
examples/SendAndReceive/SendAndReceive.ino
Normal file
44
examples/SendAndReceive/SendAndReceive.ino
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
This sketch demonstrates, how to use the SoftRS485 library.
|
||||
This library makes use of Arduinos timer 1
|
||||
*/
|
||||
|
||||
#include <SoftRS485.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user