/*
This file is linked with the documentation ! */

//---------------------------------------------------------------------------
// File:   C:\pic\GPSDO\UART_PIC16F1783.c
// Author: Wolfgang Buescher, DL4YHF 
// Date:   2015-12-25
// Purpose: Simple UART functions for PIC17(L)F178x, without ISR .
// Development System :  Microchip MPLAB IDE v8.85, 
//                 later also "MPLAB X" because "MPLAB" debugger is severely bugged,
//             and  XC8 C Compiler ("Free Mode") V1.35 .
//
//    
//---------------------------------------------------------------------------

#include "switches.h" // project specific 'compiler' switches & options 
    // (it seems impossible to define the include files in MLPAB-X,
    //  and pass those settings on to the "custom translator" CC5X.
    //  So, like it or not, everything (*.c, *.h) had to be dumped
    //  into the stupid 'project directory'. What a mess. )

// Include an awful lot of compiler-specific junk ... see gpsdo_pic_main.c
#ifdef __BORLANDC__  // compiling with Borland C ? Use WB's "PIC emulator" ..
# include "pic_emulator/xc.h"
#elif (defined __XC8) // compiling for PIC, using Microchip's "XC8" compiler ?
# include "xc.h"
# include "stdint.h"
# include "stdlib.h"  // stuff like itoa() [beware, non-standardized argument sequence]
#else // neither Borland, nor XC8 ...
# error "Your compiler is not supported here yet. Please add support yourself. But don't waste your time with CC5X."
#endif // using BORLAND C, Microchip's "XC8", or what else ?


#include "uart_pic.h" // header for THIS module ("UART functions for PIC" by DL4YHF)


char UART_sz9Temp[10]; // temporary string, used to convert integer to string, etc


