/****************************************************************************

 Filename:       hardware.c

 Description:    this file contains the hardware specific functions for the
                 PID Loop Example.
                  
 Author:        Ray Mack

****************************************************************************/

/*****************************************************************************

How it Works:

The system comes up in bare metal condition. The functions in this file are
used to activate the various GPIO systems (a Tiva concept) and then configure
those GPIO systems so they are ready for the devices to use.

**************************************************************************/


#include <stdint.h>
#include <stdbool.h>
#include <driverlib/gpio.h>
#include <driverlib/sysctl.h>
#include <driverlib/pwm.h>
#include <inc/hw_pwm.h>
#include <inc/hw_memmap.h>
#include <driverlib/timer.h>

#include <xdc/std.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>

extern uint16_t ADC_data[8];
/****************************
The control loop function that implements
PID control of an OCXO
*****************************/
#define PROPORTIONAL_PARAMETER  4
#define INTEGRAL_PARAMETER 2
#define DIFFERENTIAL_PARAMETER 0        
// differential value turned off by setting to zero. 
// Set to positive value to implement differential action
#define INTEGRAL_RAIL  100
#define NEG_INTEGRAL_RAIL -100
// maximum or minimum value that the integral term can 
// attain. This limits overshoot for very large excursions.                             
uint16_t read_ADC(void)
{
   run_internal_ADC();
   System_printf("ADC: %d\n", ADC_data[0]);
   System_flush();
   return ADC_data[0];
}
void PID_control_loop(int target_ADC_reading)
{
int error, ADC_value;
int integral_accumulator, integral_term;
int differential_term, last_error;
int proportional_term;
int timer_value;
int PWM_value;

  integral_accumulator = 0;
  while (1) // an infinite control loop
  {
    // sit here and burn cycles until the next sample time
    while (TimerLoadGet(TIMER0_BASE, TIMER_A) != 0)
    {} // empty loop
    // restart the timer
    TimerLoadSet(TIMER0_BASE, TIMER_A, 10000); 
    ADC_value = read_ADC(); // a helper function that reads the ADC0 pin.
    error = target_ADC_reading - ADC_value;
    proportional_term = PROPORTIONAL_PARAMETER * error;
    integral_accumulator += INTEGRAL_PARAMETER * error;
    if (integral_accumulator > INTEGRAL_RAIL)
        integral_accumulator = INTEGRAL_RAIL;
    else if (integral_accumulator <  NEG_INTEGRAL_RAIL)
        integral_accumulator = NEG_INTEGRAL_RAIL;
    integral_term = integral_accumulator;
    differential_term = DIFFERENTIAL_PARAMETER * (last_error - error);
    last_error = error;
    PWM_value = proportional_term + integral_term + differential_term;
    if (PWM_value > 400)
        PWM_value = 400;
    else if (PWM_value < 0)
       PWM_value = 0;
    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, PWM_value);
  }
}

