Page MenuHomec4science

adc.c
No OneTemporary

File Metadata

Created
Sun, Sep 1, 23:46
#include "p33Fxxxx.h"
#include <sbcp-uc/sbcp.h>
#include "adc.h"
#include "pindefs.h"
#include "timer.h"
#include "power_sbcp.h"
#include "buttons.h"
#include "spi_led.h"
eadc_mode adc_mode;
// These parameters are set in buttons.c at power up, they contain the last measurement of the ADC unprocessed
unsigned int last_measurement_voltage;
unsigned int last_measurement_current;
/** \brief SPI initialization function
This function is used to initialize the ADC inputs. It should be called in
the main initialization function.
\param no parameters
\return no returns
*/
void init_adc(void) {
AD1CON1bits.FORM = 0b00; // Data Output Format: integer
// AD1CON1bits.SSRC = 0b111; // Internal Counter (SAMC) ends sampling and starts conversion
// AD1CON1bits.SSRC = 2; // Sample Clock Source: GP Timer starts conversion
AD1CON1bits.SSRC = 0b000; // Manual triggering the conversion
AD1CON1bits.ASAM = 1; // ADC Sample Control: Sampling begins immediately after conversion
AD1CON1bits.AD12B = 1; // 12-bit ADC operation
AD1CON1bits.SIMSAM = 1; // 12-bit ADC operation
// AD1CON1bits.SAMP = 0; // for manually starting conversion
AD1CON2bits.ALTS = 0; // Alternate Input Sample Mode Select bit
AD1CON2bits.CSCNA = 1; // Scan Input Selections for CH0+ during Sample A bit
AD1CON2bits.VCFG = 0b00; // VREFH = AVdd, VREFL = AVss
AD1CON3bits.ADRC = 0; // ADC Clock is derived from Systems Clock
AD1CON3bits.SAMC = 3; // Auto Sample Time = minimum 3*Tad for 12 bits
// AD1CON3bits.SAMC = 12; // Auto Sample Time = 12*Tad
// AD1CON3bits.ADCS = 63; // ADC Conversion Clock Tad=Tcy*(ADCS+1)= (1/40M)*64 = 1.6us
// ADC Conversion Time for 12-bit Tc=14*Tad = 22.4us
AD1CON3bits.ADCS = 5; // ADC Conversion Clock Tad=Tcy*(ADCS+1)= (1/40M)*6 = 150ns (minimum is 120ns for 12-bit)
// ADC Conversion Time for 12-bit Tc=14*Tad = 2.1us
AD1CON2bits.SMPI = 0x0001;
AD1CSSL = 0x0000;
AD1CSSLbits.CSS0 = 1; // Enable AN0 for channel scan
AD1CSSLbits.CSS1 = 1; // Enable AN1 for channel scan
//AD1CON2bits.BUFM = 0; // Always starts filling buffer at address 0x0
// AD1CHS0: A/D Input Select Register
AD1CHS0bits.CH0SA=0; // MUXA +ve input selection (AIN0) for CH0
AD1CHS0bits.CH0NA=0; // MUXA -ve input selection (Vref-) for CH0
//AD1PCFGH/AD1PCFGL: Port Configuration Register
AD1PCFGLbits.PCFG0 = 0; // AN0 as Analog Input
AD1PCFGLbits.PCFG1 = 0; // AN1 as Analog Input
// set offsets to zero
last_measurement_voltage = 0;
last_measurement_current = 0;
// first channel to convert is AN0
adc_mode = READ_BATTERY_VOLTAGE;
IFS0bits.AD1IF = 0; // Clear the A/D interrupt flag bit
IEC0bits.AD1IE = 1; // Do Not Enable A/D interrupt
AD1CON1bits.ADON = 1; // Turn on the A/D converter
}
/**
* \brief Update the ADC-registers
* update the adc-registers to contain the value sampled the last time the T4-interrupt went off
* also check if the voltage and current is within boundaries
* This function contains too many calculations to be done in the interrupt itself
* \ingroup adc_m
*/
void adc_update(void) {
unsigned int volt = ADC_TO_mV(last_measurement_voltage);
unsigned int curr = ADC_TO_mA(last_measurement_current);
sbcp_reg_int(ADDRESS_POWER_INPUT_VOLTAGE) = volt;
sbcp_reg_int(ADDRESS_POWER_TOTAl_CURRENT) = curr;
if ( (curr > (long) sbcp_reg_int(ADDRESS_MAXIMUM_CURRENT)) && (sbcp_reg_int(ADDRESS_POWER_MONITOR_MODE) & MONITOR_CURRENT)){
sbcp_reg_int(ADDRESS_POWER_STATUS) |= POWERBOARD_CURRENT_OVER_MAXIMUM;
emergency_shutdown(1, 1, 1);//never returns
}else{
sbcp_reg_int(ADDRESS_POWER_STATUS) &= ~POWERBOARD_CURRENT_OVER_MAXIMUM;
}
if( volt > sbcp_reg_int(ADDRESS_MAXIMUM_BATTERY_VOLTAGE)){
sbcp_reg_int(ADDRESS_POWER_STATUS) |= POWERBOARD_VOLTAGE_ABOVE_MAX;
emergency_shutdown(1, 0, 0);//never returns
}else{
sbcp_reg_int(ADDRESS_POWER_STATUS) &= ~POWERBOARD_VOLTAGE_ABOVE_MAX;
}
if( volt < sbcp_reg_int(ADDRESS_CRITICAL_BATTERY_VOLTAGE)){
sbcp_reg_int(ADDRESS_POWER_STATUS) |= POWERBOARD_VOLTAGE_UNDER_CRITICAL;
emergency_shutdown(0, 0, 1);//never returns
}else{
sbcp_reg_int(ADDRESS_POWER_STATUS) &= ~POWERBOARD_VOLTAGE_UNDER_CRITICAL;
}
if( volt > sbcp_reg_int(ADDRESS_MINIMUM_BATTERY_VOLTAGE)){
sbcp_reg_int(ADDRESS_POWER_STATUS) |= POWERBOARD_VOLTAGE_ABOVE_MIN;
sbcp_reg_int(ADDRESS_POWER_STATUS) &= ~POWERBOARD_VOLTAGE_UNDER_MIN;
}else{
sbcp_reg_int(ADDRESS_POWER_STATUS) |= POWERBOARD_VOLTAGE_UNDER_MIN;
sbcp_reg_int(ADDRESS_POWER_STATUS) &= ~POWERBOARD_VOLTAGE_ABOVE_MIN;
}
}
/**
* \brief Convert a ADC-registers to mV
* \ingroup adc_m
*/
int ADC_TO_mV(int _ADC_VALUE){
long Max_Batt_mV = 3300L*(BATT_RES_LOW+BATT_RES_HIGH)/(BATT_RES_LOW);
return (int)(Max_Batt_mV * _ADC_VALUE / 4096);
}
/**
* \brief Convert a ADC-registers to mA
* \ingroup adc_m
*/
int ADC_TO_mA(int _ADC_VALUE){
return _ADC_VALUE*2/31;//*(1023/CURR_SENSITIVITY); //1023=3300*127/4096/100
}
/*=============================================================================
ADC INTERRUPT SERVICE ROUTINE
=============================================================================*/
void __attribute__((interrupt, no_auto_psv)) _ADC1Interrupt(void)
{
switch (adc_mode)
{
case READ_BATTERY_VOLTAGE:
last_measurement_voltage = ADC1BUF0;
while (AD1CON1bits.SAMP == 0);
AD1CON1bits.SAMP = 0; // continue with sampling the second channel
adc_mode = READ_BATTERY_CURRENT;
break;
case READ_BATTERY_CURRENT:
last_measurement_current = ADC1BUF0;
adc_mode = READ_BATTERY_VOLTAGE;
// we now wait until the timer0 interrupt routine (1kHz system clock) initiates a new conversion cycle
break;
default:
break;
}
IFS0bits.AD1IF = 0; // Clear the ADC1 Interrupt Flag
}

Event Timeline