/* * Loosely modelled after AVR-RS485 by Yoshinori Kohyama (http://algobit.jp/), * available at https://github.com/kohyama/AVR-RS485/ * * All bugs by Jan "Yenya" Kasprzak :-) */ #include #include #include #define BUFSIZE 128 // must be a power of two // 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_head, rx_tail, tx_head, tx_tail; static volatile char rxbuf[BUFSIZE], txbuf[BUFSIZE]; #define UART_BAUD 9600 #define UBRR_VAL ((F_CPU + 8UL * UART_BAUD) / (16UL*UART_BAUD) - 1) void rs485_init() { rx_head = rx_tail = 0; tx_head = tx_tail = 0; ctl_pin_off(); ctl_pin_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; cli(); 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); sei(); } bufptr_t rs485_readln(char *buf, bufptr_t size) { int n = 0; do { cli(); while (rx_head != rx_tail && n + 1 < size) { buf[n++] = rxbuf[rx_tail]; rx_tail = bufptr_inc(rx_tail); } sei(); } while (n == 0 || buf[n - 1] != 0x0a); buf[n] = '\0'; return n; } ISR(USART_RX_vect) { bufptr_t next; cli(); next = bufptr_inc(rx_head); rxbuf[rx_head] = UDR0; if (next != rx_tail) rx_head = next; sei(); } ISR(USART_TX_vect) { UCSR0B &= ~_BV(TXCIE0); // disable further IRQs ctl_pin_off(); } ISR(USART_UDRE_vect) { cli(); if (tx_head == tx_tail) { UCSR0A |= _BV(TXC0); // clear the pending TXC flag UCSR0B |= _BV(TXCIE0); // enable xmit complete irq UCSR0B &= ~_BV(UDRIE0); } else { UDR0 = txbuf[tx_tail]; tx_tail = bufptr_inc(tx_tail); } sei(); }