//---------------------------------------------------------------------------
void UART_Init(void) // Open the serial port, primarily used for debugging .
{ // Forget about antique, bloated, "peripheral libraries" !
  // The PIC16F1782/3 datasheet explains how to get the UART running 
  // on  page 313, Ch. 27.1.1.7, "Asynchronous Transmission Set-Up" .
  // Don't miss TABLE 27-1, "SUMMARY OF REGISTERS ASSOCIATED WITH ASYNCHRONOUS TRANSMISSION" .
  // in DS40001579E on page 314, with links to each register description.  

  // Note: The UART pin port direction (TXD, RXD) have already been initialized,
  //       including the APFCON settings to have TXD on RB6, and RXD on RB7 .
    
  // > 1. Initialize the SPBRGH, SPBRGL register pair and
  //      the BRGH and BRG16 bits to achieve the desired baud rate.
  // >  Setting the SCKP bit to '1' will invert the transmit data 
  // >  resulting in low true idle and data bit .
  //    Unfortunately there's no equivalent polarity control for RX,
  //    so when using an RS232 level shifter (MAX232..), do NOT invert !
  //    At least, the PIC can *talk to* the PC's RS232 this way,
  //    without any active component in between.
#if( TXD_INVERT_POLARITY )
  BAUDCON= 0b00011000; //  BAUD RATE CONTROL REGISTER (DS40001579E page 322) ..
#else    //  ||||||||
  BAUDCON= 0b00001000; //  similar, if a MAX232 or similar INVERTING level-converter is in use
#endif   //  ||||||||
         //  ||||||||______ b0 : ABDEN (Auto-Baud Detect; 0=no automatic baudrate detection)
         //  |||||||_______ b1 : WUE  (Wake-up enable bit)
         //  ||||||________ b2 : n.c.
         //  |||||_________ b3 : BRG16 : 0=8-bit baudrate generator, 1=16-bit baudrate generator
         //  ||||__________ b4 : SCKP (sync clock polarity, in ASYNC mode: 
         //  |||                        0=non-inverted TX data, 1=inverted .. details above)
         //  |||___________ b5 : n.c.
         //  ||____________ b6 : RCIDL  (Receive Idle flag)
         //  |_____________ b7 : ABDOVF (auto-baud detect overflow)
  TXSTA  = 0b00000100;    // 8-bit, async, BRGH=0, INITIAL VALUE, TX still diabled !
         //  ||||||||______ b0 : TX9D (9th bit of tx data, not used here)
         //  |||||||_______ b1 : TRMT (transmit shift register status)
         //  ||||||________ b2 : BRGH (High Baud Rate Select bit, 1=high speed)
         //  |||||_________ b3 : SENDB (0=don't send BREAK)
         //  ||||__________ b4 : SYNC (0=async="UART", 1=sync)
         //  |||___________ b5 : TXEN (transmit enable bit)
         //  ||____________ b6 : TX9  (0 = 8-bit transmissions)
         //  |_____________ b7 : CSRC (Clock Source Select bit, don't care in ASYNC mode)
  RCSTA  = 0b00010000;     // initial value: SPEN disabled before setting the baudrate; 8-bit, CREN (DS40001579E page 321)
         //  ||||||||______ b0 : RX9D (9th bit of rx data)
         //  |||||||_______ b1 : OERR (Overrun Error bit)
         //  ||||||________ b2 : FERR (Framing Error bit)
         //  |||||_________ b3 : ADDEN (Address Detect Enable bit, don't care in 8-bit mode)
         //  ||||__________ b4 : CREN (Continuous Receive enable bit, 1=enable rx)
         //  |||___________ b5 : SREN (Single Receive enable bit, don't care in ASYNC mode)
         //  ||____________ b6 : RX9  (9-bit receive enable bit)
         //  |_____________ b7 : SPEN (serial port enable bit)
  // Fortunately, "C" permits 16-bit access. Combines "SPBRGH:SPBRGL" from the datasheet.
  // NOTE: FORGET ABOUT DS40001579E page 323 "Example 27-1" ! For higher baudrate,
  //        baudrate = Fosc / ( 64 * (SPBRG+1) ) is unusable .
  // > It may be advantageous to use the high baud rate (BRGH = 1),
  // > or the 16-bit BRG (BRG16 = 1) to reduce the baud rate error.
  // Thus, "Example 27-1" is the WORST example, even though presented FIRST.
  // Much better suited for higher bitrates ("baudrates") :
  // Configuration with "SYNC=0, BRG16=1, BRGH=1" : Baudrate = Fosc/(4*(SPBRG+1)) .
  // Example : Fosc = 40 MHz (slightly violating the spec; permitted Fosc <= 32 MHz)
  //           Wanted: 115.2 kBit/sec 
  //    -> SPBRG = (Fosc / Bitrate) / 4 - 1 = (40000000/115200) / 4 - 1 = 85.8 .
  //    -> rounded to SPBRG = 86, resulting REAL bitrate:
  //           Fbit = Fosc / ( 4 * (SPBRG+1) ) = 114.9 kBit/sec . Should be ok. 
#if( SWI_UART_BAUDRATE==115200)    // 115200 bits/second (ANY serial port should support this)
  SPBRG = 86;  // baudrate divisor, 16 bit, details above
#elif( SWI_UART_BAUDRATE==1000000) // 1000000 bits/second for higher sampling rates, but who supports this ?
  // Wanted: 1000.0 kBit/sec -> SPBRG = (Fosc / Bitrate) / 4 - 1 = (40MHz/1MHz) / 4 - 1 = 39 . 
  SPBRG = 39; // baudrate divisor for 1 MBit/sec. Didn't work with 'Prolific'.
#elif( SWI_UART_BAUDRATE==460800) // 460800 = 115200 * 4 should be easier (with 'UART crystals')..
  // Wanted: 460.8 kBit/sec -> SPBRG = (Fosc/Bitrate)/4-1 = (40MHz/460.8kHz)/4-1 = 20.7 . 
  // Using 21 (nearest int) -> Fbit = 40MHz / (4*(21+1)) = 454545 bit/sec; 1.4 % off .
  SPBRG = 21; // baudrate divisor for 460.8kBit/sec. Ok with 'Prolific'.
#elif( SWI_UART_BAUDRATE==500000) // 500 instead of 460.8 kBit should be enough for fs_out = 20 kHz..
  // Wanted: 500.0 kBit/sec -> SPBRG = (Fosc/Bitrate)/4-1 = (40MHz/500.0kHz)/4-1 = 19 [exact].
  SPBRG = 19; // baudrate divisor for 500 kBit/sec. Failed with 'Prolific', no problem with FTDI.
#else
# error "Please add support for the new baudrate HERE !"
#endif 

  // > 2. Enable the asynchronous serial port by clearing
  //      the SYNC bit and setting the SPEN bit (.. etc, see DS40001579E page 320) 
  RCSTAbits.SPEN = 1; // serial port enable
  TXSTAbits.TXEN = 1; // transmit enable

  PIE1bits.TXIE = 0;  // disable USART transmit interrupt (we use stupid busy-spinning, but that's ok. KISS.)
  PIE1bits.RCIE = 0;  // disable USART receive interrupt as well ! (whatever the "C" stands for..)

   // > 8.    Load 8-bit data into the TXREG register. 
   // >       This will start the transmission.

} // end UART_Init()

