#Universidad del Valle de Guatemala
#Sistemas de Control Sección 11
#Cristian Alonzo 21761
#Adriana Girón 21201
#Jueves 3 de Octubre, 2024
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/adc.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include "driverlib/ssi.h"
#define FREQ_TIMER 10000
#define NUM_SPI_DATA 1
#define SPI_FREC 4000000
#define SPI_ANCHO 16
#define COTA_SUP 3.3
#define COTA_INF 0.0
float eD, Ek, ek_1 = 0.0, Ek_1 = 0.0, Referencia, Salida, ek, uk;
float kP = 1.99;
float kI = 308;
float kD = 0.003;
uint16_t uk_int = 0;
float Ts;
//*********
//
// The interrupt handler for the first timer interrupt.
//
//*********
void
Timer0IntHandler(void)
{
uint32_t pui32DataTx[NUM_SPI_DATA]; // la función put pide tipo uint32_t
uint8_t ui32Index;
uint32_t pui32ADC0Value[1];
// Clear the timer interrupt. Necesario para lanzar la próxima interrupción.
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// Trigger the ADC conversion.
ADCProcessorTrigger(ADC0_BASE, 2); // Notar el cambio de "secuencia" (2 en lugar de 3).
// Wait for conversion to be completed.
while(!ADCIntStatus(ADC0_BASE, 2, false)) // Notar el cambio de "secuencia".
{
}
// Clear the ADC interrupt flag.
ADCIntClear(ADC0_BASE, 2); // Notar el cambio de "secuencia".
// Ahora se leen dos valores, no sólo uno, según la configuración.
// En pui32ADC0Value[0] se tendrá el valor del puerto AIN0 y
// en pui32ADC0Value[1] se tendrá el valor del puerto AIN1, porque así se
// configuró en el main.
ADCSequenceDataGet(ADC0_BASE, 2, pui32ADC0Value); // Notar el cambio de "secuencia".
Referencia = pui32ADC0Value[0]*3.3/4095.0; // Convertir a voltios
Salida = pui32ADC0Value[1]*3.3/4095.0; // Convertir a voltios
ek = Referencia - Salida;
eD = ek - ek_1;
Ek = Ek_1 + ek;
Ts = 1.0/FREQ_TIMER;
uk = kP * ek + kI * Ts * Ek + (kD/Ts) * eD;
ek_1 = ek;
Ek_1 = Ek;
if(uk > COTA_SUP){
uk = COTA_SUP;
}
else if(uk < COTA_INF){
uk = COTA_INF;
}
uk_int = (uint16_t)(uk *4095/3.3);
pui32DataTx[0] = (uint32_t)((0b0111 << 12) | (0x0FFF & (uint16_t)uk_int));
// Send data
for(ui32Index = 0; ui32Index < NUM_SPI_DATA; ui32Index++)
{
// Send the data using the "blocking" put function. This function
// will wait until there is room in the send FIFO before returning.
// This allows you to assure that all the data you send makes it into
// the send FIFO.
SSIDataPut(SSI0_BASE, pui32DataTx[ui32Index]);
}
// Wait until SSI0 is done transferring all the data in the transmit FIFO.
while(SSIBusy(SSI0_BASE))
{
}
}
int
main(void)
{
uint32_t pui32residual[NUM_SPI_DATA];
// ------ Configuración del reloj ---------------------------------------------
// Set the clock to run at 80 MHz (200 MHz / 2.5) using the PLL.
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ); // 80 MHz. SÍ FUNCIONA
// ----------------------------------------------------------------------------
// ------ Configuración del SPI -----------------------------------------------
// The SSI0 peripheral must be enabled for use.
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
// For this example SSI0 is used with PortA[5:2]. The actual port and pins
// used may be different on your part, consult the data sheet for more
// information. GPIO port A needs to be enabled so these pins can be used.
// TODO: change this to whichever GPIO port you are using.
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
// Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5.
// This step is not necessary if your part does not support pin muxing.
// TODO: change this to select the port/pin you are using.
GPIOPinConfigure(GPIO_PA2_SSI0CLK);
GPIOPinConfigure(GPIO_PA3_SSI0FSS);
GPIOPinConfigure(GPIO_PA4_SSI0RX);
GPIOPinConfigure(GPIO_PA5_SSI0TX);
// Configure the GPIO settings for the SSI pins. This function also gives
// control of these pins to the SSI hardware. Consult the data sheet to
// see which functions are allocated per pin.
// The pins are assigned as follows:
// PA5 - SSI0Tx
// PA4 - SSI0Rx
// PA3 - SSI0Fss
// PA2 - SSI0CLK
// TODO: change this to select the port/pin you are using.
GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 |
GPIO_PIN_2);
// Configure and enable the SSI port for SPI master mode. Use SSI0,
// system clock supply, idle clock level low and active low clock in
// freescale SPI mode, master mode, 4MHz SSI frequency, and 16-bit data.
// For SPI mode, you can set the polarity of the SSI clock when the SSI
// unit is idle. You can also configure what clock edge you want to
// capture data on. Please reference the datasheet for more information on
// the different SPI modes.
SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
SSI_MODE_MASTER, SPI_FREC, SPI_ANCHO);
// Enable the SSI0 module.
SSIEnable(SSI0_BASE);
// Read any residual data from the SSI port. This makes sure the receive
// FIFOs are empty, so we don't read any unwanted junk. This is done here
// because the SPI SSI mode is full-duplex, which allows you to send and
// receive at the same time. The SSIDataGetNonBlocking function returns
// "true" when data was returned, and "false" when no data was returned.
// The "non-blocking" function checks if there is any data in the receive
// FIFO and does not "hang" if there isn't.
while(SSIDataGetNonBlocking(SSI0_BASE, &pui32residual[0]))
{
}
//*********
//
// This function sets up UART0 to be used for a console to display information
// as the example is running.
//
//**********
//*********
//
// Configure ADC0 for a single-ended input and a single sample. Once the
// sample is ready, an interrupt flag will be set. Using a polling method,
// the data will be read then displayed on the console via UART0.
//
//*********
// The ADC0 peripheral must be enabled for use.
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
// For this example ADC0 is used with AIN0 and AIN1.
// The actual port and pins used may be different on your part, consult
// the data sheet for more information. GPIO port E needs to be enabled
// so these pins can be used.
// TODO: change this to whichever GPIO port you are using.
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
// Select the analog ADC function for these pins.
// Consult the data sheet to see which functions are allocated per pin.
// TODO: change this to select the port/pin you are using.
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3); // Configura el pin PE3
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2); // Configura el pin PE2
// Se configura la secuencia 2, que permitiría hasta cuatro muestras (aunque
// se usarán dos).
ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_PROCESSOR, 0);
// Step 0 en la secuencia 2: Canal 0 (ADC_CTL_CH0) en modo single-ended (por defecto).
ADCSequenceStepConfigure(ADC0_BASE, 2, 0, ADC_CTL_CH0);
// Step 1 en la secuencia 2: Canal 1 (ADC_CTL_CH1) en modo single-ended (por defecto),
// y configura la bandera de interrupción (ADC_CTL_IE) para "setearse"
// cuando se tenga esta muestra. También se indica que esta es la última
// conversión en la secuencia 2 (ADC_CTL_END).
// Para más detalles del módulo ADC, consultar el datasheet.
ADCSequenceStepConfigure(ADC0_BASE, 2, 1, ADC_CTL_CH1 | ADC_CTL_IE | ADC_CTL_END);
// Since sample sequence 2 is now configured, it must be enabled.
ADCSequenceEnable(ADC0_BASE, 2); // Notar el cambio de "secuencia".
// Clear the interrupt status flag. This is done to make sure the
// interrupt flag is cleared before we sample.
ADCIntClear(ADC0_BASE, 2); // Notar el cambio de "secuencia".
// ------ Configuración del TIMER ---------------------------------------------
// Enable the peripherals used by this example.
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
// Enable processor interrupts.
IntMasterEnable();
// Configure the two 32-bit periodic timers.
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
// El tercer argumento determina la frecuencia. El reloj se puede obtener
// con SysCtlClockGet().
// La frecuencia está dada por SysCtlClockGet()/(valor del 3er argumento).
// Ejemplos: Si se pone SysCtlClockGet(), la frecuencia será de 1 Hz.
// Si se pone SysCtlClockGet()/1000, la frecuencia será de 1 kHz
//TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet());
TimerLoadSet(TIMER0_BASE, TIMER_A, (uint32_t)(SysCtlClockGet()/FREQ_TIMER));
// Setup the interrupt for the timer timeout.
IntEnable(INT_TIMER0A);
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// Enable the timers.
TimerEnable(TIMER0_BASE, TIMER_A);
// Las conversiones se hacen al darse la interrupción del timer, para que
// el muestreo sea preciso. Luego de las configuraciones, el programa se
// queda en un ciclo infinito haciendo nada.
while(1)
{
}
}