/*********************************************************************************
 SerialRx
 CFunction to create additional Rx only serial inputs for the Micromite
 Geoff Graham, May 2015

 usage:  r = SerialRx(port, baud-rate, string, timeout, maxchars, termchars)
 where   pin                is the input pin
         baud-rate          is the desired baudrate (maximum 38400 at 40MHz)
         string             is the string where received characters are to be stored
         timeout            is the timeout in milliseconds
         maxchars           is the maximum number of chars to receive (it is optional)
         termchars          is a string containing terminating chars (it is optional)
         r                  is the return value which can be:
                                1 = the data capture was terminated by a timeout
                                2 = terminated because the max nbr of chars had been received
                                3 = terminated with a match with a char in termchars
 *************************************************************************************/

#define _SUPPRESS_PLIB_WARNING                                      // required for XC1.33  Later compiler versions will need PLIB to be installed
#include <plib.h>                                                   // the pre Harmony peripheral libraries
#include "CFunctions.h"                                             // MMBasic API definitions

void Delay(unsigned int ticks);

long long int main(unsigned int *pin, unsigned int *BaudRate, char *string, unsigned int *timeout_ms, unsigned int *maxchars, char *termchars) {
    unsigned int ticks = (CurrentCpuSpeed/(*BaudRate * 2))-5;       // calculate the number of core timer ticks for each bit
    unsigned int timeout = (CurrentCpuSpeed/2000) * *timeout_ms;    // calculate the number of core timer ticks for the timeout
    volatile unsigned int *Port;                                    // the PIC32 register for the input pin
    unsigned char c;
    unsigned int current_ticks = 0;
    int i, PinBit, count = 0;

    ExtCfg(*pin, EXT_DIG_IN, 0);                                    // configure the pin as an input (will throw an error if invalid pin)
    Port = GetPortAddr(*pin, PORT);                                 // get the input pin register
    PinBit = GetPinBit(*pin);                                       // get the bit in the port register

    asm volatile("mtc0   %0, $9": "+r"(current_ticks));             // set the core timer to zero
    while(1) {
        while((*Port >> PinBit) & 1) {                              // wait for the start bit
             asm volatile("mfc0   %0, $9" : "=r"(current_ticks));   // get the core timer ticks
             if(current_ticks >= timeout) return 1;                 // return if there is a timeout
        }
        Delay(ticks/2);                                             // wait for the middle of the start bit
        if((*Port >> PinBit) & 1) continue;                         // go around again if not low
        for(c = i = 0; i < 8; i++) {
            Delay(ticks);                                           // wait for the bit
            c |= ((*Port >> PinBit) & 1) << i;                      // and add this bit in
        }
        Delay(ticks);                                               // wait for the stop bit
        if(!((*Port >> PinBit) & 1)) continue;                      // a framing error if not high
        count++;
        string[count] = c;                                          // save the character
        string[0] = count;                                          // and update the numbers of characters in the string
        if(maxchars && count >= *maxchars) return 2;                // do we have our maximun number of characters?
        if(termchars) {
            for(i = 0; i < termchars[0]; i++) {                     // check each terminating character
                if(c == termchars[i + 1]) return 3;                 // and exit if this character is one of them
            }
        }
        if(count >= 255) return 2;                                  // do not exceed the maximum string length
    }
}


// delay for a certain number of core timer ticks (one tick every 50nS at 40MHz CPU speed, 100nS at 20MHz, etc)
void Delay(unsigned int ticks) {
    unsigned int current_ticks;
    asm volatile("mfc0   %0, $9" : "=r"(current_ticks));            // get the core timer ticks
    ticks += current_ticks;                                         // this is what we want to count up to
     do {
         asm volatile("mfc0   %0, $9" : "=r"(current_ticks));       // get the core timer ticks
     } while(current_ticks < ticks);
}