//---------------------------------------------------------------------------
void UART_SendChar( char c )
  // forget about putch() / printf() ! This is a PIC with microscopic ROM !
{
  // > The TRMT bit is set when the TSR register is empty and is
  // > cleared when a character is transferred to the TSR register from the TXREG (..)
  while( ! TXSTAbits.TRMT )   // Wait until the transmit-register can accept another character
   { 
   }
  TXREG = c; // UART transmit register (at least in PIC16F1783)
} // end UART_SendChar()

void UART_SendCrNl(void)
{ UART_SendChar( '\r' );
  UART_SendChar( '\n' );
}

//---------------------------------------------------------------------------
void UART_SendString( const char *cp )
  // forget about putch() / printf() ! This is a PIC with microscopic ROM !
{
  // > The TRMT bit is set when the TSR register is empty and is
  // > cleared when a character is transferred to the TSR register from the TXREG (..)
  while( *cp )   // Wait until the transmit-register can accept another character
   { UART_SendChar( *cp++ );
     // Wonder what XC8 produces from this, when after each compilation it says:
     // > You have compiled in FREE mode.
     // > Using Omnicient Code Generation that is available in PRO mode,
     // > you could have produced up to 60% smaller and 400% faster code.  ?
     // ; while( *cp ) ...
     // 0x74A: MOVF cp, W     ; <---------------------   while-loop
     // 0x74B: MOVWF FSR0     ;                       |
     // 0x74C: MOVF 0x72, W   ;                       |
     // 0x74D: MOVWF FSR0H    ;                       |
     // 0x74E: MOVIW FSR0++   ;                       |
     // 0x74F: BTFSC STATUS, 0x2      ; --            |
     // 0x750: RETURN                 ;   |           |
     // 0x751: MOVF 0xF1(cp??), W     ; <-            |
     // 0x752: MOVWF FSR0     ; MOVWF 0x84    ?       |
     // 0x753: MOVF 0x72, W   ; MOVF  0xF2, W ?       |
     // 0x754: MOVWF FSR0H    ; MOVWF 0x85    ?       |
     // 0x755: MOVF INDF0, W  ; MOVWF 0x80, W ?       |
     // 0x756: MOVLP 0x7      ; PCLATH := 7;          |
     // 0x757: CALL 0x737     ; -> UART_SendChar( w ) |
     // 0x758: MOVLP 0x7      ;                       |
     // 0x759: MOVLW 0x1      ;                       |
     // 0x75A: ADDWF cp, F    ;                       |
     // 0x75B: MOVLW 0x0      ;                       |
     // 0x75C: ADDWFC 0x72, F ;                       |
     // 0x75D: GOTO   0x74A   ; ----------------------
   }
} // end UART_SendString()

