/************************************************************************ Title: Interrupt UART library with receive/transmit circular buffers Author: Peter Fleury http://tinyurl.com/peterfleury File: $Id: uart.h,v 1.13 2015/01/11 13:53:25 peter Exp $ Software: AVR-GCC 4.x, AVR Libc 1.4 or higher Hardware: any AVR with built-in UART/USART Usage: see Doxygen manual LICENSE: Copyright (C) 2015 Peter Fleury, GNU General Public License Version 3 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ************************************************************************/ /* Basiert auf dem Grundkonzept von Peters Fleury Lib. */ #pragma once #include #include #include #include "megaAVR0_UsartRegister.h" //* constants *// // Size of the circular rx/tx buffer, must be power of 2. constexpr uint8_t BUFFERSIZE = 64; // size of RX/TX buffers constexpr uint8_t UART_RX_BUFFER_MASK = BUFFERSIZE-1; constexpr uint8_t UART_TX_BUFFER_MASK = BUFFERSIZE-1; // high byte error return code of uart_getc() constexpr uint16_t UART_FRAME_ERROR = 0x1000; // Framing Error by UART constexpr uint16_t UART_OVERRUN_ERROR = 0x0800; // Overrun condition by UART constexpr uint16_t UART_PARITY_ERROR = 0x0400; // Parity Error by UART constexpr uint16_t UART_BUFFER_OVERFLOW = 0x0200; // receive ringbuffer overflow constexpr uint16_t UART_NO_DATA = 0x0100; // no receive data available // usart mode setting constexpr uint8_t A8N1 = 0x03; // asynchron, 8bit, no parity, 1 stop bit, (default) constexpr uint8_t A8N2 = 0x0B; // asynchron, 8bit, no parity, 2 stop bits //* module global variables *// template struct ring { T TxBuf[BUFFERSIZE]; T RxBuf[BUFFERSIZE]; T txHead = 0; T txTail = 0; T rxHead = 0; T rxTail = 0; T lastRxError = 0; }; ring buffer[4]; constexpr uint16_t BAUDREGISTER (const uint32_t baudrate) { return ( (64.0 * F_CPU / (16.0 * baudrate)) + 0.5); } /* 0 ... 3 A8N1, A8N2 */ template class usart { private: // Member optimiert public: // Default Konstruktor usart () { } // Methoden /************************************************************************* Function: init() Purpose: initialize UART and set baudrate Input: baudrate using macro UART_BAUD_SELECT() Returns: none **************************************************************************/ void init() { buffer[NUMBER].txHead = 0; buffer[NUMBER].txTail = 0; buffer[NUMBER].rxHead = 0; buffer[NUMBER].rxTail = 0; uint16_t *ptr16 = nullptr; uint8_t *ptr = nullptr; if constexpr (NUMBER < 3) { // UARTn_BAUD = BAUDREGISTER(BAUDRATE); ptr16 = reinterpret_cast (regBAUD[NUMBER]); // Adresse holen *ptr16 = static_cast (BAUDREGISTER(BAUDRATE)); // Wert an die Adresse zuweisen // Set frame format, UARTn_CTRLC = MODUS: ptr = reinterpret_cast (regCTRLC[NUMBER]); *ptr = static_cast (MODUS); // UARTn_RX_PIN = PORT_PULLUPEN_bm; // RxD Pullup ptr = (uint8_t*) (regRxPIN[NUMBER]); *ptr = static_cast (PORT_PULLUPEN_bm); // UARTn_TX_PIN |= PIN0_bm; // TxD Output ptr = reinterpret_cast (regTxPIN[NUMBER]); *ptr |= static_cast (PIN0_bm); // Enable USART receiver and transmitter and receive complete interrupt // UARTn_CTRLA = (UART_BIT_RXCIE); ptr = reinterpret_cast (regCTRLA[NUMBER]); *ptr = static_cast (UART_BIT_RXCIE); // UART0_CTRLB = (UART_BIT_RXEN)|(UART_BIT_TXEN); ptr = reinterpret_cast (regCTRLB[NUMBER]); *ptr = static_cast ((UART_BIT_RXEN)|(UART_BIT_TXEN)); } sei(); } /************************************************************************* Function: getc() Purpose: return byte from ringbuffer Returns: lower byte: received byte from ringbuffer higher byte: last receive error **************************************************************************/ uint16_t getC(void) { uint8_t tmptail; uint8_t data; uint8_t lastRxError; if ( buffer[NUMBER].rxHead == buffer[NUMBER].rxTail ) { return UART_NO_DATA; // no data available } // calculate buffer index tmptail = (buffer[NUMBER].rxTail + 1) & UART_RX_BUFFER_MASK; // get data from receive buffer data = buffer[NUMBER].RxBuf[tmptail]; lastRxError = buffer[NUMBER].lastRxError; buffer[NUMBER].rxTail = tmptail; // store buffer index buffer[NUMBER].lastRxError = 0; return (lastRxError << 8) + data; } /************************************************************************* Function: putChar() Purpose: write byte to ringbuffer for transmitting via UART Input: byte to be transmitted Returns: none **************************************************************************/ void putChar(uint8_t data) { //PORTD_OUTSET = PIN1_bm; uint8_t tmphead; tmphead = (buffer[NUMBER].txHead + 1) & UART_TX_BUFFER_MASK; while ( tmphead == buffer[NUMBER].txTail ){ ; // wait for free space in buffer } buffer[NUMBER].TxBuf[tmphead] = data; buffer[NUMBER].txHead = tmphead; // UARTn_CTRLA |= (UART_BIT_DREIE); // Data Register Empty Interrupt Enable uint8_t *ptr = reinterpret_cast (regCTRLA[NUMBER]); *ptr |= static_cast (UART_BIT_DREIE); //PORTD_OUTCLR = PIN1_bm; } /************************************************************************* Function: putString() Purpose: transmit string to UART Input: string to be transmitted Returns: none **************************************************************************/ void putString(const char *s ) { while (*s) putChar(*s++); } /************************************************************************* Function: print() Purpose: Funktion wird überladen Input: string to be transmitted Returns: none **************************************************************************/ void print(const int8_t var) { printInteger(var); } void print(const uint8_t var) { printInteger(var); } void print(const int16_t var) { printInteger(var); } void print(const uint16_t var) { printInteger(var); } void print(const int32_t var) { printInteger(var); } void print(const uint32_t var) { printInteger(var); } void print(const float var, uint8_t digits = 4) { printFloat(var, digits); } void print(const double var, uint8_t digits = 4) { printFloat(var, digits); } void print(const char *s) { while (*s) putChar(*s++); } // --- println - mit Linefeed ---------------------------------- void println(const int8_t var) { print(var); putChar('\n'); } void println(const uint8_t var) { print(var); putChar('\n'); } void println(const int16_t var) { print(var); putChar('\n'); } void println(const uint16_t var) { print(var); putChar('\n'); } void println(const int32_t var) { print(var); putChar('\n'); } void println(const uint32_t var) { print(var); putChar('\n'); } void println(const float var, uint8_t digits = 4) { print(var, digits); putChar(' '); putChar('f'); putChar('l'); putChar('o'); putChar('a'); putChar('t'); putChar('\n'); } void println(const double var, uint8_t digits = 4) { print(var, digits); putChar(' '); putChar('d'); putChar('o'); putChar('u'); putChar('b'); putChar('l'); putChar('e'); putChar('\n'); } void println(const char *s) { print(s); putChar('\n'); } void println(void) { putChar('\n'); } // --- Umformung und Weitergabe ----------------------------- template void printInteger(const T var) { char buffer[11] = {0}; if (var < 0) { ltoa(var, buffer, 10); // ltoa } else { ultoa(var, buffer, 10); // ultoa } putString(buffer); } /* --- aus der Arduino print Klasse --- */ void printFloat(double input, uint8_t digits) { // Handle negative numbers if (input < 0) { putChar('-'); input = -input; } // Round correctly so that print(1.999, 2) prints as "2.00" double rounding = 0.5; for (uint8_t i=0; i 0) { putChar('.'); } // Extract digits from the remainder one at a time while (digits-- > 0) { remainder *= 10.0; uint32_t toPrint = (uint32_t)(remainder); printInteger(toPrint); remainder -= toPrint; } } /************************************************************************* Function: putp() Purpose: transmit string from program memory to UART Input: program memory string to be transmitted Returns: none **************************************************************************/ #define putString_P(__s) puts_p(PSTR(__s)) void puts_p(const char *progmem_s ) { volatile char c; // ggf. volatile while ( (c = pgm_read_byte(progmem_s++)) ) putChar(c); } }; ISR (UART0_RX_INTERRUPT) /************************************************************************* Function: UART Receive Complete interrupt Purpose: called when the UART has received a character **************************************************************************/ { uint8_t tmphead; uint8_t data; uint8_t usr; uint8_t lastRxError; // read UART status register and UART data register usr = USART0_RXDATAH; data = UART0_DATARX; // get FERR (Frame Error) BUFOVF (Buffer Overflow) PERR (USART Parity Error) bits lastRxError = usr & ( (USART_FERR_bm)|(USART_BUFOVF_bm)|(USART_PERR_bm) ); // calculate buffer index tmphead = (buffer[0].rxHead + 1) & UART_RX_BUFFER_MASK; if ( tmphead == buffer[0].rxTail ) { // error: receive buffer overflow lastRxError = UART_BUFFER_OVERFLOW >> 8; } else { // store new index buffer[0].rxHead = tmphead; // store received data in buffer buffer[0].RxBuf[tmphead] = data; } buffer[0].lastRxError |= lastRxError; } ISR (UART0_TX_INTERRUPT) /************************************************************************* Function: UART Data Register Empty interrupt Purpose: called when the UART is ready to transmit the next byte **************************************************************************/ { //PORTD_OUTSET = PIN2_bm; // Debugging mit Logicanalyzer uint8_t tmptail; if (buffer[0].txHead != buffer[0].txTail) { // calculate and store new buffer index tmptail = (buffer[0].txTail + 1) & UART_TX_BUFFER_MASK; buffer[0].txTail = tmptail; // get one byte from buffer and write it to UART UART0_DATATX = buffer[0].TxBuf[tmptail]; // start transmission } else { // tx buffer empty, disable Data Register Empty Interrupt UART0_CTRLA &= ~(UART_BIT_DREIE); } //PORTD_OUTCLR = PIN2_bm; // Debugging mit Logicanalyzer } ISR (UART1_RX_INTERRUPT) /************************************************************************* Function: UART Receive Complete interrupt Purpose: called when the UART has received a character **************************************************************************/ { uint8_t tmphead; uint8_t data; uint8_t usr; uint8_t lastRxError; // read UART status register and UART data register usr = USART1_RXDATAH; data = UART1_DATARX; // get FERR (Frame Error) BUFOVF (Buffer Overflow) PERR (USART Parity Error) bits lastRxError = usr & ( (USART_FERR_bm)|(USART_BUFOVF_bm)|(USART_PERR_bm) ); // calculate buffer index tmphead = (buffer[1].rxHead + 1) & UART_RX_BUFFER_MASK; if ( tmphead == buffer[1].rxTail ) { // error: receive buffer overflow lastRxError = UART_BUFFER_OVERFLOW >> 8; } else { // store new index buffer[1].rxHead = tmphead; // store received data in buffer buffer[1].RxBuf[tmphead] = data; } buffer[1].lastRxError |= lastRxError; } ISR (UART1_TX_INTERRUPT) /************************************************************************* Function: UART Data Register Empty interrupt Purpose: called when the UART is ready to transmit the next byte **************************************************************************/ { //PORTD_OUTSET = PIN2_bm; // Debugging mit Logicanalyzer uint8_t tmptail; if (buffer[1].txHead != buffer[1].txTail) { // calculate and store new buffer index tmptail = (buffer[1].txTail + 1) & UART_TX_BUFFER_MASK; buffer[1].txTail = tmptail; // get one byte from buffer and write it to UART UART1_DATATX = buffer[1].TxBuf[tmptail]; // start transmission } else { // tx buffer empty, disable Data Register Empty Interrupt UART1_CTRLA &= ~(UART_BIT_DREIE); } //PORTD_OUTCLR = PIN2_bm; // Debugging mit Logicanalyzer } ISR (UART2_RX_INTERRUPT) /************************************************************************* Function: UART Receive Complete interrupt Purpose: called when the UART has received a character **************************************************************************/ { uint8_t tmphead; uint8_t data; uint8_t usr; uint8_t lastRxError; // read UART status register and UART data register usr = USART2_RXDATAH; data = UART2_DATARX; // get FERR (Frame Error) BUFOVF (Buffer Overflow) PERR (USART Parity Error) bits lastRxError = usr & ( (USART_FERR_bm)|(USART_BUFOVF_bm)|(USART_PERR_bm) ); // calculate buffer index tmphead = (buffer[2].rxHead + 1) & UART_RX_BUFFER_MASK; if ( tmphead == buffer[2].rxTail ) { // error: receive buffer overflow lastRxError = UART_BUFFER_OVERFLOW >> 8; } else { // store new index buffer[2].rxHead = tmphead; // store received data in buffer buffer[2].RxBuf[tmphead] = data; } buffer[2].lastRxError |= lastRxError; } ISR (UART2_TX_INTERRUPT) /************************************************************************* Function: UART Data Register Empty interrupt Purpose: called when the UART is ready to transmit the next byte **************************************************************************/ { //PORTD_OUTSET = PIN2_bm; // Debugging mit Logicanalyzer uint8_t tmptail; if (buffer[2].txHead != buffer[2].txTail) { // calculate and store new buffer index tmptail = (buffer[2].txTail + 1) & UART_TX_BUFFER_MASK; buffer[2].txTail = tmptail; // get one byte from buffer and write it to UART UART2_DATATX = buffer[2].TxBuf[tmptail]; // start transmission } else { // tx buffer empty, disable Data Register Empty Interrupt UART2_CTRLA &= ~(UART_BIT_DREIE); } //PORTD_OUTCLR = PIN2_bm; // Debugging mit Logicanalyzer }