/*
 * File:   main.c
 * Author: boos
 *
 * Created on February 21, 2020, 7:49 PM
 */

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover Mode (Internal/External Switchover Mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config CPUDIV = NOCLKDIV// CPU System Clock Selection Bit (NO CPU system divide)
#pragma config USBLSCLK = 48MHz // USB Low SPeed Clock Selection bit (System clock expects 48 MHz, FS/LS USB CLKENs divide-by is set to 8.)
#pragma config PLLMULT = 3x     // PLL Multipler Selection Bit (3x Output Frequency Selected)
#pragma config PLLEN = ENABLED  // PLL Enable Bit (3x or 4x PLL Enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = OFF      // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (Low-voltage programming disabled)

#include <xc.h>

// define your texts
// (can be as many as you want, total length is around 6000 characters)
char *myTexts[] = {"GREETINGS, NICE PEOPLE OF THE INTERNET!    ",
                   "HELLO THERE.  GENERAL KENOBI, YOU ARE A BOLD ONE!    ",
                   "WHAT IS THE ANSWER TO THE ULTIMATE QUESTION OF LIFE, THE UNIVERSE, AND EVERYTHING?     ",
                   "42.     ",
                   "THANK YOU SO MUCH FOR WATCHING, AND I WILL SEE YOU NEXT TIME!            "};

// how are your segments connected to the shift register?
// map        a  b  c  d  e  f  g  .
char map[] = {7, 8, 3, 2, 1, 6, 5, 4};

// declare functions
void clearDisplay (int digits);
unsigned char convertCharacterToPattern (char ASCII);
void sendValue (unsigned char value);

// define locations of shift register controls
#define STROBE RC5
#define DATA RC4
#define CLOCK RC3
#define NEXT !RA5

// variables
int text_index = 0, char_index = 0, adc_value, TIME_BUFFER, NEXT_BUFFER;

// main function
void main (void) {
    
    // set internal oscillator to 4MHz
    IRCF0 = 1;
    IRCF1 = 0;
    IRCF2 = 1;
    IRCF3 = 1;
    
    // weak pull-up for button
    TRISA5 = 1;
    WPUA5 = 1;
    nWPUEN = 0;
    
    // ADC settings
    
	// ADC sampling frequency per bit is F_osc/16
	ADCS0 = 1;
	ADCS1 = 0;
    ADCS2 = 1;
    
    // result alignment
    ADFM = 0;
    
    // RC2 as analog input
	TRISC2 = 1;
    ANSC2 = 1;
    
    // select channel AN6 (which corresponds to RC2)
    CHS0 = 0;
    CHS1 = 1;
    CHS2 = 1;
    CHS3 = 0;
    CHS4 = 0;
    
    // turn the ADC on
	ADON = 1;
    
    // set outputs and disable analog features on pin RC3
    TRISC5 = 0;
    TRISC4 = 0;
    TRISC3 = 0;
    ANSC3 = 0;
    
    // TIMER0 settings
    
	// internal clock, no prescaler, interrupt on overflow
	TMR0CS = 0;
	PSA = 1;
	TMR0IE = 1;

	// enable global interrupts
	GIE = 1;
    
    //  begin of main program
    
    // clear all eight registers
    clearDisplay(8);
    
    // main loop
    while (1) {
        
        // get potentiometer position
        GO = 1; while (GO);
        adc_value = ADRESH;
        
        // has the "NEXT" button been pressed?
        if ((NEXT) && (NEXT_BUFFER == 0)) {
            
            // remove old text
            clearDisplay(8);
            
            // reset everything to display new text
            TIME_BUFFER = 0;
            char_index = 0;
            
            // advance to the next line of text, and go back
            // to the first one if we have arrived at the end
            text_index++;
            if (text_index >= sizeof(myTexts)/sizeof(myTexts[0])) {
                text_index = 0;
            }
            
            // reset debounce variable
            NEXT_BUFFER = 50;
            
        }
        
    }
    
    return;
    
}

// the interrupt service routine
void __interrupt () isr (void) {   

    // this is called abound 3900 times per second
	if (TMR0IF) {
        
        // increase the time buffer by 1
        TIME_BUFFER++;
        
        // have we waited long enough for the text to
        // advance to the next letter?
        if (TIME_BUFFER > 4*adc_value + 100) {
            
            // reset the timing buffer variable
            TIME_BUFFER = 0;
            
            // pipe out the next text character to the shift registers
            sendValue(convertCharacterToPattern(myTexts[text_index][char_index]));
            STROBE = 1;
            STROBE = 0;
            
            // increase the character index,
            // and reset to 0 if we have reached the end
            char_index++;
            if (myTexts[text_index][char_index] == 0) {
                char_index = 0;
            }
            
        }
        
        // debounce the "NEXT" button
        if ((!NEXT) && (NEXT_BUFFER > 0)) {
            NEXT_BUFFER -= 1;
        }

		// reset interrupt flag
		TMR0IF = 0;

	}
    
}

// This function converts an ASCII character into its corresponding
// 7-segment symbol. You can modify this function to create your own
// custom 7-segment font.
unsigned char convertCharacterToPattern (char ASCII) {
    
    // what is the character?
    switch (ASCII) {
        
        // numbers         abcdefg.
        case '0': return 0b11111100;
        case '1': return 0b01100000;
        case '2': return 0b11011010;
        case '3': return 0b11110010;
        case '4': return 0b01100110;
        case '5': return 0b10110110;
        case '6': return 0b10111110;
        case '7': return 0b11100000;
        case '8': return 0b11111110;
        case '9': return 0b11110110;
        
        // letters         abcdefg.
        case 'a': return 0b11101110;
        case 'A': return 0b11101110;
        case 'b': return 0b00111110;
        case 'B': return 0b00111110;
        case 'c': return 0b00011010;
        case 'C': return 0b10011100;
        case 'd': return 0b01111010;
        case 'D': return 0b01111010;
        case 'e': return 0b10011110;
        case 'E': return 0b10011110;
        case 'f': return 0b10001110;
        case 'F': return 0b10001110;
        case 'g': return 0b10111100;
        case 'G': return 0b10111100;
        case 'h': return 0b00101110;
        case 'H': return 0b01101110;
        case 'i': return 0b00100000;
        case 'I': return 0b01100000;
        case 'j': return 0b01111000;
        case 'J': return 0b11111000;
        case 'k': return 0b10101110;
        case 'K': return 0b10101110;
        case 'l': return 0b00111100;
        case 'L': return 0b00011100;
        case 'm': return 0b10101000;
        case 'M': return 0b10101000;
        case 'n': return 0b00101010;
        case 'N': return 0b11101100;      
        case 'o': return 0b00111010;
        case 'O': return 0b11111100;
        case 'p': return 0b11001110;
        case 'P': return 0b11001110;
        case 'q': return 0b11100110;
        case 'Q': return 0b11100110;
        case 'r': return 0b00001010;
        case 'R': return 0b00001010;
        case 's': return 0b10110110;
        case 'S': return 0b10110110;
        case 't': return 0b00011110;
        case 'T': return 0b00011110;
        case 'u': return 0b00111000;
        case 'U': return 0b01111100;
        case 'v': return 0b00111000;
        case 'V': return 0b01111100;
        case 'w': return 0b01010100;
        case 'W': return 0b01010100;
        case 'x': return 0b01101110;
        case 'X': return 0b01101110;
        case 'y': return 0b01100110;
        case 'Y': return 0b01110110;
        case 'z': return 0b11011010;
        case 'Z': return 0b11011010;
        
        // symbols          abcdefg.
        case '=':  return 0b00010010;
        case '-':  return 0b00000010;
        case '_':  return 0b00010000;
        case 248:  return 0b11000110; // degree sign
        case '\'': return 0b01000000;
        case '"':  return 0b01000100;
        case '(':  return 0b10011100;
        case '[':  return 0b10011100;
        case '{':  return 0b10011100;
        case ')':  return 0b11110000;
        case ']':  return 0b11110000;
        case '}':  return 0b11110000;
        case '.':  return 0b00000001;
        case ',':  return 0b00100000;
        case ';':  return 0b00100001;
        case '!':  return 0b01000001;
        case '?':  return 0b11001011;
        case ' ':  return 0b00000000;
        
        // default symbol is an empty space
        default: return 0b00000000;
        
    }
    
}

// This function that an eight-bit value to
// the CD4094 shift registers
void sendValue (unsigned char value) {

    // auxiliary index variable
	int n = 0;
    
    // loop over all eight bits
	for (n = 0; n < 8; n++) {
        
        // Map the n-th bit to its corresponding
        // position in the shift register. This is
        // controlled by the array map[].
        DATA = (value >> (map[n]-1)) & 1;
		
        // pulse the clock pin
        CLOCK = 1;
		CLOCK = 0;
        
	}

}

// This function clears the display by sending out zeros.
void clearDisplay (int digits) {
   
    // send zeros
    DATA = 0;
    
    // send eight zeros for each digit
    for (int tmp=0; tmp<8*digits; tmp++) {
        CLOCK = 1;
        CLOCK = 0;
    }
    
    // update the registers
    STROBE = 1;
    STROBE = 0;
    
}