Added solar to adc, adc now runs on interrupt, added test case. TODO adc paramter tuning

master
Richard Meadows 2015-07-03 12:46:33 +01:00
rodzic 4429c0670d
commit cde21d13f9
10 zmienionych plików z 1343 dodań i 1043 usunięć

Wyświetl plik

@ -36,7 +36,7 @@
|[GPS_SERCOM]_IRQn|gps usart rx|1|latency not so critical. rate <= 960Hz
|EIC_IRQn|timepulse|2|latency not so critical. rate = 1
|TC2_IRQn|xosc measurement done|2|latency not critical
|ADC_IRQn|adc measurement done|2|latency not critical

Plik diff jest za duży Load Diff

Wyświetl plik

@ -25,7 +25,10 @@
#ifndef ANALOGUE_H
#define ANALOGUE_H
void start_adc_conversion_sequence(void);
uint8_t is_adc_conversion_done(void);
float get_battery(void);
float get_temperature(void);
float get_solar(void);
#endif /* ANALOGUE_H */

Wyświetl plik

@ -193,5 +193,6 @@
#define TC2_INT_PRIO 2 /* XOSC Measure Timer */
#define ADC_INT_PRIO 2 /* ADC */
#endif /* HW_CONFIG_H */

Wyświetl plik

@ -57,31 +57,31 @@
#endif
/**
* \internal Configure MUX settings for the analog pins
*
* This function will set the given ADC input pins
* to the analog function in the pin mux, giving
* the ADC access to the analog signal
*
* \param [in] pin AINxx pin to configure
*/
* \internal Configure MUX settings for the analog pins
*
* This function will set the given ADC input pins
* to the analog function in the pin mux, giving
* the ADC access to the analog signal
*
* \param [in] pin AINxx pin to configure
*/
static inline void _adc_configure_ain_pin(uint32_t pin)
{
#define PIN_INVALID_ADC_AIN 0xFFFFUL
/* Pinmapping table for AINxx -> GPIO pin number */
const uint32_t pinmapping[] = {
/* Pinmapping table for AINxx -> GPIO pin number */
const uint32_t pinmapping[] = {
//#if (SAMD20E | SAMD21E)
PIN_PA02B_ADC_AIN0, PIN_PA03B_ADC_AIN1,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_PA04B_ADC_AIN4, PIN_PA05B_ADC_AIN5,
PIN_PA06B_ADC_AIN6, PIN_PA07B_ADC_AIN7,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_PA08B_ADC_AIN16, PIN_PA09B_ADC_AIN17,
PIN_PA10B_ADC_AIN18, PIN_PA11B_ADC_AIN19,
PIN_PA02B_ADC_AIN0, PIN_PA03B_ADC_AIN1,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_PA04B_ADC_AIN4, PIN_PA05B_ADC_AIN5,
PIN_PA06B_ADC_AIN6, PIN_PA07B_ADC_AIN7,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_INVALID_ADC_AIN, PIN_INVALID_ADC_AIN,
PIN_PA08B_ADC_AIN16, PIN_PA09B_ADC_AIN17,
PIN_PA10B_ADC_AIN18, PIN_PA11B_ADC_AIN19,
/* #elif (SAMD20G | SAMD21G) */
/* PIN_PA02B_ADC_AIN0, PIN_PA03B_ADC_AIN1, */
/* PIN_PB08B_ADC_AIN2, PIN_PB09B_ADC_AIN3, */
@ -129,21 +129,21 @@ static inline void _adc_configure_ain_pin(uint32_t pin)
/* #else */
/* # error ADC pin mappings are not defined for this device. */
/* #endif */
};
};
uint32_t pin_map_result = PIN_INVALID_ADC_AIN;
uint32_t pin_map_result = PIN_INVALID_ADC_AIN;
if (pin <= ADC_EXTCHANNEL_MSB) {
pin_map_result = pinmapping[pin >> ADC_INPUTCTRL_MUXPOS_Pos];
if (pin <= ADC_EXTCHANNEL_MSB) {
pin_map_result = pinmapping[pin >> ADC_INPUTCTRL_MUXPOS_Pos];
Assert(pin_map_result != PIN_INVALID_ADC_AIN);
Assert(pin_map_result != PIN_INVALID_ADC_AIN);
system_pinmux_pin_set_config(pin_map_result,
1, // B
SYSTEM_PINMUX_PIN_DIR_INPUT,
SYSTEM_PINMUX_PIN_PULL_NONE,
false);
}
system_pinmux_pin_set_config(pin_map_result,
1, // B
SYSTEM_PINMUX_PIN_DIR_INPUT,
SYSTEM_PINMUX_PIN_PULL_NONE,
false);
}
}
/**
@ -158,313 +158,311 @@ static inline void _adc_configure_ain_pin(uint32_t pin)
* \retval ADC_STATUS_OK The configuration was successful
* \retval ADC_STATUS_ERR_INVALID_ARG Invalid argument(s) were provided
*/
static enum adc_status_code _adc_set_config(
struct adc_module *const module_inst,
struct adc_config *const config)
static enum adc_status_code _adc_set_config(struct adc_config *const config)
{
uint8_t adjres = 0;
uint32_t resolution = ADC_RESOLUTION_16BIT;
enum adc_accumulate_samples accumulate = ADC_ACCUMULATE_DISABLE;
uint8_t adjres = 0;
uint32_t resolution = ADC_RESOLUTION_16BIT;
enum adc_accumulate_samples accumulate = ADC_ACCUMULATE_DISABLE;
#ifdef SAMD20
uint8_t revision_num = ((REG_DSU_DID & DSU_DID_DIE_Msk) >> DSU_DID_DIE_Pos);
uint8_t revision_num = ((REG_DSU_DID & DSU_DID_DIE_Msk) >> DSU_DID_DIE_Pos);
#endif
/* Get the hardware module pointer */
Adc *const adc_module = module_inst->hw;
/* Get the hardware module pointer */
Adc *const adc_module = module_inst.hw;
/* Configure GCLK channel and enable clock */
system_gclk_chan_set_config(ADC_GCLK_ID, config->clock_source);
system_gclk_chan_enable(ADC_GCLK_ID);
/* Configure GCLK channel and enable clock */
system_gclk_chan_set_config(ADC_GCLK_ID, config->clock_source);
system_gclk_chan_enable(ADC_GCLK_ID);
/* Setup pinmuxing for analog inputs */
if (config->pin_scan.inputs_to_scan != 0) {
uint8_t offset = config->pin_scan.offset_start_scan;
uint8_t start_pin =
offset +(uint8_t)config->positive_input;
uint8_t end_pin =
start_pin + config->pin_scan.inputs_to_scan;
/* Setup pinmuxing for analog inputs */
if (config->pin_scan.inputs_to_scan != 0) {
uint8_t offset = config->pin_scan.offset_start_scan;
uint8_t start_pin =
offset +(uint8_t)config->positive_input;
uint8_t end_pin =
start_pin + config->pin_scan.inputs_to_scan;
while (start_pin < end_pin) {
_adc_configure_ain_pin((offset % 16)+(uint8_t)config->positive_input);
start_pin++;
offset++;
}
_adc_configure_ain_pin(config->negative_input);
} else {
_adc_configure_ain_pin(config->positive_input);
_adc_configure_ain_pin(config->negative_input);
}
while (start_pin < end_pin) {
_adc_configure_ain_pin((offset % 16)+(uint8_t)config->positive_input);
start_pin++;
offset++;
}
_adc_configure_ain_pin(config->negative_input);
} else {
_adc_configure_ain_pin(config->positive_input);
_adc_configure_ain_pin(config->negative_input);
}
/* Configure run in standby */
adc_module->CTRLA.reg = (config->run_in_standby << ADC_CTRLA_RUNSTDBY_Pos);
/* Configure run in standby */
adc_module->CTRLA.reg = (config->run_in_standby << ADC_CTRLA_RUNSTDBY_Pos);
/* Configure reference */
adc_module->REFCTRL.reg =
(config->reference_compensation_enable << ADC_REFCTRL_REFCOMP_Pos) |
(config->reference);
/* Configure reference */
adc_module->REFCTRL.reg =
(config->reference_compensation_enable << ADC_REFCTRL_REFCOMP_Pos) |
(config->reference);
/* Set adjusting result and number of samples */
switch (config->resolution) {
/* Set adjusting result and number of samples */
switch (config->resolution) {
case ADC_RESOLUTION_CUSTOM:
adjres = config->divide_result;
accumulate = config->accumulate_samples;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_CUSTOM:
adjres = config->divide_result;
accumulate = config->accumulate_samples;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_13BIT:
/* Increase resolution by 1 bit */
adjres = ADC_DIVIDE_RESULT_2;
accumulate = ADC_ACCUMULATE_SAMPLES_4;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_13BIT:
/* Increase resolution by 1 bit */
adjres = ADC_DIVIDE_RESULT_2;
accumulate = ADC_ACCUMULATE_SAMPLES_4;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_14BIT:
/* Increase resolution by 2 bit */
adjres = ADC_DIVIDE_RESULT_4;
accumulate = ADC_ACCUMULATE_SAMPLES_16;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_14BIT:
/* Increase resolution by 2 bit */
adjres = ADC_DIVIDE_RESULT_4;
accumulate = ADC_ACCUMULATE_SAMPLES_16;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
#ifdef SAMD20
/* Please see $35.1.8 for ADC errata of SAM D20.
The revisions before D have this issue.*/
case ADC_RESOLUTION_15BIT:
/* Increase resolution by 3 bit */
if(revision_num < REVISON_D_NUM) {
adjres = ADC_DIVIDE_RESULT_8;
} else {
adjres = ADC_DIVIDE_RESULT_2;
}
accumulate = ADC_ACCUMULATE_SAMPLES_64;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
/* Please see $35.1.8 for ADC errata of SAM D20.
The revisions before D have this issue.*/
case ADC_RESOLUTION_15BIT:
/* Increase resolution by 3 bit */
if(revision_num < REVISON_D_NUM) {
adjres = ADC_DIVIDE_RESULT_8;
} else {
adjres = ADC_DIVIDE_RESULT_2;
}
accumulate = ADC_ACCUMULATE_SAMPLES_64;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_16BIT:
if(revision_num < REVISON_D_NUM) {
/* Increase resolution by 4 bit */
adjres = ADC_DIVIDE_RESULT_16;
} else {
adjres = ADC_DIVIDE_RESULT_DISABLE;
}
accumulate = ADC_ACCUMULATE_SAMPLES_256;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_16BIT:
if(revision_num < REVISON_D_NUM) {
/* Increase resolution by 4 bit */
adjres = ADC_DIVIDE_RESULT_16;
} else {
adjres = ADC_DIVIDE_RESULT_DISABLE;
}
accumulate = ADC_ACCUMULATE_SAMPLES_256;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
#else
case ADC_RESOLUTION_15BIT:
/* Increase resolution by 3 bit */
adjres = ADC_DIVIDE_RESULT_2;
accumulate = ADC_ACCUMULATE_SAMPLES_64;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_15BIT:
/* Increase resolution by 3 bit */
adjres = ADC_DIVIDE_RESULT_2;
accumulate = ADC_ACCUMULATE_SAMPLES_64;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_16BIT:
/* Increase resolution by 4 bit */
adjres = ADC_DIVIDE_RESULT_DISABLE;
accumulate = ADC_ACCUMULATE_SAMPLES_256;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
case ADC_RESOLUTION_16BIT:
/* Increase resolution by 4 bit */
adjres = ADC_DIVIDE_RESULT_DISABLE;
accumulate = ADC_ACCUMULATE_SAMPLES_256;
/* 16-bit result register */
resolution = ADC_RESOLUTION_16BIT;
break;
#endif
case ADC_RESOLUTION_8BIT:
/* 8-bit result register */
resolution = ADC_RESOLUTION_8BIT;
break;
case ADC_RESOLUTION_10BIT:
/* 10-bit result register */
resolution = ADC_RESOLUTION_10BIT;
break;
case ADC_RESOLUTION_12BIT:
/* 12-bit result register */
resolution = ADC_RESOLUTION_12BIT;
break;
case ADC_RESOLUTION_8BIT:
/* 8-bit result register */
resolution = ADC_RESOLUTION_8BIT;
break;
case ADC_RESOLUTION_10BIT:
/* 10-bit result register */
resolution = ADC_RESOLUTION_10BIT;
break;
case ADC_RESOLUTION_12BIT:
/* 12-bit result register */
resolution = ADC_RESOLUTION_12BIT;
break;
default:
/* Unknown. Abort. */
return ADC_STATUS_ERR_INVALID_ARG;
}
default:
/* Unknown. Abort. */
return ADC_STATUS_ERR_INVALID_ARG;
}
adc_module->AVGCTRL.reg = ADC_AVGCTRL_ADJRES(adjres) | accumulate;
adc_module->AVGCTRL.reg = ADC_AVGCTRL_ADJRES(adjres) | accumulate;
/* Check validity of sample length value */
if (config->sample_length > 63) {
return ADC_STATUS_ERR_INVALID_ARG;
} else {
/* Configure sample length */
adc_module->SAMPCTRL.reg =
(config->sample_length << ADC_SAMPCTRL_SAMPLEN_Pos);
}
/* Check validity of sample length value */
if (config->sample_length > 63) {
return ADC_STATUS_ERR_INVALID_ARG;
} else {
/* Configure sample length */
adc_module->SAMPCTRL.reg =
(config->sample_length << ADC_SAMPCTRL_SAMPLEN_Pos);
}
while (adc_is_syncing(module_inst)) {
/* Wait for synchronization */
}
while (adc_is_syncing()) {
/* Wait for synchronization */
}
/* Configure CTRLB */
adc_module->CTRLB.reg =
config->clock_prescaler |
resolution |
(config->correction.correction_enable << ADC_CTRLB_CORREN_Pos) |
(config->freerunning << ADC_CTRLB_FREERUN_Pos) |
(config->left_adjust << ADC_CTRLB_LEFTADJ_Pos) |
(config->differential_mode << ADC_CTRLB_DIFFMODE_Pos);
/* Configure CTRLB */
adc_module->CTRLB.reg =
config->clock_prescaler |
resolution |
(config->correction.correction_enable << ADC_CTRLB_CORREN_Pos) |
(config->freerunning << ADC_CTRLB_FREERUN_Pos) |
(config->left_adjust << ADC_CTRLB_LEFTADJ_Pos) |
(config->differential_mode << ADC_CTRLB_DIFFMODE_Pos);
/* Check validity of window thresholds */
if (config->window.window_mode != ADC_WINDOW_MODE_DISABLE) {
switch (resolution) {
case ADC_RESOLUTION_8BIT:
if (config->differential_mode &&
(config->window.window_lower_value > 127 ||
config->window.window_lower_value < -128 ||
config->window.window_upper_value > 127 ||
config->window.window_upper_value < -128)) {
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
} else if (config->window.window_lower_value > 255 ||
config->window.window_upper_value > 255){
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
}
break;
case ADC_RESOLUTION_10BIT:
if (config->differential_mode &&
(config->window.window_lower_value > 511 ||
config->window.window_lower_value < -512 ||
config->window.window_upper_value > 511 ||
config->window.window_upper_value > -512)) {
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
} else if (config->window.window_lower_value > 1023 ||
config->window.window_upper_value > 1023){
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
}
break;
case ADC_RESOLUTION_12BIT:
if (config->differential_mode &&
(config->window.window_lower_value > 2047 ||
config->window.window_lower_value < -2048 ||
config->window.window_upper_value > 2047 ||
config->window.window_upper_value < -2048)) {
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
} else if (config->window.window_lower_value > 4095 ||
config->window.window_upper_value > 4095){
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
}
break;
case ADC_RESOLUTION_16BIT:
if (config->differential_mode &&
(config->window.window_lower_value > 32767 ||
config->window.window_lower_value < -32768 ||
config->window.window_upper_value > 32767 ||
config->window.window_upper_value < -32768)) {
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
} else if (config->window.window_lower_value > 65535 ||
config->window.window_upper_value > 65535){
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
}
break;
}
}
/* Check validity of window thresholds */
if (config->window.window_mode != ADC_WINDOW_MODE_DISABLE) {
switch (resolution) {
case ADC_RESOLUTION_8BIT:
if (config->differential_mode &&
(config->window.window_lower_value > 127 ||
config->window.window_lower_value < -128 ||
config->window.window_upper_value > 127 ||
config->window.window_upper_value < -128)) {
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
} else if (config->window.window_lower_value > 255 ||
config->window.window_upper_value > 255){
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
}
break;
case ADC_RESOLUTION_10BIT:
if (config->differential_mode &&
(config->window.window_lower_value > 511 ||
config->window.window_lower_value < -512 ||
config->window.window_upper_value > 511 ||
config->window.window_upper_value > -512)) {
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
} else if (config->window.window_lower_value > 1023 ||
config->window.window_upper_value > 1023){
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
}
break;
case ADC_RESOLUTION_12BIT:
if (config->differential_mode &&
(config->window.window_lower_value > 2047 ||
config->window.window_lower_value < -2048 ||
config->window.window_upper_value > 2047 ||
config->window.window_upper_value < -2048)) {
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
} else if (config->window.window_lower_value > 4095 ||
config->window.window_upper_value > 4095){
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
}
break;
case ADC_RESOLUTION_16BIT:
if (config->differential_mode &&
(config->window.window_lower_value > 32767 ||
config->window.window_lower_value < -32768 ||
config->window.window_upper_value > 32767 ||
config->window.window_upper_value < -32768)) {
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
} else if (config->window.window_lower_value > 65535 ||
config->window.window_upper_value > 65535){
/* Invalid value */
return ADC_STATUS_ERR_INVALID_ARG;
}
break;
}
}
while (adc_is_syncing(module_inst)) {
/* Wait for synchronization */
}
while (adc_is_syncing()) {
/* Wait for synchronization */
}
/* Configure window mode */
adc_module->WINCTRL.reg = config->window.window_mode;
/* Configure window mode */
adc_module->WINCTRL.reg = config->window.window_mode;
while (adc_is_syncing(module_inst)) {
/* Wait for synchronization */
}
while (adc_is_syncing()) {
/* Wait for synchronization */
}
/* Configure lower threshold */
adc_module->WINLT.reg =
config->window.window_lower_value << ADC_WINLT_WINLT_Pos;
/* Configure lower threshold */
adc_module->WINLT.reg =
config->window.window_lower_value << ADC_WINLT_WINLT_Pos;
while (adc_is_syncing(module_inst)) {
/* Wait for synchronization */
}
while (adc_is_syncing()) {
/* Wait for synchronization */
}
/* Configure lower threshold */
adc_module->WINUT.reg = config->window.window_upper_value <<
ADC_WINUT_WINUT_Pos;
/* Configure lower threshold */
adc_module->WINUT.reg = config->window.window_upper_value <<
ADC_WINUT_WINUT_Pos;
uint8_t inputs_to_scan = config->pin_scan.inputs_to_scan;
if (inputs_to_scan > 0) {
/*
* Number of input sources included is the value written to INPUTSCAN
* plus 1.
*/
inputs_to_scan--;
}
uint8_t inputs_to_scan = config->pin_scan.inputs_to_scan;
if (inputs_to_scan > 0) {
/*
* Number of input sources included is the value written to INPUTSCAN
* plus 1.
*/
inputs_to_scan--;
}
if (inputs_to_scan > (ADC_INPUTCTRL_INPUTSCAN_Msk >> ADC_INPUTCTRL_INPUTSCAN_Pos) ||
config->pin_scan.offset_start_scan > (ADC_INPUTCTRL_INPUTOFFSET_Msk >> ADC_INPUTCTRL_INPUTOFFSET_Pos)) {
/* Invalid number of input pins or input offset */
return ADC_STATUS_ERR_INVALID_ARG;
}
if (inputs_to_scan > (ADC_INPUTCTRL_INPUTSCAN_Msk >> ADC_INPUTCTRL_INPUTSCAN_Pos) ||
config->pin_scan.offset_start_scan > (ADC_INPUTCTRL_INPUTOFFSET_Msk >> ADC_INPUTCTRL_INPUTOFFSET_Pos)) {
/* Invalid number of input pins or input offset */
return ADC_STATUS_ERR_INVALID_ARG;
}
while (adc_is_syncing(module_inst)) {
/* Wait for synchronization */
}
while (adc_is_syncing()) {
/* Wait for synchronization */
}
/* Configure pin scan mode and positive and negative input pins */
adc_module->INPUTCTRL.reg =
config->gain_factor |
(config->pin_scan.offset_start_scan <<
ADC_INPUTCTRL_INPUTOFFSET_Pos) |
(inputs_to_scan << ADC_INPUTCTRL_INPUTSCAN_Pos) |
config->negative_input |
config->positive_input;
/* Configure pin scan mode and positive and negative input pins */
adc_module->INPUTCTRL.reg =
config->gain_factor |
(config->pin_scan.offset_start_scan <<
ADC_INPUTCTRL_INPUTOFFSET_Pos) |
(inputs_to_scan << ADC_INPUTCTRL_INPUTSCAN_Pos) |
config->negative_input |
config->positive_input;
/* Configure events */
adc_module->EVCTRL.reg = config->event_action;
/* Configure events */
adc_module->EVCTRL.reg = config->event_action;
/* Disable all interrupts */
adc_module->INTENCLR.reg =
(1 << ADC_INTENCLR_SYNCRDY_Pos) | (1 << ADC_INTENCLR_WINMON_Pos) |
(1 << ADC_INTENCLR_OVERRUN_Pos) | (1 << ADC_INTENCLR_RESRDY_Pos);
/* Disable all interrupts */
adc_module->INTENCLR.reg =
(1 << ADC_INTENCLR_SYNCRDY_Pos) | (1 << ADC_INTENCLR_WINMON_Pos) |
(1 << ADC_INTENCLR_OVERRUN_Pos) | (1 << ADC_INTENCLR_RESRDY_Pos);
if (config->correction.correction_enable){
/* Make sure gain_correction value is valid */
if (config->correction.gain_correction > ADC_GAINCORR_GAINCORR_Msk) {
return ADC_STATUS_ERR_INVALID_ARG;
} else {
/* Set gain correction value */
adc_module->GAINCORR.reg = config->correction.gain_correction <<
ADC_GAINCORR_GAINCORR_Pos;
}
if (config->correction.correction_enable){
/* Make sure gain_correction value is valid */
if (config->correction.gain_correction > ADC_GAINCORR_GAINCORR_Msk) {
return ADC_STATUS_ERR_INVALID_ARG;
} else {
/* Set gain correction value */
adc_module->GAINCORR.reg = config->correction.gain_correction <<
ADC_GAINCORR_GAINCORR_Pos;
}
/* Make sure offset correction value is valid */
if (config->correction.offset_correction > 2047 ||
config->correction.offset_correction < -2048) {
return ADC_STATUS_ERR_INVALID_ARG;
} else {
/* Set offset correction value */
adc_module->OFFSETCORR.reg = config->correction.offset_correction <<
ADC_OFFSETCORR_OFFSETCORR_Pos;
}
}
/* Make sure offset correction value is valid */
if (config->correction.offset_correction > 2047 ||
config->correction.offset_correction < -2048) {
return ADC_STATUS_ERR_INVALID_ARG;
} else {
/* Set offset correction value */
adc_module->OFFSETCORR.reg = config->correction.offset_correction <<
ADC_OFFSETCORR_OFFSETCORR_Pos;
}
}
/* Load in the fixed device ADC calibration constants */
adc_module->CALIB.reg =
ADC_CALIB_BIAS_CAL(
(*(uint32_t *)ADC_FUSES_BIASCAL_ADDR >> ADC_FUSES_BIASCAL_Pos)
) |
ADC_CALIB_LINEARITY_CAL(
(*(uint64_t *)ADC_FUSES_LINEARITY_0_ADDR >> ADC_FUSES_LINEARITY_0_Pos)
);
/* Load in the fixed device ADC calibration constants */
adc_module->CALIB.reg =
ADC_CALIB_BIAS_CAL(
(*(uint32_t *)ADC_FUSES_BIASCAL_ADDR >> ADC_FUSES_BIASCAL_Pos)
) |
ADC_CALIB_LINEARITY_CAL(
(*(uint64_t *)ADC_FUSES_LINEARITY_0_ADDR >> ADC_FUSES_LINEARITY_0_Pos)
);
return ADC_STATUS_OK;
return ADC_STATUS_OK;
}
/**
@ -483,55 +481,191 @@ static enum adc_status_code _adc_set_config(
* \retval ADC_STATUS_BUSY The module is busy with a reset operation
* \retval ADC_STATUS_ERR_DENIED The module is enabled
*/
enum adc_status_code adc_init(
struct adc_module *const module_inst,
Adc *hw,
struct adc_config *config)
enum adc_status_code adc_init(Adc *hw,
struct adc_config *config)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(hw);
Assert(config);
/* Sanity check arguments */
/* Associate the software module instance with the hardware module */
module_inst->hw = hw;
Assert(hw);
Assert(config);
/* Turn on the digital interface clock */
system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, PM_APBCMASK_ADC);
/* Associate the software module instance with the hardware module */
module_inst.hw = hw;
if (hw->CTRLA.reg & ADC_CTRLA_SWRST) {
/* We are in the middle of a reset. Abort. */
return ADC_STATUS_BUSY;
}
/* Turn on the digital interface clock */
system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, PM_APBCMASK_ADC);
if (hw->CTRLA.reg & ADC_CTRLA_ENABLE) {
/* Module must be disabled before initialization. Abort. */
return ADC_STATUS_ERR_DENIED;
}
if (hw->CTRLA.reg & ADC_CTRLA_SWRST) {
/* We are in the middle of a reset. Abort. */
return ADC_STATUS_BUSY;
}
/* Store the selected reference for later use */
module_inst->reference = config->reference;
if (hw->CTRLA.reg & ADC_CTRLA_ENABLE) {
/* Module must be disabled before initialization. Abort. */
return ADC_STATUS_ERR_DENIED;
}
#if ADC_CALLBACK_MODE == true
for (uint8_t i = 0; i < ADC_CALLBACK_N; i++) {
module_inst->callback[i] = NULL;
};
/* Store the selected reference for later use */
module_inst.reference = config->reference;
module_inst->registered_callback_mask = 0;
module_inst->enabled_callback_mask = 0;
module_inst->remaining_conversions = 0;
module_inst->job_status = ADC_STATUS_OK;
for (uint8_t i = 0; i < ADC_CALLBACK_N; i++) {
module_inst.callback[i] = NULL;
};
_adc_instances[0] = module_inst;
module_inst.registered_callback_mask = 0;
module_inst.enabled_callback_mask = 0;
module_inst.remaining_conversions = 0;
module_inst.job_status = ADC_STATUS_OK;
if (config->event_action == ADC_EVENT_ACTION_DISABLED &&
!config->freerunning) {
module_inst->software_trigger = true;
} else {
module_inst->software_trigger = false;
}
#endif
if (config->event_action == ADC_EVENT_ACTION_DISABLED &&
!config->freerunning) {
module_inst.software_trigger = true;
} else {
module_inst.software_trigger = false;
}
/* Write configuration to module */
return _adc_set_config(module_inst, config);
/* Write configuration to module */
return _adc_set_config(config);
}
/** Interrupt handler for the ADC module. */
void ADC_Handler(void)
{
/* get interrupt flags and mask out enabled callbacks */
uint32_t flags = module_inst.hw->INTFLAG.reg;
if (flags & ADC_INTFLAG_RESRDY) {
/* job is complete. update status,disable interrupt
* and call callback */
module_inst.job_status = ADC_STATUS_OK;
adc_disable_interrupt(ADC_INTERRUPT_RESULT_READY);
(module_inst.callback[ADC_CALLBACK_READ_BUFFER])();
}
if (flags & ADC_INTFLAG_WINMON) {
module_inst.hw->INTFLAG.reg = ADC_INTFLAG_WINMON;
if ((module_inst.enabled_callback_mask & (1 << ADC_CALLBACK_WINDOW)) &&
(module_inst.registered_callback_mask & (1 << ADC_CALLBACK_WINDOW))) {
(module_inst.callback[ADC_CALLBACK_WINDOW])();
}
}
if (flags & ADC_INTFLAG_OVERRUN) {
module_inst.hw->INTFLAG.reg = ADC_INTFLAG_OVERRUN;
if ((module_inst.enabled_callback_mask & (1 << ADC_CALLBACK_ERROR)) &&
(module_inst.registered_callback_mask & (1 << ADC_CALLBACK_ERROR))) {
(module_inst.callback[ADC_CALLBACK_ERROR])();
}
}
}
/**
* \brief Registers a callback
*
* Registers a callback function which is implemented by the user.
*
* \note The callback must be enabled by for the interrupt handler to call it
* when the condition for the callback is met.
*
* \param[in] module Pointer to ADC software instance struct
* \param[in] callback_func Pointer to callback function
* \param[in] callback_type Callback type given by an enum
*
*/
void adc_register_callback(
adc_callback_t callback_func,
enum adc_callback callback_type)
{
/* Sanity check arguments */
Assert(callback_func);
/* Register callback function */
module_inst.callback[callback_type] = callback_func;
/* Set the bit corresponding to the callback_type */
module_inst.registered_callback_mask |= (1 << callback_type);
}
/**
* \brief Unregisters a callback
*
* Unregisters a callback function which is implemented by the user.
*
* \param[in] module Pointer to ADC software instance struct
* \param[in] callback_type Callback type given by an enum
*
*/
void adc_unregister_callback(
enum adc_callback callback_type)
{
/* Unregister callback function */
module_inst.callback[callback_type] = NULL;
/* Clear the bit corresponding to the callback_type */
module_inst.registered_callback_mask &= ~(1 << callback_type);
}
/**
* \brief Read multiple samples from ADC
*
* Read \c samples samples from the ADC into the buffer \c buffer.
* If there is no hardware trigger defined (event action) the
* driver will retrigger the ADC conversion whenever a conversion
* is complete until \c samples samples has been acquired. To avoid
* jitter in the sampling frequency using an event trigger is advised.
*
* \param[in] module_inst Pointer to the ADC software instance struct
* \param[in] samples Number of samples to acquire
* \param[out] buffer Buffer to store the ADC samples
*
* \return Status of the job start
* \retval STATUS_OK The conversion job was started successfully and is
* in progress
* \retval STATUS_BUSY The ADC is already busy with another job
*/
enum adc_status_code adc_read_buffer_job(
uint16_t *buffer,
uint16_t samples)
{
Assert(samples);
Assert(buffer);
if(module_inst.remaining_conversions != 0 ||
module_inst.job_status == ADC_STATUS_BUSY){
return ADC_STATUS_BUSY;
}
module_inst.job_status = ADC_STATUS_BUSY;
module_inst.remaining_conversions = samples;
module_inst.job_buffer = buffer;
adc_enable_interrupt(ADC_INTERRUPT_RESULT_READY);
if(module_inst.software_trigger == true) {
adc_start_conversion();
}
return ADC_STATUS_OK;
}
/**
* \brief Gets the status of a job
*
* Gets the status of an ongoing or the last job.
*
* \param [in] module_inst Pointer to the ADC software instance struct
* \param [in] type Type of job to abort
*
* \return Status of the job
*/
enum adc_status_code adc_get_job_status(
enum adc_job_type type)
{
if (type == ADC_JOB_READ_BUFFER ) {
return module_inst.job_status;
} else {
return ADC_STATUS_ERR_INVALID_ARG;
}
}

