COSA
An Object-Oriented Platform for Arduino Programming
RS485.cpp
Go to the documentation of this file.
1 
21 #include "Cosa/Types.h"
22 #if !defined(BOARD_ATTINY)
23 #include "RS485.hh"
24 #include "Cosa/RTT.hh"
25 #include <util/crc16.h>
26 
27 static uint8_t
28 crc7(const void* buf, size_t size)
29 {
30  uint8_t* bp = (uint8_t*) buf;
31  uint8_t crc = 0;
32  while (size--) {
33  uint8_t data = *bp++;
34  data ^= crc << 1;
35  if (data & 0x80) data ^= 9;
36  crc = data ^ (crc & 0x78) ^ (crc << 4) ^ ((crc >> 3) & 0x0f);
37  }
38  crc = (crc << 1) ^ (crc << 4) ^ (crc & 0x70) ^ ((crc >> 3) & 0x0f);
39  return (crc | 1);
40 }
41 
42 static uint16_t
43 crc_xmodem(const void* buf, size_t len)
44 {
45  if (UNLIKELY(len == 0)) return (0);
46  uint8_t* bp = (uint8_t*) buf;
47  uint16_t crc = 0;
48  do crc = _crc_xmodem_update(crc, *bp++); while (--len);
49  return (crc);
50 }
51 
52 int
54 {
55  // Check if the buffer is full
56  while (m_obuf->putchar(c) == IOStream::EOF)
57  yield();
58 
59  // Enable the transmitter
60  *UCSRnB() |= _BV(UDRIE0);
61  return (c & 0xff);
62 }
63 
64 void
66 {
67  int c = m_obuf->getchar();
68  if (c != IOStream::EOF) {
69  *UCSRnA() |= _BV(TXC0);
70  *UDRn() = c;
71  }
72  else {
73  *UCSRnB() &= ~_BV(UDRIE0);
74  *UCSRnB() |= _BV(TXCIE0);
75  }
76 }
77 
78 void
80 {
81  *UCSRnB() &= ~_BV(TXCIE0);
83 }
84 
85 int
86 RS485::send(const void* buf, size_t len, uint8_t dest)
87 {
88  // Check illegal message size, address and state
89  if (UNLIKELY(len == 0 || len > PAYLOAD_MAX)) return (EINVAL);
90  if (UNLIKELY(dest == m_addr)) return (EINVAL);
91  if (UNLIKELY(m_addr != MASTER && dest != MASTER)) return (EINVAL);
92  if (UNLIKELY(m_de.is_set())) return (EINVAL);
93 
94  // Build message header and calculate payload check-sum
95  header_t header;
96  header.length = len;
97  header.dest = dest;
98  header.src = m_addr;
99  header.crc = crc7(&header, sizeof(header) - 1);
100  uint16_t crc = crc_xmodem(buf, len);
101 
102  // Write message; SOT, header, payload and crc
103  m_de.set();
104  DELAY(100);
105  if (putchar(SOT) < 0) return (EIO);
106  if (write(&header, sizeof(header)) != (int) sizeof(header)) return (EIO);
107  if (write(buf, len) != (int) len) return (EIO);
108  if (write(&crc, sizeof(crc)) != sizeof(crc)) return (EIO);
109  return (len);
110 }
111 
112 int
113 RS485::recv(void* buf, size_t len, uint32_t ms)
114 {
115  uint32_t start = RTT::millis();
116  uint16_t crc;
117 
118  // Receive state-machine; start symbol, header, payload and check-sum
119  switch (m_state) {
120  case 0: // Wait for transmission to complete and start symbol
121  while (m_de.is_set()) Power::sleep(SLEEP_MODE_IDLE);
122  while (getchar() != SOT) {
123  if ((ms != 0) && (RTT::millis() - start > ms)) return (ETIME);
124  yield();
125  }
126  m_state = 1;
127 
128  case 1: // Read message header and verify header check-sum
129  while (available() != sizeof(header_t)) {
130  if ((ms != 0) && (RTT::millis() - start > ms)) return (ETIME);
131  yield();
132  }
133  m_state = 2;
134  if (m_ibuf->read(&m_header, sizeof(header_t)) != (int) sizeof(header_t))
135  goto error;
136  if (m_header.crc != crc7(&m_header, sizeof(header_t) - 1))
137  goto error;
138 
139  case 2: // Read message payload and verify payload check-sum
140  while (available() != (int) (m_header.length + sizeof(crc))) {
141  if ((ms != 0) && (RTT::millis() - start > ms)) return (ETIME);
142  yield();
143  }
144  m_state = 3;
145  }
146 
147  // Check that the given buffer can hold incoming message
148  if (m_header.length > len) goto error;
149  if (m_ibuf->read(buf, m_header.length) != (int) m_header.length) goto error;
150  if (m_ibuf->read(&crc, sizeof(crc)) != sizeof(crc)) goto error;
151  if (crc_xmodem(buf, m_header.length) != crc) return (0);
152  m_state = 0;
153 
154  // Check that the message was addressed to this device
155  if ((m_header.dest == m_addr) || (m_header.dest == BROADCAST))
156  return (m_header.length);
157  return (0);
158 
159  error:
160  // Something went wrong; flush buffer and signal data error
161  m_ibuf->empty();
162  m_state = 0;
163  return (EFAULT);
164 }
165 
166 #define UART_TX_ISR(vec,nr) \
167 ISR(vec ## _TX_vect) \
168 { \
169  if (UNLIKELY(UART::uart[nr] == NULL)) return; \
170  UART::uart[nr]->on_tx_interrupt(); \
171 }
172 
173 #if defined(USART_UDRE_vect)
174 UART_TX_ISR(USART, 0)
175 #endif
176 #if defined(USART1_UDRE_vect)
177 UART_TX_ISR(USART1, 1)
178 #endif
179 #if defined(USART2_UDRE_vect)
180 UART_TX_ISR(USART2, 2)
181 #endif
182 #if defined(USART3_UDRE_vect)
183 UART_TX_ISR(USART3, 3)
184 #endif
185 #endif
#define EIO
Definition: Errno.h:32
#define TXCIE0
Definition: Leonardo.hh:302
uint8_t dest
Destination node address.
Definition: RS485.hh:52
#define TXC0
Definition: Leonardo.hh:296
#define EINVAL
Definition: Errno.h:49
static uint16_t _crc_xmodem_update(uint16_t crc, uint8_t data)
Definition: SD.cpp:86
virtual void on_udre_interrupt()
Definition: RS485.cpp:65
virtual int putchar(char c)
virtual void on_transmit_completed()
Definition: RS485.hh:177
virtual int putchar(char c)
Definition: RS485.cpp:53
IOStream::Device * m_obuf
Output Buffer/Device.
Definition: UART.hh:196
volatile uint8_t * UCSRnA() const
Definition: UART.hh:209
uint8_t src
Source node address.
Definition: RS485.hh:53
#define UART_TX_ISR(vec, nr)
Definition: RS485.cpp:166
virtual int write(const void *buf, size_t size)
virtual int read(void *buf, size_t size)
const uint16_t PAYLOAD_MAX
Definition: RS485.hh:145
virtual void empty()
void set() const
Definition: OutputPin.hh:85
static uint16_t crc_xmodem(const void *buf, size_t len)
Definition: RS485.cpp:43
int send(const void *buf, size_t len, uint8_t dest=MASTER)
Definition: RS485.cpp:86
virtual int getchar()
Definition: UART.hh:139
#define ETIME
Definition: Errno.h:89
#define DELAY(us)
Definition: Types.h:280
uint8_t m_state
Definition: RS485.hh:157
static void sleep(uint8_t mode=POWER_SLEEP_MODE)
Definition: Power.cpp:30
static const uint8_t SOT
Definition: RS485.hh:47
static uint8_t crc7(const void *buf, size_t size)
Definition: RS485.cpp:28
header_t m_header
Definition: RS485.hh:148
bool is_set() const
Definition: Pin.hh:112
IOStream::Device * m_ibuf
Input Buffer/Device.
Definition: UART.hh:195
static uint32_t millis()
Definition: RTT.cpp:121
void(* yield)()
static const int EOF
Definition: IOStream.hh:33
static const uint8_t MASTER
Definition: RS485.hh:67
static const uint8_t BROADCAST
Definition: RS485.hh:64
int recv(void *buf, size_t len, uint32_t ms=0L)
Definition: RS485.cpp:113
virtual void on_tx_interrupt()
Definition: RS485.cpp:79
uint8_t crc
Header check-sum.
Definition: RS485.hh:54
uint8_t m_addr
Definition: RS485.hh:154
uint8_t length
Number of bytes in payload.
Definition: RS485.hh:51
#define UDRIE0
Definition: Leonardo.hh:301
virtual int getchar()
#define UNLIKELY(x)
Definition: Types.h:153
virtual int available()
Definition: UART.hh:87
volatile uint8_t * UCSRnB() const
Definition: UART.hh:218
volatile uint8_t * UDRn() const
Definition: UART.hh:245
OutputPin m_de
Definition: RS485.hh:151
#define EFAULT
Definition: Errno.h:41