Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F120712847
adc.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sun, Jul 6, 12:04
Size
5 KB
Mime Type
text/x-c
Expires
Tue, Jul 8, 12:04 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
27235409
Attached To
R6618 Oncilla Power Management Board Firmware
adc.c
View Options
#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
Log In to Comment