]> www.fi.muni.cz Git - openparking.git/blobdiff - firmware/modbus.c
error counters to modbus
[openparking.git] / firmware / modbus.c
old mode 100755 (executable)
new mode 100644 (file)
index a49bd87..c895c5f
 #include <util/atomic.h>
 #include <util/delay.h>
 
+#include "clock.h"
 #include "modbus.h"
 
-#define BUFSIZE 128    // must be a power of two
+#define BUFSIZE 180 // maximum request size
 
 // configure the control pin
 #define ctl_pin_setup()        do { DDRD  |=  _BV(PD2); } while (0)
 #define ctl_pin_on()   do { PORTD |=  _BV(PD2); } while (0)
 #define ctl_pin_off()  do { PORTD &= ~_BV(PD2); } while (0)
 
-#define BUFMASK (BUFSIZE-1)
-#if (BUFSIZE & BUFMASK)
-#error BUFSIZE must be a power of two
-#endif
-
 #if BUFSIZE > 255
 typedef uint16_t bufptr_t;
 #else
 typedef uint8_t  bufptr_t;
 #endif
 
-#define bufptr_inc(x)  ((x + 1) & BUFMASK)
-
-static volatile bufptr_t rx_bytes, tx_head, tx_tail;
-static volatile uint8_t rxbuf[BUFSIZE], txbuf[BUFSIZE];
+static volatile bufptr_t buf_len, tx_ptr;
+static volatile uint8_t buffer[BUFSIZE], transmitting;
 static volatile uint16_t last_rx;
 #ifndef mb_unit_id
 static uint8_t mb_unit_id;
@@ -42,15 +36,12 @@ static uint8_t mb_unit_id;
 
 #define UART_BAUD      9600
 #define UBRR_VAL        ((F_CPU + 8UL * UART_BAUD) / (16UL*UART_BAUD) - 1)
-#define wait_one_byte()        _delay_us(10*1000000/UART_BAUD)
 
-#define get_clock()    (TCNT1)
-#define CLOCK_SPEED    (F_CPU/1024)
 /*
  * According to Wikipedia, it is indeed 28 bits = 3.5 bytes without
  * start- and stopbits.
  */
-#define TIMEOUT                (28*CLOCK_SPEED/UART_BAUD)
+#define REQ_TIMEOUT            (28*CLOCK_HZ/UART_BAUD)
 
 uint16_t hold_regs[MB_N_HOLD_REGS];
 
@@ -66,8 +57,8 @@ static uint16_t hold_regs_ee[MB_N_HOLD_REGS_EEPROM] EEMEM = {
 
 void modbus_init(uint8_t unit)
 {
-       rx_bytes = 0;
-       tx_head = tx_tail = 0;
+       buf_len = 0;
+       transmitting = 0;
 
        if (unit)
                mb_unit_id = unit;
@@ -82,31 +73,13 @@ void modbus_init(uint8_t unit)
        ctl_pin_off();
        ctl_pin_setup();
 
+       // Serial port setup
        UBRR0 = UBRR_VAL;
        UCSR0A = 0;
        UCSR0B = _BV(RXCIE0)|_BV(RXEN0)|_BV(TXEN0);
        UCSR0C = _BV(UCSZ01)|_BV(UCSZ00);
 }
 
-void rs485_send(char *p)
-{
-       bufptr_t next;
-
-       if (*p == '\0')
-               return;
-
-       ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
-               next = bufptr_inc(tx_head);
-               while (next != tx_tail && *p != '\0') {
-                       txbuf[tx_head] = *p++;
-                       tx_head = next;
-                       next = bufptr_inc(tx_head);
-               }
-               ctl_pin_on();
-               UCSR0B |= _BV(UDRIE0);
-       }
-}
-
 static uint16_t compute_crc(volatile uint8_t *buf, bufptr_t len)
 {
        bufptr_t i;
@@ -130,21 +103,21 @@ static uint16_t compute_crc(volatile uint8_t *buf, bufptr_t len)
 
 static void make_exception(mb_exception code)
 {
-       txbuf[1] |= 0x80;
-       txbuf[2] = code;
-       tx_head = 3;
+       buffer[1] |= 0x80;
+       buffer[2] = code;
+       buf_len = 3;
 }
 
 #define get_word(ptr, off) (((uint16_t)ptr[off] << 8) | ptr[off+1])
 void put_byte(uint8_t byte)
 {
-       txbuf[tx_head++] = byte;
+       buffer[buf_len++] = byte;
 }
 
 void put_word(uint16_t word)
 {
-       txbuf[tx_head++] = word >> 8;
-       txbuf[tx_head++] = word & 0xFF;
+       buffer[buf_len++] = word >> 8;
+       buffer[buf_len++] = word & 0xFF;
 }
 
 static mb_exception read_holding_regs(uint16_t start, uint16_t len)
@@ -186,99 +159,119 @@ static mb_exception write_single_reg(uint16_t reg, uint16_t val)
        return MB_OK;
 }
 
