X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=firmware%2Fmodbus.c;h=c895c5f30ec71336e08a4820b059b9cfec22f827;hb=087a60aae1ad8d3f96f94601c68910154f884735;hp=a49bd877e1c778357e5f3f39f3f1b5ece6defbec;hpb=21e8a8ac43a7de8539e40ef6dfe7e2b7c731176c;p=openparking.git diff --git a/firmware/modbus.c b/firmware/modbus.c old mode 100755 new mode 100644 index a49bd87..c895c5f --- a/firmware/modbus.c +++ b/firmware/modbus.c @@ -11,30 +11,24 @@ #include #include +#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++]; } }