Wyświetl plik

@ -24,54 +24,112 @@
#include "samd20.h"
#include "adc/adc.h"
#include "system/interrupt.h"
#include "hw_config.h"
struct adc_module adc_instance;
float battery_v, solar_v;
#define TEMP_ADC ADC_POSITIVE_INPUT_TEMP
#define ADC_GAINF ADC_GAIN_FACTOR_DIV2
#define ADC_GAINF_VAL 0.5
#define ADC_RESOLUTION ADC_RESOLUTION_12BIT
#define ADC_RESOLUTION_VAL 4096
enum {
ADC_PHASE_NONE,
ADC_PHASE_CONVERT_BATTERY,
ADC_PHASE_CONVERT_SOLAR,
ADC_PHASE_DONE,
} adc_phase = ADC_PHASE_NONE;
void adc_complete_callback(void);
void configure_adc(enum adc_positive_input input)
{
struct adc_config config_adc;
adc_get_config_defaults(&config_adc);
config_adc.clock_source = GCLK_GENERATOR_0;
config_adc.reference = ADC_REFERENCE_INT1V;
config_adc.clock_prescaler = ADC_CLOCK_PRESCALER_DIV64;
config_adc.resolution = ADC_RESOLUTION;
config_adc.gain_factor = ADC_GAIN_FACTOR_DIV2;
config_adc.positive_input = input;
config_adc.accumulate_samples = ADC_ACCUMULATE_DISABLE;
config_adc.run_in_standby = true;
adc_init(&adc_instance, ADC, &config_adc);
adc_enable(&adc_instance);
adc_init(ADC, &config_adc);
adc_enable();
adc_register_callback(adc_complete_callback, ADC_CALLBACK_READ_BUFFER);
adc_enable_interrupt(ADC_INTERRUPT_RESULT_READY);
irq_register_handler(ADC_IRQn, ADC_INT_PRIO);
}
/**
* Called on a ADC result ready event
*/
void adc_complete_callback(void) {
uint16_t result;
float pin_v;
adc_read(&result);
pin_v = (float)result / (ADC_GAINF_VAL * ADC_RESOLUTION_VAL);
if (adc_phase == ADC_PHASE_CONVERT_BATTERY) {
/* Battery */
/* Calcuate the battery votage */
battery_v = pin_v / BATTERY_ADC_CHANNEL_DIV;
/* Next up: Solar */
configure_adc(SOLAR_ADC_CHANNEL);
adc_start_conversion();
} else {
/* Solar */
/* Calculate the solar voltage */
solar_v = pin_v / SOLAR_ADC_CHANNEL_DIV;
}
adc_phase++;
}
void start_adc_conversion_sequence(void)
{
/* First up: Battery */
configure_adc(BATTERY_ADC_CHANNEL);
adc_start_conversion();
adc_phase = ADC_PHASE_CONVERT_BATTERY;
}
uint8_t is_adc_conversion_done(void) {
return (adc_phase == ADC_PHASE_DONE);
}
float get_battery(void)
{
configure_adc(BATTERY_ADC_CHANNEL);
adc_start_conversion(&adc_instance);
uint16_t result;
do {
/* Wait for conversion to be done and read out result */
} while (adc_read(&adc_instance, &result) == ADC_STATUS_BUSY);
/* 12-bit, 1V ref, div 2 */
return (float)(result * 2) / (4096 * BATTERY_ADC_CHANNEL_DIV);
return battery_v;
}
float get_temperature(void)
float get_solar(void)
{
configure_adc(TEMP_ADC);
adc_start_conversion(&adc_instance);
uint16_t result;
do {
/* Wait for conversion to be done and read out result */
} while (adc_read(&adc_instance, &result) == ADC_STATUS_BUSY);
/* 12-bit, 1V ref, div 2 */
float voltage = (float)(result * 2) / 4096;
float millivolt_offset = (voltage - 0.667) * 1000;
/* Temperature? Uncalibrated.. */
return 25 + (millivolt_offset / 2.4);
return solar_v;
}
/* float get_temperature(void) */
/* { */
/* configure_adc(TEMP_ADC); */
/* adc_start_conversion(&adc_instance); */
/* uint16_t result; */
/* do { */
/* /\* Wait for conversion to be done and read out result *\/ */
/* } while (adc_read(&adc_instance, &result) == ADC_STATUS_BUSY); */
/* /\* 12-bit, 1V ref, div 2 *\/ */
/* float voltage = (float)(result * 2) / 4096; */
/* float millivolt_offset = (voltage - 0.667) * 1000; */
/* /\* Temperature? Uncalibrated.. *\/ */
/* return 25 + (millivolt_offset / 2.4); */
/* } */

Wyświetl plik

@ -59,6 +59,7 @@ struct tracker_datapoint* collect_data(void)
* ---- Analogue ----
*/
datapoint.battery = get_battery();
datapoint.solar = get_solar();
datapoint.temperature = telemetry_si_temperature();

Wyświetl plik

@ -0,0 +1,41 @@
#ifndef __verification__
#define __verification__
#endif
/****************************//* adc_battery_solar_read_tc *//****************************/
/**
* Write a description of your test case here
*/
#include "analogue.h"
/* Parameters in */
struct adc_battery_solar_read_tc_params {
/* Input paramters to your test case go here */
uint32_t dummy;
} adc_battery_solar_read_tc_params;
/* Results out */
struct adc_battery_solar_read_tc_results {
/* Result values should be populated here */
float battery;
float solar;
} adc_battery_solar_read_tc_results;
/* Function */
__verification__ void adc_battery_solar_read_tc(void) {
/**
* The main body of the test case goes here.
*
* Use the input parameters to run the test case. Populate the
* results structure at the end
*/
start_adc_conversion_sequence();
while (!is_adc_conversion_done());
adc_battery_solar_read_tc_results.battery = get_battery();
adc_battery_solar_read_tc_results.solar = get_solar();
}

Wyświetl plik

@ -0,0 +1,43 @@
#!/usr/bin/env python
# ------------------------------------------------------------------------------
# Imports
# ------------------------------------------------------------------------------
import sys
sys.path.append("./test")
import main
from random import randint
# ------------------------------------------------------------------------------
# Test Script
# ------------------------------------------------------------------------------
class adc_battery_solar_read_tc:
def __init__(self):
self.name = self.__class__.__name__
self.iterations = 20
def get_test(self):
"""Returns some suitable test parameters"""
params = main.struct_adc_battery_solar_read_tc_params()
"""
Assign input parameters here
"""
return params
def is_correct(self, params, result, print_info):
"""Returns if a result is correct for the given parameters"""
"""
Compare result and params here, decide sth.
Can use print_info
"""
print_info("Battery: {}V".format(result["battery"]))
print_info("Solar: {}V".format(result["solar"]))
return True

Wyświetl plik

@ -44,6 +44,7 @@
#include "location_aprs_file.h"
#include "backlog_write_read.h"
#include "mem_erase_all.h"
#include "adc_battery_solar_read.h"
/* [new_tc] */