//---------------------------------------------------------------------------
void UART_SendDecimal( short i16 )  // sends i16 as decimal string.
{ // Note: itoa() is NON STANDARD ! ! Microchip declares it as 
  // >  extern char * itoa(char * buf, int val, int base);
  // while many others use an incompatible, non-intuitive argument list:
  // >  extern char * itoa(int val, char * buf, int base);
#if(0) && (defined __XC8) // compiling for PIC, using Microchip's "XC8" compiler ?
  UART_SendString( itoa( UART_sz9Temp, i16, 10/*base*/)  );
  // program size using   itoa : 2150 code memory words with XC8 "free".
  // program size without itoa : 2042 code memory words with XC8 "free". 
#else // do NOT use itoa() ...
  // For most other PIC C compilers, we don't want to depend on itoa() & co,
  // so -as usual- roll our own. Here: integer-to-string conversion..  
  // Principle: Convert integer to decimal, using the temp buffer to reverse the digits later.
  uint8_t i;
# ifdef __CC5X__
  uint8_t bTemp;
  if( i16 < 0 )
   {  
      UART_SendChar( '-' );
      i16 = -i16; 
   }
  // Convert integer to decimal string, ending with the MOST SIGNIFICANT digit:
  i = 0;
  do
   { // ex: UART_sz9Temp[i++] = '0' + (i16 % 10);
     // ex: UART_sz9Temp[i] = '0' + (u16 % 10);  // still too complex for CC5X !
     bTemp = (uint16_t)i16 % 10;
     // > Error[2] : Sign problems, please typecast one operand to unsigned (uns8)
     bTemp += '0';
     UART_sz9Temp[i] = bTemp;
     // > Error[1] C:\pic\GPSDO\UART_PIC16F1783.C 211 : Unable to generate code
     // > (The C syntax is correct. However, CC5X is unable to generate code.
     // > The workaround is often to split the code into simpler statements,
     // > using an extra variable to store temporary results. Sometimes it is
     // > enough to change the sequence of operations)
     i++;
     i16 = (uint16_t)i16 / (uint16_t)10;
     // > Error[2] : Sign problems, please typecast one operand to unsigned (uns8)
   } while( i16>0 );
  // Print the string in reversed order, because the most significant digit was converted LAST:
  while( i>0 ) // i = number of decimal digits from the above loop
   { --i;      // don't use UART_sz9Temp[--i] .. that would be asking too much for CC5X !
     UART_SendChar( UART_sz9Temp[i] );
   }
# else  // here the non-brain-damaged implementation :
  if( i16 < 0 )
   { UART_SendChar( '-' );
     i16 = -i16;
   }
  // Convert integer to decimal string, ending with the MOST SIGNIFICANT digit:
  i = 0;
  do
   { UART_sz9Temp[i++] = '0' + (i16 % 10);  // XC8 has no problems with this, CC5X surrenders
     i16 /= 10;
   } while( i16>0 );
  // Print the string in reversed order, because the most significant digit was converted LAST:
  while( i>0 ) // i = number of decimal digits from the above loop
   { UART_SendChar( UART_sz9Temp[--i] );
   }
# endif // __CC5X__ ?     
#endif // don't use itoa() ?

} // end UART_SendDecimal()

//---------------------------------------------------------------------------
uint8_t UART_ReadChar(void)  // Reads the next character from the UART's FIFO.
{ // Returns 0x00 when 'nothing received'. Thus only for ASCII data.
  // Details about reception from the "EUSART" in DS40001579E, page 315 .
  // > The RCIF interrupt flag bit will be set when there is an
  // > unread character in the FIFO, regardless of the state of
  // > interrupt enable bits.
  if( PIR1bits.RCIF )
   { return RCREG; 
   }
  // Arrived here: Nothing received. On this occasion, check for an RX-overflow,
  // because (from DS40001579E, page 315ff) :
  // > If the receive FIFO is overrun, no additional characters will be received
  // > until the overrun condition is cleared. (..) The error must be cleared 
  // > by either clearing the CREN bit of the RCSTA register  or by resetting 
  // > the EUSART by clearing the SPEN bit of the RCSTA register.
  // WB: Decided to try the less radical method (do not disturb transmission):
  if( RCSTAbits.OERR ) // 'Overrun Error bit' in the 'Receive Status' set ?
   { RCSTAbits.CREN = 0; // "clear error by clearing the CREN bit".. but :
     // This actually DISABLES THE RECEIVER (in asynchronous mode) !
     NOP();              // alias _nop() alias __nop() ... holy shit
     RCSTAbits.CREN = 1; // re-enable reception in async mode
   }
  return 0x00;
} // end UART_ReadChar()

