/** Main.c file generated by CCS New Project wizard
 *
 * Created:	Wed Mar 22 2023
 * Processor:	PIC16F877
 * Compiler: 	CCS for PIC
 * Author:	2111739
 */

#include <16F877.h>
#include "main.h"
#use delay(crystal = 8MHz)

// Global variables
unsigned long int us_north = 0;
unsigned long int us_east = 0;
unsigned long int us_south = 0;
unsigned long int us_west = 0;

unsigned long int north_south = 0;
unsigned long int east_west = 0;

/** Global state variables */
unsigned int buttons = 0;
unsigned int traffic_state = 0;

/** List holding Seven Segment display sequences */
unsigned int countdown[] = {0x10, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0};

/* When a pedestrian button is pressed, this ISR is triggered. */
#INT_RB
void RB_isr(void)
{
  printf("INT IN RB \r\n");
  buttons = 1;
  delay_ms(200); // debouncing to prevent multiple triggers
  clear_interrupt(INT_RB);
}

/** Timer 2 cycles through each ADC channel and saves the current value to a global variable */
#INT_TIMER2
void TIMER2_isr(void)
{
  set_adc_channel(0);    // Change ADC channel
  delay_us(30);          // Delay to allow the ADC to settle. Required after changing channel and before reads.
  us_north = read_adc(); // Storing the ADC value in a global variable

  set_adc_channel(1);
  delay_us(30);
  us_east = read_adc();

  set_adc_channel(2);
  delay_us(30);
  us_south = read_adc();

  set_adc_channel(3);
  delay_us(30);
  us_west = read_adc();

  clear_interrupt(INT_TIMER2); // Clearing the interrupt so it can trigger again.
}

int main(void)
{
  setup_adc_ports(AN0_AN1_AN2_AN3_AN4);
  setup_adc(ADC_CLOCK_INTERNAL);

  setup_timer_2(T2_DIV_BY_1, 125, 16); // Interrupt for reading ADC values

  stop_traffic();                // Stop all traffic before interrupts are enabled
  output_d(0xff);                // Invalid BCD combination to turn the Seven Segment Displays off
  
  enable_interrupts(INT_RB);     // Turn on Interrupts for changes on pins B4-7 (Pedestrian buttons)
  enable_interrupts(INT_TIMER2); // Turn on ADC read interrupt
  enable_interrupts(GLOBAL);

  unsigned char count = 0x10;

  while (True)
  {
    if (buttons != 0)
    {
      if (traffic_state == 1) {
	 red_traffic(LIGHT_N_R, LIGHT_N_A, LIGHT_S_R, LIGHT_S_A);
      } else if (traffic_state == 2) {
	 red_traffic(LIGHT_E_R, LIGHT_E_A, LIGHT_W_R, LIGHT_W_A);
      }
      disable_interrupts(GLOBAL);
      delay_ms(3000);
      pedestrians();
      enable_interrupts(INT_RB); // Re-enable interrupts
      enable_interrupts(INT_TIMER2);
      enable_interrupts(GLOBAL);
    }
    else
    {
      check_for_traffic(); // Use the values read from the ADC to determine if there is any traffic
    }
  }
}


/** Checks for traffic across North-South and East-West traffic lights */
void check_for_traffic()
{
  north_south = us_north + us_south;
  east_west = us_east + us_west;

  if ((north_south != 0) & (north_south + 1 > east_west))
  {
    if (traffic_state == 2 | traffic_state == 3)
    {
      red_traffic(LIGHT_E_R, LIGHT_E_A, LIGHT_W_R, LIGHT_W_A);
      amber_traffic(LIGHT_N_R, LIGHT_N_A, LIGHT_S_R, LIGHT_S_A);
    }
    else if (traffic_state == 1)  // Already green
      break;
    traffic_state = 1;
    
    green_traffic(LIGHT_N_R, LIGHT_N_A, LIGHT_S_R, LIGHT_S_A);
  }
  else if (east_west > north_south)
  {
    if (traffic_state == 1| traffic_state == 3)
    {
      red_traffic(LIGHT_N_R, LIGHT_N_A, LIGHT_S_R, LIGHT_S_A);
      amber_traffic(LIGHT_E_R, LIGHT_E_A, LIGHT_W_R, LIGHT_W_A);
    }
    else if (traffic_state == 2)
      break;
    traffic_state = 2;
    
    green_traffic(LIGHT_E_R, LIGHT_E_A, LIGHT_W_R, LIGHT_W_A);
  }
  else
  {
    traffic_state = 3;
  }
}

void amber_traffic(int signal_1_r, int signal_1_a, int signal_2_r, int signal_2_a)
{
  delay_ms(1000);
  output_high(signal_1_a); // Get ready
  output_high(signal_2_a);
  delay_ms(1000);
}

void green_traffic(int signal_1_r, int signal_1_a, int signal_2_r, int signal_2_a)
{ 
  output_low(signal_1_r);
  output_low(signal_2_r); // Go
  output_low(signal_1_a);
  output_low(signal_2_a);
}

void red_traffic(int signal_1_r, int signal_1_a, int signal_2_r, int signal_2_a)
{
  output_high(signal_1_a); // Stop if safe
  output_high(signal_2_a);
  delay_ms(1000);
  output_high(signal_1_r); // Stop
  output_high(signal_2_r);
  output_low(signal_1_a);
  output_low(signal_2_a);
  delay_ms(1000);
}

void stop_traffic()
{
  output_high(LIGHT_N_R); // Turn all lights red
  output_high(LIGHT_E_R);
  output_high(LIGHT_S_R);
  output_high(LIGHT_W_R);

  output_low(LIGHT_N_A);
  output_low(LIGHT_E_A);
  output_low(LIGHT_S_A);
  output_low(LIGHT_W_A);
}

void pedestrians()
{
  stop_traffic();
  output_high(SSD_LEFT_EN); // Enable Seven Segment Displays
  output_high(SSD_RIGHT_EN);
  for (int i = 0; i < 11; i++)
  {
    output_d(countdown[i]);
    delay_ms(1000);
  }
  output_low(SSD_LEFT_EN); // Disable Seven Segment Displays
  output_low(SSD_RIGHT_EN);

  buttons = 0; // Turn off pedestrian waiting flag
}