-void modbus_poll()
+uint8_t modbus_poll()
 {
        bufptr_t packet_len;
        uint16_t crc;
        uint8_t rv;
 
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
-               if (rx_bytes == 0) // nothing received yet
-                       return;
+               if (transmitting)
+                       return 0;
 
-               if (get_clock() - last_rx < TIMEOUT) // still receiving
-                       return;
+               if (get_clock() - last_rx < REQ_TIMEOUT) // still receiving
+                       return 0;
 
-               if (rx_bytes < 4) { // too short
-                       rx_bytes = 0;
-                       return;
+               if (buf_len < 4) { // too short (or not for us)
+                       buf_len = 0;
+                       return 0;
                }
 
-               if (rxbuf[0] != mb_unit_id) { // not for myself
-                       rx_bytes = 0;
-                       return;
-               }
+               transmitting = 1; // disable further reads
+               packet_len = buf_len;
+       }
 
-               if (tx_tail) { // still sending?
-                       rx_bytes = 0;
-                       return;
-               }
+       crc = compute_crc(buffer, packet_len - 2);
 
-               packet_len = rx_bytes; // make a copy
+       if ((crc & 0xFF) != buffer[packet_len-2]
+               || (crc >> 8) != buffer[packet_len-1]) { // bad CRC
+               ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
+                       transmitting = 0;
+                       buf_len = 0;
+               }
+               return 1;
        }
 
-       crc = compute_crc(rxbuf, packet_len - 2);
+       packet_len -= 2; // strip the CRC
+       buf_len = 2; // keep the first two bytes (unit ID and function) for TX
 
-       if ((crc & 0xFF) != rxbuf[packet_len-2]
-               || (crc >> 8) != rxbuf[packet_len-1]) // bad crc
-               goto out;
-
-       txbuf[0] = rxbuf[0]; // not mb_unit_id in case it gets changed
-       txbuf[1] = rxbuf[1];
-       tx_head = 2;
+       rv = MB_ILLEGAL_FUNC;
 
-       rv = MB_OK;
-       switch (rxbuf[1]) { // function
+       switch (buffer[1]) { // function
        case 3:
-               rv = read_holding_regs(get_word(rxbuf, 2), get_word(rxbuf, 4));
+               if (packet_len == 6)
+                       rv = read_holding_regs(
+                               get_word(buffer, 2),
+                               get_word(buffer, 4)
+                       );
                break;
        case 6:
-               rv = write_single_reg(get_word(rxbuf, 2), get_word(rxbuf, 4));
+               if (packet_len == 6)
+                       rv = write_single_reg(
+                               get_word(buffer, 2),
+                               get_word(buffer, 4)
+                       );
                break;
-       default:
-               make_exception(MB_ILLEGAL_FUNC); // illegal function
        }
        
        if (rv)
                make_exception(rv);
-send:
-       if (tx_head) {
-               crc = compute_crc(txbuf, tx_head);
-               txbuf[tx_head++] = crc & 0xFF;
-               txbuf[tx_head++] = crc >> 8;
 
-               tx_tail = 0;
+       // append the CRC
+       crc = compute_crc(buffer, buf_len);
+       put_byte(crc & 0xFF);
+       put_byte(crc >> 8);
 
-               ctl_pin_on();
-               UCSR0B |= _BV(UDRIE0);
-       }
-out:
-       ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
-               rx_bytes = 0;
-       }
+       // send out the reply
+       tx_ptr = 0;
+       ctl_pin_on();
+       UCSR0B |= _BV(UDRIE0);
+
+       return 1;
 }
 
 ISR(USART_RX_vect)
 {
-       ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
-               rxbuf[rx_bytes] = UDR0;
-               if (rx_bytes + 1 < BUFSIZE) // ignore overruns
-                       rx_bytes++;
-               last_rx = get_clock();
+       uint8_t rx_byte = UDR0;
+       clock_t now = get_clock();
+
+       if (transmitting) // how did we get here? discard it
+               goto out;
+
+       if (buf_len && buffer[0] != mb_unit_id) // not for us
+               goto out;
+
+       if (buf_len == BUFSIZE) { // overrun - discard the packet
+               buffer[0] = 0xFF;
+               buf_len = 1;
+               goto out;
        }
+
+       if (now - last_rx >= REQ_TIMEOUT) { // new packet; start over
+               buf_len = 0;
+       }
+
+       // TODO: we can probably calculate the CRC here as well
+       buffer[buf_len++] = rx_byte;
+out:
+       last_rx = now;
+}
+
+ISR(USART_TX_vect)
+{
+       UCSR0B &= ~_BV(TXCIE0); // disable further IRQs
+       ctl_pin_off();
+       buf_len = 0;
+       transmitting = 0; // enable receiving
 }
 
 ISR(USART_UDRE_vect)
 {
-       ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
-               if (tx_head == tx_tail) {
-                       UCSR0B &= ~_BV(UDRIE0);
-                       tx_tail = tx_head = 0;
-                       wait_one_byte(); // FIXME: too long busy-wait
-                       ctl_pin_off();
-               } else {
-                       UDR0 = txbuf[tx_tail];
-                       tx_tail = bufptr_inc(tx_tail);
-               }
+       if (tx_ptr >= buf_len) {
+               UCSR0A |= _BV(TXC0); // clear the pending TXC flag
+               UCSR0B |= _BV(TXCIE0); // enable xmit complete irq
+               UCSR0B &= ~_BV(UDRIE0); // disable ourselves
+       } else {
+               UDR0 = buffer[tx_ptr++];
        }
 }