ADC now works in interrupt mode. No idea why DMA was unreliable.

Initial button push / release detection and mode switch detection working. No callbacks implemented yet.
This commit is contained in:
true 2023-10-19 19:53:04 -07:00
parent ecc478661f
commit 13700b96ca
4 changed files with 147 additions and 66 deletions

View File

@ -13,8 +13,16 @@
void adc_init(); #define ADC_CHANNELS 6
#define ADC_HISTLEN 16
extern uint16_t adc_avg[ADC_CHANNELS];
void adc_init();
uint8_t adc_next(); uint8_t adc_next();

106
src/adc.c
View File

@ -13,20 +13,27 @@
* of about 33KHz, still well beyond what is needed in this application. * of about 33KHz, still well beyond what is needed in this application.
* this value may change in the future. * this value may change in the future.
* *
* note: attempted to use DMA for this and scanning all channels
* at once, but that didn't work. would not reliably read channels.
* tried many different variations and it just wasn't working.
* because of this I moved to using interrupt and the built-in
* wait mode just in case things went slow.
*
* file creation: 20231015 0121 * file creation: 20231015 0121
*/ */
#include <stdlib.h> #include <stdlib.h>
#include "adc.h"
#include "testo.h" #include "testo.h"
#define SAMPLE_TIME LL_ADC_SAMPLINGTIME_239CYCLES_5 #define SAMPLE_TIME LL_ADC_SAMPLINGTIME_239CYCLES_5
#define ADC_CHANNELS 6 #define ADC_SEQ_RDY 0xff
#define ADC_HISTLEN 16 #define ADC_SEQ_STARTUP 0xfe
#define CONF_CALIBRATE (1 << 0) #define CONF_CALIBRATE (1 << 0)
#define CONF_USEPROBE (1 << 1) #define CONF_USEPROBE (1 << 1)
@ -35,8 +42,10 @@
static uint16_t adc_read[ADC_CHANNELS]; static uint16_t adc_read[ADC_CHANNELS];
static uint16_t adc_hist[ADC_CHANNELS][ADC_HISTLEN]; static uint16_t adc_hist[ADC_CHANNELS][ADC_HISTLEN];
static uint16_t adc_avg[ADC_CHANNELS];
uint16_t adc_avg[ADC_CHANNELS];
static uint8_t adc_seq = 0;
static uint8_t adc_idx = ADC_HISTLEN + 1; static uint8_t adc_idx = ADC_HISTLEN + 1;
static int16_t vref; static int16_t vref;
@ -61,13 +70,15 @@ void adc_init()
ADC_CHSELR_CHSEL11 | ADC_CHSELR_CHSEL12; ADC_CHSELR_CHSEL11 | ADC_CHSELR_CHSEL12;
ADC1->SMPR = SAMPLE_TIME; ADC1->SMPR = SAMPLE_TIME;
// run in non-circular DMA mode, with wait mode enabled // run in one-shot mode (scan all channels) but with wait mode active
ADC1->CFGR1 = ADC_CFGR1_OVRMOD | ADC_CFGR1_WAIT | LL_ADC_REG_DMA_TRANSFER_LIMITED; ADC1->CFGR1 = ADC_CFGR1_OVRMOD | ADC_CFGR1_WAIT;
// by default, DMA selection for all channels is ADC // enable end of conversion interrupt
// so all we need to do is set the peripheral address ADC1->IER = ADC_IER_EOCIE;
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
DMA1_Channel1->CMAR = (uint32_t)adc_read; // and enable the interrupt source in the NVIC
NVIC_EnableIRQ(ADC_COMP_IRQn);
NVIC_SetPriority(ADC_COMP_IRQn, 3);
} }
/* /*
@ -89,16 +100,8 @@ void adc_go()
// stop if conversion in progress (it shouldn't ever be) // stop if conversion in progress (it shouldn't ever be)
adc_stop(); adc_stop();
// configure and enable DMA // restart sequence
DMA1_Channel1->CCR = 0; adc_seq = 0;
DMA1_Channel1->CNDTR = ADC_CHANNELS;
DMA1_Channel1->CCR = LL_DMA_PRIORITY_HIGH |
LL_DMA_MDATAALIGN_HALFWORD |
LL_DMA_PDATAALIGN_HALFWORD |
LL_DMA_MEMORY_INCREMENT |
LL_DMA_MODE_NORMAL |
LL_DMA_DIRECTION_PERIPH_TO_MEMORY |
DMA_CCR_EN;
// enable and start ADC // enable and start ADC
ADC1->ISR = 0x1e; // clear all interrupt flags (per DS; mistranslated) ADC1->ISR = 0x1e; // clear all interrupt flags (per DS; mistranslated)
@ -122,6 +125,7 @@ void adc_switch0()
adc_hist[0][i] = 0; adc_hist[0][i] = 0;
} }
// reset history index
adc_idx = 0; adc_idx = 0;
// force ADC read to get fresh data // force ADC read to get fresh data
@ -133,32 +137,44 @@ uint8_t adc_next()
uint8_t i, j; uint8_t i, j;
int16_t w; int16_t w;
// stop if conversion in progress (it shouldn't ever be) // on first boot, do a calibration
adc_stop();
// start ADC calibration
if (calibrate) {
calibrate = 0;
// disable ADC and start calibration
ADC1->CR &= ~(ADC_CR_ADEN);
ADC1->CR |= ADC_CR_ADCAL;
}
// copy data to history from last read
if (adc_idx > ADC_HISTLEN) { if (adc_idx > ADC_HISTLEN) {
// nothing to copy when first starting // nothing to copy when first starting
adc_idx = 0; adc_idx = 0;
calibrate = 1; calibrate = 1;
} else { }
// copy data to history
// start ADC calibration
if (calibrate) {
adc_stop();
// disable ADC and start calibration
ADC1->CR &= ~(ADC_CR_ADEN);
ADC1->CR |= ADC_CR_ADCAL;
// clear cal flag; reset sequence to allow conversions to start
calibrate = 0;
adc_seq = ADC_SEQ_RDY;
// wait for calibration to complete, if started
while (ADC1->CR & ADC_CR_ADCAL) {};
// do our first round of conversions
adc_go();
return ADC_SEQ_STARTUP;
}
if (adc_seq == ADC_SEQ_RDY) {
// copy read data to history
for (i = 0; i < ADC_CHANNELS; i++) { for (i = 0; i < ADC_CHANNELS; i++) {
adc_hist[i][adc_idx] = adc_read[i]; adc_hist[i][adc_idx] = adc_read[i];
} }
// next one // and start over again
// if we're looping, then average our last reads
adc_idx++; adc_idx++;
// if we're looping, then average our last reads
if (adc_idx >= ADC_HISTLEN) { if (adc_idx >= ADC_HISTLEN) {
adc_idx = 0; adc_idx = 0;
@ -182,10 +198,22 @@ uint8_t adc_next()
} }
} }
// wait for calibration to complete, if started
while (ADC1->CR & ADC_CR_ADCAL) {};
adc_go(); adc_go();
return adc_idx; return adc_idx;
} }
void ADC_COMP_IRQHandler()
{
if (adc_seq != 0xff) {
adc_read[adc_seq++] = ADC1->DR;
// is this the end of conversions?
if (ADC1->ISR & ADC_ISR_EOSEQ) {
adc_seq = ADC_SEQ_RDY;
}
}
// clear all interrupt flags
ADC1->ISR = 0x1e;
}

View File

@ -81,12 +81,6 @@ static inline void systick_init()
// configure timebase with interrupt at 4096Hz // configure timebase with interrupt at 4096Hz
// this assumes we'll always be running at 8MHz // this assumes we'll always be running at 8MHz
SysTick_Config((8000000 / 4096) - 1); SysTick_Config((8000000 / 4096) - 1);
// configure unused DMA channels to not be on ADC
// (otherwise ADC can shows reads of 0... it's a hardware bug)
SYSCFG->CFGR3 = LL_SYSCFG_DMA_MAP_ADC | // ch0
LL_SYSCFG_DMA_MAP_TIM17_UP << 8 | // ch1
LL_SYSCFG_DMA_MAP_TIM17_UP << 16; // ch2
} }
/* /*
@ -124,6 +118,7 @@ int main()
/* /*
* main application interrupt * main application interrupt
*/ */
void SysTick_Handler(void) void SysTick_Handler(void)
{ {
uint16_t cs; uint16_t cs;
@ -144,18 +139,12 @@ void SysTick_Handler(void)
// shifted counter for use in the program // shifted counter for use in the program
cs = ctr >> 2; cs = ctr >> 2;
// adc (512Hz/16 avg = 32Hz update rate) // adc tested to result in about 61 reads/second
if (cs & 1) { if (!adc_next()) {
if (!adc_next()) { // adc has new computed results, so we can do userio, probe, etc
// adc has new computed results, if (uptime || cs) {
// so we can do userio, probe, etc userio_parse();
}
// but the results are only valid }
// after our first cycle
if (uptime || cs) {
userio_parse();
}
}
}
} }
} }

View File

@ -25,19 +25,24 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include "adc.h"
#include "testo.h"
#define MODE_CONT 0 #define MODE_CONT 0
#define MODE_CONT_TARGET (4096 * (1 - (1 / 4))) #define MODE_CONT_TARGET 3072 // (4096 * (1 - (1 / 4)))
#define MODE_FUN 1 #define MODE_FUN 1
#define MODE_FUN_TARGET (4096 * (1 - (1 / 3))) #define MODE_FUN_TARGET 2731 // (4096 * (1 - (1 / 3)))
#define MODE_DIODE 2 #define MODE_DIODE 2
#define MODE_DIODE_TARGET (4096 * (1 - (1 / 2))) #define MODE_DIODE_TARGET 2048 // (4096 * 1 - ((1 / 2)))
#define MODE_HYSTERESIS 60 // 3x worst case expected (1% tol) #define MODE_SET_LIMIT 60 // 3x worst case expected (1% tol)
#define MODE_CHANGE_TICKS 4 // how many samples before switching modes
#define MODE_ANALOG_MIN 20 #define MODE_ANALOG_MIN 20
@ -46,7 +51,7 @@
uint8_t mode; uint8_t mode;
uint8_t mode_next; uint8_t mode_next;
uint8_t mode_count; uint8_t mode_count;
static const uint16_t mode_targets[] = { static const int16_t mode_targets[] = {
MODE_CONT_TARGET, MODE_FUN_TARGET, MODE_DIODE_TARGET MODE_CONT_TARGET, MODE_FUN_TARGET, MODE_DIODE_TARGET
}; };
@ -64,5 +69,56 @@ void userio_update()
void userio_parse() void userio_parse()
{ {
uint8_t i;
volatile int16_t m, w;
// button
// debounce is handled by the fact that we've had to have averaged
// about 16ms of measured hold time below the zero threshold.
m = adc_avg[ADC_SET_MODE];
if (m < MODE_ANALOG_MIN) {
// button is pushed
btn = 1;
if (btn_held != 0xffff) btn_held++;
} else if (btn == 1) {
// button is released
btn = 0;
}
// mode
if (!btn) {
for (i = 0; i < 4; i++) {
// clear mode_count if we aren't actively changing modes
if (i == 3) {
mode_count = 0;
break;
}
// normalize mode selection
w = abs(m - mode_targets[i]);
// determine if we mode switch
if (w < MODE_SET_LIMIT) {
// currently in this mode? if so, ignore it
if (mode == i) continue;
// is the next mode set to this item?
if (mode_next == i) {
// increment the verification count
mode_count++;
if (mode_count >= MODE_CHANGE_TICKS) {
// we've got enough data to do a change of mode
mode = i;
mode_count = 0;
}
} else {
// set the next mode to the newly selected mode
mode_next = i;
mode_count = 0;
}
break;
}
}
}
} }