#if( RXD_INVERT_POLARITY )   // invert the polarity of received data by software ?
//---------------------------------------------------------------------------
uint8_t UART_InvertRcvdChar( uint8_t bRcvdChar )
{ 
  switch( bRcvdChar ) // try to convert "garbage" into the original character
   { // This kind-of "look-up table" was made by simply examining
     // the 'garbage', printed as decimal code in WinPic's terminal.
     // THIS ONLY WORKS WITH A SUFFICIENT GAP BETWEEN CHARACTERS !
     // Note: Sorting this table by case-values, and filling the
     //       the gaps with "return 0x00" did not motivate the compiler
     //       to use a computed jump into a list of "retlw"s -> Eeek...
 //  case   0: return 0x00;
 //  case   1: return 0x00;
 //  case   2: return 0x00;
     case   3: return ' ';
     case   4: return 'p';
     case   5: return 'P';
     case   6: return '0'; // digit zero
     case   8: return 'x';
     case   9: return 'h';
     case  10: return 'X';
     case  11: return 'H';
     case  12: return '8';
 //  case  13: return 0x00;
 //  case  14: return 0x00;
 //  case  15: return 0x00;
 //  case  16: return 0x00;
     case  17: return 't';
     case  18: return 'l';  // "L" lower case
     case  19: return 'd';
 //  case  20: return 0x00;
     case  21: return 'T';
     case  22: return 'L';
     case  23: return 'D';
     case  25: return '4';
 //  case  26: return 0x00;
 //  case  27: return 0x00;
 //  case  28: return 0x00;
 //  case  29: return 0x00;
 //  case  30: return 0x00;
 //  case  31: return 0x00;
 //  case  32: return 0x00;
     case  33: return 'z';
     case  34: return 'v';
     case  35: return 'r';
     case  36: return 'n';
     case  37: return 'j';
     case  38: return 'f';
     case  39: return 'b';
 //  case  40: return 0x00;
     case  41: return 'Z';
     case  42: return 'V';
     case  43: return 'R';
     case  44: return 'N';
     case  45: return 'J';
     case  46: return 'F';
     case  47: return 'B';
 //  case  48: return 0x00;
 //  case  49: return 0x00;
     case  50: return '6';
     case  51: return '2';

     case  67: return 'y';
     case  68: return 'w';
     case  69: return 'u';
     case  70: return 's';
     case  71: return 'q';
     case  72: return 'o';
     case  73: return 'm';
     case  74: return 'k';
     case  75: return 'i';
     case  76: return 'g';
     case  77: return 'e';
     case  78: return 'c';
     case  79: return 'a';
  // case  80: return 0x00;
  // case  81: return 0x00;
  // case  82: return 0x00;
     case  83: return 'Y';
     case  84: return 'W';
     case  85: return 'U';
     case  86: return 'S';
     case  87: return 'Q';
     case  88: return 'O';
     case  89: return 'M';
     case  90: return 'K';
     case  91: return 'I';
     case  92: return 'G';
     case  93: return 'E';
     case  94: return 'C';
     case  95: return 'A';
     case  99: return '9';
     case 100: return '7';
     case 101: return '5';
     case 102: return '3';
     case 103: return '1';  // digit one
#   if(0)  // (1)=normal compilation, (0)=test
     default:  return 0x00;      // discard anything else
#   else
     default:  return bRcvdChar; // pass-through anything else (unchanged)
#   endif
   }
} // end UART_InvertRcvdChar()
#endif // RXD_INVERT_POLARITY ?



/*
EOF ( UART_PIC16F1783.c ) */