WIP Code
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:
parent
ecc478661f
commit
13700b96ca
|
@ -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();
|
||||
|
||||
|
||||
|
|
106
src/adc.c
106
src/adc.c
|
@ -13,20 +13,27 @@
|
|||
* of about 33KHz, still well beyond what is needed in this application.
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "adc.h"
|
||||
#include "testo.h"
|
||||
|
||||
|
||||
|
||||
#define SAMPLE_TIME LL_ADC_SAMPLINGTIME_239CYCLES_5
|
||||
|
||||
#define ADC_CHANNELS 6
|
||||
#define ADC_HISTLEN 16
|
||||
#define ADC_SEQ_RDY 0xff
|
||||
#define ADC_SEQ_STARTUP 0xfe
|
||||
|
||||
#define CONF_CALIBRATE (1 << 0)
|
||||
#define CONF_USEPROBE (1 << 1)
|
||||
|
@ -35,8 +42,10 @@
|
|||
|
||||
static uint16_t adc_read[ADC_CHANNELS];
|
||||
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 int16_t vref;
|
||||
|
@ -61,13 +70,15 @@ void adc_init()
|
|||
ADC_CHSELR_CHSEL11 | ADC_CHSELR_CHSEL12;
|
||||
ADC1->SMPR = SAMPLE_TIME;
|
||||
|
||||
// run in non-circular DMA mode, with wait mode enabled
|
||||
ADC1->CFGR1 = ADC_CFGR1_OVRMOD | ADC_CFGR1_WAIT | LL_ADC_REG_DMA_TRANSFER_LIMITED;
|
||||
// run in one-shot mode (scan all channels) but with wait mode active
|
||||
ADC1->CFGR1 = ADC_CFGR1_OVRMOD | ADC_CFGR1_WAIT;
|
||||
|
||||
// by default, DMA selection for all channels is ADC
|
||||
// so all we need to do is set the peripheral address
|
||||
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
|
||||
DMA1_Channel1->CMAR = (uint32_t)adc_read;
|
||||
// enable end of conversion interrupt
|
||||
ADC1->IER = ADC_IER_EOCIE;
|
||||
|
||||
// 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)
|
||||
adc_stop();
|
||||
|
||||
// configure and enable DMA
|
||||
DMA1_Channel1->CCR = 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;
|
||||
// restart sequence
|
||||
adc_seq = 0;
|
||||
|
||||
// enable and start ADC
|
||||
ADC1->ISR = 0x1e; // clear all interrupt flags (per DS; mistranslated)
|
||||
|
@ -122,6 +125,7 @@ void adc_switch0()
|
|||
adc_hist[0][i] = 0;
|
||||
}
|
||||
|
||||
// reset history index
|
||||
adc_idx = 0;
|
||||
|
||||
// force ADC read to get fresh data
|
||||
|
@ -133,32 +137,44 @@ uint8_t adc_next()
|
|||
uint8_t i, j;
|
||||
int16_t w;
|
||||
|
||||
// stop if conversion in progress (it shouldn't ever be)
|
||||
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
|
||||
// on first boot, do a calibration
|
||||
if (adc_idx > ADC_HISTLEN) {
|
||||
// nothing to copy when first starting
|
||||
adc_idx = 0;
|
||||
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++) {
|
||||
adc_hist[i][adc_idx] = adc_read[i];
|
||||
}
|
||||
|
||||
// next one
|
||||
// if we're looping, then average our last reads
|
||||
// and start over again
|
||||
adc_idx++;
|
||||
|
||||
// if we're looping, then average our last reads
|
||||
if (adc_idx >= ADC_HISTLEN) {
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
27
src/main.c
27
src/main.c
|
@ -81,12 +81,6 @@ static inline void systick_init()
|
|||
// configure timebase with interrupt at 4096Hz
|
||||
// this assumes we'll always be running at 8MHz
|
||||
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
|
||||
*/
|
||||
|
||||
void SysTick_Handler(void)
|
||||
{
|
||||
uint16_t cs;
|
||||
|
@ -144,18 +139,12 @@ void SysTick_Handler(void)
|
|||
// shifted counter for use in the program
|
||||
cs = ctr >> 2;
|
||||
|
||||
// adc (512Hz/16 avg = 32Hz update rate)
|
||||
if (cs & 1) {
|
||||
if (!adc_next()) {
|
||||
// adc has new computed results,
|
||||
// so we can do userio, probe, etc
|
||||
|
||||
// but the results are only valid
|
||||
// after our first cycle
|
||||
if (uptime || cs) {
|
||||
userio_parse();
|
||||
}
|
||||
}
|
||||
}
|
||||
// adc tested to result in about 61 reads/second
|
||||
if (!adc_next()) {
|
||||
// adc has new computed results, so we can do userio, probe, etc
|
||||
if (uptime || cs) {
|
||||
userio_parse();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
66
src/userio.c
66
src/userio.c
|
@ -25,19 +25,24 @@
|
|||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "adc.h"
|
||||
#include "testo.h"
|
||||
|
||||
|
||||
|
||||
#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_TARGET (4096 * (1 - (1 / 3)))
|
||||
#define MODE_FUN_TARGET 2731 // (4096 * (1 - (1 / 3)))
|
||||
|
||||
#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
|
||||
|
||||
|
@ -46,7 +51,7 @@
|
|||
uint8_t mode;
|
||||
uint8_t mode_next;
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -64,5 +69,56 @@ void userio_update()
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue