/* * File: uart.tpp * Author: naopross * * Created on May 2, 2018, 7:05 PM * * Note: Templated functions inside a namespace cannot be created outside of a * namespace because of a g++ bug in version 4.8.0 on which xc++ is based. * See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 */ #ifndef UART_TPP #define UART_TPP #include "hwconfig.hpp" #include "uart.hpp" extern "C" { // #include #include #include } /* templated functions */ namespace uart { // access buffers template inline std::queue& rx_buffer() { static_assert(dev != 0 && dev <= devices_count, "invalid device number"); return uart::_rx_buffer[dev -1]; } template inline std::queue& tx_buffer() { static_assert(dev != 0 && dev <= devices_count, "invalid device number"); return uart::_tx_buffer[dev -1]; } template inline bool echo(bool enabled) { static_assert(dev != 0 && dev <= devices_count, "invalid device number"); _echo[dev -1] = enabled; } template inline bool echo_enabled() { static_assert(dev != 0 && dev <= devices_count, "invalid device number"); return _echo[dev -1]; } // read from serial device template uint8_t read() { uint8_t byte = rx_buffer().front(); rx_buffer().pop(); return byte; } template uint8_t read_wait() { while (rx_buffer().empty()); uint8_t byte = rx_buffer().front(); rx_buffer().pop(); return byte; } #if 0 template std::string read(const unsigned len) { std::string str = ""; unsigned i = len; if (rx_buffer().size() < len) { i = rx_buffer().size(); } while (i--) { str += read(); } return str; } template std::string readline(const std::string& eol = "\n\r") { int eol_c = 0; std::string str = ""; unsigned i = rx_buffer().size(); while (i--) { str += read(); // compare string endings // TODO: make it more efficient if (eol_c == eol.size()) { break; } if (str.back() == eol[eol_c]) { eol_c++; } } } template unsigned read(uint8_t *buffer, const unsigned numbytes) { uint8_t *bptr = buffer; unsigned len = numbytes; while (len--) { *(bptr++) = read(); } } #endif // write to serial device template void write(const uint8_t byte) { // disable send data flag IEC1bits.U1TXIE = 0; tx_buffer().push(byte); // enable send data flag IEC1bits.U1TXIE = 1; } template void write(const std::string &str) { // TODO: if size of str is reasonale, push to queue AND THEN send, // right now on each call the interrupt is enabled and disabled // for (const char &c : str) { // write(c); // } // disable send data flag IEC1bits.U1TXIE = 0; for (int i = 0; i < str.size(); i++) { tx_buffer<1>().push(static_cast(str[i])); } // enable send data flag IEC1bits.U1TXIE = 1; } template unsigned write(const uint8_t *buffer, const unsigned numbytes) { uint8_t *bptr = buffer; unsigned len = numbytes; while (len--) { write(*(bptr++)); } } template void print(const std::string &str) { write(str); write("\n\r"); } } /* specializations for UART1 */ #ifdef DEBUG #include "pin.tpp" io_pin<2> led_blue(&LATBbits, &TRISBbits, &PORTBbits); io_pin<3> led_green(&LATBbits, &TRISBbits, &PORTBbits); #endif void __ISR(_UART_1_VECTOR, IPL1AUTO) usart_1_isr() { if (IFS1bits.U1RXIF) { #ifdef DEBUG led_blue.toggle(); #endif // wait for data to be available while (!U1STAbits.URXDA); uart::rx_buffer<1>().push(static_cast(U1RXREG)); // echo if (uart::echo_enabled<1>()) { // uart::write<1>(uart::rx_buffer<1>().front()); IEC1bits.U1TXIE = 0; uart::tx_buffer<1>().push(uart::rx_buffer<1>().front()); IEC1bits.U1TXIE = 1; } IFS1bits.U1RXIF = 0; } // on each interrupt call, send one character else if (IFS1bits.U1TXIF) { #ifdef DEBUG led_green.toggle(); #endif // disable TX interrupt flag IEC1bits.U1TXIE = 0; // write data U1TXREG = static_cast(uart::tx_buffer<1>().front()); uart::tx_buffer<1>().pop(); // if not empty, enable tx flag if (!uart::tx_buffer<1>().empty()) { IEC1bits.U1TXIE = 1; } IFS1bits.U1TXIF = 0; } else { if (U1STAbits.OERR == 1) { U1STAbits.OERR = 0; } IFS1bits.U1EIF = 0; } } namespace uart { template<> void initialize<1>() { // STSEL 1S; // IREN disabled; // PDSEL 8N; // RTSMD disabled; // RXINV disabled; // SIDL disabled; // WAKE disabled; // ABAUD disabled; // LPBACK disabled; // BRGH enabled; // UEN TX_RX; // ON enabled; U1MODE = 0x8008; // UTXISEL TX_ONE_CHAR; // UTXINV disabled; // ADDR 0; // URXEN disabled; // OERR disabled; // ADM_EN disabled; // URXISEL RX_ONE_CHAR; // UTXBRK disabled; // UTXEN disabled; // ADDEN disabled; U1STA = 0x0; // U1TXREG 0; U1TXREG = 0x0; // BaudRate = 9600; // Frequency = 1000000 Hz; // BRG 25; U1BRG = 0x19; IEC1bits.U1RXIE = 1; U1STAbits.UTXEN = 1; U1STAbits.URXEN = 1; // Enabling UART U1MODEbits.ON = 1; // UERI: UART1 Error // Priority: 1 // SubPriority: 0 IPC7bits.U1IP = 1; IPC7bits.U1IS = 0; // map uart pins to port F hw::regunlock(); // unlock PPS CFGCONbits.IOLOCK = 0; U1RXRbits.U1RXR = 0x0004; //RF1->UART1:U1RX; RPF0Rbits.RPF0R = 0x0003; //RF0->UART1:U1TX; CFGCONbits.IOLOCK = 1; // lock PPS hw::reglock(); } #if 0 template<> void set_baudrate<1>(long baud) { } #endif } #endif