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();
|
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.
|
* 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;
|
||||||
|
}
|
17
src/main.c
17
src/main.c
|
@ -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,
|
// adc has new computed results, so we can do userio, probe, etc
|
||||||
// so we can do userio, probe, etc
|
|
||||||
|
|
||||||
// but the results are only valid
|
|
||||||
// after our first cycle
|
|
||||||
if (uptime || cs) {
|
if (uptime || cs) {
|
||||||
userio_parse();
|
userio_parse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
66
src/userio.c
66
src/userio.c
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue