Been on all day at this and ready to smash something! :)
I need to do three 'steps' or 'tasks' which are:
1. read 5 channels
2. read 3 channels, which are the same as above minus two (there's a pin aux)
3. read 2 channels repeatedly filling up one of two banks with additional looping.
For brevity I've removed (3) to just do a single read and then call (1).
Each step works from adc interrupt. In the case of (1) and (2) the SAMPLE is wired to STARTED by PPI. Primarily this is because the RTC tick at 32KHz) is too fast.
Then this PPI is disabled and the RTC TICK EVENT is wired to STARTED instead. This gives 30.5us, and the 2 channels plus overhead are well within it.
Behaviour
The routine(s) loop twice and then end with no further interrupts. I believe the ADC is stuck on (2) because the TICK EVENT isn't being pumped through when the PPI channel is re-enabled.
I think I've tried everything to get this working but maybe another pair of eyes could help!
extern struct statusProto status;
#define ADC_RESOLUTION SAADC_RESOLUTION_VAL_12bit
#define ADC_SAMPLETIME_CFG_SLOW SAADC_CH_CONFIG_TACQ_40us
#define ADC_SAMPLETIME_CFG_MPPT_VOLTAGE SAADC_CH_CONFIG_TACQ_10us
#define ADC_SAMPLETIME_CFG_MPPT_CURRENT SAADC_CH_CONFIG_TACQ_10us
// ADC takes 2us per channel, 4us + 15us + 5us = 24us
// 32,768Hz = 30.52us
// 667 (fit into ram)*30 = 20ms
#define MPPT_SAMPLES 1
// 200ms total / 20ms
#define MPPT_LOOP 2
enum {
ADC_CH_USB,
ADC_CH_FRONT,
ADC_CH_REAR,
ADC_CH_STORE,
ADC_CH_LIGHTSENSE,
ADC_CH_MPPT_VOLTAGE,
ADC_CH_MPPT_CURRENT
};
struct mpptReadProto {
int16_t voltage;
int16_t current;
};
struct {
volatile struct {
int16_t usb;
int16_t front;
int16_t rear;
int16_t storage;
int16_t lightsense;
} voltages;
volatile struct {
int16_t usb;
int16_t front;
int16_t rear;
} currents;
struct mpptReadProto banks[2][MPPT_SAMPLES];
} reads;
static void step3n (void);
static void step3 (void);
static void step2 (void);
static void step1 (void);
static void (*step)(void);
static uint8_t loop;
static struct mpptTrackProto track;
static struct mpptReadProto *currentBank;
static void step1 (void) {
step = &step2;
NRF_PPI->CHENSET = 1 << CFG_PPI_ADC_SAMPLEONSTART;
NRF_GPIO->OUTCLR = 1 << CFG_PIN_ADC_MUX;
NRF_SAADC->CH[ADC_CH_USB].PSELP = CFG_ADC_USB_PSEL;
NRF_SAADC->CH[ADC_CH_FRONT].PSELP = CFG_ADC_FRONT_PSEL;
NRF_SAADC->CH[ADC_CH_REAR].PSELP = CFG_ADC_REAR_PSEL;
NRF_SAADC->CH[ADC_CH_STORE].PSELP = CFG_ADC_STORE_PSEL;
NRF_SAADC->CH[ADC_CH_LIGHTSENSE].PSELP = CFG_ADC_LIGHTSENSE_PSEL;
NRF_SAADC->RESULT.PTR = (uint32_t) &reads.voltages;
NRF_SAADC->RESULT.MAXCNT = sizeof(reads.voltages) / 2;
NRF_SAADC->TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger << SAADC_TASKS_START_TASKS_START_Pos;
}
static void step2 (void) {
step = &step3;
NRF_GPIO->OUTSET = 1 << CFG_PIN_ADC_MUX;
// rather than wait for adc task compute we do here and send as event
// this is only benefical if the storage read is greater than 500ms (adc task)
if (reads.voltages.storage < STORAGE_VOLTAGE_CRITICAL_ON) {
POWERMANAGER_EventStorageCritical();
}
NRF_SAADC->CH[ADC_CH_STORE].PSELP = SAADC_CH_PSELP_PSELP_NC;
NRF_SAADC->CH[ADC_CH_LIGHTSENSE].PSELP = SAADC_CH_PSELP_PSELP_NC;
NRF_SAADC->RESULT.PTR = (uint32_t) &reads.currents;
NRF_SAADC->RESULT.MAXCNT = sizeof(reads.currents) / 2;
NRF_SAADC->TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger << SAADC_TASKS_START_TASKS_START_Pos;
}
static void step3 (void) {
currentBank = &reads.banks[0][0];
step = &step3n;
NRF_PPI->CHENCLR = 1 << CFG_PPI_ADC_SAMPLEONSTART;
NRF_SAADC->CH[ADC_CH_USB].PSELP = SAADC_CH_PSELP_PSELP_NC;
NRF_SAADC->CH[ADC_CH_FRONT].PSELP = SAADC_CH_PSELP_PSELP_NC;
NRF_SAADC->CH[ADC_CH_REAR].PSELP = SAADC_CH_PSELP_PSELP_NC;
NRF_SAADC->CH[ADC_CH_MPPT_VOLTAGE].PSELP = CFG_ADC_MPPT_VOLTAGE_PSEL;
NRF_SAADC->CH[ADC_CH_MPPT_CURRENT].PSELP = CFG_ADC_MPPT_CURRENT_PSEL;
NRF_SAADC->RESULT.PTR = (uint32_t) &reads.banks[0];
NRF_SAADC->RESULT.MAXCNT = sizeof(reads.banks[0]) / sizeof(reads.banks[0][0]);
NRF_SAADC->TASKS_START = SAADC_TASKS_START_TASKS_START_Trigger << SAADC_TASKS_START_TASKS_START_Pos;
NRF_PPI->CHENSET = 1 << CFG_PPI_ADC_SAMPLEBYTIMER;
}
static void step3n (void) {
// code cut down to remove looping
NRF_SAADC->CH[ADC_CH_MPPT_VOLTAGE].PSELP = SAADC_CH_PSELP_PSELP_NC;
NRF_SAADC->CH[ADC_CH_MPPT_CURRENT].PSELP = SAADC_CH_PSELP_PSELP_NC;
NRF_PPI->CHENCLR = 1 << CFG_PPI_ADC_SAMPLEBYTIMER;
step1();
}
void SAADC_IRQHandler (void) {
NRF_SAADC->EVENTS_END = SAADC_EVENTS_END_EVENTS_END_NotGenerated << SAADC_EVENTS_END_EVENTS_END_Pos;
step();
}
uint32_t ADC_TaskCalibrate (void) {
NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos;
NRF_SAADC->TASKS_CALIBRATEOFFSET = SAADC_TASKS_CALIBRATEOFFSET_TASKS_CALIBRATEOFFSET_Trigger << SAADC_TASKS_CALIBRATEOFFSET_TASKS_CALIBRATEOFFSET_Pos;
return CFG_SCHEDULER_TIMER_MINIMUM;
}
uint32_t ADC_TaskStart (void) {
NRF_SAADC->EVENTS_END = SAADC_EVENTS_END_EVENTS_END_NotGenerated << SAADC_EVENTS_END_EVENTS_END_Pos;
NRF_SAADC->INTENSET = SAADC_INTENSET_END_Enabled << SAADC_INTENSET_END_Pos;
// Note RTC Event tick is enabled elsewhere!
step1();
return CFG_SCHEDULER_TIMER_MINIMUM;
}
inline void ADC_Init (void) {
NRF_GPIO->PIN_CNF[CFG_PIN_ADC_STORE] = pinDisconnectInputBuffer;
NRF_GPIO->PIN_CNF[CFG_PIN_ADC_USB] = pinDisconnectInputBuffer;
NRF_GPIO->PIN_CNF[CFG_PIN_ADC_FRONT] = pinDisconnectInputBuffer;
NRF_GPIO->PIN_CNF[CFG_PIN_ADC_REAR] = pinDisconnectInputBuffer;
NRF_GPIO->PIN_CNF[CFG_PIN_ADC_MPPT_VOLTAGE] = pinDisconnectInputBuffer;
NRF_GPIO->PIN_CNF[CFG_PIN_ADC_MPPT_CURRENT] = pinDisconnectInputBuffer;
NRF_GPIO->PIN_CNF[CFG_PIN_ADC_LIGHTSENSE] = pinDisconnectInputBuffer;
NRF_GPIO->PIN_CNF[CFG_PIN_ADC_MUX] = pinDisconnectInputBuffer | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->OUTSET = 1 << CFG_PIN_ADC_MUX;
NRF_SAADC->RESOLUTION = SAADC_RESOLUTION_VAL_12bit << SAADC_RESOLUTION_VAL_Pos;
NRF_SAADC->CH[ADC_CH_USB].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1 << SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW << SAADC_CH_CONFIG_TACQ_Pos;
NRF_SAADC->CH[ADC_CH_FRONT].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1 << SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW << SAADC_CH_CONFIG_TACQ_Pos;
NRF_SAADC->CH[ADC_CH_REAR].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1 << SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW << SAADC_CH_CONFIG_TACQ_Pos;
NRF_SAADC->CH[ADC_CH_STORE].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1_2 << SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW << SAADC_CH_CONFIG_TACQ_Pos;
NRF_SAADC->CH[ADC_CH_LIGHTSENSE].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain2 << SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_SLOW << SAADC_CH_CONFIG_TACQ_Pos;
NRF_SAADC->CH[ADC_CH_MPPT_VOLTAGE].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1_6 << SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_MPPT_VOLTAGE << SAADC_CH_CONFIG_TACQ_Pos;
NRF_SAADC->CH[ADC_CH_MPPT_CURRENT].CONFIG = SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos | SAADC_CH_CONFIG_GAIN_Gain1_6 << SAADC_CH_CONFIG_GAIN_Pos | ADC_SAMPLETIME_CFG_MPPT_CURRENT << SAADC_CH_CONFIG_TACQ_Pos;
NRF_PPI->CH[CFG_PPI_ADC_SAMPLEONSTART].EEP = (uint32_t) &NRF_SAADC->EVENTS_STARTED;
NRF_PPI->CH[CFG_PPI_ADC_SAMPLEONSTART].TEP = (uint32_t) &NRF_SAADC->TASKS_SAMPLE;
NRF_PPI->CH[CFG_PPI_ADC_SAMPLEBYTIMER].EEP = (uint32_t) &NRF_RTC1->EVENTS_TICK;
NRF_PPI->CH[CFG_PPI_ADC_SAMPLEBYTIMER].TEP = (uint32_t) &NRF_SAADC->TASKS_SAMPLE;
NVIC_SetPriority(SAADC_IRQn, CFG_INTERRUPT_PRIORITY_ADC);
NVIC_EnableIRQ(SAADC_IRQn);
}
void ADC_Shutdown (void) {
NRF_SAADC->ENABLE = SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos;
}